UNPKG

4.59 kBJavaScriptView Raw
1var fs = require('fs'),
2 path = require('path'),
3
4 tags = require('./lib/tags'),
5 parser = require('./lib/parser'),
6 filters = require('./lib/filters'),
7 helpers = require('./lib/helpers'),
8 dateformat = require('./lib/dateformat'),
9
10 _ = require('underscore'),
11
12 config = {
13 allowErrors: false,
14 autoescape: true,
15 cache: true,
16 encoding: 'utf8',
17 filters: filters,
18 root: '/',
19 tags: tags,
20 extensions: {},
21 tzOffset: 0
22 },
23 _config = _.extend({}, config),
24 CACHE = {};
25
26// Call this before using the templates
27exports.init = function (options) {
28 CACHE = {};
29 _config = _.extend({}, config, options);
30 _config.filters = _.extend(filters, options.filters);
31 _config.tags = _.extend(tags, options.tags);
32
33 dateformat.defaultTZOffset = _config.tzOffset;
34};
35
36function TemplateError(error) {
37 return { render: function () {
38 return '<pre>' + error.stack + '</pre>';
39 }};
40}
41
42function createTemplate(data, id) {
43 var template = {
44 // Allows us to include templates from the compiled code
45 compileFile: exports.compileFile,
46 // These are the blocks inside the template
47 blocks: {},
48 // Distinguish from other tokens
49 type: parser.TEMPLATE,
50 // The template ID (path relative to tempalte dir)
51 id: id
52 },
53 tokens,
54 code,
55 render;
56
57 // The template token tree before compiled into javascript
58 if (_config.allowErrors) {
59 template.tokens = parser.parse.call(template, data, _config.tags, _config.autoescape);
60 } else {
61 try {
62 template.tokens = parser.parse.call(template, data, _config.tags, _config.autoescape);
63 } catch (e) {
64 return new TemplateError(e);
65 }
66 }
67
68 // The raw template code
69 code = parser.compile.call(template);
70
71 // The compiled render function - this is all we need
72 render = new Function('_context', '_parents', '_filters', '_', '_ext', [
73 '_parents = _parents ? _parents.slice() : [];',
74 '_context = _context || {};',
75 // Prevents circular includes (which will crash node without warning)
76 'var j = _parents.length,',
77 ' _output = "",',
78 ' _this = this;',
79 // Note: this loop averages much faster than indexOf across all cases
80 'while (j--) {',
81 ' if (_parents[j] === this.id) {',
82 ' return "Circular import of template " + this.id + " in " + _parents[_parents.length-1];',
83 ' }',
84 '}',
85 // Add this template as a parent to all includes in its scope
86 '_parents.push(this.id);',
87 code,
88 'return _output;',
89 ].join(''));
90
91 template.render = function (context, parents) {
92 if (_config.allowErrors) {
93 return render.call(this, context, parents, _config.filters, _, _config.extensions);
94 }
95 try {
96 return render.call(this, context, parents, _config.filters, _, _config.extensions);
97 } catch (e) {
98 return new TemplateError(e);
99 }
100 };
101
102 return template;
103}
104
105function getTemplate(source, options) {
106 var key = options.filename || source;
107 if (_config.cache || options.cache) {
108 if (!CACHE.hasOwnProperty(key)) {
109 CACHE[key] = createTemplate(source, key);
110 }
111
112 return CACHE[key];
113 }
114
115 return createTemplate(source, key);
116}
117
118exports.compileFile = function (filepath) {
119 var tpl, get;
120
121 if (filepath[0] === '/') {
122 filepath = filepath.substr(1);
123 }
124
125 if (_config.cache && CACHE.hasOwnProperty(filepath)) {
126 return CACHE[filepath];
127 }
128
129 if (typeof window !== 'undefined') {
130 throw new TemplateError({ stack: 'You must pre-compile all templates in-browser. Use `swig.compile(template);`.' });
131 }
132
133 get = function () {
134 var file = ((/^\//).test(filepath) || (/^.:/).test(filepath)) ? filepath : _config.root + '/' + filepath,
135 data = fs.readFileSync(file, config.encoding);
136 tpl = getTemplate(data, { filename: filepath });
137 };
138
139 if (_config.allowErrors) {
140 get();
141 } else {
142 try {
143 get();
144 } catch (error) {
145 tpl = new TemplateError(error);
146 }
147 }
148 return tpl;
149};
150
151exports.compile = function (source, options) {
152 options = options || {};
153 var tmpl = getTemplate(source, options || {});
154
155 return function (source, options) {
156 return tmpl.render(source, options);
157 };
158};