An open API service indexing awesome lists of open source software.

https://github.com/enthal/temple

Static file generation from simple templates, YAML'd content, and markdown — where templates walk a content object graph.
https://github.com/enthal/temple

Last synced: 3 months ago
JSON representation

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

Awesome Lists containing this project

README

          

# temple

**Static file generation from simple
[templates](https://lodash.com/docs/4.17.4#template),
[YAML](http://www.yaml.org/spec/1.2/spec.html)'d content, and [markdown](https://www.google.com/search?q=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`](https://lodash.com/docs/4.17.4#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](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.,
```yaml
- $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 [`|-`](http://www.yaml.org/spec/1.2/spec.html#id2794534), denoting in YAML a multiline string (whitespace clipped), which here happens to be markdown.

The page template might look something like this:
```html


<%- title %>


<%= $.recurse(body) %>

```

Here, the `title` value is interpolated as the content of a `` 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:
```yaml
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](https://lodash.com/docs/4.17.4) as variable `_`. This is "batteries included" JavaScript.

Unlike JSON, YAML supports [named references](http://www.yaml.org/spec/1.2/spec.html#id2785586) (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!

# 🍺