jstatic
A simple, easily extensible, static documents generation task for Grunt.
Getting Started
This plugin requires Grunt ~0.4.1
If you haven't used Grunt before, be sure to check out the Getting Started guide, as it explains how to create a Gruntfile as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:
npm install jstatic --save-dev
Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:
grunt.loadNpmTasks('jstatic');
The "jstatic" task
Overview
Read jstatic - Flexible static site generation for an introduction to jstatic concepts. In your project's Gruntfile, add a section named jstatic
to the data object passed into grunt.initConfig()
.
grunt.initConfig({
jstatic: {
options: {
// Task-specific options go here.
},
your_target: {
// Target-specific file lists and/or options go here.
},
},
})
jstatic converts files using a very simple algorithm. Every Files Array entry is treated as a separate conversion Flow. During each flow, file details are read into a dictionary with the following properties.
srcPath
- the source path of the filebasename
- the name part of the fileext
- the extension of the source filedestPath
- the path where the file will be written to at the end of this flow.content
- the file contents
This dictionary is then passed sequentially through a series of generators (which modify/add items in the dictionary etc.). The resulting data is then written at the destination.
Configuring jstatic
The minimum configuration required is a Files Array for each target, specifying the source files and destination.
Each File Array entry can contain additional properties that determine the conversion. The following file array properties are valid.
- All standard File Array properties.
name
String (default: generated): This is a unique string that identifies this entry. Used when there are dependencies between conversions.outExt
String (default: ".html"): The file extension for the output files.depends
Array<String> (default: []): A list of names of File Array entries on which this conversions is dependant. The generated results of all these dependencies will be available for the current conversion.generators
Array<String|Object> (default: ["yafm", "permalink", "swig"]): A list of generator names as string or optionally an object of the form{type:"<generator-name>", .. other params .. }
for cases where specific conversion parameters need to be specified. These params vary from generator to generator.
eg:
grunt.initConfig({
jstatic: {
site: {
files: [
{
src: "content/index.html",
dest: "site"
},
{
name: "my-flow",
src: ["content/post/*.md"],
dest: "site/post",
generators: [
"yafm",
"markdown",
{type: "swig", layout: "templates/blog_layout.html"}
]
}
],
}
},
});
Options
The following global, or per-task options can be used with jstatic
extraContext
Array<String|Object> (default: []): List containing either a) paths to json or yaml files b) Objects. The data from this list will be passed as paramdata
to the generators.<generator-name>
Object: This dictionary of params is merged with generator params passed in the file array list, thus allowing global configuration of generators.
Builtin Generators
yafm - YAml Front Matter Processor
YFM is an optional section of valid YAML that is placed at the top of a page and is used for maintaining metadata for the page and its contents.
This generator extracts YAML Front Matter from the input file into the file dictionary and sets the content to the remaining portion.
swig
This generator applies swig templating engine to the file contents. The file dictionary
is available as context inside swig templates. The final generated results of dependencies
are also available inside the templates, as variable with same name as the file array entry.
Additionally fields in the global extraContext
is also available.
Params
layout
String (defualt: undefined) - If provided, then after rendering an input file, the rendered contents is passed as a variablebody
to the layout file.
you can also include any swig initialization options to initialize the swig library.
markdown
This generator formats the file contents using the marked markdown engine
summary
This generator extracts a single paragraph summary of markdown contents and adds it as summary
field in the file dictionary.
permalink
This generator generates a URL from the file destination path and adds it as a permalink
field in the file dictionary.
Params
-
link
String|Function (default:function(entry, prefix, pathElems){return prefix + pathElems.join("/");}
): A template string or a function that returns the link.A template string can contain interpolated file entry properties eg:
"hello$(sep)$(basename)_$(page)$(outExt)"
.The function should be of the form
function (entry, prefix, pathElems, outExt, dest)
where the parameters areentry
- the file entryprefix
- the linkPrefixpathElems
- array of items in the link pathoutExt
- the output file extensiondest
- the flow destination directory
-
linkPrefix
String (default: "/"): This string is prefixed to the link field in a page's context. -
linkPathStrip
Number (default: 1): This many path elements are stripped off from the beginning of a file's destination path, before building the link.
paginator
This generator clones each file in the current flow based on the length of a dependency result. It generates as many clones as the number of pages required to accomodate the length. The following properties are inserted into each file entry
page
- The page object for this page. This is the value returned by the pageBy function for this entry.pageItems
- the Entries in the current pagepageCount
- the total number of pagespageSize
- the size of each page.
Params
pivot
String - name of the dependency whose length will be used to compute the number of pagespageSize
Number (default: 5) - number of items per page.pageBy
Fuction (default:function(entry,index){return Math.floor(index/pageSize)+1;}
) - a function that identifies the page that an entry in the pivot belongs to. paginator groups entries into pages based this result.
sequencer
sequencer adds next, previous and whole sequence references to each file entry. This can be used for building navigation between items in a flow. The following properties are inserted into each file entry
prev
- reference to the previous file entry in this flownext
- reference to the next file entry in this flowsequence
- reference to an array of all file entries in this flow
destination
destination allows the user to modify the destination filename and location.
Params
dest
String|function - A template string or a function that returns the filename
A template string can contain interpolated file entry properties eg: "hello$(sep)$(basename)_$(page)$(outExt)"
.
The function should be of the form function (entry, outExt, sep, dest)
where the parameters are
entry
- the file entryoutExt
- the output file extensionsep
- the path separator characterdest
- the flow destination directory
Common Tasks
Generating Collection Pages
During each conversion, an array of final file dictionaries of all dependencies are passed in a depends
parameter to the generators. The swig generator makes this parameter available inside the template, thus allowing us to generate collection summary pages, like blog home pages, archive pages, tag/category pages etc..
Eg: If we configure the files array
files: [
{
src: "content/index.html",
dest: "site"
depends: ["blog_flow"] // We need to put a list of blog entries in the index page
},
{
name: "blog_flow",
src: ["content/post/*.md"],
dest: "site/post",
generators: [
"yafm",
"markdown",
"permalink",
{type: "swig", layout: "templates/blog_layout.html"}
]
}
]
then index.html can generate list of posts like so
<ul>
{% for file in blog_flow|sortBy("publishTime")|reverse %}
<li>{{file.publishTime|date("d M Y")}} <a href="{{file.permalink}}">{{file.title}}</a></li>
{% endfor %}
</ul>
Pagination
Pagination can be done using the paginator. In the previous example if we want to paginate the index.html file by the entries in the "blog_flow", we simply add a paginator
generators: [
{type: "paginator", pivot: "blog_flow", pageSize: 5},
"swig"
]
The index.html file can be modified to generate only the contents for the current page.
<ul>
{% for file in pageItems|sortBy("publishTime")|reverse %}
<li>{{file.publishTime|date("d M Y")}} <a href="{{file.permalink}}">{{file.title}}</a></li>
{% endfor %}
</ul>
This will generate index1.html, index2.html etc. one for each page. Check the destination generator for changing file names. Also check the sequencer on how to create page navigation.
The paginator also accepts an optional pageBy
parameter. This is a function that identifies the page that each entry in the pivot belongs to. The default functionality of pageBy is to identifiy page based on the index, but this can be overriden to create any kind of pagination behaviour.
Eg: The following paginator will page the entries by the month and year
{
type:"paginator",
pivot: "items",
pageBy: function(entry) {
var time = entry.publishTime;
return new Date(time.getFullYear(), time.getMonth());
}
}
Note that the page can be any type, in the above example, the page is a Date object
Writing Custom Generators
Generators can be registered using the jstatic.registerGenerator
function
jstatic.registerGenerator("myGenerator", function(iter, params, flow, depends) {
...
return newIter;
});
where the parameters are
iter
- This is an iterator from the previous generator. callingiter()
once will return one file entry. Anundefined
response indicates the end of the sequence.params
- This dictionary contains paramters for the generator. This is a merge of the files array entry and the global options for this generator.flow
- A reference to the flow object.depends
- A dictionary containing the results of all the dependencies of this flow.
Each generator should return an iterator that generates its output.
eg: The following generator adds the length of the title into the file entry
jstatic.registerGenerator("myGenerator", function(iter, params, flow, depends) {
return function() {
var entry = iter();
if(!entry) return;
entry.titleLength = entry.title.length;
return entry;
};
});
Change Log
v2.2.0
- 14th Sep, 2013- pageBy functinality added to paginator
- Added stash. A way to bring jstatic results into other Grunt tasks
- Sorting in sequencer
- Added link template in permalink generator
- Added passThru generator