A flexible static site generator – http://jnordberg.github.com/wintersmith/
- Easy to use
- Generated sites can be hosted anywhere (output is plain html)
- Write articles/pages using markdown
- Robust templating using Jade
- Preview server (no need to rebuild every time you make a change)
- Highly configurable
- Extendable using plugins
- FAST!
First install wintersmith using npm:
$ npm install wintersmith -g
This will install wintersmith globally on your system so that you can access the wintersmith
command from anywhere. Once that is complete run:
$ wintersmith new <path>
Where <path>
is the location you want the site to be generated. This creates a skeleton site with a basic set of templates and some articles, while not strictly needed it's a good starting point.
Now enter the directory and start the preview server:
$ cd <path>
$ wintersmith preview
At this point you are ready to start customizing your site. Point your browser to http://localhost:8080
and start editing templates and articles.
When done run:
$ wintersmith build
This generates your site and places it in the build/
directory - all ready to be copied to your web server!
And remember to give the old --help
a look :-)
A wintersmith site is built up of two main components, contents and templates.
Contents is a directory where all the sites raw material goes (markdown files, images, javascript etc). This directory is then scanned to produce what's internally called a ContentTree.
The ContentTree is a nested object built up of ContentPlugins and looks something like this:
{
"myfile.md": {MarkdownPlugin} // plugin instance, subclass of ContentPlugin
"some-dir/": { // another ContentTree instance
"image.jpg": {StaticPlugin}
"random.file": {StaticPlugin}
}
}
This content tree is provided in full to all plugins in turn when rendering. This gives you a lot of flexibility when writing plugins, you could for example write a plugin that generates a mosaic using images located in a specific directory.
Wintersmith comes with a default Page plugin that renders markdown content using templates. This plugin takes markdown (combined with some metadata, more on this later) compiles it and provides it to a template along with the content tree and some utility functions.
This brings us to the second component, the template directory. All templates found in this directory are loaded and are also passed to the content plugins when rendering.
By default only .jade
templates are loaded, but you can easily add template plugins to use a template engine of your choosing.
Check the examples/
directory for some inspiration on how you can use wintersmith or the showcase to see what others are doing.
Configuration can be done with command-line options, a config file or both. The config file will be looked for as config.json
in the root of your site (you can set a custom path using --config
).
Name | Default | Description |
---|---|---|
output | ./build | output directory, this is where the generated site is output |
contents | ./contents | contents directory, where to look for site contents (markdown, images, etc) |
templates | ./templates | template directory, where to look for templates |
locals | {} | javascript object to pass to all templates when rendering, useful for storing metadata for your site. can also be a path to a json file |
require | [] | list of modules to load and provide to the template context |
plugins | [] | list of plugin modules to load |
ignore | [] | list of files/globpatterns in contents to ignore |
All paths can either be relative or absolute. Relative paths will be resolved from the current directory or --chdir
if set.
A page is either a markdown file with metadata on top or a json file located in the contents directory.
---
title: My first post
date: 2012-12-12 12:12
author: John Hjort <[email protected]>
template: article.jade
----
# Hello friends!
Life is wonderful, isn't it?
or use json to simply pass metadata to a template:
{
"template": "template.jade",
"meta": {
"greta": 123,
"peta": [1, 2, 3]
}
}
Pages will be rendered as html, so for example index.md
would be rendered to index.html
and some-dir/data.json
to some-dir/data.html
.
All relative links in the markdown will be resolved correctly when rendering. This means you can just place image.png in the same directory and simply include it in your markdown as ![my image](image.png)
This is especially convenient when using a markdown editor (read Mou if you're on a mac).
Metadata is parsed using js-yaml and will be accessible in the template as page.metadata
.
There are two special metadata keys, The first one is template
which specifies what template to render the page with. If the key is omitted or set to none
the page will not be rendered (but still available in the content tree).
The second one is filename
which can be used to override the output filename of the page. Useful if you need to output a .xml
file or something similar.
When a page is rendered to a template the page instance is available as page
in the template context. The content tree is also available as contents
and the config.locals object as locals
.
underscore.js is also available as _
to provide some utility to aid you sorting and filtering the content tree.
The Page model (inherits from ContentPlugin)
Properties:
metadata | the metadata object |
title | `metadata.title` or `Untitled` |
date | Date object from `metadata.date` if set or unix epoch time |
rfc822date | a rfc-822 formatted string made from `date` |
body | unparsed markdown content |
html | parsed markdown content |
Wintersmith has two types of plugins, content plugins that transform contents and template plugins that are provided to the content plugins to help render contents.
A list of 3rd party plugins can be found on the wiki.
A content plugin is a subclass of ContentPlugin
and should provide a fromFile
class method, a render
instance method and a getFilename
instance method.
render
is called with the content tree, template list, locals and a callback. Have a look in src/contents.coffee
it's pretty well documented.
Content plugins are registered using the registerContentPlugin
function.
registerContentPlugin(group, pattern, plugin) | |
---|---|
group |
string - plugin group name
|
pattern |
string - glob pattern (e.g.
|
plugin |
class - the ContentPlugin subclass |
A template plugins is a subclass of TemplatePlugin
and should also provide a fromFile
class method and a render
instance method.
Template plugins are registered using:
function registerTemplatePlugin(pattern, plugin) { .. }
where pattern is the glob pattern to match in the template directory and plugin is the plugin subclass.
The easiest way to load a wintersmith plugin is to use the plugins
config option.
Example:
myplugin.coffee
module.exports = (wintersmith, callback) ->
class TextPlugin extends wintersmith.ContentPlugin
constructor: (@_filename, @_text) ->
getFilename: ->
@_filename
render: (locals, contents, templates, callback) ->
# do something with the text!
callback null, new Buffer @_text
TextPlugin.fromFile = (filename, base, callback) ->
fs.readFile path.join(base, filename), (error, buffer) ->
if error
callback error
else
callback null, new TextPlugin filename, buffer.toString()
wintersmith.registerContentPlugin 'text', '**/*.txt', TextPlugin
callback() # tell the plugin manager we are done
To use this plugin simply pass the path to the file to the cli tool (--plugins ./myplugin.coffee
)
You can also use globally or locally installed modules as plugins.
example:
var wintersmith = require('wintersmith');
var options = {
'output': '/var/www/pub',
'contents': '/foo/contents',
'contents': '/foo/templates',
'plugins': ['some-plugin'],
'locals': {foo: 'bar'}
};
wintersmith(options, callback(error) {
if (error) {
throw error;
} else {
console.log('great success!');
}
});
// you can also use the api to get the content tree
wintersmith.loadContents('path/to/contents', callback(error, contents) {
// do something with the content tree
});
There are more API methods defined, have a look at the source it's pretty well-commented.
Wintersmith is written by Johan Nordberg using CoffeeScript and licensed under the MIT-license.
The name is a nod to blacksmith which inspired this project (and Terry Pratchett of course).
Some of the great node.js modules that wintersmith uses:
Check the package.json
for a complete list.
© 2012 FFFF00 Agents AB