enthal / temple

Static file generation from simple templates, YAML'd content, and markdown — where templates walk a content object graph.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

temple

Static file generation from simple templates, YAML'd content, and markdown — where templates walk a content object graph.

Alpha software

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.

Usage

Temple processes content and template files into output files (named in the content).

  • Each template file:
    • is named as X.html, where X 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;
  • The YAML content file:
    • contains the entire site content;
    • is at the top level a YAML sequence. See below for details;

via Gulp

const temple = require('temple-cms').gulp;

gulp.task('template', function () {
  gulp.src('./templates/*.html')
    .pipe(temple('./content.yml'))
    .pipe(gulp.dest('out'))
});

via Command Line

temple -h
temple -c content.yml -t templates/ -s static/ -o out/

As invoked above:

  • templates/ contains a set of files named as X.html, where X 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 to out/ (preserving directory nesting) before file generation from templates.

See the example content and templates.

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 template page (in templates/page.html).
  • The (optional) $path sends the output of the templated content to a file, in this case to out/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.

Assets, style, and --static

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 which temple 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.

Why?

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!

TODO

  • 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.
  • 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!

🍺

About

Static file generation from simple templates, YAML'd content, and markdown — where templates walk a content object graph.


Languages

Language:JavaScript 91.0%Language:HTML 9.0%