StraticJS

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

Beta

Let's talk about blogging

Traditional platforms

WYSIWYG

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

Jekyll

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

Layouts

Includes

Collections

Posts

Plugins

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

Gulp

Build system

Streams of in-memory file objects

Transforms connected by pipes

Example

/* var pug = require('gulp-pug'), etc. */
gulp.task('html', function() {
    gulp.src(['src/*.pug'])
        .pipe(pug())
        .pipe(rename({ extname: '.html' }))
        .pipe(gulp.dest('dist'));
});

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

Foundations

Gulp

Pug

Markdown

Stylus

Yeoman

All broadly applicable

Example: post pages

---
title: "Example post"
time:
  epoch: 1434272547
  utcoffset: "UTC-8"
author: "Alex Jordan"
categories:
  - 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(frontMatter())
               .pipe(remark({quiet: true}).use(remarkHtml))
               .pipe(dateInPath()) /* Stratic module */
               .pipe(addsrc('src/blog/post.pug'))
               .pipe(attachToTemplate('post.pug'))
               .pipe(pug({pretty: true, basedir: __dirname}))
               .pipe(rename({ extname: '.html' }))
               .pipe(gulp.dest('dist/blog'));
});

Composability

dateInPath() is Stratic-specific

The rest are generic Gulp modules

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

Consequences

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

stratic-parse-header

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(frontMatter())
	           .pipe(remark({quiet: true}).use(remarkHtml))
	           .pipe(dateInPath())  /* Stratic module */
	           .pipe(addsrc('src/blog/index.pug'))
	           .pipe(postsToIndex('index.pug'))  /* Stratic module */
	           .pipe(paginateIndexes())  /* Stratic module */
	           .pipe(pug({pretty: true, basedir: __dirname}))
	           .pipe(rename({ extname: '.html' }))
	           .pipe(gulp.dest('dist/blog'));
});	

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

Done

Hate Pug?

Rewrite templates

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

Replace .pipe(pug())

Done

What's coming down the pipe?

Vinyl file objects

Conventional property names on file objects

file.data.propname - file.data is conventional for "extra" data across the Gulp ecosystem

Property names

file.data.title

file.data.author

file.data.time.epoch - Unix time epoch

file.data.time.utcoffset - timezone information

file.data.categories

Index-related property names

file.data.indexType - one of main, year, month, or category

file.data.includedYears - populated on main indexes

file.data.year - populated on year indexes

file.data.includedMonths - populated on year indexes

file.data.month - populated on month indexes

file.data.category - populated on category indexes

Templates

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(frontMatter())
	           .pipe(remark({quiet: true}).use(remarkHtml))
	           .pipe(dateInPath())  /* Stratic module */
	           .pipe(addsrc('src/blog/index.pug'))
	           .pipe(postsToIndex('index.pug'))  /* Stratic module */
	           .pipe(indexesToRss({
		           title: 'strugee.net blog'
	           }, 'https://strugee.net/blog/'))  /* Stratic module */
	           .pipe(rename({ extname: '.rss' }))
	           .pipe(gulp.dest('dist/blog'));
});

Current issues

(It's a beta, remember)

Performance

Duplication in gulpfile.js

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

Project-wide docs

Better debugging/error handling

Engaging

#stratic on Freenode

straticjs/RFCs

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

Thanks!

I've been AJ

Questions?

Access this presentation again

https://strugee.net/presentation-straticjs/

QR code to presentation URL

Or, check out the source.