@rhinostone/swig-twig 2.5.1 → 2.5.2

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/parser.js CHANGED
@@ -529,6 +529,7 @@ exports.parse = function (swig, source, opts, tags, filters) {
529
529
  var line = 1;
530
530
  var stack = [];
531
531
  var parent = null;
532
+ var parentExpr = null;
532
533
  var tokens = [];
533
534
  var blocks = {};
534
535
  var inVerbatim = false;
@@ -674,6 +675,7 @@ exports.parse = function (swig, source, opts, tags, filters) {
674
675
  if (token) {
675
676
  if (token.name === 'extends') {
676
677
  parent = token.args.length ? String(token.args[0]) : null;
678
+ parentExpr = token.irExpr && token.irExpr.file;
677
679
  } else if (token.block && !stack.length) {
678
680
  blocks[token.args.join('')] = token;
679
681
  }
@@ -733,6 +735,7 @@ exports.parse = function (swig, source, opts, tags, filters) {
733
735
  return {
734
736
  name: opts.filename,
735
737
  parent: parent,
738
+ parentExpr: parentExpr,
736
739
  tokens: tokens,
737
740
  blocks: blocks
738
741
  };
@@ -3,26 +3,28 @@
3
3
  *
4
4
  * Declares a parent template for inheritance:
5
5
  *
6
- * {% extends "layout.twig" %}
6
+ * {% extends "layout.twig" %} (static — resolved at compile time)
7
+ * {% extends some_var %} (dynamic — resolved at render time)
8
+ * {% extends a ? "x" : "y" %} (dynamic)
7
9
  *
8
- * Twig supports both static string paths (handled here) and dynamic
9
- * expressions (`{% extends some_var %}`, `{% extends a ? b : c %}`). This
10
- * session rejects dynamic extends at parse time — the engine's parent-
11
- * chain resolution (`engine.getParents` + `remapBlocks` +
12
- * `importNonBlocks`) walks the chain statically at compile time, so a
13
- * runtime-valued parent cannot be resolved without reworking the engine.
14
- * Dynamic extends is tracked for a later session; the rejection is
15
- * deliberate, not an oversight.
10
+ * A static string-literal path is pushed as the single `token.args`
11
+ * element; the parser's splitter lifts it onto `template.parent`, and the
12
+ * engine's parent-chain resolution (`engine.getParents` + `remapBlocks` +
13
+ * `importNonBlocks`) walks the chain statically at compile time.
16
14
  *
17
- * The parser's splitter reads `token.args[0]` and stashes it on
18
- * `template.parent` (see `packages/swig-twig/lib/parser.js` line 609).
19
- * This tag must therefore push the *unquoted* path as the single
20
- * `token.args` element.
15
+ * A dynamic path is lowered through `parser.parseExpr` into an IRExpr and
16
+ * stashed on `token.irExpr.file`; the splitter lifts it onto the sibling
17
+ * `template.parentExpr` slot, which the async codegen path
18
+ * (`buildExtendsDeferred`) prefers over the string literal and resolves at
19
+ * render time via `_swig.getTemplate`. Dynamic extends therefore requires
20
+ * an async loader (`renderFile` with a callback); on the synchronous
21
+ * render path a runtime-valued parent is unresolvable. Mirrors the
22
+ * dynamic-path lowering in `include.js`.
21
23
  *
22
24
  * Compile emits nothing — `extends.compile` returns undefined. The
23
25
  * backend's emit loop skips undefined returns. Extends is a parse-time
24
- * declaration carried via `template.parent` metadata; no runtime code
25
- * is generated for the `{% extends %}` tag itself.
26
+ * declaration carried via `template.parent` / `template.parentExpr`
27
+ * metadata; no runtime code is generated for the `{% extends %}` tag itself.
26
28
  */
27
29
 
28
30
  var utils = require('@rhinostone/swig-core/lib/utils');
@@ -34,23 +36,26 @@ exports.ends = false;
34
36
  exports.block = true;
35
37
 
36
38
  /**
37
- * Parse the `{% extends %}` tag body. Extracts the STRING literal path,
38
- * strips surrounding quotes, and stashes the result as `token.args[0]`
39
- * for the parser's splitter to pick up.
39
+ * Parse the `{% extends %}` tag body.
40
40
  *
41
- * Rejects anything other than a single STRING token dynamic extends
42
- * (VAR, FUNCTION, expressions) is not supported in this session.
41
+ * A single STRING literal is stashed (quotes stripped) as `token.args[0]`
42
+ * for the splitter to lift onto `template.parent`. Any other expression
43
+ * (VAR, member access, ternary, ...) is a dynamic path: it is lowered
44
+ * through `parser.parseExpr` into an IRExpr on `token.irExpr.file` (the
45
+ * splitter lifts it onto `template.parentExpr`) and the raw source text is
46
+ * kept on `token.args[0]` so the sync `template.parent` string stays
47
+ * truthy. Mirrors `include.js`.
43
48
  *
44
49
  * @param {string} str Tag body.
45
50
  * @param {number} line Source line of the opening `{%`.
46
- * @param {object} parser The Twig parser module (unused — path is a
47
- * bare string literal).
51
+ * @param {object} parser The Twig parser module (exposes `parseExpr`).
48
52
  * @param {object} types Twig lexer token-type enum.
49
53
  * @param {Array} stack Open-tag stack (unused — extends has no body).
50
54
  * @param {object} opts Per-call options (honors `opts.filename`).
51
55
  * @param {object} swig Swig instance (unused).
52
- * @param {object} token In-progress TagToken. `token.args` gets the
53
- * unquoted parent path as its single element.
56
+ * @param {object} token In-progress TagToken. Gets `token.args` (raw or
57
+ * unquoted path) and, for a dynamic path,
58
+ * `token.irExpr.file` (the lowered IRExpr).
54
59
  * @return {boolean} Always `true` on success. Throws otherwise.
55
60
  */
56
61
  exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
@@ -62,17 +67,23 @@ exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
62
67
  if (!pathTok) {
63
68
  utils.throwError('Expected parent template path in "extends" tag', line, opts.filename);
64
69
  }
65
- if (pathTok.type !== types.STRING) {
66
- utils.throwError('Dynamic "extends" is not supported — parent path must be a string literal', line, opts.filename);
67
- }
68
70
 
69
- pos += 1;
70
- while (pos < tokens.length && tokens[pos].type === types.WHITESPACE) { pos += 1; }
71
- if (pos < tokens.length) {
72
- utils.throwError('Unexpected token "' + tokens[pos].match + '" after parent path in "extends" tag', line, opts.filename);
71
+ if (pathTok.type === types.STRING) {
72
+ pos += 1;
73
+ while (pos < tokens.length && tokens[pos].type === types.WHITESPACE) { pos += 1; }
74
+ if (pos < tokens.length) {
75
+ utils.throwError('Unexpected token "' + tokens[pos].match + '" after parent path in "extends" tag', line, opts.filename);
76
+ }
77
+ token.args = [pathTok.match.replace(/^['"]|['"]$/g, '')];
78
+ return true;
73
79
  }
74
80
 
75
- token.args = [pathTok.match.replace(/^['"]|['"]$/g, '')];
81
+ // Dynamic path: lower to an IRExpr so it resolves at render time on the
82
+ // async codegen path. The raw source is kept on token.args[0] so the
83
+ // sync template.parent string stays truthy (the async dispatch guard
84
+ // keys on it); buildExtendsDeferred prefers template.parentExpr.
85
+ token.irExpr = { file: parser.parseExpr(tokens) };
86
+ token.args = [utils.strip(str)];
76
87
  return true;
77
88
  };
78
89
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhinostone/swig-twig",
3
- "version": "2.5.1",
3
+ "version": "2.5.2",
4
4
  "description": "Twig-syntax frontend for the @rhinostone/swig-core template engine. Part of the @rhinostone/swig multi-flavor family.",
5
5
  "keywords": [
6
6
  "template",
@@ -22,7 +22,7 @@
22
22
  "node": ">=12"
23
23
  },
24
24
  "peerDependencies": {
25
- "@rhinostone/swig-core": "2.5.1"
25
+ "@rhinostone/swig-core": "2.5.2"
26
26
  },
27
27
  "publishConfig": {
28
28
  "access": "public"