@rhinostone/swig-core 2.7.2 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cache.js +4 -1
- package/lib/engine.js +118 -2
- package/lib/loaders/memory.js +8 -0
- package/lib/runtime.js +327 -0
- package/lib/tokenparser.js +5 -0
- package/package.json +1 -1
package/lib/cache.js
CHANGED
|
@@ -43,7 +43,10 @@ exports.cacheGet = function (key, options, engineCache, memoryStore) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
if (engineCache === 'memory') {
|
|
46
|
-
|
|
46
|
+
// The store is a plain object, so an identifier-style loader that returns
|
|
47
|
+
// bare names (the loader contract allows it) could otherwise read an
|
|
48
|
+
// inherited member such as "constructor" as a cache hit.
|
|
49
|
+
return Object.prototype.hasOwnProperty.call(memoryStore, key) ? memoryStore[key] : undefined;
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
return engineCache.get(key);
|
package/lib/engine.js
CHANGED
|
@@ -191,7 +191,15 @@ exports.getParents = function (tokens, options, deps) {
|
|
|
191
191
|
|
|
192
192
|
parentFile = parentFile || options.filename;
|
|
193
193
|
parentFile = deps.resolve(parentName, parentFile);
|
|
194
|
-
|
|
194
|
+
// Only a compile-cached entry carries the parse tree (compile extends the
|
|
195
|
+
// wrapper with the token properties); a registered template function does
|
|
196
|
+
// not. Falling through to parseFile keeps a registration from shadowing a
|
|
197
|
+
// loadable parent, which would otherwise remap blocks against an undefined
|
|
198
|
+
// token list and render an empty string with no error.
|
|
199
|
+
parent = deps.cacheGet(parentFile, options);
|
|
200
|
+
if (!parent || !parent.tokens) {
|
|
201
|
+
parent = deps.parseFile(parentFile, utils.extend({}, options, { filename: parentFile }));
|
|
202
|
+
}
|
|
195
203
|
parentName = parent.parent;
|
|
196
204
|
|
|
197
205
|
if (parentFiles.indexOf(parentFile) !== -1) {
|
|
@@ -322,6 +330,111 @@ exports.install = function (self, frontend) {
|
|
|
322
330
|
}
|
|
323
331
|
};
|
|
324
332
|
|
|
333
|
+
/*!
|
|
334
|
+
* Wrap a pre-compiled template function in the locals-binding call shape
|
|
335
|
+
* cached entries have — include-emitted code invokes them as
|
|
336
|
+
* `(context)`, so the raw five-argument function is the wrong shape.
|
|
337
|
+
* @private
|
|
338
|
+
*/
|
|
339
|
+
function buildRegistered(fn) {
|
|
340
|
+
var context = getLocals(),
|
|
341
|
+
contextLength = utils.keys(context).length;
|
|
342
|
+
|
|
343
|
+
return function registered(locals, blocks) {
|
|
344
|
+
var lcls;
|
|
345
|
+
if (locals && contextLength) {
|
|
346
|
+
lcls = utils.extend({}, context, locals);
|
|
347
|
+
} else if (locals && !contextLength) {
|
|
348
|
+
lcls = locals;
|
|
349
|
+
} else if (!locals && contextLength) {
|
|
350
|
+
lcls = context;
|
|
351
|
+
} else {
|
|
352
|
+
lcls = {};
|
|
353
|
+
}
|
|
354
|
+
return fn(self, lcls, filters, utils, efn, blocks);
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/*!
|
|
359
|
+
* Shared argument checks for register / registerBundle, so a bundle can be
|
|
360
|
+
* validated in full before any entry is committed.
|
|
361
|
+
* @private
|
|
362
|
+
*/
|
|
363
|
+
function validateRegistration(path, fn) {
|
|
364
|
+
if (typeof path !== 'string' || !path.length) {
|
|
365
|
+
throw new Error('Template registration path must be a non-empty string.');
|
|
366
|
+
}
|
|
367
|
+
if (typeof fn !== 'function') {
|
|
368
|
+
throw new Error('Registered template for "' + path + '" is not a function. Pass a pre-compiled template function.');
|
|
369
|
+
}
|
|
370
|
+
if (!self.options.cache) {
|
|
371
|
+
throw new Error('Cannot register templates while caching is disabled (cache: ' + self.options.cache + '). The registry is the compile cache.');
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Register a pre-compiled template function under a template path so
|
|
377
|
+
* later `include` lookups and `compileFile` / `renderFile` calls are
|
|
378
|
+
* served from the template cache without compiling. The path is
|
|
379
|
+
* normalized through the active loader's `resolve` so a registration
|
|
380
|
+
* and an include-site lookup agree on the cache key.
|
|
381
|
+
*
|
|
382
|
+
* The registered entry is stored as a locals-binding wrapper with the
|
|
383
|
+
* same call contract as `compile`'s cached closure — `include`-emitted
|
|
384
|
+
* code invokes cached entries as `(context)`, so caching the raw
|
|
385
|
+
* five-argument template function (what `run`'s priming side effect
|
|
386
|
+
* stores) breaks any partial that touches `_ctx` / `_filters`. The
|
|
387
|
+
* wrapper closes over this instance's filters and default locals,
|
|
388
|
+
* mirroring `compiled(locals, blocks)` below.
|
|
389
|
+
*
|
|
390
|
+
* Throws when caching is disabled on the instance (any falsy `cache`) —
|
|
391
|
+
* the registry IS the compile cache, so nothing could retain the
|
|
392
|
+
* registration. `invalidateCache()` therefore also drops every
|
|
393
|
+
* registration made against the built-in memory cache.
|
|
394
|
+
*
|
|
395
|
+
* A registered entry cannot serve as an `{% extends %}` parent: it carries
|
|
396
|
+
* no parse tree, so the parent's source must still be reachable through
|
|
397
|
+
* the loader.
|
|
398
|
+
*
|
|
399
|
+
* @param {string} path Template path to register under.
|
|
400
|
+
* @param {function} fn Pre-compiled template function (CLI/AOT output).
|
|
401
|
+
* @return {object} The instance, for chaining.
|
|
402
|
+
*/
|
|
403
|
+
self.register = function (path, fn) {
|
|
404
|
+
validateRegistration(path, fn);
|
|
405
|
+
|
|
406
|
+
cacheSet(self.options.loader.resolve(path), {}, buildRegistered(fn));
|
|
407
|
+
return self;
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Register a map of pre-compiled template functions — the shape the
|
|
412
|
+
* CLI's `--recursive --register` bundle produces.
|
|
413
|
+
*
|
|
414
|
+
* Every entry is validated before any is committed, so a malformed bundle
|
|
415
|
+
* leaves the registry untouched instead of half-populated.
|
|
416
|
+
*
|
|
417
|
+
* @param {object} map `{ path: templateFn }` pairs.
|
|
418
|
+
* @return {object} The instance, for chaining.
|
|
419
|
+
*/
|
|
420
|
+
self.registerBundle = function (map) {
|
|
421
|
+
var entries = [];
|
|
422
|
+
|
|
423
|
+
if (!map || typeof map !== 'object') {
|
|
424
|
+
throw new Error('Template bundle must be an object of { path: templateFn } pairs.');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
utils.each(map, function (fn, path) {
|
|
428
|
+
entries.push([path, fn]);
|
|
429
|
+
validateRegistration(path, fn);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
utils.each(entries, function (entry) {
|
|
433
|
+
self.register(entry[0], entry[1]);
|
|
434
|
+
});
|
|
435
|
+
return self;
|
|
436
|
+
};
|
|
437
|
+
|
|
325
438
|
self.setFilter = function (name, method) {
|
|
326
439
|
if (typeof method !== "function") {
|
|
327
440
|
throw new Error('Filter "' + name + '" is not a valid function.');
|
|
@@ -634,7 +747,10 @@ exports.install = function (self, frontend) {
|
|
|
634
747
|
self.run = function (tpl, locals, filepath) {
|
|
635
748
|
var context = getLocals({ locals: locals });
|
|
636
749
|
if (filepath) {
|
|
637
|
-
|
|
750
|
+
// Cache the same wrapper `register` stores, under the same resolved
|
|
751
|
+
// key, so a template primed this way is actually usable by a later
|
|
752
|
+
// include instead of crashing on the raw function's argument shape.
|
|
753
|
+
cacheSet(self.options.loader.resolve(filepath), {}, buildRegistered(tpl));
|
|
638
754
|
}
|
|
639
755
|
return tpl(self, context, filters, utils, efn);
|
|
640
756
|
};
|
package/lib/loaders/memory.js
CHANGED
|
@@ -28,6 +28,14 @@ module.exports = function (mapping, basepath) {
|
|
|
28
28
|
*/
|
|
29
29
|
ret.resolve = function (to, from) {
|
|
30
30
|
if (basepath) {
|
|
31
|
+
// A basepath ignores "from", so a climbing path can never mean what it
|
|
32
|
+
// says: path.resolve either walks out of the root or, when the root is
|
|
33
|
+
// "/", silently clamps and returns a different template. Reject it the
|
|
34
|
+
// way the filesystem loader rejects an escaping path, rather than
|
|
35
|
+
// resolving to something the caller did not ask for.
|
|
36
|
+
if ((/^\.\.([\\/]|$)/).test(path.normalize(to))) {
|
|
37
|
+
utils.throwError('Template "' + to + '" resolves outside the loader root "' + basepath + '".');
|
|
38
|
+
}
|
|
31
39
|
from = basepath;
|
|
32
40
|
} else {
|
|
33
41
|
from = (from) ? path.dirname(from) : '/';
|
package/lib/runtime.js
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
var utils = require('./utils'),
|
|
2
|
+
cache = require('./cache'),
|
|
3
|
+
memoryLoader = require('./loaders/memory');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Runtime-only engine factory for the @rhinostone/swig family.
|
|
7
|
+
*
|
|
8
|
+
* Builds an instance that EXECUTES pre-compiled templates against a
|
|
9
|
+
* registration cache and contains no lexer, parser, or code generator —
|
|
10
|
+
* a browser bundle built from this module ships no `new Function`, so
|
|
11
|
+
* pages under a strict Content-Security-Policy (no `unsafe-eval`) can
|
|
12
|
+
* render AOT-compiled templates. Every compiling entry point is a stub
|
|
13
|
+
* that throws a clear error pointing at the CLI-plus-register workflow.
|
|
14
|
+
*
|
|
15
|
+
* Mirrors the relevant engine.js contracts:
|
|
16
|
+
* - `register` stores the same locals-binding wrapper shape that
|
|
17
|
+
* `compile`'s cached closure has (what include-emitted code calls).
|
|
18
|
+
* - `compileFile` resolves through the active loader, then serves the
|
|
19
|
+
* cache; a miss throws instead of falling through to a compiler.
|
|
20
|
+
* - `run(tpl, locals, filepath)` matches engine semantics.
|
|
21
|
+
*
|
|
22
|
+
* The default loader is a memory loader rooted at "/", so registration
|
|
23
|
+
* keys and root-relative include lookups both flatten to the exact keys a
|
|
24
|
+
* `swig compile --recursive --register` bundle carries. A basepath ignores
|
|
25
|
+
* the resolving-from path, so an include inside a bundled partial must name
|
|
26
|
+
* its target from the bundle root; a climbing path is rejected rather than
|
|
27
|
+
* silently resolved to a different template.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/*!
|
|
31
|
+
* Empty function used as a fallback in compiled template code.
|
|
32
|
+
*/
|
|
33
|
+
function efn() { return ''; }
|
|
34
|
+
|
|
35
|
+
/*!
|
|
36
|
+
* Build the error every unavailable compiling entry point throws.
|
|
37
|
+
*/
|
|
38
|
+
function noCompiler(what) {
|
|
39
|
+
return new Error(what + ' — the runtime-only build has no compiler. Precompile with the swig CLI (swig compile --recursive --register) and register the output, or load the full swig bundle instead.');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create a runtime-only instance.
|
|
44
|
+
*
|
|
45
|
+
* @param {object} frontend Per-flavor wiring.
|
|
46
|
+
* @param {object} frontend.filters The flavor's filter catalog.
|
|
47
|
+
* @param {object} [frontend.defaults] Option overrides (locals, cache, loader).
|
|
48
|
+
* @return {object} The runtime instance.
|
|
49
|
+
*/
|
|
50
|
+
exports.create = function (frontend) {
|
|
51
|
+
var filters = utils.extend({}, frontend.filters),
|
|
52
|
+
self = {};
|
|
53
|
+
|
|
54
|
+
function validateRuntimeOptions(options) {
|
|
55
|
+
if (!options) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (options.hasOwnProperty('loader') && options.loader) {
|
|
59
|
+
if (!options.loader.load || !options.loader.resolve) {
|
|
60
|
+
throw new Error('Invalid loader option ' + JSON.stringify(options.loader) + ' found. Expected { load: function (pathname, cb) { ... }, resolve: function (to, from) { ... } }.');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (options.hasOwnProperty('cache') && options.cache && options.cache !== 'memory') {
|
|
64
|
+
if (!options.cache.get || !options.cache.set) {
|
|
65
|
+
throw new Error('Invalid cache option ' + JSON.stringify(options.cache) + ' found. Expected "memory" or { get: function (key) { ... }, set: function (key, value) { ... } }.');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
validateRuntimeOptions(frontend.defaults);
|
|
71
|
+
|
|
72
|
+
self.options = utils.extend({
|
|
73
|
+
autoescape: true,
|
|
74
|
+
locals: {},
|
|
75
|
+
cache: 'memory',
|
|
76
|
+
loader: memoryLoader({}, '/')
|
|
77
|
+
}, frontend.defaults || {});
|
|
78
|
+
|
|
79
|
+
self.cache = {};
|
|
80
|
+
self.extensions = {};
|
|
81
|
+
|
|
82
|
+
function getLocals(options) {
|
|
83
|
+
if (!options || !options.locals) {
|
|
84
|
+
return self.options.locals;
|
|
85
|
+
}
|
|
86
|
+
return utils.extend({}, self.options.locals, options.locals);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function cacheGet(key, options) {
|
|
90
|
+
return cache.cacheGet(key, options, self.options.cache, self.cache);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function cacheSet(key, options, val) {
|
|
94
|
+
cache.cacheSet(key, options, val, self.options.cache, self.cache);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Merge options into this instance. Compile-time options are not
|
|
99
|
+
* accepted — the runtime build has no parser to configure.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* rt.setDefaults({ locals: { site: 'Acme' } });
|
|
103
|
+
*
|
|
104
|
+
* @param {object} options Option overrides (locals, cache, loader).
|
|
105
|
+
* @return {undefined}
|
|
106
|
+
*/
|
|
107
|
+
self.setDefaults = function (options) {
|
|
108
|
+
validateRuntimeOptions(options);
|
|
109
|
+
self.options = utils.extend(self.options, options || {});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Empty the template cache. Because the cache IS the registry, this
|
|
114
|
+
* also drops every registration made against the built-in memory cache.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* rt.invalidateCache();
|
|
118
|
+
*
|
|
119
|
+
* @return {undefined}
|
|
120
|
+
*/
|
|
121
|
+
self.invalidateCache = function () {
|
|
122
|
+
if (self.options.cache === 'memory') {
|
|
123
|
+
self.cache = {};
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Add or replace a filter available to registered templates. Filters
|
|
129
|
+
* bind at render time, so this affects templates registered earlier.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* rt.setFilter('shout', function (input) { return input + '!'; });
|
|
133
|
+
*
|
|
134
|
+
* @param {string} name Filter name as used in templates.
|
|
135
|
+
* @param {function} method Filter implementation.
|
|
136
|
+
* @return {undefined}
|
|
137
|
+
*/
|
|
138
|
+
self.setFilter = function (name, method) {
|
|
139
|
+
if (typeof method !== "function") {
|
|
140
|
+
throw new Error('Filter "' + name + '" is not a valid function.');
|
|
141
|
+
}
|
|
142
|
+
filters[name] = method;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Expose an object to compiled template code as `_ext[name]`.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* rt.setExtension('translate', function (key) { return dictionary[key]; });
|
|
150
|
+
*
|
|
151
|
+
* @param {string} name Name the compiled code refers to.
|
|
152
|
+
* @param {*} object Value to expose.
|
|
153
|
+
* @return {undefined}
|
|
154
|
+
*/
|
|
155
|
+
self.setExtension = function (name, object) {
|
|
156
|
+
self.extensions[name] = object;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/*!
|
|
160
|
+
* Wrap a pre-compiled template function in the locals-binding call shape
|
|
161
|
+
* cached entries have, mirroring the engine.
|
|
162
|
+
* @private
|
|
163
|
+
*/
|
|
164
|
+
function buildRegistered(fn) {
|
|
165
|
+
var context = getLocals(),
|
|
166
|
+
contextLength = utils.keys(context).length;
|
|
167
|
+
|
|
168
|
+
return function registered(locals, blocks) {
|
|
169
|
+
var lcls;
|
|
170
|
+
if (locals && contextLength) {
|
|
171
|
+
lcls = utils.extend({}, context, locals);
|
|
172
|
+
} else if (locals && !contextLength) {
|
|
173
|
+
lcls = locals;
|
|
174
|
+
} else if (!locals && contextLength) {
|
|
175
|
+
lcls = context;
|
|
176
|
+
} else {
|
|
177
|
+
lcls = {};
|
|
178
|
+
}
|
|
179
|
+
return fn(self, lcls, filters, utils, efn, blocks);
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/*!
|
|
184
|
+
* Shared argument checks for register / registerBundle, so a bundle is
|
|
185
|
+
* validated in full before any entry is committed.
|
|
186
|
+
* @private
|
|
187
|
+
*/
|
|
188
|
+
function validateRegistration(path, fn) {
|
|
189
|
+
if (typeof path !== 'string' || !path.length) {
|
|
190
|
+
throw new Error('Template registration path must be a non-empty string.');
|
|
191
|
+
}
|
|
192
|
+
if (typeof fn !== 'function') {
|
|
193
|
+
throw new Error('Registered template for "' + path + '" is not a function. Pass a pre-compiled template function.');
|
|
194
|
+
}
|
|
195
|
+
if (!self.options.cache) {
|
|
196
|
+
throw new Error('Cannot register templates while caching is disabled (cache: ' + self.options.cache + '). The registry is the compile cache.');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Register a pre-compiled template function under a template path.
|
|
202
|
+
* Mirrors the engine.js register contract — see that JSDoc for the
|
|
203
|
+
* key-normalization and wrapper-shape rationale.
|
|
204
|
+
*
|
|
205
|
+
* @param {string} path Template path to register under.
|
|
206
|
+
* @param {function} fn Pre-compiled template function (CLI/AOT output).
|
|
207
|
+
* @return {object} The instance, for chaining.
|
|
208
|
+
*/
|
|
209
|
+
self.register = function (path, fn) {
|
|
210
|
+
validateRegistration(path, fn);
|
|
211
|
+
cacheSet(self.options.loader.resolve(path), {}, buildRegistered(fn));
|
|
212
|
+
return self;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Register a map of pre-compiled template functions — the shape the
|
|
217
|
+
* CLI's `--recursive --register` bundle produces.
|
|
218
|
+
*
|
|
219
|
+
* @param {object} map `{ path: templateFn }` pairs.
|
|
220
|
+
* @return {object} The instance, for chaining.
|
|
221
|
+
*/
|
|
222
|
+
self.registerBundle = function (map) {
|
|
223
|
+
var entries = [];
|
|
224
|
+
|
|
225
|
+
if (!map || typeof map !== 'object') {
|
|
226
|
+
throw new Error('Template bundle must be an object of { path: templateFn } pairs.');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
utils.each(map, function (fn, path) {
|
|
230
|
+
entries.push([path, fn]);
|
|
231
|
+
validateRegistration(path, fn);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
utils.each(entries, function (entry) {
|
|
235
|
+
self.register(entry[0], entry[1]);
|
|
236
|
+
});
|
|
237
|
+
return self;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Run a pre-compiled template function against locals. Passing a
|
|
242
|
+
* <var>filepath</var> also registers it, so later `include` lookups and
|
|
243
|
+
* `compileFile` calls resolve to it. Prefer `register` when no render is
|
|
244
|
+
* wanted.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* rt.run(templateFn, { name: 'Jane' }, 'page.html');
|
|
248
|
+
*
|
|
249
|
+
* @param {function} tpl Pre-compiled template function.
|
|
250
|
+
* @param {object} [locals] Template context.
|
|
251
|
+
* @param {string} [filepath] Path to register the template under.
|
|
252
|
+
* @return {string} Rendered output.
|
|
253
|
+
*/
|
|
254
|
+
self.run = function (tpl, locals, filepath) {
|
|
255
|
+
var context = getLocals({ locals: locals });
|
|
256
|
+
if (filepath) {
|
|
257
|
+
cacheSet(self.options.loader.resolve(filepath), {}, buildRegistered(tpl));
|
|
258
|
+
}
|
|
259
|
+
return tpl(self, context, filters, utils, efn);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
self.compileFile = function (pathname, options, cb) {
|
|
263
|
+
var resolved, cached, err;
|
|
264
|
+
|
|
265
|
+
if (!options) {
|
|
266
|
+
options = {};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
resolved = self.options.loader.resolve(pathname, options.resolveFrom);
|
|
270
|
+
cached = cacheGet(resolved, options);
|
|
271
|
+
|
|
272
|
+
if (cached) {
|
|
273
|
+
if (cb) {
|
|
274
|
+
cb(null, cached);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
return cached;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
err = noCompiler('Template "' + pathname + '" is not registered');
|
|
281
|
+
if (cb) {
|
|
282
|
+
cb(err);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
throw err;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
self.renderFile = function (pathName, locals, cb) {
|
|
289
|
+
if (cb) {
|
|
290
|
+
self.compileFile(pathName, {}, function (err, fn) {
|
|
291
|
+
var result;
|
|
292
|
+
|
|
293
|
+
if (err) {
|
|
294
|
+
cb(err);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
result = fn(locals);
|
|
300
|
+
} catch (err2) {
|
|
301
|
+
cb(err2);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
cb(null, result);
|
|
306
|
+
});
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return self.compileFile(pathName)(locals);
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
self.getTemplate = function (pathname) {
|
|
314
|
+
return Promise.reject(noCompiler('Async template loading of "' + pathname + '" is unavailable'));
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
self.parse = function () { throw noCompiler('parse() is unavailable'); };
|
|
318
|
+
self.parseFile = function () { throw noCompiler('parseFile() is unavailable'); };
|
|
319
|
+
self.precompile = function () { throw noCompiler('precompile() is unavailable'); };
|
|
320
|
+
self.compile = function () { throw noCompiler('compile() is unavailable'); };
|
|
321
|
+
self.render = function () { throw noCompiler('render(source) is unavailable'); };
|
|
322
|
+
self.setTag = function () { throw noCompiler('setTag() is parse-time machinery and is unavailable'); };
|
|
323
|
+
self.renderFileAsync = function () { throw noCompiler('renderFileAsync() is unavailable'); };
|
|
324
|
+
self.compileFileAsync = function () { throw noCompiler('compileFileAsync() is unavailable'); };
|
|
325
|
+
|
|
326
|
+
return self;
|
|
327
|
+
};
|
package/lib/tokenparser.js
CHANGED
|
@@ -591,6 +591,11 @@ TokenParser.prototype = {
|
|
|
591
591
|
bail('Invalid filter "' + fname + '"');
|
|
592
592
|
}
|
|
593
593
|
var fargs;
|
|
594
|
+
// `fargs` must be reset each iteration — `var` is function-scoped
|
|
595
|
+
// (a bare declaration does not reset it), so without this an
|
|
596
|
+
// arg-bearing filter earlier in the chain would leak its args onto
|
|
597
|
+
// a later no-arg filter (e.g. `x|replace("a","b")|upper`).
|
|
598
|
+
fargs = undefined;
|
|
594
599
|
if (tok.type === _t.FILTER) {
|
|
595
600
|
fargs = parseArgList(_t.PARENCLOSE);
|
|
596
601
|
}
|