diff options
Diffstat (limited to 'node_modules/consolidate/lib')
| -rw-r--r-- | node_modules/consolidate/lib/consolidate.js | 1679 |
1 files changed, 1679 insertions, 0 deletions
diff --git a/node_modules/consolidate/lib/consolidate.js b/node_modules/consolidate/lib/consolidate.js new file mode 100644 index 00000000..0b8d60b4 --- /dev/null +++ b/node_modules/consolidate/lib/consolidate.js @@ -0,0 +1,1679 @@ +'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; |
