Introduction
--clean
argument (java -jar bascule-0.3.4.jar generate --clean
) to bypass the caching mechanism. I will slowly work on getting Bascule back up to full functionality, but I am not looking to extend or add new features.
Bascule is a static website generator, inspired by tools such as jekyll, jbake and Griffin. Content is written as a series of markdown files, which are scanned, combined with HTML templates written using handlebars, and output as a series of simple HTML files. These files are uploaded to a web server. There are no databases, no server-side processing, no complex configuration, just plain, SEO-friendly HTML.
The project is defined using a simple yaml configuration file, and each web page or post Markdown file should be prefaced with a small block of yaml text (though bascule will try to make sense of a file which is missing this block). The yaml configuration file contains a few key values (such as sitename
, author
and theme
) but you can mix in your own values to customise your project.
Individual pages or posts can be tagged with multiple tags, and bascule will generate listing pages for each tag used - such as all posts tagged 'bascule'.
Bascule builds a cache of your project, so only those markdown files which have been changed since the last generation are regenerated, saving generation time. It is always possible to trigger a full regeneration by adding the clean (-c) flag.
Bascule is also extensible - it is possible to configure the processing pipeline to, for instance, skip the tag listing pages mentioned above, or to write new generators, such as a sitemap.xml generator or a PDF generator.
User Guide
Bascule is generally used to create blog-type websites, such as this one, but it can be used to create any static website. At its core, individual web pages are modelled as Post
objects, but bascule makes little distinction between posts or pages. The structure of your website is determined purely by the structure of the markdown source files and folders. Different types of post can be rendered using different template types.
For instance, all posts may have a date, an author, some meta-data tags, and contain a link to the previous and next post. A page might only have a date, no tags, and no next/previous links. It's up to you. This guide uses the terms post and page pretty much interchangeably. There are some downsides to this approach which I'm still working on.
Project configuration
A project is defined with a yaml configuration file. Bascule will search for a yaml file with the same name as its parent folder, but you can override this with the --project
command line option, specifying the name of the yaml file to use.
Details to follow, sample below:
siteName: Liam John Davison
dateFormat: "dd/MM/yyyy"
dateTimeFormat: HH:mm:ss dd/MM/yyyy
author: Liam Davison
theme: liamjd-theme
tagskey: tags
postsPerPage: 10
host: http://www.liamjd.org/
directories:
source: sources
output: site
assets: assets
templates: liamjd-theme/templates
custom:
posts: posts
pdf: pdf
pdfTemplates: liamjd-theme/templates/fop
generators: [IndexPageGenerator, PostNavigationGenerator, TaxonomyNavigationGenerator, org.liamjd.bascule.extra.generators.pdf.SitePDFGenerator, org.liamjd.bascule.extra.generators.sitemap.SitemapXMLGenerator, org.liamjd.bascule.extra.generators.lunr.LunrJSIndexGenerator]
$debug: false
Markdown sources
Pages and posts are written in the markdown formatting language as implemented by the flexmark-common library. All pages and posts should have a short frontispiece written using yaml, defining some of the key metadata for the page. Bascule will attempt to render any page which is missing this frontispiece, but for the most reliable behaviour it should always be included. The frontispiece must be wrapped in three dashes (---). Here is an example frontispiece, taken from the markdown for this very page:
---
title: Bascule Static Site Generator
author: Liam Davison
layout: page
date: 23/12/2018
slug: bascule
---
The yaml defines a number of key metadata elements which bascule expects: the title
, the author
, the layout
handlebars template uses to render the page, the date
and a slug
, which is used in generating the URL for the page. Most posts may also include a tags
element, a bracked comma-seperated list. Bascule is very flexible, and you can add your own custom elements here too - Bascule simply adds any elements it doesn't recognise to the page model, so they are available for use in your handlebars templates.
Tag | Required | Multiple values? | Default value if missing |
---|---|---|---|
title | yes | no | n/a |
author | no | no | from project yaml configuration, or empty |
layout | yes | no | n/a |
date | no | no | from the markdown file created date |
slug | no | no | from the markdown file filename |
tags | no | yes | empty |
(custom elements) | no | yes | empty |
Here's another example yaml frontispiece, from the pdf generation in bascule post:
---
title: PDF Generation from Bascule
author: Liam Davison
layout: post
date: 08/12/2018
tags: [software development, kotlin, bascule]
slug: pdf-generation-from-bascule
---
Bascule uses the flexmark library to parse and render HTML from Markdown. It is configured with a few extensions enabled. I'm considering options to make this more configurable or extendable, but I want at the very least a sensible set of defaults. The default extensions are:
- AttributesExtension
- YamlFrontMatterExtension (a core requirement for bascule)
- TablesExtension (to allow simple templates like the yaml table above)
- HydeExtension, a custom extension allowing embedding of HTML files into the Markdown output; I've used it to create image galleries
In addition, the following parameters are set:
HtmlRenderer.GENERATE_HEADER_ID,true
to give headings unique IDsHtmlRenderer.RENDER_HEADER_ID,true
to give headings unique IDsHtmlRenderer.INDENT_SIZE,2
to indent the HTML output for readability
Handlebars templates
HTML layouts for posts and pages are prepared using the handlebars templating language, as implemented by the handlebars.java library. For each page or post, bascule generates a page model which contains all the meta-data elements from the project configuration file, the yaml frontispiece from each markdown source file, and a few calculated values. It's quite a comprehensive list. Custom generators can add to this model, as some of the default provided extensions like the TaxonomyNavigationGenerator
do.
model object | value |
---|---|
sourceFileName | from the source markdown file name |
url | calculated URL, based on the folder structure and slug |
title | page or post title |
author | page or post author, or project author |
layout | handlebars layout template name, such as post or list |
date | date of page or post |
tags | list of tags |
slug | slug which forms the basis of the URL |
attributes | any custom elements added to the yaml frontispiece |
newer | link to a newer post than this one |
older | link to a older post than this one |
content | The main body of the page or post, as formatted HTML |
rawContent | The markdown source for the entire page |
For listing pages, i.e. pages which contain links to one or more links to other pages, the following additional model objects are defined:
model object | value |
---|---|
currentPage | number of the current page in the list |
totalPages | total number of pages/posts which make up this list |
isFirst | true if this is the first page in the list, null otherwise |
isLast | true if this is the last page in the list, null otherwise |
previousPage | number of the previous page in the list |
nextPage | number of the next page in the list |
nextIsLast | true if the next page is the last |
prevIsFirst | true if the previous page is the first |
totalPosts | total number of pages across the entire list |
posts | all of the pages/posts (can get quite big) |
pagination | a special object representing the 1,2,...9,10 pagination markers |
tag | the current tag generated for this list, if the list comes from a tagging taxonomy |
title | A page title for this list |
See the pagination examples for more details.
Pagination
Pagination is quite a tricky subject for Bascule to handle. Each blog post contains a link to its successor and predecessor posts, and these are easily used in the Handlebars templates like this:
<a href="/{{older.url}}" title="Older post: {{older.title}}">{{older.title}}</a><br/>
But what we really need is a way of generating a listing of posts and pages.
Theming
Extending Bascule
It is possible to extend Bascule by adding additional Generators. In the project root, create a folder called plugins
. Any .jar
file in this folder will be scanned for classes which implement the GeneratorPipeline
interface, which declares a single function, process(project, renderer, fileHandler, clean)
.
Then, tell your project to use the additional generators by updating the generators
block in the project.yaml
with the name of your generator plugin class. For instance, for this website, I have written a generator which creates a lunr.js search index document. This drives the search functionality on the website.
Another use case could be to generate a PDF version of each web page, and I have a sample implementation in my bascule-extras repository.
Links
Sources on Github:
Core libraries used:
Bascule is written in Kotlin.