You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1679 lines
37 KiB
1679 lines
37 KiB
'use strict'; |
|
/* |
|
* Engines which do not support caching of their file contents |
|
* should use the `read()` function defined in consolidate.js |
|
* On top of this, when an engine compiles to a `Function`, |
|
* these functions should either be cached within consolidate.js |
|
* or the engine itself via `options.cache`. This will allow |
|
* users and frameworks to pass `options.cache = true` for |
|
* `NODE_ENV=production`, however edit the file(s) without |
|
* re-loading the application in development. |
|
*/ |
|
|
|
/** |
|
* Module dependencies. |
|
*/ |
|
|
|
var fs = require('fs'); |
|
var path = require('path'); |
|
var Promise = require('bluebird'); |
|
|
|
var join = path.join; |
|
var resolve = path.resolve; |
|
var extname = path.extname; |
|
var dirname = path.dirname; |
|
var isAbsolute = path.isAbsolute; |
|
|
|
var readCache = {}; |
|
|
|
/** |
|
* Require cache. |
|
*/ |
|
|
|
var cacheStore = {}; |
|
|
|
/** |
|
* Require cache. |
|
*/ |
|
|
|
var requires = {}; |
|
|
|
/** |
|
* Clear the cache. |
|
* |
|
* @api public |
|
*/ |
|
|
|
exports.clearCache = function() { |
|
readCache = {}; |
|
cacheStore = {}; |
|
}; |
|
|
|
/** |
|
* Conditionally cache `compiled` template based |
|
* on the `options` filename and `.cache` boolean. |
|
* |
|
* @param {Object} options |
|
* @param {Function} compiled |
|
* @return {Function} |
|
* @api private |
|
*/ |
|
|
|
function cache(options, compiled) { |
|
// cachable |
|
if (compiled && options.filename && options.cache) { |
|
delete readCache[options.filename]; |
|
cacheStore[options.filename] = compiled; |
|
return compiled; |
|
} |
|
|
|
// check cache |
|
if (options.filename && options.cache) { |
|
return cacheStore[options.filename]; |
|
} |
|
|
|
return compiled; |
|
} |
|
|
|
/** |
|
* Read `path` with `options` with |
|
* callback `(err, str)`. When `options.cache` |
|
* is true the template string will be cached. |
|
* |
|
* @param {String} options |
|
* @param {Function} cb |
|
* @api private |
|
*/ |
|
|
|
function read(path, options, cb) { |
|
var str = readCache[path]; |
|
var cached = options.cache && str && typeof str === 'string'; |
|
|
|
// cached (only if cached is a string and not a compiled template function) |
|
if (cached) return cb(null, str); |
|
|
|
// read |
|
fs.readFile(path, 'utf8', function(err, str) { |
|
if (err) return cb(err); |
|
// remove extraneous utf8 BOM marker |
|
str = str.replace(/^\uFEFF/, ''); |
|
if (options.cache) readCache[path] = str; |
|
cb(null, str); |
|
}); |
|
} |
|
|
|
/** |
|
* Read `path` with `options` with |
|
* callback `(err, str)`. When `options.cache` |
|
* is true the partial string will be cached. |
|
* |
|
* @param {String} options |
|
* @param {Function} fn |
|
* @api private |
|
*/ |
|
|
|
function readPartials(path, options, cb) { |
|
if (!options.partials) return cb(); |
|
var partials = options.partials; |
|
var keys = Object.keys(partials); |
|
|
|
function next(index) { |
|
if (index === keys.length) return cb(null); |
|
var key = keys[index]; |
|
var partialPath = partials[key]; |
|
|
|
if (partialPath === undefined || partialPath === null || partialPath === false) { |
|
return next(++index); |
|
} |
|
|
|
var file; |
|
if (isAbsolute(partialPath)) { |
|
if (extname(partialPath) !== '') { |
|
file = partialPath; |
|
} else { |
|
file = join(partialPath + extname(path)); |
|
} |
|
} else { |
|
file = join(dirname(path), partialPath + extname(path)); |
|
} |
|
|
|
read(file, options, function(err, str) { |
|
if (err) return cb(err); |
|
options.partials[key] = str; |
|
next(++index); |
|
}); |
|
} |
|
|
|
next(0); |
|
} |
|
|
|
/** |
|
* promisify |
|
*/ |
|
function promisify(cb, fn) { |
|
return new Promise(function(resolve, reject) { |
|
cb = cb || function(err, html) { |
|
if (err) { |
|
return reject(err); |
|
} |
|
resolve(html); |
|
}; |
|
fn(cb); |
|
}); |
|
} |
|
|
|
/** |
|
* fromStringRenderer |
|
*/ |
|
|
|
function fromStringRenderer(name) { |
|
return function(path, options, cb) { |
|
options.filename = path; |
|
|
|
return promisify(cb, function(cb) { |
|
readPartials(path, options, function(err) { |
|
if (err) return cb(err); |
|
if (cache(options)) { |
|
exports[name].render('', options, cb); |
|
} else { |
|
read(path, options, function(err, str) { |
|
if (err) return cb(err); |
|
exports[name].render(str, options, cb); |
|
}); |
|
} |
|
}); |
|
}); |
|
}; |
|
} |
|
|
|
/** |
|
* velocity support. |
|
*/ |
|
|
|
exports.velocityjs = fromStringRenderer('velocityjs'); |
|
|
|
/** |
|
* velocity string support. |
|
*/ |
|
|
|
exports.velocityjs.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.velocityjs || (requires.velocityjs = require('velocityjs')); |
|
try { |
|
options.locals = options; |
|
cb(null, engine.render(str, options).trimLeft()); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Liquid support. |
|
*/ |
|
|
|
exports.liquid = fromStringRenderer('liquid'); |
|
|
|
/** |
|
* Liquid string support. |
|
*/ |
|
|
|
/** |
|
* Note that in order to get filters and custom tags we've had to push |
|
* all user-defined locals down into @locals. However, just to make things |
|
* backwards-compatible, any property of `options` that is left after |
|
* processing and removing `locals`, `meta`, `filters`, `customTags` and |
|
* `includeDir` will also become a local. |
|
*/ |
|
|
|
function _renderTinyliquid(engine, str, options, cb) { |
|
var context = engine.newContext(); |
|
var k; |
|
|
|
/** |
|
* Note that there's a bug in the library that doesn't allow us to pass |
|
* the locals to newContext(), hence looping through the keys: |
|
*/ |
|
|
|
if (options.locals) { |
|
for (k in options.locals) { |
|
context.setLocals(k, options.locals[k]); |
|
} |
|
delete options.locals; |
|
} |
|
|
|
if (options.meta) { |
|
context.setLocals('page', options.meta); |
|
delete options.meta; |
|
} |
|
|
|
/** |
|
* Add any defined filters: |
|
*/ |
|
|
|
if (options.filters) { |
|
for (k in options.filters) { |
|
context.setFilter(k, options.filters[k]); |
|
} |
|
delete options.filters; |
|
} |
|
|
|
/** |
|
* Set up a callback for the include directory: |
|
*/ |
|
|
|
var includeDir = options.includeDir || process.cwd(); |
|
|
|
context.onInclude(function(name, callback) { |
|
var extname = path.extname(name) ? '' : '.liquid'; |
|
var filename = path.resolve(includeDir, name + extname); |
|
|
|
fs.readFile(filename, {encoding: 'utf8'}, function(err, data) { |
|
if (err) return callback(err); |
|
callback(null, engine.parse(data)); |
|
}); |
|
}); |
|
delete options.includeDir; |
|
|
|
/** |
|
* The custom tag functions need to have their results pushed back |
|
* through the parser, so set up a shim before calling the provided |
|
* callback: |
|
*/ |
|
|
|
var compileOptions = { |
|
customTags: {} |
|
}; |
|
|
|
if (options.customTags) { |
|
var tagFunctions = options.customTags; |
|
|
|
for (k in options.customTags) { |
|
/*Tell jshint there's no problem with having this function in the loop */ |
|
/*jshint -W083 */ |
|
compileOptions.customTags[k] = function(context, name, body) { |
|
var tpl = tagFunctions[name](body.trim()); |
|
context.astStack.push(engine.parse(tpl)); |
|
}; |
|
/*jshint +W083 */ |
|
} |
|
delete options.customTags; |
|
} |
|
|
|
/** |
|
* Now anything left in `options` becomes a local: |
|
*/ |
|
|
|
for (k in options) { |
|
context.setLocals(k, options[k]); |
|
} |
|
|
|
/** |
|
* Finally, execute the template: |
|
*/ |
|
|
|
var tmpl = cache(context) || cache(context, engine.compile(str, compileOptions)); |
|
tmpl(context, cb); |
|
} |
|
|
|
exports.liquid.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.liquid; |
|
var Liquid; |
|
|
|
try { |
|
// set up tinyliquid engine |
|
engine = requires.liquid = require('tinyliquid'); |
|
|
|
// use tinyliquid engine |
|
_renderTinyliquid(engine, str, options, cb); |
|
|
|
return; |
|
|
|
} catch (err) { |
|
|
|
// set up liquid-node engine |
|
try { |
|
Liquid = requires.liquid = require('liquid-node'); |
|
engine = new Liquid.Engine(); |
|
} catch (err) { |
|
throw err; |
|
} |
|
|
|
} |
|
|
|
// use liquid-node engine |
|
try { |
|
var locals = options.locals || {}; |
|
|
|
if (options.meta) { |
|
locals.pages = options.meta; |
|
delete options.meta; |
|
} |
|
|
|
/** |
|
* Add any defined filters: |
|
*/ |
|
|
|
if (options.filters) { |
|
engine.registerFilters(options.filters); |
|
delete options.filters; |
|
} |
|
|
|
/** |
|
* Set up a callback for the include directory: |
|
*/ |
|
|
|
var includeDir = options.includeDir || process.cwd(); |
|
engine.fileSystem = new Liquid.LocalFileSystem(includeDir, 'liquid'); |
|
delete options.includeDir; |
|
|
|
/** |
|
* The custom tag functions need to have their results pushed back |
|
* through the parser, so set up a shim before calling the provided |
|
* callback: |
|
*/ |
|
|
|
if (options.customTags) { |
|
var tagFunctions = options.customTags; |
|
|
|
for (k in options.customTags) { |
|
engine.registerTag(k, tagFunctions[k]); |
|
} |
|
delete options.customTags; |
|
} |
|
|
|
/** |
|
* Now anything left in `options` becomes a local: |
|
*/ |
|
|
|
for (var k in options) { |
|
locals[k] = options[k]; |
|
} |
|
|
|
/** |
|
* Finally, execute the template: |
|
*/ |
|
|
|
return engine |
|
.parseAndRender(str, locals) |
|
.nodeify(function(err, result) { |
|
if (err) { |
|
throw new Error(err); |
|
} else { |
|
return cb(null, result); |
|
} |
|
}); |
|
|
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Jade support. |
|
*/ |
|
|
|
exports.jade = function(path, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.jade; |
|
if (!engine) { |
|
try { |
|
engine = requires.jade = require('jade'); |
|
} catch (err) { |
|
try { |
|
engine = requires.jade = require('then-jade'); |
|
} catch (otherError) { |
|
throw err; |
|
} |
|
} |
|
} |
|
|
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compileFile(path, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Jade string support. |
|
*/ |
|
|
|
exports.jade.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.jade; |
|
if (!engine) { |
|
try { |
|
engine = requires.jade = require('jade'); |
|
} catch (err) { |
|
try { |
|
engine = requires.jade = require('then-jade'); |
|
} catch (otherError) { |
|
throw err; |
|
} |
|
} |
|
} |
|
|
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Dust support. |
|
*/ |
|
|
|
exports.dust = fromStringRenderer('dust'); |
|
|
|
/** |
|
* Dust string support. |
|
*/ |
|
|
|
exports.dust.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.dust; |
|
if (!engine) { |
|
try { |
|
engine = requires.dust = require('dust'); |
|
} catch (err) { |
|
try { |
|
engine = requires.dust = require('dustjs-helpers'); |
|
} catch (err) { |
|
engine = requires.dust = require('dustjs-linkedin'); |
|
} |
|
} |
|
} |
|
|
|
var ext = 'dust'; |
|
var views = '.'; |
|
|
|
if (options) { |
|
if (options.ext) ext = options.ext; |
|
if (options.views) views = options.views; |
|
if (options.settings && options.settings.views) views = options.settings.views; |
|
} |
|
if (!options || (options && !options.cache)) engine.cache = {}; |
|
|
|
engine.onLoad = function(path, callback) { |
|
if (extname(path) === '') path += '.' + ext; |
|
if (path[0] !== '/') path = views + '/' + path; |
|
read(path, options, callback); |
|
}; |
|
|
|
try { |
|
var templateName; |
|
if (options.filename) { |
|
templateName = options.filename.replace(new RegExp('^' + views + '/'), '').replace(new RegExp('\\.' + ext), ''); |
|
} |
|
|
|
var tmpl = cache(options) || cache(options, engine.compileFn(str, templateName)); |
|
tmpl(options, cb); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Swig support. |
|
*/ |
|
|
|
exports.swig = fromStringRenderer('swig'); |
|
|
|
/** |
|
* Swig string support. |
|
*/ |
|
|
|
exports.swig.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.swig; |
|
if (!engine) { |
|
try { |
|
engine = requires.swig = require('swig'); |
|
} catch (err) { |
|
try { |
|
engine = requires.swig = require('swig-templates'); |
|
} catch (otherError) { |
|
throw err; |
|
} |
|
} |
|
} |
|
|
|
try { |
|
if (options.cache === true) options.cache = 'memory'; |
|
engine.setDefaults({ cache: options.cache }); |
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Atpl support. |
|
*/ |
|
|
|
exports.atpl = fromStringRenderer('atpl'); |
|
|
|
/** |
|
* Atpl string support. |
|
*/ |
|
|
|
exports.atpl.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.atpl || (requires.atpl = require('atpl')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Liquor support, |
|
*/ |
|
|
|
exports.liquor = fromStringRenderer('liquor'); |
|
|
|
/** |
|
* Liquor string support. |
|
*/ |
|
|
|
exports.liquor.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.liquor || (requires.liquor = require('liquor')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Twig support. |
|
*/ |
|
|
|
exports.twig = fromStringRenderer('twig'); |
|
|
|
/** |
|
* Twig string support. |
|
*/ |
|
|
|
exports.twig.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.twig || (requires.twig = require('twig').twig); |
|
var templateData = { |
|
data: str |
|
}; |
|
try { |
|
var tmpl = cache(templateData) || cache(templateData, engine(templateData)); |
|
cb(null, tmpl.render(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* EJS support. |
|
*/ |
|
|
|
exports.ejs = fromStringRenderer('ejs'); |
|
|
|
/** |
|
* EJS string support. |
|
*/ |
|
|
|
exports.ejs.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.ejs || (requires.ejs = require('ejs')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Eco support. |
|
*/ |
|
|
|
exports.eco = fromStringRenderer('eco'); |
|
|
|
/** |
|
* Eco string support. |
|
*/ |
|
|
|
exports.eco.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.eco || (requires.eco = require('eco')); |
|
try { |
|
cb(null, engine.render(str, options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Jazz support. |
|
*/ |
|
|
|
exports.jazz = fromStringRenderer('jazz'); |
|
|
|
/** |
|
* Jazz string support. |
|
*/ |
|
|
|
exports.jazz.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.jazz || (requires.jazz = require('jazz')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
tmpl.eval(options, function(str) { |
|
cb(null, str); |
|
}); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* JQTPL support. |
|
*/ |
|
|
|
exports.jqtpl = fromStringRenderer('jqtpl'); |
|
|
|
/** |
|
* JQTPL string support. |
|
*/ |
|
|
|
exports.jqtpl.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.jqtpl || (requires.jqtpl = require('jqtpl')); |
|
try { |
|
engine.template(str, str); |
|
cb(null, engine.tmpl(str, options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Haml support. |
|
*/ |
|
|
|
exports.haml = fromStringRenderer('haml'); |
|
|
|
/** |
|
* Haml string support. |
|
*/ |
|
|
|
exports.haml.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.haml || (requires.haml = require('hamljs')); |
|
try { |
|
options.locals = options; |
|
cb(null, engine.render(str, options).trimLeft()); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Hamlet support. |
|
*/ |
|
|
|
exports.hamlet = fromStringRenderer('hamlet'); |
|
|
|
/** |
|
* Hamlet string support. |
|
*/ |
|
|
|
exports.hamlet.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.hamlet || (requires.hamlet = require('hamlet')); |
|
try { |
|
options.locals = options; |
|
cb(null, engine.render(str, options).trimLeft()); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Whiskers support. |
|
*/ |
|
|
|
exports.whiskers = function(path, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.whiskers || (requires.whiskers = require('whiskers')); |
|
engine.__express(path, options, cb); |
|
}); |
|
}; |
|
|
|
/** |
|
* Whiskers string support. |
|
*/ |
|
|
|
exports.whiskers.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.whiskers || (requires.whiskers = require('whiskers')); |
|
try { |
|
cb(null, engine.render(str, options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Coffee-HAML support. |
|
*/ |
|
|
|
exports['haml-coffee'] = fromStringRenderer('haml-coffee'); |
|
|
|
/** |
|
* Coffee-HAML string support. |
|
*/ |
|
|
|
exports['haml-coffee'].render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires['haml-coffee'] || (requires['haml-coffee'] = require('haml-coffee')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Hogan support. |
|
*/ |
|
|
|
exports.hogan = fromStringRenderer('hogan'); |
|
|
|
/** |
|
* Hogan string support. |
|
*/ |
|
|
|
exports.hogan.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.hogan || (requires.hogan = require('hogan.js')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
cb(null, tmpl.render(options, options.partials)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* templayed.js support. |
|
*/ |
|
|
|
exports.templayed = fromStringRenderer('templayed'); |
|
|
|
/** |
|
* templayed.js string support. |
|
*/ |
|
|
|
exports.templayed.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.templayed || (requires.templayed = require('templayed')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine(str)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Handlebars support. |
|
*/ |
|
|
|
exports.handlebars = fromStringRenderer('handlebars'); |
|
|
|
/** |
|
* Handlebars string support. |
|
*/ |
|
|
|
exports.handlebars.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.handlebars || (requires.handlebars = require('handlebars')); |
|
try { |
|
for (var partial in options.partials) { |
|
engine.registerPartial(partial, options.partials[partial]); |
|
} |
|
for (var helper in options.helpers) { |
|
engine.registerHelper(helper, options.helpers[helper]); |
|
} |
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Underscore support. |
|
*/ |
|
|
|
exports.underscore = fromStringRenderer('underscore'); |
|
|
|
/** |
|
* Underscore string support. |
|
*/ |
|
|
|
exports.underscore.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.underscore || (requires.underscore = require('underscore')); |
|
try { |
|
for (var partial in options.partials) { |
|
options.partials[partial] = engine.template(options.partials[partial]); |
|
} |
|
var tmpl = cache(options) || cache(options, engine.template(str, null, options)); |
|
cb(null, tmpl(options).replace(/\n$/, '')); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Lodash support. |
|
*/ |
|
|
|
exports.lodash = fromStringRenderer('lodash'); |
|
|
|
/** |
|
* Lodash string support. |
|
*/ |
|
|
|
exports.lodash.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.lodash || (requires.lodash = require('lodash')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine.template(str, options)); |
|
cb(null, tmpl(options).replace(/\n$/, '')); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Pug support. (formerly Jade) |
|
*/ |
|
|
|
exports.pug = function(path, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.pug; |
|
if (!engine) { |
|
try { |
|
engine = requires.pug = require('pug'); |
|
} catch (err) { |
|
try { |
|
engine = requires.pug = require('then-pug'); |
|
} catch (otherError) { |
|
throw err; |
|
} |
|
} |
|
} |
|
|
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compileFile(path, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Pug string support. |
|
*/ |
|
|
|
exports.pug.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.pug; |
|
if (!engine) { |
|
try { |
|
engine = requires.pug = require('pug'); |
|
} catch (err) { |
|
try { |
|
engine = requires.pug = require('then-pug'); |
|
} catch (otherError) { |
|
throw err; |
|
} |
|
} |
|
} |
|
|
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* QEJS support. |
|
*/ |
|
|
|
exports.qejs = fromStringRenderer('qejs'); |
|
|
|
/** |
|
* QEJS string support. |
|
*/ |
|
|
|
exports.qejs.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
try { |
|
var engine = requires.qejs || (requires.qejs = require('qejs')); |
|
engine.render(str, options).then(function(result) { |
|
cb(null, result); |
|
}, function(err) { |
|
cb(err); |
|
}).done(); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Walrus support. |
|
*/ |
|
|
|
exports.walrus = fromStringRenderer('walrus'); |
|
|
|
/** |
|
* Walrus string support. |
|
*/ |
|
|
|
exports.walrus.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.walrus || (requires.walrus = require('walrus')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine.parse(str)); |
|
cb(null, tmpl.compile(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Mustache support. |
|
*/ |
|
|
|
exports.mustache = fromStringRenderer('mustache'); |
|
|
|
/** |
|
* Mustache string support. |
|
*/ |
|
|
|
exports.mustache.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.mustache || (requires.mustache = require('mustache')); |
|
try { |
|
cb(null, engine.to_html(str, options, options.partials)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Just support. |
|
*/ |
|
|
|
exports.just = function(path, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.just; |
|
if (!engine) { |
|
var JUST = require('just'); |
|
engine = requires.just = new JUST(); |
|
} |
|
engine.configure({ useCache: options.cache }); |
|
engine.render(path, options, cb); |
|
}); |
|
}; |
|
|
|
/** |
|
* Just string support. |
|
*/ |
|
|
|
exports.just.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var JUST = require('just'); |
|
var engine = new JUST({ root: { page: str }}); |
|
engine.render('page', options, cb); |
|
}); |
|
}; |
|
|
|
/** |
|
* ECT support. |
|
*/ |
|
|
|
exports.ect = function(path, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.ect; |
|
if (!engine) { |
|
var ECT = require('ect'); |
|
engine = requires.ect = new ECT(options); |
|
} |
|
engine.configure({ cache: options.cache }); |
|
engine.render(path, options, cb); |
|
}); |
|
}; |
|
|
|
/** |
|
* ECT string support. |
|
*/ |
|
|
|
exports.ect.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var ECT = require('ect'); |
|
var engine = new ECT({ root: { page: str }}); |
|
engine.render('page', options, cb); |
|
}); |
|
}; |
|
|
|
/** |
|
* mote support. |
|
*/ |
|
|
|
exports.mote = fromStringRenderer('mote'); |
|
|
|
/** |
|
* mote string support. |
|
*/ |
|
|
|
exports.mote.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.mote || (requires.mote = require('mote')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compile(str)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Toffee support. |
|
*/ |
|
|
|
exports.toffee = function(path, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var toffee = requires.toffee || (requires.toffee = require('toffee')); |
|
toffee.__consolidate_engine_render(path, options, cb); |
|
}); |
|
}; |
|
|
|
/** |
|
* Toffee string support. |
|
*/ |
|
|
|
exports.toffee.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.toffee || (requires.toffee = require('toffee')); |
|
try { |
|
engine.str_render(str, options, cb); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* doT support. |
|
*/ |
|
|
|
exports.dot = fromStringRenderer('dot'); |
|
|
|
/** |
|
* doT string support. |
|
*/ |
|
|
|
exports.dot.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.dot || (requires.dot = require('dot')); |
|
var extend = (requires.extend || (requires.extend = require('util')._extend)); |
|
try { |
|
var settings = {}; |
|
settings = extend(settings, engine.templateSettings); |
|
settings = extend(settings, options ? options.dot : {}); |
|
var tmpl = cache(options) || cache(options, engine.template(str, settings, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* bracket support. |
|
*/ |
|
|
|
exports.bracket = fromStringRenderer('bracket'); |
|
|
|
/** |
|
* bracket string support. |
|
*/ |
|
|
|
exports.bracket.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.bracket || (requires.bracket = require('bracket-template')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine.default.compile(str, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Ractive support. |
|
*/ |
|
|
|
exports.ractive = fromStringRenderer('ractive'); |
|
|
|
/** |
|
* Ractive string support. |
|
*/ |
|
|
|
exports.ractive.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var Engine = requires.ractive || (requires.ractive = require('ractive')); |
|
|
|
var template = cache(options) || cache(options, Engine.parse(str)); |
|
options.template = template; |
|
|
|
if (options.data === null || options.data === undefined) { |
|
var extend = (requires.extend || (requires.extend = require('util')._extend)); |
|
|
|
// Shallow clone the options object |
|
options.data = extend({}, options); |
|
|
|
// Remove consolidate-specific properties from the clone |
|
var i; |
|
var length; |
|
var properties = ['template', 'filename', 'cache', 'partials']; |
|
for (i = 0, length = properties.length; i < length; i++) { |
|
var property = properties[i]; |
|
delete options.data[property]; |
|
} |
|
} |
|
|
|
try { |
|
cb(null, new Engine(options).toHTML()); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Nunjucks support. |
|
*/ |
|
|
|
exports.nunjucks = fromStringRenderer('nunjucks'); |
|
|
|
/** |
|
* Nunjucks string support. |
|
*/ |
|
|
|
exports.nunjucks.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
|
|
try { |
|
|
|
var engine = options.nunjucksEnv || requires.nunjucks || (requires.nunjucks = require('nunjucks')); |
|
|
|
var env = engine; |
|
|
|
// deprecated fallback support for express |
|
// <https://github.com/tj/consolidate.js/pull/152> |
|
// <https://github.com/tj/consolidate.js/pull/224> |
|
if (options.settings && options.settings.views) { |
|
env = engine.configure(options.settings.views); |
|
} else if (options.nunjucks && options.nunjucks.configure) { |
|
env = engine.configure.apply(engine, options.nunjucks.configure); |
|
} |
|
|
|
// |
|
// because `renderString` does not initiate loaders |
|
// we must manually create a loader for it based off |
|
// either `options.settings.views` or `options.nunjucks` or `options.nunjucks.root` |
|
// |
|
// <https://github.com/mozilla/nunjucks/issues/730> |
|
// <https://github.com/crocodilejs/node-email-templates/issues/182> |
|
// |
|
|
|
// so instead we simply check if we passed a custom loader |
|
// otherwise we create a simple file based loader |
|
if (options.loader) { |
|
env = new engine.Environment(options.loader); |
|
} else if (options.settings && options.settings.views) { |
|
env = new engine.Environment( |
|
new engine.FileSystemLoader(options.settings.views) |
|
); |
|
} else if (options.nunjucks && options.nunjucks.loader) { |
|
if (typeof options.nunjucks.loader === 'string') { |
|
env = new engine.Environment(new engine.FileSystemLoader(options.nunjucks.loader)); |
|
} else { |
|
env = new engine.Environment( |
|
new engine.FileSystemLoader( |
|
options.nunjucks.loader[0], |
|
options.nunjucks.loader[1] |
|
) |
|
); |
|
} |
|
} |
|
|
|
env.renderString(str, options, cb); |
|
} catch (err) { |
|
throw cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* HTMLing support. |
|
*/ |
|
|
|
exports.htmling = fromStringRenderer('htmling'); |
|
|
|
/** |
|
* HTMLing string support. |
|
*/ |
|
|
|
exports.htmling.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.htmling || (requires.htmling = require('htmling')); |
|
try { |
|
var tmpl = cache(options) || cache(options, engine.string(str)); |
|
cb(null, tmpl.render(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Rendering function |
|
*/ |
|
function requireReact(module, filename) { |
|
var babel = requires.babel || (requires.babel = require('babel-core')); |
|
|
|
var compiled = babel.transformFileSync(filename, { presets: [ 'react' ] }).code; |
|
|
|
return module._compile(compiled, filename); |
|
} |
|
|
|
exports.requireReact = requireReact; |
|
|
|
/** |
|
* Converting a string into a node module. |
|
*/ |
|
function requireReactString(src, filename) { |
|
var babel = requires.babel || (requires.babel = require('babel-core')); |
|
|
|
if (!filename) filename = ''; |
|
var m = new module.constructor(); |
|
filename = filename || ''; |
|
|
|
// Compile Using React |
|
var compiled = babel.transform(src, { presets: [ 'react' ] }).code; |
|
|
|
// Compile as a module |
|
m.paths = module.paths; |
|
m._compile(compiled, filename); |
|
|
|
return m.exports; |
|
} |
|
|
|
/** |
|
* A naive helper to replace {{tags}} with options.tags content |
|
*/ |
|
function reactBaseTmpl(data, options) { |
|
|
|
var exp; |
|
var regex; |
|
|
|
// Iterates through the keys in file object |
|
// and interpolate / replace {{key}} with it's value |
|
for (var k in options) { |
|
if (options.hasOwnProperty(k)) { |
|
exp = '{{' + k + '}}'; |
|
regex = new RegExp(exp, 'g'); |
|
if (data.match(regex)) { |
|
data = data.replace(regex, options[k]); |
|
} |
|
} |
|
} |
|
|
|
return data; |
|
} |
|
|
|
/** |
|
* Plates Support. |
|
*/ |
|
|
|
exports.plates = fromStringRenderer('plates'); |
|
|
|
/** |
|
* Plates string support. |
|
*/ |
|
|
|
exports.plates.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.plates || (requires.plates = require('plates')); |
|
var map = options.map || undefined; |
|
try { |
|
var tmpl = engine.bind(str, options, map); |
|
cb(null, tmpl); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* The main render parser for React bsaed templates |
|
*/ |
|
function reactRenderer(type) { |
|
|
|
if (require.extensions) { |
|
|
|
// Ensure JSX is transformed on require |
|
if (!require.extensions['.jsx']) { |
|
require.extensions['.jsx'] = requireReact; |
|
} |
|
|
|
// Supporting .react extension as well as test cases |
|
// Using .react extension is not recommended. |
|
if (!require.extensions['.react']) { |
|
require.extensions['.react'] = requireReact; |
|
} |
|
|
|
} |
|
|
|
// Return rendering fx |
|
return function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
// React Import |
|
var ReactDOM = requires.ReactDOM || (requires.ReactDOM = require('react-dom/server')); |
|
var react = requires.react || (requires.react = require('react')); |
|
|
|
// Assign HTML Base |
|
var base = options.base; |
|
delete options.base; |
|
|
|
var enableCache = options.cache; |
|
delete options.cache; |
|
|
|
var isNonStatic = options.isNonStatic; |
|
delete options.isNonStatic; |
|
|
|
// Start Conversion |
|
try { |
|
|
|
var Code; |
|
var Factory; |
|
|
|
var baseStr; |
|
var content; |
|
var parsed; |
|
|
|
if (!cache(options)) { |
|
// Parsing |
|
if (type === 'path') { |
|
var path = resolve(str); |
|
delete require.cache[path]; |
|
Code = require(path); |
|
} else { |
|
Code = requireReactString(str); |
|
} |
|
Factory = cache(options, react.createFactory(Code)); |
|
|
|
} else { |
|
Factory = cache(options); |
|
} |
|
|
|
parsed = new Factory(options); |
|
content = (isNonStatic) ? ReactDOM.renderToString(parsed) : ReactDOM.renderToStaticMarkup(parsed); |
|
|
|
if (base) { |
|
baseStr = readCache[str] || fs.readFileSync(resolve(base), 'utf8'); |
|
|
|
if (enableCache) { |
|
readCache[str] = baseStr; |
|
} |
|
|
|
options.content = content; |
|
content = reactBaseTmpl(baseStr, options); |
|
} |
|
|
|
cb(null, content); |
|
|
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
} |
|
|
|
/** |
|
* React JS Support |
|
*/ |
|
exports.react = reactRenderer('path'); |
|
|
|
/** |
|
* React JS string support. |
|
*/ |
|
exports.react.render = reactRenderer('string'); |
|
|
|
/** |
|
* ARC-templates support. |
|
*/ |
|
|
|
exports['arc-templates'] = fromStringRenderer('arc-templates'); |
|
|
|
/** |
|
* ARC-templates string support. |
|
*/ |
|
|
|
exports['arc-templates'].render = function(str, options, cb) { |
|
var readFileWithOptions = Promise.promisify(read); |
|
var consolidateFileSystem = {}; |
|
consolidateFileSystem.readFile = function(path) { |
|
return readFileWithOptions(path, options); |
|
}; |
|
|
|
return promisify(cb, function(cb) { |
|
try { |
|
var engine = requires['arc-templates']; |
|
if (!engine) { |
|
var Engine = require('arc-templates/dist/es5'); |
|
engine = requires['arc-templates'] = new Engine({ filesystem: consolidateFileSystem }); |
|
} |
|
|
|
var compiler = cache(options) || cache(options, engine.compileString(str, options.filename)); |
|
compiler.then(function(func) { return func(options); }) |
|
.then(function(result) { cb(null, result.content); }) |
|
.catch(cb); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Vash support |
|
*/ |
|
exports.vash = fromStringRenderer('vash'); |
|
|
|
/** |
|
* Vash string support |
|
*/ |
|
exports.vash.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.vash || (requires.vash = require('vash')); |
|
|
|
try { |
|
// helper system : https://github.com/kirbysayshi/vash#helper-system |
|
if (options.helpers) { |
|
for (var key in options.helpers) { |
|
if (!options.helpers.hasOwnProperty(key) || typeof options.helpers[key] !== 'function') { |
|
continue; |
|
} |
|
engine.helpers[key] = options.helpers[key]; |
|
} |
|
} |
|
|
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
tmpl(options, function sealLayout(err, ctx) { |
|
if (err) cb(err); |
|
ctx.finishLayout(); |
|
cb(null, ctx.toString().replace(/\n$/, '')); |
|
}); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Slm support. |
|
*/ |
|
|
|
exports.slm = fromStringRenderer('slm'); |
|
|
|
/** |
|
* Slm string support. |
|
*/ |
|
|
|
exports.slm.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.slm || (requires.slm = require('slm')); |
|
|
|
try { |
|
var tmpl = cache(options) || cache(options, engine.compile(str, options)); |
|
cb(null, tmpl(options)); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Marko support. |
|
*/ |
|
|
|
exports.marko = function(path, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.marko || (requires.marko = require('marko')); |
|
options.writeToDisk = !!options.cache; |
|
|
|
try { |
|
var tmpl = cache(options) || cache(options, engine.load(path, options)); |
|
tmpl.renderToString(options, cb); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Marko string support. |
|
*/ |
|
|
|
exports.marko.render = function(str, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.marko || (requires.marko = require('marko')); |
|
options.writeToDisk = !!options.cache; |
|
options.filename = options.filename || 'string.marko'; |
|
|
|
try { |
|
var tmpl = cache(options) || cache(options, engine.load(options.filename, str, options)); |
|
tmpl.renderToString(options, cb); |
|
} catch (err) { |
|
cb(err); |
|
} |
|
}); |
|
}; |
|
|
|
/** |
|
* Teacup support. |
|
*/ |
|
exports.teacup = function(path, options, cb) { |
|
return promisify(cb, function(cb) { |
|
var engine = requires.teacup || (requires.teacup = require('teacup/lib/express')); |
|
require.extensions['.teacup'] = require.extensions['.coffee']; |
|
if (path[0] !== '/') { |
|
path = join(process.cwd(), path); |
|
} |
|
if (!options.cache) { |
|
var callback = cb; |
|
cb = function() { |
|
delete require.cache[path]; |
|
callback.apply(this, arguments); |
|
}; |
|
} |
|
engine.renderFile(path, options, cb); |
|
}); |
|
}; |
|
|
|
/** |
|
* Teacup string support. |
|
*/ |
|
exports.teacup.render = function(str, options, cb) { |
|
var coffee = require('coffee-script'); |
|
var vm = require('vm'); |
|
var sandbox = { |
|
module: {exports: {}}, |
|
require: require |
|
}; |
|
return promisify(cb, function(cb) { |
|
vm.runInNewContext(coffee.compile(str), sandbox); |
|
var tmpl = sandbox.module.exports; |
|
cb(null, tmpl(options)); |
|
}); |
|
}; |
|
|
|
/** |
|
* expose the instance of the engine |
|
*/ |
|
exports.requires = requires;
|
|
|