consolidate.js 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792
  1. 'use strict';
  2. /*
  3. * Engines which do not support caching of their file contents
  4. * should use the `read()` function defined in consolidate.js
  5. * On top of this, when an engine compiles to a `Function`,
  6. * these functions should either be cached within consolidate.js
  7. * or the engine itself via `options.cache`. This will allow
  8. * users and frameworks to pass `options.cache = true` for
  9. * `NODE_ENV=production`, however edit the file(s) without
  10. * re-loading the application in development.
  11. */
  12. /**
  13. * Module dependencies.
  14. */
  15. var fs = require('fs');
  16. var path = require('path');
  17. var Promise = require('bluebird');
  18. var join = path.join;
  19. var resolve = path.resolve;
  20. var extname = path.extname;
  21. var dirname = path.dirname;
  22. var isAbsolute = path.isAbsolute;
  23. var readCache = {};
  24. /**
  25. * Require cache.
  26. */
  27. var cacheStore = {};
  28. /**
  29. * Require cache.
  30. */
  31. var requires = {};
  32. /**
  33. * Clear the cache.
  34. *
  35. * @api public
  36. */
  37. exports.clearCache = function() {
  38. readCache = {};
  39. cacheStore = {};
  40. };
  41. /**
  42. * Conditionally cache `compiled` template based
  43. * on the `options` filename and `.cache` boolean.
  44. *
  45. * @param {Object} options
  46. * @param {Function} compiled
  47. * @return {Function}
  48. * @api private
  49. */
  50. function cache(options, compiled) {
  51. // cachable
  52. if (compiled && options.filename && options.cache) {
  53. delete readCache[options.filename];
  54. cacheStore[options.filename] = compiled;
  55. return compiled;
  56. }
  57. // check cache
  58. if (options.filename && options.cache) {
  59. return cacheStore[options.filename];
  60. }
  61. return compiled;
  62. }
  63. /**
  64. * Read `path` with `options` with
  65. * callback `(err, str)`. When `options.cache`
  66. * is true the template string will be cached.
  67. *
  68. * @param {String} options
  69. * @param {Function} cb
  70. * @api private
  71. */
  72. function read(path, options, cb) {
  73. var str = readCache[path];
  74. var cached = options.cache && str && typeof str === 'string';
  75. // cached (only if cached is a string and not a compiled template function)
  76. if (cached) return cb(null, str);
  77. // read
  78. fs.readFile(path, 'utf8', function(err, str) {
  79. if (err) return cb(err);
  80. // remove extraneous utf8 BOM marker
  81. str = str.replace(/^\uFEFF/, '');
  82. if (options.cache) readCache[path] = str;
  83. cb(null, str);
  84. });
  85. }
  86. /**
  87. * Read `path` with `options` with
  88. * callback `(err, str)`. When `options.cache`
  89. * is true the partial string will be cached.
  90. *
  91. * @param {String} options
  92. * @param {Function} fn
  93. * @api private
  94. */
  95. function readPartials(path, options, cb) {
  96. if (!options.partials) return cb();
  97. var keys = Object.keys(options.partials);
  98. var partials = {};
  99. function next(index) {
  100. if (index === keys.length) return cb(null, partials);
  101. var key = keys[index];
  102. var partialPath = options.partials[key];
  103. if (partialPath === undefined || partialPath === null || partialPath === false) {
  104. return next(++index);
  105. }
  106. var file;
  107. if (isAbsolute(partialPath)) {
  108. if (extname(partialPath) !== '') {
  109. file = partialPath;
  110. } else {
  111. file = join(partialPath + extname(path));
  112. }
  113. } else {
  114. file = join(dirname(path), partialPath + extname(path));
  115. }
  116. read(file, options, function(err, str) {
  117. if (err) return cb(err);
  118. partials[key] = str;
  119. next(++index);
  120. });
  121. }
  122. next(0);
  123. }
  124. /**
  125. * promisify
  126. */
  127. function promisify(cb, fn) {
  128. return new Promise(function(resolve, reject) {
  129. cb = cb || function(err, html) {
  130. if (err) {
  131. return reject(err);
  132. }
  133. resolve(html);
  134. };
  135. fn(cb);
  136. });
  137. }
  138. /**
  139. * fromStringRenderer
  140. */
  141. function fromStringRenderer(name) {
  142. return function(path, options, cb) {
  143. options.filename = path;
  144. return promisify(cb, function(cb) {
  145. readPartials(path, options, function(err, partials) {
  146. var extend = (requires.extend || (requires.extend = require('util')._extend));
  147. var opts = extend({}, options);
  148. opts.partials = partials;
  149. if (err) return cb(err);
  150. if (cache(opts)) {
  151. exports[name].render('', opts, cb);
  152. } else {
  153. read(path, opts, function(err, str) {
  154. if (err) return cb(err);
  155. exports[name].render(str, opts, cb);
  156. });
  157. }
  158. });
  159. });
  160. };
  161. }
  162. /**
  163. * velocity support.
  164. */
  165. exports.velocityjs = fromStringRenderer('velocityjs');
  166. /**
  167. * velocity string support.
  168. */
  169. exports.velocityjs.render = function(str, options, cb) {
  170. return promisify(cb, function(cb) {
  171. var engine = requires.velocityjs || (requires.velocityjs = require('velocityjs'));
  172. try {
  173. options.locals = options;
  174. cb(null, engine.render(str, options).trimLeft());
  175. } catch (err) {
  176. cb(err);
  177. }
  178. });
  179. };
  180. /**
  181. * Liquid support.
  182. */
  183. exports.liquid = fromStringRenderer('liquid');
  184. /**
  185. * Liquid string support.
  186. */
  187. /**
  188. * Note that in order to get filters and custom tags we've had to push
  189. * all user-defined locals down into @locals. However, just to make things
  190. * backwards-compatible, any property of `options` that is left after
  191. * processing and removing `locals`, `meta`, `filters`, `customTags` and
  192. * `includeDir` will also become a local.
  193. */
  194. function _renderTinyliquid(engine, str, options, cb) {
  195. var context = engine.newContext();
  196. var k;
  197. /**
  198. * Note that there's a bug in the library that doesn't allow us to pass
  199. * the locals to newContext(), hence looping through the keys:
  200. */
  201. if (options.locals) {
  202. for (k in options.locals) {
  203. context.setLocals(k, options.locals[k]);
  204. }
  205. delete options.locals;
  206. }
  207. if (options.meta) {
  208. context.setLocals('page', options.meta);
  209. delete options.meta;
  210. }
  211. /**
  212. * Add any defined filters:
  213. */
  214. if (options.filters) {
  215. for (k in options.filters) {
  216. context.setFilter(k, options.filters[k]);
  217. }
  218. delete options.filters;
  219. }
  220. /**
  221. * Set up a callback for the include directory:
  222. */
  223. var includeDir = options.includeDir || process.cwd();
  224. context.onInclude(function(name, callback) {
  225. var extname = path.extname(name) ? '' : '.liquid';
  226. var filename = path.resolve(includeDir, name + extname);
  227. fs.readFile(filename, {encoding: 'utf8'}, function(err, data) {
  228. if (err) return callback(err);
  229. callback(null, engine.parse(data));
  230. });
  231. });
  232. delete options.includeDir;
  233. /**
  234. * The custom tag functions need to have their results pushed back
  235. * through the parser, so set up a shim before calling the provided
  236. * callback:
  237. */
  238. var compileOptions = {
  239. customTags: {}
  240. };
  241. if (options.customTags) {
  242. var tagFunctions = options.customTags;
  243. for (k in options.customTags) {
  244. /*Tell jshint there's no problem with having this function in the loop */
  245. /*jshint -W083 */
  246. compileOptions.customTags[k] = function(context, name, body) {
  247. var tpl = tagFunctions[name](body.trim());
  248. context.astStack.push(engine.parse(tpl));
  249. };
  250. /*jshint +W083 */
  251. }
  252. delete options.customTags;
  253. }
  254. /**
  255. * Now anything left in `options` becomes a local:
  256. */
  257. for (k in options) {
  258. context.setLocals(k, options[k]);
  259. }
  260. /**
  261. * Finally, execute the template:
  262. */
  263. var tmpl = cache(context) || cache(context, engine.compile(str, compileOptions));
  264. tmpl(context, cb);
  265. }
  266. exports.liquid.render = function(str, options, cb) {
  267. return promisify(cb, function(cb) {
  268. var engine = requires.liquid;
  269. var Liquid;
  270. try {
  271. // set up tinyliquid engine
  272. engine = requires.liquid = require('tinyliquid');
  273. // use tinyliquid engine
  274. _renderTinyliquid(engine, str, options, cb);
  275. return;
  276. } catch (err) {
  277. // set up liquid-node engine
  278. try {
  279. Liquid = requires.liquid = require('liquid-node');
  280. engine = new Liquid.Engine();
  281. } catch (err) {
  282. throw err;
  283. }
  284. }
  285. // use liquid-node engine
  286. try {
  287. var locals = options.locals || {};
  288. if (options.meta) {
  289. locals.pages = options.meta;
  290. delete options.meta;
  291. }
  292. /**
  293. * Add any defined filters:
  294. */
  295. if (options.filters) {
  296. engine.registerFilters(options.filters);
  297. delete options.filters;
  298. }
  299. /**
  300. * Set up a callback for the include directory:
  301. */
  302. var includeDir = options.includeDir || process.cwd();
  303. engine.fileSystem = new Liquid.LocalFileSystem(includeDir, 'liquid');
  304. delete options.includeDir;
  305. /**
  306. * The custom tag functions need to have their results pushed back
  307. * through the parser, so set up a shim before calling the provided
  308. * callback:
  309. */
  310. if (options.customTags) {
  311. var tagFunctions = options.customTags;
  312. for (k in options.customTags) {
  313. engine.registerTag(k, tagFunctions[k]);
  314. }
  315. delete options.customTags;
  316. }
  317. /**
  318. * Now anything left in `options` becomes a local:
  319. */
  320. for (var k in options) {
  321. locals[k] = options[k];
  322. }
  323. /**
  324. * Finally, execute the template:
  325. */
  326. return engine
  327. .parseAndRender(str, locals)
  328. .nodeify(function(err, result) {
  329. if (err) {
  330. throw new Error(err);
  331. } else {
  332. return cb(null, result);
  333. }
  334. });
  335. } catch (err) {
  336. cb(err);
  337. }
  338. });
  339. };
  340. /**
  341. * Jade support.
  342. */
  343. exports.jade = function(path, options, cb) {
  344. return promisify(cb, function(cb) {
  345. var engine = requires.jade;
  346. if (!engine) {
  347. try {
  348. engine = requires.jade = require('jade');
  349. } catch (err) {
  350. try {
  351. engine = requires.jade = require('then-jade');
  352. } catch (otherError) {
  353. throw err;
  354. }
  355. }
  356. }
  357. try {
  358. var tmpl = cache(options) || cache(options, engine.compileFile(path, options));
  359. cb(null, tmpl(options));
  360. } catch (err) {
  361. cb(err);
  362. }
  363. });
  364. };
  365. /**
  366. * Jade string support.
  367. */
  368. exports.jade.render = function(str, options, cb) {
  369. return promisify(cb, function(cb) {
  370. var engine = requires.jade;
  371. if (!engine) {
  372. try {
  373. engine = requires.jade = require('jade');
  374. } catch (err) {
  375. try {
  376. engine = requires.jade = require('then-jade');
  377. } catch (otherError) {
  378. throw err;
  379. }
  380. }
  381. }
  382. try {
  383. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  384. cb(null, tmpl(options));
  385. } catch (err) {
  386. cb(err);
  387. }
  388. });
  389. };
  390. /**
  391. * Dust support.
  392. */
  393. exports.dust = fromStringRenderer('dust');
  394. /**
  395. * Dust string support.
  396. */
  397. exports.dust.render = function(str, options, cb) {
  398. return promisify(cb, function(cb) {
  399. var engine = requires.dust;
  400. if (!engine) {
  401. try {
  402. engine = requires.dust = require('dust');
  403. } catch (err) {
  404. try {
  405. engine = requires.dust = require('dustjs-helpers');
  406. } catch (err) {
  407. engine = requires.dust = require('dustjs-linkedin');
  408. }
  409. }
  410. }
  411. var ext = 'dust';
  412. var views = '.';
  413. if (options) {
  414. if (options.ext) ext = options.ext;
  415. if (options.views) views = options.views;
  416. if (options.settings && options.settings.views) views = options.settings.views;
  417. }
  418. if (!options || (options && !options.cache)) engine.cache = {};
  419. engine.onLoad = function(path, callback) {
  420. if (extname(path) === '') path += '.' + ext;
  421. if (path[0] !== '/') path = views + '/' + path;
  422. read(path, options, callback);
  423. };
  424. try {
  425. var templateName;
  426. if (options.filename) {
  427. templateName = options.filename.replace(new RegExp('^' + views + '/'), '').replace(new RegExp('\\.' + ext), '');
  428. }
  429. var tmpl = cache(options) || cache(options, engine.compileFn(str, templateName));
  430. tmpl(options, cb);
  431. } catch (err) {
  432. cb(err);
  433. }
  434. });
  435. };
  436. /**
  437. * Swig support.
  438. */
  439. exports.swig = fromStringRenderer('swig');
  440. /**
  441. * Swig string support.
  442. */
  443. exports.swig.render = function(str, options, cb) {
  444. return promisify(cb, function(cb) {
  445. var engine = requires.swig;
  446. if (!engine) {
  447. try {
  448. engine = requires.swig = require('swig');
  449. } catch (err) {
  450. try {
  451. engine = requires.swig = require('swig-templates');
  452. } catch (otherError) {
  453. throw err;
  454. }
  455. }
  456. }
  457. try {
  458. if (options.cache === true) options.cache = 'memory';
  459. engine.setDefaults({ cache: options.cache });
  460. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  461. cb(null, tmpl(options));
  462. } catch (err) {
  463. cb(err);
  464. }
  465. });
  466. };
  467. /**
  468. * Razor support.
  469. */
  470. exports.razor = function(path, options, cb) {
  471. return promisify(cb, function(cb) {
  472. var engine = requires.razor;
  473. if (!engine) {
  474. try {
  475. engine = requires.razor = require('razor-tmpl');
  476. } catch (err) {
  477. throw err;
  478. }
  479. }
  480. try {
  481. var tmpl = cache(options) || cache(options, (locals) => {
  482. console.log('Rendering razor file', path);
  483. return engine.renderFileSync(path, locals);
  484. });
  485. cb(null, tmpl(options));
  486. } catch (err) {
  487. cb(err);
  488. }
  489. });
  490. };
  491. /**
  492. * razor string support.
  493. */
  494. exports.razor.render = function(str, options, cb) {
  495. return promisify(cb, function(cb) {
  496. try {
  497. var engine = requires.razor = require('razor-tmpl');
  498. } catch (err) {
  499. throw err;
  500. }
  501. try {
  502. var tf = engine.compile(str);
  503. var tmpl = cache(options) || cache(options, tf);
  504. cb(null, tmpl(options));
  505. } catch (err) {
  506. cb(err);
  507. }
  508. });
  509. };
  510. /**
  511. * Atpl support.
  512. */
  513. exports.atpl = fromStringRenderer('atpl');
  514. /**
  515. * Atpl string support.
  516. */
  517. exports.atpl.render = function(str, options, cb) {
  518. return promisify(cb, function(cb) {
  519. var engine = requires.atpl || (requires.atpl = require('atpl'));
  520. try {
  521. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  522. cb(null, tmpl(options));
  523. } catch (err) {
  524. cb(err);
  525. }
  526. });
  527. };
  528. /**
  529. * Liquor support,
  530. */
  531. exports.liquor = fromStringRenderer('liquor');
  532. /**
  533. * Liquor string support.
  534. */
  535. exports.liquor.render = function(str, options, cb) {
  536. return promisify(cb, function(cb) {
  537. var engine = requires.liquor || (requires.liquor = require('liquor'));
  538. try {
  539. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  540. cb(null, tmpl(options));
  541. } catch (err) {
  542. cb(err);
  543. }
  544. });
  545. };
  546. /**
  547. * Twig support.
  548. */
  549. exports.twig = fromStringRenderer('twig');
  550. /**
  551. * Twig string support.
  552. */
  553. exports.twig.render = function(str, options, cb) {
  554. return promisify(cb, function(cb) {
  555. var engine = requires.twig || (requires.twig = require('twig').twig);
  556. var templateData = {
  557. data: str,
  558. allowInlineIncludes: options.allowInlineIncludes,
  559. namespaces: options.namespaces,
  560. path: options.path
  561. };
  562. try {
  563. var tmpl = cache(templateData) || cache(templateData, engine(templateData));
  564. cb(null, tmpl.render(options));
  565. } catch (err) {
  566. cb(err);
  567. }
  568. });
  569. };
  570. /**
  571. * EJS support.
  572. */
  573. exports.ejs = fromStringRenderer('ejs');
  574. /**
  575. * EJS string support.
  576. */
  577. exports.ejs.render = function(str, options, cb) {
  578. return promisify(cb, function(cb) {
  579. var engine = requires.ejs || (requires.ejs = require('ejs'));
  580. try {
  581. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  582. cb(null, tmpl(options));
  583. } catch (err) {
  584. cb(err);
  585. }
  586. });
  587. };
  588. /**
  589. * Eco support.
  590. */
  591. exports.eco = fromStringRenderer('eco');
  592. /**
  593. * Eco string support.
  594. */
  595. exports.eco.render = function(str, options, cb) {
  596. return promisify(cb, function(cb) {
  597. var engine = requires.eco || (requires.eco = require('eco'));
  598. try {
  599. cb(null, engine.render(str, options));
  600. } catch (err) {
  601. cb(err);
  602. }
  603. });
  604. };
  605. /**
  606. * Jazz support.
  607. */
  608. exports.jazz = fromStringRenderer('jazz');
  609. /**
  610. * Jazz string support.
  611. */
  612. exports.jazz.render = function(str, options, cb) {
  613. return promisify(cb, function(cb) {
  614. var engine = requires.jazz || (requires.jazz = require('jazz'));
  615. try {
  616. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  617. tmpl.eval(options, function(str) {
  618. cb(null, str);
  619. });
  620. } catch (err) {
  621. cb(err);
  622. }
  623. });
  624. };
  625. /**
  626. * JQTPL support.
  627. */
  628. exports.jqtpl = fromStringRenderer('jqtpl');
  629. /**
  630. * JQTPL string support.
  631. */
  632. exports.jqtpl.render = function(str, options, cb) {
  633. return promisify(cb, function(cb) {
  634. var engine = requires.jqtpl || (requires.jqtpl = require('jqtpl'));
  635. try {
  636. engine.template(str, str);
  637. cb(null, engine.tmpl(str, options));
  638. } catch (err) {
  639. cb(err);
  640. }
  641. });
  642. };
  643. /**
  644. * Haml support.
  645. */
  646. exports.haml = fromStringRenderer('haml');
  647. /**
  648. * Haml string support.
  649. */
  650. exports.haml.render = function(str, options, cb) {
  651. return promisify(cb, function(cb) {
  652. var engine = requires.haml || (requires.haml = require('hamljs'));
  653. try {
  654. options.locals = options;
  655. cb(null, engine.render(str, options).trimLeft());
  656. } catch (err) {
  657. cb(err);
  658. }
  659. });
  660. };
  661. /**
  662. * Hamlet support.
  663. */
  664. exports.hamlet = fromStringRenderer('hamlet');
  665. /**
  666. * Hamlet string support.
  667. */
  668. exports.hamlet.render = function(str, options, cb) {
  669. return promisify(cb, function(cb) {
  670. var engine = requires.hamlet || (requires.hamlet = require('hamlet'));
  671. try {
  672. options.locals = options;
  673. cb(null, engine.render(str, options).trimLeft());
  674. } catch (err) {
  675. cb(err);
  676. }
  677. });
  678. };
  679. /**
  680. * Whiskers support.
  681. */
  682. exports.whiskers = function(path, options, cb) {
  683. return promisify(cb, function(cb) {
  684. var engine = requires.whiskers || (requires.whiskers = require('whiskers'));
  685. engine.__express(path, options, cb);
  686. });
  687. };
  688. /**
  689. * Whiskers string support.
  690. */
  691. exports.whiskers.render = function(str, options, cb) {
  692. return promisify(cb, function(cb) {
  693. var engine = requires.whiskers || (requires.whiskers = require('whiskers'));
  694. try {
  695. cb(null, engine.render(str, options));
  696. } catch (err) {
  697. cb(err);
  698. }
  699. });
  700. };
  701. /**
  702. * Coffee-HAML support.
  703. */
  704. exports['haml-coffee'] = fromStringRenderer('haml-coffee');
  705. /**
  706. * Coffee-HAML string support.
  707. */
  708. exports['haml-coffee'].render = function(str, options, cb) {
  709. return promisify(cb, function(cb) {
  710. var engine = requires['haml-coffee'] || (requires['haml-coffee'] = require('haml-coffee'));
  711. try {
  712. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  713. cb(null, tmpl(options));
  714. } catch (err) {
  715. cb(err);
  716. }
  717. });
  718. };
  719. /**
  720. * Hogan support.
  721. */
  722. exports.hogan = fromStringRenderer('hogan');
  723. /**
  724. * Hogan string support.
  725. */
  726. exports.hogan.render = function(str, options, cb) {
  727. return promisify(cb, function(cb) {
  728. var engine = requires.hogan || (requires.hogan = require('hogan.js'));
  729. try {
  730. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  731. cb(null, tmpl.render(options, options.partials));
  732. } catch (err) {
  733. cb(err);
  734. }
  735. });
  736. };
  737. /**
  738. * templayed.js support.
  739. */
  740. exports.templayed = fromStringRenderer('templayed');
  741. /**
  742. * templayed.js string support.
  743. */
  744. exports.templayed.render = function(str, options, cb) {
  745. return promisify(cb, function(cb) {
  746. var engine = requires.templayed || (requires.templayed = require('templayed'));
  747. try {
  748. var tmpl = cache(options) || cache(options, engine(str));
  749. cb(null, tmpl(options));
  750. } catch (err) {
  751. cb(err);
  752. }
  753. });
  754. };
  755. /**
  756. * Handlebars support.
  757. */
  758. exports.handlebars = fromStringRenderer('handlebars');
  759. /**
  760. * Handlebars string support.
  761. */
  762. exports.handlebars.render = function(str, options, cb) {
  763. return promisify(cb, function(cb) {
  764. var engine = requires.handlebars || (requires.handlebars = require('handlebars'));
  765. try {
  766. for (var partial in options.partials) {
  767. engine.registerPartial(partial, options.partials[partial]);
  768. }
  769. for (var helper in options.helpers) {
  770. engine.registerHelper(helper, options.helpers[helper]);
  771. }
  772. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  773. cb(null, tmpl(options));
  774. } catch (err) {
  775. cb(err);
  776. }
  777. });
  778. };
  779. /**
  780. * Underscore support.
  781. */
  782. exports.underscore = fromStringRenderer('underscore');
  783. /**
  784. * Underscore string support.
  785. */
  786. exports.underscore.render = function(str, options, cb) {
  787. return promisify(cb, function(cb) {
  788. var engine = requires.underscore || (requires.underscore = require('underscore'));
  789. try {
  790. const partials = {};
  791. for (var partial in options.partials) {
  792. partials[partial] = engine.template(options.partials[partial]);
  793. }
  794. options.partials = partials;
  795. var tmpl = cache(options) || cache(options, engine.template(str, null, options));
  796. cb(null, tmpl(options).replace(/\n$/, ''));
  797. } catch (err) {
  798. cb(err);
  799. }
  800. });
  801. };
  802. /**
  803. * Lodash support.
  804. */
  805. exports.lodash = fromStringRenderer('lodash');
  806. /**
  807. * Lodash string support.
  808. */
  809. exports.lodash.render = function(str, options, cb) {
  810. return promisify(cb, function(cb) {
  811. var engine = requires.lodash || (requires.lodash = require('lodash'));
  812. try {
  813. var tmpl = cache(options) || cache(options, engine.template(str, options));
  814. cb(null, tmpl(options).replace(/\n$/, ''));
  815. } catch (err) {
  816. cb(err);
  817. }
  818. });
  819. };
  820. /**
  821. * Pug support. (formerly Jade)
  822. */
  823. exports.pug = function(path, options, cb) {
  824. return promisify(cb, function(cb) {
  825. var engine = requires.pug;
  826. if (!engine) {
  827. try {
  828. engine = requires.pug = require('pug');
  829. } catch (err) {
  830. try {
  831. engine = requires.pug = require('then-pug');
  832. } catch (otherError) {
  833. throw err;
  834. }
  835. }
  836. }
  837. try {
  838. var tmpl = cache(options) || cache(options, engine.compileFile(path, options));
  839. cb(null, tmpl(options));
  840. } catch (err) {
  841. cb(err);
  842. }
  843. });
  844. };
  845. /**
  846. * Pug string support.
  847. */
  848. exports.pug.render = function(str, options, cb) {
  849. return promisify(cb, function(cb) {
  850. var engine = requires.pug;
  851. if (!engine) {
  852. try {
  853. engine = requires.pug = require('pug');
  854. } catch (err) {
  855. try {
  856. engine = requires.pug = require('then-pug');
  857. } catch (otherError) {
  858. throw err;
  859. }
  860. }
  861. }
  862. try {
  863. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  864. cb(null, tmpl(options));
  865. } catch (err) {
  866. cb(err);
  867. }
  868. });
  869. };
  870. /**
  871. * QEJS support.
  872. */
  873. exports.qejs = fromStringRenderer('qejs');
  874. /**
  875. * QEJS string support.
  876. */
  877. exports.qejs.render = function(str, options, cb) {
  878. return promisify(cb, function(cb) {
  879. try {
  880. var engine = requires.qejs || (requires.qejs = require('qejs'));
  881. engine.render(str, options).then(function(result) {
  882. cb(null, result);
  883. }, function(err) {
  884. cb(err);
  885. }).done();
  886. } catch (err) {
  887. cb(err);
  888. }
  889. });
  890. };
  891. /**
  892. * Walrus support.
  893. */
  894. exports.walrus = fromStringRenderer('walrus');
  895. /**
  896. * Walrus string support.
  897. */
  898. exports.walrus.render = function(str, options, cb) {
  899. return promisify(cb, function(cb) {
  900. var engine = requires.walrus || (requires.walrus = require('walrus'));
  901. try {
  902. var tmpl = cache(options) || cache(options, engine.parse(str));
  903. cb(null, tmpl.compile(options));
  904. } catch (err) {
  905. cb(err);
  906. }
  907. });
  908. };
  909. /**
  910. * Mustache support.
  911. */
  912. exports.mustache = fromStringRenderer('mustache');
  913. /**
  914. * Mustache string support.
  915. */
  916. exports.mustache.render = function(str, options, cb) {
  917. return promisify(cb, function(cb) {
  918. var engine = requires.mustache || (requires.mustache = require('mustache'));
  919. try {
  920. cb(null, engine.render(str, options, options.partials));
  921. } catch (err) {
  922. cb(err);
  923. }
  924. });
  925. };
  926. /**
  927. * Just support.
  928. */
  929. exports.just = function(path, options, cb) {
  930. return promisify(cb, function(cb) {
  931. var engine = requires.just;
  932. if (!engine) {
  933. var JUST = require('just');
  934. engine = requires.just = new JUST();
  935. }
  936. engine.configure({ useCache: options.cache });
  937. engine.render(path, options, cb);
  938. });
  939. };
  940. /**
  941. * Just string support.
  942. */
  943. exports.just.render = function(str, options, cb) {
  944. return promisify(cb, function(cb) {
  945. var JUST = require('just');
  946. var engine = new JUST({ root: { page: str }});
  947. engine.render('page', options, cb);
  948. });
  949. };
  950. /**
  951. * ECT support.
  952. */
  953. exports.ect = function(path, options, cb) {
  954. return promisify(cb, function(cb) {
  955. var engine = requires.ect;
  956. if (!engine) {
  957. var ECT = require('ect');
  958. engine = requires.ect = new ECT(options);
  959. }
  960. engine.configure({ cache: options.cache });
  961. engine.render(path, options, cb);
  962. });
  963. };
  964. /**
  965. * ECT string support.
  966. */
  967. exports.ect.render = function(str, options, cb) {
  968. return promisify(cb, function(cb) {
  969. var ECT = require('ect');
  970. var engine = new ECT({ root: { page: str }});
  971. engine.render('page', options, cb);
  972. });
  973. };
  974. /**
  975. * mote support.
  976. */
  977. exports.mote = fromStringRenderer('mote');
  978. /**
  979. * mote string support.
  980. */
  981. exports.mote.render = function(str, options, cb) {
  982. return promisify(cb, function(cb) {
  983. var engine = requires.mote || (requires.mote = require('mote'));
  984. try {
  985. var tmpl = cache(options) || cache(options, engine.compile(str));
  986. cb(null, tmpl(options));
  987. } catch (err) {
  988. cb(err);
  989. }
  990. });
  991. };
  992. /**
  993. * Toffee support.
  994. */
  995. exports.toffee = function(path, options, cb) {
  996. return promisify(cb, function(cb) {
  997. var toffee = requires.toffee || (requires.toffee = require('toffee'));
  998. toffee.__consolidate_engine_render(path, options, cb);
  999. });
  1000. };
  1001. /**
  1002. * Toffee string support.
  1003. */
  1004. exports.toffee.render = function(str, options, cb) {
  1005. return promisify(cb, function(cb) {
  1006. var engine = requires.toffee || (requires.toffee = require('toffee'));
  1007. try {
  1008. engine.str_render(str, options, cb);
  1009. } catch (err) {
  1010. cb(err);
  1011. }
  1012. });
  1013. };
  1014. /**
  1015. * doT support.
  1016. */
  1017. exports.dot = fromStringRenderer('dot');
  1018. /**
  1019. * doT string support.
  1020. */
  1021. exports.dot.render = function(str, options, cb) {
  1022. return promisify(cb, function(cb) {
  1023. var engine = requires.dot || (requires.dot = require('dot'));
  1024. var extend = (requires.extend || (requires.extend = require('util')._extend));
  1025. try {
  1026. var settings = {};
  1027. settings = extend(settings, engine.templateSettings);
  1028. settings = extend(settings, options ? options.dot : {});
  1029. var tmpl = cache(options) || cache(options, engine.template(str, settings, options));
  1030. cb(null, tmpl(options));
  1031. } catch (err) {
  1032. cb(err);
  1033. }
  1034. });
  1035. };
  1036. /**
  1037. * bracket support.
  1038. */
  1039. exports.bracket = fromStringRenderer('bracket');
  1040. /**
  1041. * bracket string support.
  1042. */
  1043. exports.bracket.render = function(str, options, cb) {
  1044. return promisify(cb, function(cb) {
  1045. var engine = requires.bracket || (requires.bracket = require('bracket-template'));
  1046. try {
  1047. var tmpl = cache(options) || cache(options, engine.default.compile(str, options));
  1048. cb(null, tmpl(options));
  1049. } catch (err) {
  1050. cb(err);
  1051. }
  1052. });
  1053. };
  1054. /**
  1055. * Ractive support.
  1056. */
  1057. exports.ractive = fromStringRenderer('ractive');
  1058. /**
  1059. * Ractive string support.
  1060. */
  1061. exports.ractive.render = function(str, options, cb) {
  1062. return promisify(cb, function(cb) {
  1063. var Engine = requires.ractive || (requires.ractive = require('ractive'));
  1064. var template = cache(options) || cache(options, Engine.parse(str));
  1065. options.template = template;
  1066. if (options.data === null || options.data === undefined) {
  1067. var extend = (requires.extend || (requires.extend = require('util')._extend));
  1068. // Shallow clone the options object
  1069. options.data = extend({}, options);
  1070. // Remove consolidate-specific properties from the clone
  1071. var i;
  1072. var length;
  1073. var properties = ['template', 'filename', 'cache', 'partials'];
  1074. for (i = 0, length = properties.length; i < length; i++) {
  1075. var property = properties[i];
  1076. delete options.data[property];
  1077. }
  1078. }
  1079. try {
  1080. cb(null, new Engine(options).toHTML());
  1081. } catch (err) {
  1082. cb(err);
  1083. }
  1084. });
  1085. };
  1086. /**
  1087. * Nunjucks support.
  1088. */
  1089. exports.nunjucks = fromStringRenderer('nunjucks');
  1090. /**
  1091. * Nunjucks string support.
  1092. */
  1093. exports.nunjucks.render = function(str, options, cb) {
  1094. return promisify(cb, function(cb) {
  1095. try {
  1096. var engine = options.nunjucksEnv || requires.nunjucks || (requires.nunjucks = require('nunjucks'));
  1097. var env = engine;
  1098. // deprecated fallback support for express
  1099. // <https://github.com/tj/consolidate.js/pull/152>
  1100. // <https://github.com/tj/consolidate.js/pull/224>
  1101. if (options.settings && options.settings.views) {
  1102. env = engine.configure(options.settings.views);
  1103. } else if (options.nunjucks && options.nunjucks.configure) {
  1104. env = engine.configure.apply(engine, options.nunjucks.configure);
  1105. }
  1106. //
  1107. // because `renderString` does not initiate loaders
  1108. // we must manually create a loader for it based off
  1109. // either `options.settings.views` or `options.nunjucks` or `options.nunjucks.root`
  1110. //
  1111. // <https://github.com/mozilla/nunjucks/issues/730>
  1112. // <https://github.com/crocodilejs/node-email-templates/issues/182>
  1113. //
  1114. // so instead we simply check if we passed a custom loader
  1115. // otherwise we create a simple file based loader
  1116. if (options.loader) {
  1117. env = new engine.Environment(options.loader);
  1118. } else if (options.settings && options.settings.views) {
  1119. env = new engine.Environment(
  1120. new engine.FileSystemLoader(options.settings.views)
  1121. );
  1122. } else if (options.nunjucks && options.nunjucks.loader) {
  1123. if (typeof options.nunjucks.loader === 'string') {
  1124. env = new engine.Environment(new engine.FileSystemLoader(options.nunjucks.loader));
  1125. } else {
  1126. env = new engine.Environment(
  1127. new engine.FileSystemLoader(
  1128. options.nunjucks.loader[0],
  1129. options.nunjucks.loader[1]
  1130. )
  1131. );
  1132. }
  1133. }
  1134. env.renderString(str, options, cb);
  1135. } catch (err) {
  1136. throw cb(err);
  1137. }
  1138. });
  1139. };
  1140. /**
  1141. * HTMLing support.
  1142. */
  1143. exports.htmling = fromStringRenderer('htmling');
  1144. /**
  1145. * HTMLing string support.
  1146. */
  1147. exports.htmling.render = function(str, options, cb) {
  1148. return promisify(cb, function(cb) {
  1149. var engine = requires.htmling || (requires.htmling = require('htmling'));
  1150. try {
  1151. var tmpl = cache(options) || cache(options, engine.string(str));
  1152. cb(null, tmpl.render(options));
  1153. } catch (err) {
  1154. cb(err);
  1155. }
  1156. });
  1157. };
  1158. /**
  1159. * Rendering function
  1160. */
  1161. function requireReact(module, filename) {
  1162. var babel = requires.babel || (requires.babel = require('babel-core'));
  1163. var compiled = babel.transformFileSync(filename, { presets: [ 'react' ] }).code;
  1164. return module._compile(compiled, filename);
  1165. }
  1166. exports.requireReact = requireReact;
  1167. /**
  1168. * Converting a string into a node module.
  1169. */
  1170. function requireReactString(src, filename) {
  1171. var babel = requires.babel || (requires.babel = require('babel-core'));
  1172. if (!filename) filename = '';
  1173. var m = new module.constructor();
  1174. filename = filename || '';
  1175. // Compile Using React
  1176. var compiled = babel.transform(src, { presets: [ 'react' ] }).code;
  1177. // Compile as a module
  1178. m.paths = module.paths;
  1179. m._compile(compiled, filename);
  1180. return m.exports;
  1181. }
  1182. /**
  1183. * A naive helper to replace {{tags}} with options.tags content
  1184. */
  1185. function reactBaseTmpl(data, options) {
  1186. var exp;
  1187. var regex;
  1188. // Iterates through the keys in file object
  1189. // and interpolate / replace {{key}} with it's value
  1190. for (var k in options) {
  1191. if (options.hasOwnProperty(k)) {
  1192. exp = '{{' + k + '}}';
  1193. regex = new RegExp(exp, 'g');
  1194. if (data.match(regex)) {
  1195. data = data.replace(regex, options[k]);
  1196. }
  1197. }
  1198. }
  1199. return data;
  1200. }
  1201. /**
  1202. * Plates Support.
  1203. */
  1204. exports.plates = fromStringRenderer('plates');
  1205. /**
  1206. * Plates string support.
  1207. */
  1208. exports.plates.render = function(str, options, cb) {
  1209. return promisify(cb, function(cb) {
  1210. var engine = requires.plates || (requires.plates = require('plates'));
  1211. var map = options.map || undefined;
  1212. try {
  1213. var tmpl = engine.bind(str, options, map);
  1214. cb(null, tmpl);
  1215. } catch (err) {
  1216. cb(err);
  1217. }
  1218. });
  1219. };
  1220. /**
  1221. * The main render parser for React bsaed templates
  1222. */
  1223. function reactRenderer(type) {
  1224. if (require.extensions) {
  1225. // Ensure JSX is transformed on require
  1226. if (!require.extensions['.jsx']) {
  1227. require.extensions['.jsx'] = requireReact;
  1228. }
  1229. // Supporting .react extension as well as test cases
  1230. // Using .react extension is not recommended.
  1231. if (!require.extensions['.react']) {
  1232. require.extensions['.react'] = requireReact;
  1233. }
  1234. }
  1235. // Return rendering fx
  1236. return function(str, options, cb) {
  1237. return promisify(cb, function(cb) {
  1238. // React Import
  1239. var ReactDOM = requires.ReactDOM || (requires.ReactDOM = require('react-dom/server'));
  1240. var react = requires.react || (requires.react = require('react'));
  1241. // Assign HTML Base
  1242. var base = options.base;
  1243. delete options.base;
  1244. var enableCache = options.cache;
  1245. delete options.cache;
  1246. var isNonStatic = options.isNonStatic;
  1247. delete options.isNonStatic;
  1248. // Start Conversion
  1249. try {
  1250. var Code;
  1251. var Factory;
  1252. var baseStr;
  1253. var content;
  1254. var parsed;
  1255. if (!cache(options)) {
  1256. // Parsing
  1257. if (type === 'path') {
  1258. var path = resolve(str);
  1259. delete require.cache[path];
  1260. Code = require(path);
  1261. } else {
  1262. Code = requireReactString(str);
  1263. }
  1264. Factory = cache(options, react.createFactory(Code));
  1265. } else {
  1266. Factory = cache(options);
  1267. }
  1268. parsed = new Factory(options);
  1269. content = (isNonStatic) ? ReactDOM.renderToString(parsed) : ReactDOM.renderToStaticMarkup(parsed);
  1270. if (base) {
  1271. baseStr = readCache[str] || fs.readFileSync(resolve(base), 'utf8');
  1272. if (enableCache) {
  1273. readCache[str] = baseStr;
  1274. }
  1275. options.content = content;
  1276. content = reactBaseTmpl(baseStr, options);
  1277. }
  1278. cb(null, content);
  1279. } catch (err) {
  1280. cb(err);
  1281. }
  1282. });
  1283. };
  1284. }
  1285. /**
  1286. * React JS Support
  1287. */
  1288. exports.react = reactRenderer('path');
  1289. /**
  1290. * React JS string support.
  1291. */
  1292. exports.react.render = reactRenderer('string');
  1293. /**
  1294. * ARC-templates support.
  1295. */
  1296. exports['arc-templates'] = fromStringRenderer('arc-templates');
  1297. /**
  1298. * ARC-templates string support.
  1299. */
  1300. exports['arc-templates'].render = function(str, options, cb) {
  1301. var readFileWithOptions = Promise.promisify(read);
  1302. var consolidateFileSystem = {};
  1303. consolidateFileSystem.readFile = function(path) {
  1304. return readFileWithOptions(path, options);
  1305. };
  1306. return promisify(cb, function(cb) {
  1307. try {
  1308. var engine = requires['arc-templates'];
  1309. if (!engine) {
  1310. var Engine = require('arc-templates/dist/es5');
  1311. engine = requires['arc-templates'] = new Engine({ filesystem: consolidateFileSystem });
  1312. }
  1313. var compiler = cache(options) || cache(options, engine.compileString(str, options.filename));
  1314. compiler.then(function(func) { return func(options); })
  1315. .then(function(result) { cb(null, result.content); })
  1316. .catch(cb);
  1317. } catch (err) {
  1318. cb(err);
  1319. }
  1320. });
  1321. };
  1322. /**
  1323. * Vash support
  1324. */
  1325. exports.vash = fromStringRenderer('vash');
  1326. /**
  1327. * Vash string support
  1328. */
  1329. exports.vash.render = function(str, options, cb) {
  1330. return promisify(cb, function(cb) {
  1331. var engine = requires.vash || (requires.vash = require('vash'));
  1332. try {
  1333. // helper system : https://github.com/kirbysayshi/vash#helper-system
  1334. if (options.helpers) {
  1335. for (var key in options.helpers) {
  1336. if (!options.helpers.hasOwnProperty(key) || typeof options.helpers[key] !== 'function') {
  1337. continue;
  1338. }
  1339. engine.helpers[key] = options.helpers[key];
  1340. }
  1341. }
  1342. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  1343. tmpl(options, function sealLayout(err, ctx) {
  1344. if (err) cb(err);
  1345. ctx.finishLayout();
  1346. cb(null, ctx.toString().replace(/\n$/, ''));
  1347. });
  1348. } catch (err) {
  1349. cb(err);
  1350. }
  1351. });
  1352. };
  1353. /**
  1354. * Slm support.
  1355. */
  1356. exports.slm = fromStringRenderer('slm');
  1357. /**
  1358. * Slm string support.
  1359. */
  1360. exports.slm.render = function(str, options, cb) {
  1361. return promisify(cb, function(cb) {
  1362. var engine = requires.slm || (requires.slm = require('slm'));
  1363. try {
  1364. var tmpl = cache(options) || cache(options, engine.compile(str, options));
  1365. cb(null, tmpl(options));
  1366. } catch (err) {
  1367. cb(err);
  1368. }
  1369. });
  1370. };
  1371. /**
  1372. * Marko support.
  1373. */
  1374. exports.marko = function(path, options, cb) {
  1375. return promisify(cb, function(cb) {
  1376. var engine = requires.marko || (requires.marko = require('marko'));
  1377. options.writeToDisk = !!options.cache;
  1378. try {
  1379. var tmpl = cache(options) || cache(options, engine.load(path, options));
  1380. tmpl.renderToString(options, cb);
  1381. } catch (err) {
  1382. cb(err);
  1383. }
  1384. });
  1385. };
  1386. /**
  1387. * Marko string support.
  1388. */
  1389. exports.marko.render = function(str, options, cb) {
  1390. return promisify(cb, function(cb) {
  1391. var engine = requires.marko || (requires.marko = require('marko'));
  1392. options.writeToDisk = !!options.cache;
  1393. options.filename = options.filename || 'string.marko';
  1394. try {
  1395. var tmpl = cache(options) || cache(options, engine.load(options.filename, str, options));
  1396. tmpl.renderToString(options, cb);
  1397. } catch (err) {
  1398. cb(err);
  1399. }
  1400. });
  1401. };
  1402. /**
  1403. * Teacup support.
  1404. */
  1405. exports.teacup = function(path, options, cb) {
  1406. return promisify(cb, function(cb) {
  1407. var engine = requires.teacup || (requires.teacup = require('teacup/lib/express'));
  1408. require.extensions['.teacup'] = require.extensions['.coffee'];
  1409. if (path[0] !== '/') {
  1410. path = join(process.cwd(), path);
  1411. }
  1412. if (!options.cache) {
  1413. var callback = cb;
  1414. cb = function() {
  1415. delete require.cache[path];
  1416. callback.apply(this, arguments);
  1417. };
  1418. }
  1419. engine.renderFile(path, options, cb);
  1420. });
  1421. };
  1422. /**
  1423. * Teacup string support.
  1424. */
  1425. exports.teacup.render = function(str, options, cb) {
  1426. var coffee = require('coffee-script');
  1427. var vm = require('vm');
  1428. var sandbox = {
  1429. module: {exports: {}},
  1430. require: require
  1431. };
  1432. return promisify(cb, function(cb) {
  1433. vm.runInNewContext(coffee.compile(str), sandbox);
  1434. var tmpl = sandbox.module.exports;
  1435. cb(null, tmpl(options));
  1436. });
  1437. };
  1438. /**
  1439. * Squirrelly support.
  1440. */
  1441. exports.squirrelly = fromStringRenderer('squirrelly');
  1442. /**
  1443. * Squirrelly string support.
  1444. */
  1445. exports.squirrelly.render = function(str, options, cb) {
  1446. return promisify(cb, function(cb) {
  1447. var engine = requires.squirrelly || (requires.squirrelly = require('squirrelly'));
  1448. try {
  1449. for (var partial in options.partials) {
  1450. engine.definePartial(partial, options.partials[partial]);
  1451. }
  1452. for (var helper in options.helpers) {
  1453. engine.defineHelper(helper, options.helpers[helper]);
  1454. }
  1455. var tmpl = cache(options) || cache(options, engine.Compile(str, options));
  1456. cb(null, tmpl(options, engine));
  1457. } catch (err) {
  1458. cb(err);
  1459. }
  1460. });
  1461. };
  1462. /**
  1463. * Twing support.
  1464. */
  1465. exports.twing = fromStringRenderer('twing');
  1466. /**
  1467. * Twing string support.
  1468. */
  1469. exports.twing.render = function(str, options, cb) {
  1470. return promisify(cb, function(cb) {
  1471. var engine = requires.twing || (requires.twing = require('twing'));
  1472. try {
  1473. new engine.TwingEnvironment(new engine.TwingLoaderNull()).createTemplate(str).then((twingTemplate) => {
  1474. twingTemplate.render(options).then((rendTmpl) => {
  1475. var tmpl = cache(options) || cache(options, rendTmpl);
  1476. cb(null, tmpl);
  1477. });
  1478. });
  1479. } catch (err) {
  1480. cb(err);
  1481. }
  1482. });
  1483. };
  1484. /**
  1485. * expose the instance of the engine
  1486. */
  1487. exports.requires = requires;