Commandbox-jasper SSG generator
A static site generator using CommandBox cfml commandboxJuly 1, 2022 / Robert Zehnder
Photo by Patrick Fore on Unsplash
I recently had the chance to get familiar with CommandBox while I was updating my blog. My last post outlined how I used CommandBox to allow me to manage my blog from the command line. I enjoyed that experience so I thought I would work with CommandBox once again.
Last year I wrote a static site generator called Jasper. So far I have built Jasper using ColdBox as well as FW1. I am going to take things one step further and I will implement Jasper as a CommandBox module. Apparently I really enjoy writing SSGs.
I decided to split the project in to two main parts: the jasper-cli blog scaffold and the jasper
command that handles generating the static site.
The jasper blog scaffold contains a very basic blog.
jasper
. It is expected to be in the root directory and must exist.dist/
folder at build time (wip)The "guts" of the skeleton app are in the /src
folder and contains all the ColdFusion files required to render a blog page.
The markdown for posts are stored in src/posts/
and the current base template contains two posts.
All data required to render the page is expected to be in the prc
variable structure, the same as a ColdBox applicatiop. As an added benefit, the pages were already setup that way.
The blog scaffold in place, next I need to actually be able to produce an HTML site from the src/
contents. The commandbox-jasper module makes this possible and it is very easy to install.
box install commandbox-jasper
Once installed, from the root directory of the blog scaffold the static HTML can be created with box jasper build
. This will build the all the individual pages for the site: the index, posts, as and finally posts by tag. It must be run from blog root directory.
Brad Wood gave me the tip that including
a CFML in a savecontent
block is the easiest way to render a CFML page. Once that was figured out, everything else just falls in place.
First, setup the faux prc
scope and then include the page, save the generated html, and finally save teh file to the dist/
directory.
Currently the build method handles writing out the three main page types: index, post, and tag.
component extends="commandbox.system.BaseCommand" {
property name="JasperService" inject="JasperService@commandbox-jasper";
function run() {
command( "jasper cache build" ).run();
var conf = deserializeJSON( fileRead( fileSystemUtil.resolvePath( "jasperconfig.json" ), "utf-8" ) );
var posts = deserializeJSON( fileRead( fileSystemUtil.resolvePath( "post-cache.json" ), "utf-8" ) );
var tags = JasperService.getTags( posts );
var html = "";
var prc = {
"meta" : {},
"posts" : posts,
"html" : "",
"tagCloud" : JasperService.getTags( posts )
};
prc.site.append( conf.meta );
// get the home page
savecontent variable="html" {
include fileSystemUtil.resolvePath( "src/index.cfm" );
}
fileWrite( fileSystemUtil.resolvePath( "dist/index.html" ), html );
// Build all posts
var files = JasperService.list( path = fileSystemUtil.resolvePath( "src/posts" ) );
files.each( ( file ) => {
print.line( "Generating... dist/post/" & file.name.listFirst( "." ) & ".html" );
var html = "";
var prc = {
"meta" : {},
"post" : {},
"html" : "",
"tagCloud" : tags
};
prc.site.append( conf.meta );
prc.post.append(
JasperService.getPostData( fname = fileSystemUtil.resolvePath( "src/posts/" & file.name ) )
);
prc.site.title &= " - " & prc.post.title;
savecontent variable="html" {
include fileSystemUtil.resolvePath( "src/post.cfm" );
}
fileWrite( fileSystemUtil.resolvePath( "dist/post/" & prc.post.slug & ".html" ), html );
} );
// build tags
tags.each( ( tag ) => {
print.line( "Generating... dist/tag/" & lCase( tag ).replace( " ", "-", "all" ) & ".html" );
var html = "";
var prc = {
"meta" : {},
"tag" : lCase( tag ),
"posts" : [],
"html" : "",
"tagCloud" : tags
};
prc.site.append( conf.meta );
prc.posts = posts.filter( ( post ) => {
return post.tags.findNoCase( prc.tag );
} );
prc.site.title &= " - " & lCase( prc.tag );
savecontent variable="html" {
include fileSystemUtil.resolvePath( "src/tags.cfm" );
}
fileWrite( fileSystemUtil.resolvePath( "dist/tag/" & tag.replace( " ", "-", "all" ) & ".html" ), html );
} )
}
}
Once the static site is built it can be served up using netlify
or with your favorite http server.
There is still quite a bit to be ironed out before it is usable. OpenGraph/Twitter meta data is not currently working on posts which should be easy to fix. Error handling is non-existant, but the code functions well enough it does not error out most of the time. Currently the pages are built manually instead of dynamically from jasperconfig.json
. All that will come.
This was a fun project that let me work with a lot of my favorite stuff: ColdFusion, CommandBox, blogs, and markdown. I look forward to playing around with this a lot more.
This also makes it very easy to get started with your own CF static site.
git clone https://github.com/robertz/jasper-cli.git
cd jasper-cli
box install commandbox-jasper
box jasper build
cd dist
netlify dev
Done.