87 lines
3.1 KiB
JavaScript
87 lines
3.1 KiB
JavaScript
|
var extend = require('deepmerge');
|
||
|
var fm = require('front-matter');
|
||
|
var path = require('path');
|
||
|
var through = require('through2');
|
||
|
var stripBom = require('strip-bom');
|
||
|
var processRoot = require('./processRoot');
|
||
|
|
||
|
module.exports = function() {
|
||
|
return through.obj(render.bind(this));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Renders a page with a layout. The page also has access to any loaded partials, helpers, or data.
|
||
|
* @param {object} file - Vinyl file being parsed.
|
||
|
* @param {string} enc - Vinyl file encoding.
|
||
|
* @param {function} cb - Callback that passes the rendered page through the stream.
|
||
|
*/
|
||
|
function render(file, enc, cb) {
|
||
|
try {
|
||
|
// Get the HTML for the current page and layout
|
||
|
var page = fm(stripBom(file.contents.toString()));
|
||
|
var pageData;
|
||
|
|
||
|
// Determine which layout to use
|
||
|
var basePath = path.relative(this.options.root, path.dirname(file.path));
|
||
|
var layout =
|
||
|
page.attributes.layout ||
|
||
|
(this.options.pageLayouts && this.options.pageLayouts[basePath]) ||
|
||
|
'default';
|
||
|
var layoutTemplate = this.layouts[layout];
|
||
|
|
||
|
if (!layoutTemplate) {
|
||
|
if (layout === 'default') {
|
||
|
throw new Error('Panini error: you must have a layout named "default".');
|
||
|
}
|
||
|
else {
|
||
|
throw new Error('Panini error: no layout named "'+layout+'" exists.');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now create Handlebars templates out of them
|
||
|
var pageTemplate = this.Handlebars.compile(page.body + '\n');
|
||
|
|
||
|
// Build page data with globals
|
||
|
pageData = extend({}, this.data);
|
||
|
|
||
|
// Add any data from stream plugins
|
||
|
pageData = (file.data) ? extend(pageData, file.data) : pageData;
|
||
|
|
||
|
// Add this page's front matter
|
||
|
pageData = extend(pageData, page.attributes);
|
||
|
|
||
|
// Finish by adding constants
|
||
|
pageData = extend(pageData, {
|
||
|
page: path.basename(file.path, path.extname(file.path)),
|
||
|
layout: layout,
|
||
|
root: processRoot(file.path, this.options.root)
|
||
|
});
|
||
|
|
||
|
// Add special ad-hoc partials for #ifpage and #unlesspage
|
||
|
this.Handlebars.registerHelper('ifpage', require('../helpers/ifPage')(pageData.page));
|
||
|
this.Handlebars.registerHelper('unlesspage', require('../helpers/unlessPage')(pageData.page));
|
||
|
|
||
|
// Finally, add the page as a partial called "body", and render the layout template
|
||
|
this.Handlebars.registerPartial('body', pageTemplate);
|
||
|
file.contents = new Buffer(layoutTemplate(pageData));
|
||
|
}
|
||
|
catch (e) {
|
||
|
if (layoutTemplate) {
|
||
|
// Layout was parsed properly so we can insert the error message into the body of the layout
|
||
|
this.Handlebars.registerPartial('body', 'Panini: template could not be parsed <br> \n <pre>{{error}}</pre>');
|
||
|
file.contents = new Buffer(layoutTemplate({ error: e }));
|
||
|
}
|
||
|
else {
|
||
|
// Not even once - write error directly into the HTML output so the user gets an error
|
||
|
// Maintain basic html structure to allow Livereloading scripts to be injected properly
|
||
|
file.contents = new Buffer('<!DOCTYPE html><html><head><title>Panini error</title></head><body><pre>'+e+'</pre></body></html>');
|
||
|
}
|
||
|
|
||
|
throw new Error('Panini: rendering error occured.\n' + e);
|
||
|
}
|
||
|
finally {
|
||
|
// This sends the modified file back into the stream
|
||
|
cb(null, file);
|
||
|
}
|
||
|
}
|