UNPKG

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