Static file generation from simple templates, YAML'd content, and markdown — where templates walk a content object graph.
This is a work in progress. I add features only as I need them. Breaking changes may be introduced at any time.
You probably shouldn't use this yet, but I do.
Temple processes content and template files into output files (named in the content).
- Each template file:
- is named as
X.html
, whereX
is the template name (the extension is actually arbitrary); - may contain JavaScript, interpolated with literal text, like HTML;
- is interpreted per the lodash
_.template
function;
- is named as
- The YAML content file:
- contains the entire site content;
- is at the top level a YAML sequence. See below for details;
const temple = require('temple-cms').gulp;
gulp.task('template', function () {
gulp.src('./templates/*.html')
.pipe(temple('./content.yml'))
.pipe(gulp.dest('out'))
});
temple -h
temple -c content.yml -t templates/ -s static/ -o out/
As invoked above:
templates/
contains a set of files named asX.html
, whereX
is the template name.content.yaml
contains the entire site content.out/
is the root to which files are generated (see the$path
content variable, below).static/
contains files to be copied toout/
(preserving directory nesting) before file generation from templates.
See the example content and templates.
The YAML content file contains a series (array) of mappings (objects). Each such object:
- names a template (with key
$t
) - may name an output relative file
$path
- may include other key/value pairs to be passed to the template (as JavaScript variables).
E.g.,
- $t: page
$path: index.html
title: hello world
body: |-
# Here's content, as markdown.
- The initial
-
and subsequent indentation make this, in YAML terms, a single mapping in a series (object in an array, in JS terms). - The
$t
key here elects templatepage
(intemplates/page.html
). - The (optional)
$path
sends the output of the templated content to a file, in this case toout/index.html
. - The
body
value here is prefixed with|-
, denoting in YAML a multiline string (whitespace clipped), which here happens to be markdown.
The page template might look something like this:
<html>
<head>
<title><%- title %></title>
</head>
<body>
<%= $.recurse(body) %>
</body>
</html>
Here, the title
value is interpolated as the content of a <title>
element. The <%-
interpolator HTML-escapes the value, whereas <%=
inserts it verbatim. That's appropriate for the body, which we include not directly, but via the magic function $.recurse
. In this case, the body
value in this content object is a string, so $.recurse
treats it as markdown.
That's where things get interesting:
body:
- $t: faq
question: What's the answer to the Ultimate Question of Life, The Universe, and Everything?
answer: 42
- $t: faq
question: What _never_ gets old?
answer: Recursion.
In this new case, we see a body
as a sequence of two mappings/objects, each of $t: faq
. In this case, $.recurse
(from page
, above) processes both objects, delegating to each elected template (faq
). The keys in each object are meaningful only in the context of the template. The template makes the rules. Here, it's faq.html
that knows what question
and answer
mean. Thanks to $.recurse
, content may nest indefinitely.
Note that the templates also recieve lodash as variable _
. This is "batteries included" JavaScript.
Unlike JSON, YAML supports named references (called anchors and aliases), which the YAML parser uses to effect an object graph, rather than a strict hierarchy. This allows for content re-use.
Anything in the directory named by the (optional) --static
CLI flag gets copied to the --out
directory before templating happens. That means:
- CSS and images and such should live there and may be referenced by template output
- These files may be generated by a larger build pipeline in which
temple
is invoked, about whichtemple
has no opinion. - Alternately these files may all be committed to the source repo.
- Files output from templating (named by
$file
) may clobber these static files. So it may be a good idea to put (some of?) your static files in a subdirectory.
There's already Jekyll/Liquid and so many others. Jekyll has many features. So why bother?
I wanted control, and to see how simple this could be. I wanted a focus on content as an object graph, and templates as components that work together to walk that graph. I wanted markdown to fit into that orchestration—Jekyll focuses on markdown first. I wanted the shoulders of giants: YAML, _.template
(it's just JavaScript), markdown. I wanted to work toward an elegant orchestration of these three things. I wanted a clean separation of content (in YAML), structure (as HTML), and style (static CSS). I wanted unopinionated static site generation, not complicated web component technologies. And I wanted to store content and templates and style in git
and GitHub, which we know by heart and where we get branching/merging and Forks and Pull Requests for free, not a complicated CMS that mashes together too many concerns but can't handle change control. I win!
- Support multiple content files
- Probably via some
import
mechanism, probably via a YAML custom type/handler. - Maybe by just reading or catenating all the files in a given directory.
- Note the namespacing issue of yaml aliases to anchors in a different file.
- Probably via some
- Better error reporting with path into content graph for context.
$.inherit
to find values in enclosing content scopes.- Consider replacing content keyed values as bare variables in template JavaScript with some single surrounding variable (maybe invert the meaning of
$
), to control the name space and allow for optional values.- Presently a template throws an exception where it uses a variable for which there is no key in the content, and I can't change it: that's how JavaScript works.
- Consider replacing automatic markdownification of strings in
$.recurse
with something less magic, probably via a YAML custom type/handler. - Integrate with gulp?
- SaaS against GitHub webhooks?
- Publish as
npm
module!