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
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
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
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
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
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
Please give design feedback, file bugs, and fix issues!
Thanks!
I've been AJ
Questions?
Access this presentation again