AJ Jordan

About moi

Free software nerd

Node.js aficionado

Unix philosophy fan

Author of StraticJS

What is StraticJS?

One-time generation of static HTML, CSS & JS blogs

Written in Node.js

Based on Gulp build system

Focused on directly exposing (clean) internals

Easier to understand, cleaner mental model, more flexible


Let's talk about blogging

Traditional platforms


Incoming request invokes code to render the page

Contents typically retrieved from a database

What happens when you refresh?

Same thing

Code is run even if content didn't change

Enter static site generators

Push page computation from runtime to build-time


Written in Ruby by Tom Preston-Werner

Brought static sites into vogue

Works through well-defined directories

_sass, _posts, _layouts, etc.

In other words: magic

Have to learn what all the directories "mean"

Build process isn't clear

How do these actually get turned into a static site?

Too many concepts






Understandable generally, but Jekyll-specific implementation

Unix philosophy

Do one thing and do it well

Write programs to work together

Write programs to handle text streams, because that is a universal interface

(- Doug McIlroy)

What one thing does Jekyll do?

Something that maps to Unix


Build system

Streams of in-memory file objects

Transforms connected by pipes


/* var pug = require('gulp-pug'), etc. */
gulp.task('html', function() {
        .pipe(rename({ extname: '.html' }))

So what is StraticJS?

A set of hypermodular Gulp plugins...

...and a Yeoman generator.

That's it.

How do I use it?

$ npm install -g yo generator-stratic gulp
$ mkdir stratic-project && cd stratic-project
$ yo stratic
$ gulp serve







All broadly applicable

Example: post pages

title: "Example post"
  epoch: 1434272547
  utcoffset: "UTC-8"
author: "Alex Jordan"
  - development

# A Markdown Header

Here's some text in this blog post.

Example: post pages

gulp.task('posts', function() {
    return gulp.src('src/blog/*.md')
               .pipe(remark({quiet: true}).use(remarkHtml))
               .pipe(dateInPath()) /* Stratic module */
               .pipe(pug({pretty: true, basedir: __dirname}))
               .pipe(rename({ extname: '.html' }))


dateInPath() is Stratic-specific

The rest are generic Gulp modules

Some (attachToTemplate()) were written for Stratic but are broadly useful


No plugin ecosystem

No need to reinvent the world

Each piece works on its own

You can literally read the build process

Hand-maintained sites can be incrementally converted

Case study


Original metadata system

Based on parsing natural-looking Markdown text with values split by "

Terrible idea

So pain

stratic-parse-header example

# Post information

This post was published as "Post title" at
"1419680682 UTC-8" by "AJ Jordan", and was
categorized as "personal".

# Post text

The post text would go here.

stratic-parse-header deprecation

Change Markdown to use YAML frontmatter

Replace .pipe(parse()) with .pipe(frontmatter())

Such easy

I touched the parts that were affected

Nothing else.

Complex example: generating indexes

gulp.task('post-index', function() {
	return gulp.src('src/blog/*.md')
	           .pipe(remark({quiet: true}).use(remarkHtml))
	           .pipe(dateInPath())  /* Stratic module */
	           .pipe(postsToIndex('index.pug'))  /* Stratic module */
	           .pipe(paginateIndexes())  /* Stratic module */
	           .pipe(pug({pretty: true, basedir: __dirname}))
	           .pipe(rename({ extname: '.html' }))

More composability

Bare minimum of Stratic-specific modules (still)

dateInPath(), postsToIndex(), and paginateIndexes()

Each component doesn't care about the other components

It Just Works™ with loose property name conventions on Vinyl file objects

This is taken really seriously

Hate Markdown?

Replace YAML frontmatter parser

Replace Remark


Hate Pug?

Rewrite templates

Rename template filename (e.g. addsrc() argument)

Replace .pipe(pug())


What's coming down the pipe?

Vinyl file objects

Conventional property names on file objects - is conventional for "extra" data across the Gulp ecosystem

Property names - Unix time epoch - timezone information

Index-related property names - one of main, year, month, or category - populated on main indexes - populated on year indexes - populated on year indexes - populated on month indexes - populated on category indexes


Post templates are responsible for rendering out HTML

Directly controlled by you

Example post template

Example index template

Example post render mixin

Final example

gulp.task('rss', function() {
	return gulp.src('src/blog/*.md')
	           .pipe(remark({quiet: true}).use(remarkHtml))
	           .pipe(dateInPath())  /* Stratic module */
	           .pipe(postsToIndex('index.pug'))  /* Stratic module */
		           title: ' blog'
	           }, ''))  /* Stratic module */
	           .pipe(rename({ extname: '.rss' }))

Current issues

(It's a beta, remember)


Duplication in gulpfile.js

Small missing features (drafts, default categories, etc.)

Project-wide docs

Better debugging/error handling


#stratic on Freenode


Please give design feedback, file bugs, and fix issues!


I've been AJ


Access this presentation again

QR code to presentation URL

Or, check out the source.