'use strict'; /** * Module dependencies. */ var fs = require('fs'); var util = require('engine-utils'); var Emitter = require('component-emitter'); var utils = require('./utils'); var engine = util.fromStringRenderer('base'); Emitter(engine); /** * Expose the Engine constructor */ engine.Engine = utils.Engine; /** * expose engine `defaults` */ engine.options = { name: 'base', dest: {ext: '.html'} }; /** * Return a compiled function from the given template * `string` and `options`. * * ```js * var engine = require('engine-base'); * var fn = engine.compileSync('<%= name %>'); * console.log(fn({name: 'Halle'})); //=> 'Halle' * ``` * @param {String} `str` Template string to compile. * @param {Object} `options` Options or settings to pass to base * @return {Function} * @api public */ function compileSync(str, locals) { try { locals = locals || {}; locals.settings = locals.settings || {}; var settings = {}; var picked = utils.merge(pick(locals), pick(locals.settings)); var delims = picked.delims; var opts = picked.opts; var fns = picked.fns; settings.imports = utils.merge({}, fns.helpers, fns.imports); settings = utils.merge({}, settings, delims); if (locals.debugEngine === true) { inspectHelpers(settings, opts); } delete settings.imports.with; delete settings.imports.if; var base = new utils.Engine(settings); return base.compile(str, settings); } catch (err) { throw err; } } /** * Return a compiled function from the given template * `string` and `options` can `callback` * * ```js * var engine = require('engine-base'); * engine.compile('<%= name %>', function (err, fn) { * console.log(fn({name: 'Halle'})); //=> 'Halle' * }); * ``` * @param {String} `str` Template string to compile. * @param {Object} `options` Options or settings to pass to engine. * @param {Function} `cb` Callback function * @api public */ function compile(str, options, cb) { if (typeof options === 'function') { return compile(str, {}, options); } if (typeof cb !== 'function') { return compileSync(str, options); } try { cb(null, compileSync(str, options)); } catch (err) { cb(err); } } /** * Render templates synchronously. * * ```js * var engine = require('engine-base'); * engine.renderSync('<%= name %>', {name: 'Halle'}); * //=> 'Halle' * ``` * * @param {Object} `str` The string to render. * @param {Object} `options` Object of options. * @option {Object} `settings` Settings to pass to Lo-Dash. * @option {Arrary} `delims` Template delimiters, generated by [delimiter-regex] * @option {Object} `imports` Template helpers to pass to Lo-Dash. * @return {String} Rendered string. * @api public */ function renderSync(str, locals) { locals = locals || {}; locals.settings = locals.settings || {}; var settings = {}; var picked = utils.merge(pick(locals), pick(locals.settings)); var delims = picked.delims; var opts = picked.opts; var fns = picked.fns; settings.imports = utils.merge({}, fns.helpers, fns.imports); settings = utils.merge({}, settings, delims); if (locals.debugEngine === true) { inspectHelpers(settings, opts); } if (typeof str === 'function') { var ctx = utils.omit(locals, ['helpers', 'imports']); return str(ctx); } try { var base = new utils.Engine(settings); return base.render(str, locals); } catch (err) { throw err; } } /** * String support. Render the given `str` * and invoke the callback `callback(err, str)`. * * ```js * var engine = require('engine-base'); * engine.render('<%= name %>', {name: 'Jon'}, function (err, content) { * console.log(content); //=> 'Jon' * }); * ``` * * @param {String} `str` * @param {Object|Function} `locals` or callback. * @property {Object} `cache` enable template caching * @property {String} `filename` filename required for caching * @param {Function} `callback` * @api public */ function render(str, locals, cb) { if (typeof locals === 'function') { return render(str, {}, locals); } if (typeof cb !== 'function') { return renderSync(str, locals); } try { cb(null, renderSync(str, locals)); } catch (err) { return cb(err); } } /** * File support. Render a file at the given `filepath` * and callback `callback(err, str)`. * * ```js * var engine = require('engine-base'); * engine.renderFile('foo/bar/baz.tmpl', {name: 'Halle'}); * //=> 'Halle' * ``` * * @param {String} `path` * @param {Object|Function} `options` or callback function. * @param {Function} `callback` * @api public */ function renderFile(fp, opts, cb) { if (typeof opts === 'function') { return renderFile(fp, {}, opts); } var str = fs.readFileSync(fp, 'utf8'); render(str, opts, cb); } /** * Handle custom delimiters */ function delimsObject(delims) { var a = delims[0], b = delims[1]; var res = {}; res.interpolate = utils.delims(a + '=', b); res.evaluate = utils.delims(a, b); res.escape = utils.delims(a + '-', b); return res; } /** * Inspect helpers if `debugEngine` is enabled */ function inspectHelpers(settings, opts) { var helpers = Object.keys(settings.imports); for (var key in opts) { if (helpers.indexOf(key) !== -1) { var msg = conflictMessage(settings, opts, key); var err = new Error(msg); err.id = 'helper-conflict'; err.engine = 'engine-base'; engine.emit('error', err); } } } /** * Conflict report displayed when the same key exists as both * a helper name and the key of a (data) property on the context. */ function conflictMessage(settings, options, key) { var type1 = typeof settings.imports[key]; var type2 = typeof options[key]; return 'Property "' + key + '" is defined on ' + 'more than one object: \n' + ' - `settings.imports` as ' + article(type1) + ' ' + type1 + '\n' + ' - `options` as ' + article(type2) + ' ' + type2; } function article(word) { var n = /^[aeiou]/.test(word); return n ? 'an' : 'a'; } function pick(obj) { var res = {}; res.delims = utils.pick(obj, ['interpolate', 'evaluate', 'escape']); res.opts = utils.omit(obj, ['helpers', 'imports']); res.fns = utils.pick(obj, ['helpers', 'imports']); if (Array.isArray(obj.delims)) { res.delims = utils.merge({}, delimsObject(obj.delims), res.delims); } return res; } /** * Express support. */ engine.__express = engine.renderFile; /** * Expose `engine` */ module.exports = engine; /** * Expose `engine` methods */ module.exports.render = render; module.exports.renderFile = renderFile; module.exports.renderSync = renderSync; module.exports.compile = compile; module.exports.compileSync = compileSync;