@rhinostone/swig 2.0.0-alpha.1 → 2.0.0-alpha.3

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/tags/macro.js CHANGED
@@ -16,27 +16,18 @@
16
16
  *
17
17
  * @param {...arguments} arguments User-defined arguments.
18
18
  */
19
+ var ir = require('@rhinostone/swig-core/lib/ir');
20
+
19
21
  exports.compile = function (compiler, args, content, parents, options, blockName) {
20
22
  var fnName = args.shift();
21
-
22
- return '_ctx.' + fnName + ' = function (' + args.join('') + ') {\n' +
23
- ' var _output = "",\n' +
24
- ' __ctx = _utils.extend({}, _ctx);\n' +
25
- ' _utils.each(_ctx, function (v, k) {\n' +
26
- ' if (["' + args.join('","') + '"].indexOf(k) !== -1) { delete _ctx[k]; }\n' +
27
- ' });\n' +
28
- compiler(content, parents, options, blockName) + '\n' +
29
- ' _ctx = _utils.extend(_ctx, __ctx);\n' +
30
- ' return _output;\n' +
31
- '};\n' +
32
- '_ctx.' + fnName + '.safe = true;\n';
23
+ return ir.macro(fnName, args, [ir.legacyJS(compiler(content, parents, options, blockName))]);
33
24
  };
34
25
 
35
26
  // CVE-2023-25345: prototype-chain properties that must not be used as macro
36
27
  // names. The macro tag assigns the compiled function to _ctx, so dangerous
37
28
  // names would pollute the prototype chain. Shared constant in
38
29
  // @rhinostone/swig-core — see .claude/security.md.
39
- var _dangerousProps = require('../../packages/swig-core/lib/security').dangerousProps;
30
+ var _dangerousProps = require('@rhinostone/swig-core/lib/security').dangerousProps;
40
31
 
41
32
  exports.parse = function (str, line, parser, types) {
42
33
  var name;
@@ -45,7 +36,24 @@ exports.parse = function (str, line, parser, types) {
45
36
  if (token.match.indexOf('.') !== -1) {
46
37
  throw new Error('Unexpected dot in macro argument "' + token.match + '" on line ' + line + '.');
47
38
  }
48
- this.out.push(token.match);
39
+ if (!name) {
40
+ // No FUNCTION/FUNCTIONEMPTY token emitted (e.g. `{% macro foo %}` with
41
+ // no parens): this VAR is the macro name. CVE-2023-25345: block
42
+ // prototype-chain property names as macro names.
43
+ if (_dangerousProps.indexOf(token.match) !== -1) {
44
+ throw new Error('Unsafe macro name "' + token.match + '" is not allowed (CVE-2023-25345) on line ' + line + '.');
45
+ }
46
+ name = token.match;
47
+ this.out.push(name);
48
+ return;
49
+ }
50
+ // CVE-2023-25345: macros assign `_ctx.<paramName>` implicitly inside
51
+ // the IIFE scaffolding, so a prototype-chain property name as a
52
+ // parameter would pollute the prototype chain at invocation time.
53
+ if (_dangerousProps.indexOf(token.match) !== -1) {
54
+ throw new Error('Unsafe macro argument "' + token.match + '" is not allowed (CVE-2023-25345) on line ' + line + '.');
55
+ }
56
+ this.out.push({ name: token.match });
49
57
  });
50
58
 
51
59
  parser.on(types.FUNCTION, function (token) {
@@ -79,7 +87,7 @@ exports.parse = function (str, line, parser, types) {
79
87
  });
80
88
 
81
89
  parser.on(types.COMMA, function () {
82
- return true;
90
+ return;
83
91
  });
84
92
 
85
93
  parser.on('*', function () {
@@ -1,3 +1,5 @@
1
+ var ir = require('@rhinostone/swig-core/lib/ir');
2
+
1
3
  /**
2
4
  * Inject the content from the parent template's block of the same name into the current block.
3
5
  *
@@ -33,7 +35,12 @@ exports.compile = function (compiler, args, content, parents, options, blockName
33
35
  // Silly JSLint "Strange Loop" requires return to be in a conditional
34
36
  if (breaker && parentFile !== parent.name) {
35
37
  block = parent.blocks[blockName];
36
- return block.compile(compiler, [blockName], block.content, parents.slice(i + 1), options) + '\n';
38
+ // Phase 2: the block tag now returns `IRBlock { body: [...] }`.
39
+ // Splice the matched parent block's body into an IRParent node;
40
+ // the backend emits body verbatim plus a trailing newline for
41
+ // byte-identity with the pre-Phase-2 JS-string emission shape.
42
+ var blockNode = block.compile(compiler, [blockName], block.content, parents.slice(i + 1), options);
43
+ return ir.parent((blockNode && blockNode.body) ? blockNode.body.concat([ir.legacyJS('\n')]) : [ir.legacyJS('\n')]);
37
44
  }
38
45
  }
39
46
  };
@@ -48,4 +55,4 @@ exports.parse = function (str, line, parser, types, stack, opts) {
48
55
  });
49
56
 
50
57
  return true;
51
- };
58
+ };
package/lib/tags/raw.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // Magic tag, hardcoded into parser
2
2
 
3
+ var ir = require('@rhinostone/swig-core/lib/ir');
4
+
3
5
  /**
4
6
  * Forces the content to not be auto-escaped. All swig instructions will be ignored and the content will be rendered exactly as it was given.
5
7
  *
@@ -12,7 +14,16 @@
12
14
  *
13
15
  */
14
16
  exports.compile = function (compiler, args, content, parents, options, blockName) {
15
- return compiler(content, parents, options, blockName);
17
+ var nodes = [];
18
+ var i;
19
+ for (i = 0; i < content.length; i++) {
20
+ if (typeof content[i] === 'string') {
21
+ nodes.push(ir.raw(content[i]));
22
+ } else {
23
+ nodes.push(ir.legacyJS(compiler([content[i]], parents, options, blockName)));
24
+ }
25
+ }
26
+ return nodes;
16
27
  };
17
28
  exports.parse = function (str, line, parser) {
18
29
  parser.on('*', function (token) {
package/lib/tags/set.js CHANGED
@@ -31,13 +31,68 @@
31
31
  * @param {literal} assignement Any valid JavaScript assignement. <code data-language="js">=, +=, *=, /=, -=</code>
32
32
  * @param {*} value Valid variable output.
33
33
  */
34
- exports.compile = function (compiler, args) {
35
- return args.join(' ') + ';\n';
34
+ var ir = require('@rhinostone/swig-core/lib/ir'),
35
+ _t = require('@rhinostone/swig-core/lib/tokentypes');
36
+
37
+ // Pure-dot LHS shape: `_ctx.foo` or `_ctx.foo.bar.baz`. Bracket-touched
38
+ // targets (`_ctx.foo["bar"]`, `_ctx.foo[bar]`, mixed dot+bracket) fail
39
+ // this match and stay on the transitional string fallback per Session
40
+ // 14b Commit 10's narrow scope — the bracket-lvalue contract is a
41
+ // cross-flavor design call and is deferred to a dedicated session.
42
+ var _pureDotTarget = /^_ctx\.([a-zA-Z_$][\w$]*)((?:\.[a-zA-Z_$][\w$]*)*)$/;
43
+
44
+ exports.compile = function (compiler, args, content, parents, options, blockName, token) {
45
+ // Target migrates to structured IRVarRef when the LHS is pure-dot;
46
+ // otherwise stays a transitional string fragment. Value prefers the
47
+ // lowered IRExpr carried on the token when lowerExpr produced one;
48
+ // otherwise fall back to the joined JS-source fragments the parse
49
+ // handler emitted.
50
+ var target = args[0];
51
+ var match = typeof target === 'string' && _pureDotTarget.exec(target);
52
+ if (match) {
53
+ var path = [match[1]];
54
+ if (match[2]) {
55
+ var rest = match[2].split('.');
56
+ for (var pi = 1; pi < rest.length; pi++) { path.push(rest[pi]); }
57
+ }
58
+ target = ir.varRef(path);
59
+ }
60
+ var value = (token && token.irExpr) ? token.irExpr : args.slice(2).join(' ');
61
+ return ir.set(target, args[1], value);
36
62
  };
37
63
 
38
64
  // CVE-2023-25345: prototype-chain properties that must not be assignable.
39
65
  // Shared constant in @rhinostone/swig-core — see .claude/security.md.
40
- var _dangerousProps = require('../../packages/swig-core/lib/security').dangerousProps;
66
+ var _dangerousProps = require('@rhinostone/swig-core/lib/security').dangerousProps;
67
+
68
+ exports.lowerExpr = function (parser, tokens) {
69
+ // The set tag's parse handler consumes LHS VAR / BRACKETOPEN / STRING /
70
+ // BRACKETCLOSE / DOTKEY / ASSIGNMENT tokens up to (and including) the
71
+ // assignment operator; lowerExpr only concerns itself with the RHS.
72
+ // Locate the first ASSIGNMENT, slice the tail, and hand it to parseExpr.
73
+ // Fall back (return undefined) if the tail contains FILTER / FILTEREMPTY
74
+ // (filter pipes are not part of the expression grammar yet — Session
75
+ // 14+ Output-site work) or a nested ASSIGNMENT (parseExpr does not
76
+ // lower assignments, and bracket-write-as-assignment on the RHS would
77
+ // misparse silently).
78
+ var assignIdx = -1, i;
79
+ for (i = 0; i < tokens.length; i++) {
80
+ if (tokens[i].type === _t.ASSIGNMENT) {
81
+ assignIdx = i;
82
+ break;
83
+ }
84
+ }
85
+ if (assignIdx === -1) { return undefined; }
86
+ var rhsTokens = tokens.slice(assignIdx + 1);
87
+ for (i = 0; i < rhsTokens.length; i++) {
88
+ if (rhsTokens[i].type === _t.FILTER ||
89
+ rhsTokens[i].type === _t.FILTEREMPTY ||
90
+ rhsTokens[i].type === _t.ASSIGNMENT) {
91
+ return undefined;
92
+ }
93
+ }
94
+ return parser.parseExpr(rhsTokens);
95
+ };
41
96
 
42
97
  exports.parse = function (str, line, parser, types) {
43
98
  var nameSet = '',
package/lib/utils.js CHANGED
@@ -1,13 +1,11 @@
1
1
  /**
2
2
  * Phase 1 carve bridge — utilities moved to @rhinostone/swig-core.
3
3
  *
4
- * This shim re-routes the in-repo relative require so every existing
5
- * consumer (`require('./utils')` from lib/, `require('../utils')` from
6
- * lib/tags/ and lib/loaders/, etc.) keeps resolving without churn, and
7
- * browserify@2 which predates scoped packages and cannot resolve
8
- * `@rhinostone/swig-core` — keeps producing an identical browser
9
- * bundle. Removed when the native frontend restructures into
10
- * packages/swig/ (Phase 2). See .claude/architecture/multi-flavor-ir.md.
4
+ * This shim re-routes the in-repo consumer requires (`require('./utils')`
5
+ * from lib/, `require('../utils')` from lib/tags/ and lib/loaders/) so
6
+ * every call site keeps resolving without churn. Removed when the native
7
+ * frontend restructures into packages/swig/ (Phase 2). See
8
+ * .claude/architecture/multi-flavor-ir.md.
11
9
  */
12
10
 
13
- module.exports = require('../packages/swig-core/lib/utils');
11
+ module.exports = require('@rhinostone/swig-core/lib/utils');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhinostone/swig",
3
- "version": "2.0.0-alpha.1",
3
+ "version": "2.0.0-alpha.3",
4
4
  "description": "A simple, powerful, and extendable templating engine for node.js and browsers, similar to Django, Jinja2, and Twig.",
5
5
  "keywords": [
6
6
  "template",
@@ -21,18 +21,20 @@
21
21
  "Rhinostone <contact@gina.io>"
22
22
  ],
23
23
  "dependencies": {
24
+ "@rhinostone/swig-core": "2.0.0-alpha.3",
24
25
  "terser": "^5.46.1",
25
26
  "yargs": "^17.7.2"
26
27
  },
27
28
  "devDependencies": {
28
29
  "blanket": "~1.1",
29
- "browserify": "~2",
30
+ "esbuild": "^0.28.0",
30
31
  "eslint": "^8.57.1",
31
32
  "expect.js": "~0.2",
32
33
  "express": "~3",
33
34
  "lodash": "~1.3.1",
34
35
  "mocha": "1.12.0",
35
36
  "mocha-phantomjs": "~3.1",
37
+ "path-browserify": "^1.0.1",
36
38
  "phantomjs": "~1.9.1",
37
39
  "travis-cov": "~0.2"
38
40
  },