This repository has been archived by the owner on Apr 11, 2018. It is now read-only.
/
index.js
146 lines (126 loc) · 4.08 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
var fs = require('fs'),
path = require('path'),
tags = require('./lib/tags'),
parser = require('./lib/parser'),
filters = require('./lib/filters'),
helpers = require('./lib/helpers'),
_ = require('underscore'),
config,
CACHE = {};
// Call this before using the templates
exports.init = function (options) {
config = _.extend({
allowErrors: false,
autoescape: true,
encoding: 'utf8',
root: '/',
}, options);
config.filters = _.extend(filters, options.filters);
config.tags = _.extend(tags, options.tags);
};
function TemplateError(error) {
return { render: function () {
return '<pre>' + error.stack + '</pre>';
}};
}
function createTemplate(data, id) {
var template = {
// Allows us to include templates from the compiled code
fromFile: exports.fromFile,
// These are the blocks inside the template
blocks: {},
// Distinguish from other tokens
type: parser.TEMPLATE,
// The template ID (path relative to tempalte dir)
id: id
},
tokens,
code,
render;
// The template token tree before compiled into javascript
if (config.allowErrors) {
template.tokens = parser.parse.call(template, data, config.tags, config.autoescape);
} else {
try {
template.tokens = parser.parse.call(template, data, config.tags, config.autoescape);
} catch (e) {
return new TemplateError(e);
}
}
// The raw template code
code = parser.compile.call(template);
// The compiled render function - this is all we need
render = new Function('__context', '__parents', '__filters', '__escape', '_', [
'__parents = __parents ? __parents.slice() : [];',
// Prevents circular includes (which will crash node without warning)
'var j = __parents.length,',
' __output = "",',
' __this = this;',
// Note: this loop averages much faster than indexOf across all cases
'while (j--) {',
' if (__parents[j] === this.id) {',
' return "Circular import of template " + this.id + " in " + __parents[__parents.length-1];',
' }',
'}',
// Add this template as a parent to all includes in its scope
'__parents.push(this.id);',
code,
'return __output;',
].join(''));
template.render = function (context, parents) {
if (config.allowErrors) {
return render.call(this, context, parents, config.filters, helpers.escaper, _);
} else {
try {
return render.call(this, context, parents, config.filters, helpers.escaper, _);
} catch (e) {
return new TemplateError(e);
}
}
};
return template;
}
exports.fromFile = function (filepath) {
if (filepath[0] === '/') {
filepath = filepath.substr(1);
}
if (CACHE.hasOwnProperty(filepath)) {
return CACHE[filepath];
}
var get = function () {
var file = ((/^\//).test(filepath)) ? filepath : config.root + '/' + filepath,
data = fs.readFileSync(file, config.encoding);
CACHE[filepath] = createTemplate(data, filepath);
};
if (config.allowErrors) {
get();
} else {
try {
get();
} catch (error) {
CACHE[filepath] = new TemplateError(error);
}
}
return CACHE[filepath];
};
exports.fromString = function (string) {
if (!CACHE.hasOwnProperty(string)) {
CACHE[string] = createTemplate(string, string);
}
return CACHE[string];
};
exports.compile = function (source, options, callback) {
var self = this;
if (typeof source === 'string') {
return function (options) {
var tmpl = exports.fromString(source);
return tmpl.render(options);
};
} else {
return source;
}
};
exports.render = function (template, options) {
template = exports.compile(template, options);
return template(options);
};