@rhinostone/swig-core 2.1.0 → 2.2.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/engine.js +105 -4
- package/package.json +1 -1
package/lib/engine.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
var utils = require('./utils'),
|
|
2
|
+
ir = require('./ir'),
|
|
2
3
|
backend = require('./backend'),
|
|
3
4
|
cache = require('./cache');
|
|
4
5
|
|
|
@@ -79,6 +80,82 @@ exports.importNonBlocks = function (blocks, tokens) {
|
|
|
79
80
|
});
|
|
80
81
|
};
|
|
81
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Build an `IRExtendsDeferred` node from a parsed child template.
|
|
85
|
+
*
|
|
86
|
+
* Walks `tokens.blocks` (the parser's map of every block-level tag that
|
|
87
|
+
* appeared at top level — both `{% block %}` overrides and non-block
|
|
88
|
+
* preludes like `{% set %}`, `{% import %}`, `{% macro %}`) and:
|
|
89
|
+
*
|
|
90
|
+
* - For each `{% block %}` token: invokes its `.compile(...)` with an
|
|
91
|
+
* empty parents list to obtain an `IRBlock` (body is one `IRLegacyJS`
|
|
92
|
+
* wrapping the recursively-compiled JS source — same shape sync mode
|
|
93
|
+
* produces). The IRBlock is keyed under the block name in `childBlocks`.
|
|
94
|
+
* - For each non-`block` block-level token: invokes its `.compile(...)`
|
|
95
|
+
* and wraps any JS-string return in `IRLegacyJS`. The resulting IR
|
|
96
|
+
* node is pushed onto `childIRs` (the prelude list).
|
|
97
|
+
*
|
|
98
|
+
* The backend's `IRExtendsDeferred` emit branch handles runtime parent
|
|
99
|
+
* resolution via `_swig.getTemplate` and the `_blocks` parameter contract
|
|
100
|
+
* (see `packages/swig-core/lib/backend.js`).
|
|
101
|
+
*
|
|
102
|
+
* `tokens.parent` is lifted into an `IRLiteral('string', …)` so the
|
|
103
|
+
* backend's `emitExpr` path produces a quoted JS string literal. The
|
|
104
|
+
* native parser stashes `tokens.parent` as the literal text of the
|
|
105
|
+
* extends argument after stripping enclosing quotes (lib/parser.js:273),
|
|
106
|
+
* which works for `{% extends "layout.html" %}` but for a bare
|
|
107
|
+
* identifier (`{% extends parent_var %}`) yields a pre-lowered JS
|
|
108
|
+
* source fragment such as `((typeof _ctx.parent_var !== "undefined")
|
|
109
|
+
* ? _ctx.parent_var : …)`. Embedded here as a string literal it
|
|
110
|
+
* becomes a garbage template lookup at runtime
|
|
111
|
+
* (`Template not found: /((typeof _ctx.parent_var …`). Closing the
|
|
112
|
+
* dynamic-extends gap requires the parser to stash an IRExpr on
|
|
113
|
+
* `tokens.parent` and this helper to lower it through
|
|
114
|
+
* `ir.extendsDeferred`'s `parentExpr` slot — already designed as
|
|
115
|
+
* `<IRExpr>` per the deferred IR contract.
|
|
116
|
+
*
|
|
117
|
+
* @param {object} tokens Parsed child template (must have `.parent`).
|
|
118
|
+
* @param {object} options Per-call Swig options; `options.filename` is
|
|
119
|
+
* used as the deferred resolveFrom.
|
|
120
|
+
* @return {object} IRExtendsDeferred node.
|
|
121
|
+
* @private
|
|
122
|
+
*/
|
|
123
|
+
function buildExtendsDeferred(tokens, options) {
|
|
124
|
+
var childBlocks = {};
|
|
125
|
+
var childIRs = [];
|
|
126
|
+
utils.each(tokens.blocks, function (blockToken) {
|
|
127
|
+
var args = blockToken.args ? blockToken.args.slice(0) : [];
|
|
128
|
+
var content = blockToken.content ? blockToken.content.slice(0) : [];
|
|
129
|
+
if (blockToken.name === 'block') {
|
|
130
|
+
var blockName = args.join('');
|
|
131
|
+
var blockIR = blockToken.compile(backend.compile, args, content, [], options, blockName, blockToken);
|
|
132
|
+
if (blockIR && typeof blockIR === 'object' && typeof blockIR.type === 'string') {
|
|
133
|
+
childBlocks[blockName] = blockIR;
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
var result = blockToken.compile(backend.compile, args, content, [], options, undefined, blockToken);
|
|
138
|
+
if (result === undefined || result === null || result === '') { return; }
|
|
139
|
+
if (typeof result === 'string') {
|
|
140
|
+
childIRs.push(ir.legacyJS(result));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (utils.isArray(result)) {
|
|
144
|
+
utils.each(result, function (n) { childIRs.push(n); });
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (typeof result === 'object' && typeof result.type === 'string') {
|
|
148
|
+
childIRs.push(result);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return ir.extendsDeferred(
|
|
152
|
+
ir.literal('string', tokens.parent),
|
|
153
|
+
childBlocks,
|
|
154
|
+
childIRs,
|
|
155
|
+
options.filename || ''
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
82
159
|
/**
|
|
83
160
|
* Walk a template's `extends` chain and build the parent token tree.
|
|
84
161
|
* Detects circular inheritance.
|
|
@@ -307,12 +384,24 @@ exports.install = function (self, frontend) {
|
|
|
307
384
|
|
|
308
385
|
self.precompile = function (source, options) {
|
|
309
386
|
var tokens = self.parse(source, options),
|
|
310
|
-
parents
|
|
387
|
+
parents,
|
|
311
388
|
tpl;
|
|
312
389
|
|
|
313
|
-
if (
|
|
314
|
-
|
|
315
|
-
|
|
390
|
+
if (options && options.codegenMode === 'async' && tokens.parent) {
|
|
391
|
+
// Async extends — defer parent walking and block remapping to
|
|
392
|
+
// runtime via the IRExtendsDeferred emit branch in backend.js.
|
|
393
|
+
// The deferred node carries the child's blocks + non-block
|
|
394
|
+
// preludes; the backend resolves the parent via _swig.getTemplate
|
|
395
|
+
// and threads block overrides through the _blocks parameter
|
|
396
|
+
// contract.
|
|
397
|
+
parents = [];
|
|
398
|
+
tokens.tokens = [buildExtendsDeferred(tokens, options)];
|
|
399
|
+
} else {
|
|
400
|
+
parents = getParentsInternal(tokens, options);
|
|
401
|
+
if (parents.length) {
|
|
402
|
+
tokens.tokens = exports.remapBlocks(tokens.blocks, parents[0].tokens);
|
|
403
|
+
exports.importNonBlocks(tokens.blocks, tokens.tokens);
|
|
404
|
+
}
|
|
316
405
|
}
|
|
317
406
|
|
|
318
407
|
try {
|
|
@@ -497,6 +586,18 @@ exports.install = function (self, frontend) {
|
|
|
497
586
|
|
|
498
587
|
self.renderFile = function (pathName, locals, cb) {
|
|
499
588
|
if (cb) {
|
|
589
|
+
// Async loader opt-in: route through getTemplate when
|
|
590
|
+
// loader.async === true. Explicit flag only — load.length is
|
|
591
|
+
// not a dispatch signal (the built-in fs + memory loaders are
|
|
592
|
+
// dual-mode with length 2 and must keep the sync-cb path).
|
|
593
|
+
if (self.options.loader && self.options.loader.async === true) {
|
|
594
|
+
self.getTemplate(pathName)
|
|
595
|
+
.then(function (fn) { return fn(locals); })
|
|
596
|
+
.then(function (result) { cb(null, result.output); })
|
|
597
|
+
.catch(cb);
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
|
|
500
601
|
self.compileFile(pathName, {}, function (err, fn) {
|
|
501
602
|
var result;
|
|
502
603
|
|