Grender is a static site generator. It combines source files with metadata to produce a website.
If you have a working Go installation, you can easily get an up-to-date grender binary.
go get github.com/peterbourgon/grender
Grender is designed to power a large class of simple websites, including but not limited to blogs. It was created as a response to the perceived complexity of Jekyll, both in terms of configuration and implementation.
Grender renders source files from the source directory (specified by the
commandline flag -source
, default src
) into the target directory
(-target
, default tgt
). Grender can render a single source file using
metadata provided in the file itself. Metadata is always valid JSON, and can be
put at the top of certain source files if it's separated by a line containing
only ---
.
See the example.
Metadata doesn't need to be in the source file directly. A valid .json file provides metadata to every source file in the same directory. In case of collision, grender prefers "closer" metadata; a file's specific metadata always overrides a directory's metadata, for example. .json files are read in lexigraphical order, before any source files are read.
See the example. Note that the .json file isn't copied to the target dir.
.json files provide metadata not only to every source file in the same directory, but to all files in all subdirectories, too. In this way you can "layer" metadata. The source file always receives a single, well-composed metadata object for rendering.
See the example. The concept and application of composable metadata is grender's Secret Sauce™.
You can refer to the same content from multiple source files by using imports. Save the shared content in a file; use the .source extension so grender knows not to copy it to the target directory. Then use one of the import directives to import it:
{{ importhtml "../relative/path.html.source" }}
for HTML snippets{{ importcss "../relative/path.css.source" }}
for CSS snippets{{ importjs "../relative/path.js.source" }}
for JS snippets
See the example.
Sometimes it's nice to specify a page merely as its content, and leave it to the rendering engine to put that into a nice template. .md (Markdown) files have this behavior by default. Grender expects to find a "template" key in their metadata, and uses that filename as the template into which the rendered Markdown is placed. Rendered content is available under the "content" key.
Template files should have the extension .template, so that grender knows not to copy them to the target directory.
See the example.
Bonus: if a Markdown filename matches the format YYYY-MM-DD-some-text.md, grender will treat that file as a "blog entry", and perform special behavior. Given 2013-03-04-foo-bar-baz.md:
- default metadata key title, value "Foo bar baz"
- default metadata key date, value "2013 03 04"
- default target file is 2013/03/04/foo-bar-baz.html (relative to source)
- http-equiv refresh redirects to the target URL are written for all of the following relative URLs: 2013/03/04/index.html, 2013/03/4/index.html, 2013/3/04/index.html, 2013/3/4/index.html
So far we have enough tools to build a basic website. But we don't have any way
of linking to pages we don't explicitly know about. Grender solves this using
something called the Global Key (specified by the commandline flag
-global.key
, default files
). Any template may refer to any other file,
including all of its contextual metadata, via this key.
Say you want to build a list of every file available in the "blog" directory. In your template, you can do:
<ul>
{{ range .files.blog }}
<li> <a href="{{ .url }}">{{ .title }}</a> </li>
{{ end }}
</ul>
(url
is a special key that grender autopopulates in the Global Key space for
every rendered file.) And what if you only want to list most of the files in
the "blog" directory? You can create blog/default.json
:
{ "list": true }
And for the pages you don't want to include:
{ "list": false }
---
Content here
Then, in your template:
{{ range .files.blog }}
{{ if .list }}
<a href="{{ .url }}">{{ .title }}</a>
{{ end }}
{{ end }}
See the complete example.