1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | Object.defineProperty(exports, "__esModule", { value: true });
|
10 | exports.template = exports.templateParser = void 0;
|
11 | const source_map_1 = require("source-map");
|
12 |
|
13 | const kInterpolateRe = /<%=([\s\S]+?)%>/g;
|
14 |
|
15 | const kCommentRe = /<%#([\s\S]+?)%>/g;
|
16 |
|
17 |
|
18 |
|
19 | const kEscapeRe = /<%-([\s\S]+?)%>/g;
|
20 | const kEvaluateRe = /<%([\s\S]+?)%>/g;
|
21 |
|
22 | const kHtmlEscapes = {
|
23 | '&': '&',
|
24 | '<': '<',
|
25 | '>': '>',
|
26 | '"': '"',
|
27 | "'": ''',
|
28 | '`': '`',
|
29 | };
|
30 |
|
31 | const reUnescapedHtml = new RegExp(`[${Object.keys(kHtmlEscapes).join('')}]`, 'g');
|
32 | function _positionFor(content, offset) {
|
33 | let line = 1;
|
34 | let column = 0;
|
35 | for (let i = 0; i < offset - 1; i++) {
|
36 | if (content[i] == '\n') {
|
37 | line++;
|
38 | column = 0;
|
39 | }
|
40 | else {
|
41 | column++;
|
42 | }
|
43 | }
|
44 | return {
|
45 | line,
|
46 | column,
|
47 | };
|
48 | }
|
49 |
|
50 |
|
51 |
|
52 | function templateParser(sourceText, fileName) {
|
53 | const children = [];
|
54 |
|
55 | const reExpressions = [kEscapeRe, kCommentRe, kInterpolateRe, kEvaluateRe];
|
56 | const reDelimiters = RegExp(reExpressions.map((x) => x.source).join('|') + '|$', 'g');
|
57 | const parsed = sourceText.split(reDelimiters);
|
58 | let offset = 0;
|
59 |
|
60 |
|
61 | let start = _positionFor(sourceText, offset);
|
62 | let end;
|
63 | const increment = reExpressions.length + 1;
|
64 | for (let i = 0; i < parsed.length; i += increment) {
|
65 | const [content, escape, comment, interpolate, evaluate] = parsed.slice(i, i + increment);
|
66 | if (content) {
|
67 | end = _positionFor(sourceText, offset + content.length);
|
68 | offset += content.length;
|
69 | children.push({ kind: 'content', content, start, end });
|
70 | start = end;
|
71 | }
|
72 | if (escape) {
|
73 | end = _positionFor(sourceText, offset + escape.length + 5);
|
74 | offset += escape.length + 5;
|
75 | children.push({ kind: 'escape', expression: escape, start, end });
|
76 | start = end;
|
77 | }
|
78 | if (comment) {
|
79 | end = _positionFor(sourceText, offset + comment.length + 5);
|
80 | offset += comment.length + 5;
|
81 | children.push({ kind: 'comment', text: comment, start, end });
|
82 | start = end;
|
83 | }
|
84 | if (interpolate) {
|
85 | end = _positionFor(sourceText, offset + interpolate.length + 5);
|
86 | offset += interpolate.length + 5;
|
87 | children.push({
|
88 | kind: 'interpolate',
|
89 | expression: interpolate,
|
90 | start,
|
91 | end,
|
92 | });
|
93 | start = end;
|
94 | }
|
95 | if (evaluate) {
|
96 | end = _positionFor(sourceText, offset + evaluate.length + 5);
|
97 | offset += evaluate.length + 5;
|
98 | children.push({ kind: 'evaluate', expression: evaluate, start, end });
|
99 | start = end;
|
100 | }
|
101 | }
|
102 | return {
|
103 | fileName,
|
104 | content: sourceText,
|
105 | children,
|
106 | };
|
107 | }
|
108 | exports.templateParser = templateParser;
|
109 |
|
110 |
|
111 |
|
112 |
|
113 | function templateFast(ast, options) {
|
114 | const module = options && options.module ? 'module.exports.default =' : '';
|
115 | const reHtmlEscape = reUnescapedHtml.source.replace(/[']/g, "\\\\\\'");
|
116 | return `
|
117 | return ${module} function(obj) {
|
118 | obj || (obj = {});
|
119 | let __t;
|
120 | let __p = '';
|
121 | const __escapes = ${JSON.stringify(kHtmlEscapes)};
|
122 | const __escapesre = new RegExp('${reHtmlEscape}', 'g');
|
123 |
|
124 | const __e = function(s) {
|
125 | return s ? s.replace(__escapesre, function(key) { return __escapes[key]; }) : '';
|
126 | };
|
127 | with (obj) {
|
128 | ${ast.children
|
129 | .map((node) => {
|
130 | switch (node.kind) {
|
131 | case 'content':
|
132 | return `__p += ${JSON.stringify(node.content)};`;
|
133 | case 'interpolate':
|
134 | return `__p += ((__t = (${node.expression})) == null) ? '' : __t;`;
|
135 | case 'escape':
|
136 | return `__p += __e(${node.expression});`;
|
137 | case 'evaluate':
|
138 | return node.expression;
|
139 | }
|
140 | })
|
141 | .join('\n')}
|
142 | }
|
143 |
|
144 | return __p;
|
145 | };
|
146 | `;
|
147 | }
|
148 |
|
149 |
|
150 |
|
151 | function templateWithSourceMap(ast, options) {
|
152 | const sourceUrl = ast.fileName;
|
153 | const module = options && options.module ? 'module.exports.default =' : '';
|
154 | const reHtmlEscape = reUnescapedHtml.source.replace(/[']/g, "\\\\\\'");
|
155 | const preamble = new source_map_1.SourceNode(1, 0, sourceUrl, '').add(new source_map_1.SourceNode(1, 0, sourceUrl, [
|
156 | `return ${module} function(obj) {\n`,
|
157 | ' obj || (obj = {});\n',
|
158 | ' let __t;\n',
|
159 | ' let __p = "";\n',
|
160 | ` const __escapes = ${JSON.stringify(kHtmlEscapes)};\n`,
|
161 | ` const __escapesre = new RegExp('${reHtmlEscape}', 'g');\n`,
|
162 | `\n`,
|
163 | ` const __e = function(s) { `,
|
164 | ` return s ? s.replace(__escapesre, function(key) { return __escapes[key]; }) : '';`,
|
165 | ` };\n`,
|
166 | ` with (obj) {\n`,
|
167 | ]));
|
168 | const end = ast.children.length
|
169 | ? ast.children[ast.children.length - 1].end
|
170 | : { line: 0, column: 0 };
|
171 | const nodes = ast.children
|
172 | .reduce((chunk, node) => {
|
173 | let code = '';
|
174 | switch (node.kind) {
|
175 | case 'content':
|
176 | code = [
|
177 | new source_map_1.SourceNode(node.start.line, node.start.column, sourceUrl, '__p = __p'),
|
178 | ...node.content.split('\n').map((line, i, arr) => {
|
179 | return new source_map_1.SourceNode(node.start.line + i, i == 0 ? node.start.column : 0, sourceUrl, '\n + ' + JSON.stringify(line + (i == arr.length - 1 ? '' : '\n')));
|
180 | }),
|
181 | new source_map_1.SourceNode(node.end.line, node.end.column, sourceUrl, ';\n'),
|
182 | ];
|
183 | break;
|
184 | case 'interpolate':
|
185 | code = [
|
186 | new source_map_1.SourceNode(node.start.line, node.start.column, sourceUrl, '__p += ((__t = '),
|
187 | ...node.expression.split('\n').map((line, i, arr) => {
|
188 | return new source_map_1.SourceNode(node.start.line + i, i == 0 ? node.start.column : 0, sourceUrl, line + (i == arr.length - 1 ? '' : '\n'));
|
189 | }),
|
190 | new source_map_1.SourceNode(node.end.line, node.end.column, sourceUrl, ') == null ? "" : __t);\n'),
|
191 | ];
|
192 | break;
|
193 | case 'escape':
|
194 | code = [
|
195 | new source_map_1.SourceNode(node.start.line, node.start.column, sourceUrl, '__p += __e('),
|
196 | ...node.expression.split('\n').map((line, i, arr) => {
|
197 | return new source_map_1.SourceNode(node.start.line + i, i == 0 ? node.start.column : 0, sourceUrl, line + (i == arr.length - 1 ? '' : '\n'));
|
198 | }),
|
199 | new source_map_1.SourceNode(node.end.line, node.end.column, sourceUrl, ');\n'),
|
200 | ];
|
201 | break;
|
202 | case 'evaluate':
|
203 | code = [
|
204 | ...node.expression.split('\n').map((line, i, arr) => {
|
205 | return new source_map_1.SourceNode(node.start.line + i, i == 0 ? node.start.column : 0, sourceUrl, line + (i == arr.length - 1 ? '' : '\n'));
|
206 | }),
|
207 | new source_map_1.SourceNode(node.end.line, node.end.column, sourceUrl, '\n'),
|
208 | ];
|
209 | break;
|
210 | }
|
211 | return chunk.add(new source_map_1.SourceNode(node.start.line, node.start.column, sourceUrl, code));
|
212 | }, preamble)
|
213 | .add(new source_map_1.SourceNode(end.line, end.column, sourceUrl, [' };\n', '\n', ' return __p;\n', '}\n']));
|
214 | const code = nodes.toStringWithSourceMap({
|
215 | file: sourceUrl,
|
216 | sourceRoot: (options && options.sourceRoot) || '.',
|
217 | });
|
218 |
|
219 |
|
220 | code.map.setSourceContent(sourceUrl, ast.content);
|
221 | return (code.code +
|
222 | '\n//# sourceMappingURL=data:application/json;base64,' +
|
223 | Buffer.from(code.map.toString()).toString('base64'));
|
224 | }
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 | function template(content, options) {
|
241 | const sourceUrl = (options && options.sourceURL) || 'ejs';
|
242 | const ast = templateParser(content, sourceUrl);
|
243 | let source;
|
244 |
|
245 | if (options && options.sourceMap) {
|
246 | source = templateWithSourceMap(ast, options);
|
247 | }
|
248 | else {
|
249 | source = templateFast(ast, options);
|
250 | }
|
251 |
|
252 |
|
253 |
|
254 | const fn = Function('module', source);
|
255 | const module = options && options.module ? (options.module === true ? { exports: {} } : options.module) : null;
|
256 | const result = fn(module);
|
257 |
|
258 |
|
259 | result.source = source;
|
260 | return result;
|
261 | }
|
262 | exports.template = template;
|