Create a gist now

Instantly share code, notes, and snippets.

This snippet is for rendering Jade templates on the server for pushState single page apps in actionHero. Useful when you need to bootstrap model data on initial page load. It also pre-compiles the templates on server start and will recompile if they change.
var path = require('path')
, _ = require('lodash')
, Streamifier = require('streamifier');
exports.action = {
name: 'index',
description: 'server-side rendered index',
outputExample: {},
inputs: { required: [], optional: [] },
run: function(api, connection, next){
var server = api.servers.servers[connection.type];
try{
var _file = connection.params.file || 'index.html'
, file = _file.split('.')[0] + '.jade'
// get the corresponding template
, key = path.normalize(api.configData.general.serverTemplatesDirectory + '/' + file);
if (_(api.templates.compiled).has(key)) {
// retrieve the models
api.component.list(null, null, function(err, comps) {
if (err) {
connection.error = err;
next(connection, true);
} else {
// render the template with the model data
var html = api.templates.compiled[key]({models: comps})
// server.sendFile operates on streams, so we change our string into a stream
, htmlStream = Streamifier.createReadStream(html);
// here we pass the original connection otherwise we get an error about missing a missing prototype method 'destroy' once the stream is complete (in version 6.2.2)
server.sendFile(connection._original_connection, null, htmlStream, 'text/html', html.length);
}
});
} else {
connection.error = 'File not found';
connection.rawConnection.responseHttpCode = 404;
next(connection, true);
}catch(e){
console.log(e)
connection.error = 'Your connection type, `' + connection.type + '`, can not be sent files';
next(connection, true);
}
}
};
//- !{} tells jade not to escape the data
script(type='text/javascript').
var initialComponents = !{JSON.stringify(models)};
var fs = require('fs')
, path = require('path')
, jade = require('jade');
exports.templates = function(api, next){
api.templates = { compiled: {} };
// add serverTemplatesDirectory to your config.js. Generally you'll store this outside your 'public' folder
var templatepath = api.configData.general.serverTemplatesDirectory;
api.log('Compiling Jade Templates', 'notice');
fs.readdirSync(templatepath).forEach(function(file) {
var absFile = path.resolve(templatepath, file)
, stats = fs.statSync(absFile);
if (stats.isFile()) {
api.templates.compiled[absFile] = jade.compile(fs.readFileSync(absFile), {filename: absFile});
fs.watch(absFile, { persistent: false }, function(event, filename) {
if (filename) {
if (event === 'change') {
api.log('Re-compiling Jade Template: ' + filename, 'notice');
api.templates.compiled[absFile] = jade.compile(fs.readFileSync(absFile), {filename: absFile});
}
} else {
api.log('Unable to determine changed file for recompiling Jade Template. Template was not re-compiled.', 'error');
}
});
}
});
next();
}
exports.routes = {
get: [
{ path: '/components', action: 'componentList' },
{ path: '/components/:id', action: 'componentView' },
{ path: '/', action: 'index' } // default route (for root url), always keep this as the last route!
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment