@rhinostone/swig-core 2.0.0-alpha.5 → 2.0.0-alpha.8
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/README.md +38 -0
- package/lib/backend.js +4 -8
- package/lib/cache.js +1 -2
- package/lib/engine.js +2 -3
- package/lib/filters.js +1 -2
- package/lib/index.js +1 -2
- package/lib/ir.js +2 -7
- package/lib/security.js +0 -3
- package/lib/tokenparser.js +4 -9
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
@rhinostone/swig-core
|
|
2
|
+
=====================
|
|
3
|
+
|
|
4
|
+
[](https://www.npmjs.org/package/@rhinostone/swig-core) [](https://socket.dev/npm/package/@rhinostone/swig-core)
|
|
5
|
+
|
|
6
|
+
> **Experimental alpha — IR ABI is unstable across alpha minors.** This package is the shared runtime for the `@rhinostone/swig` family of template engines. It is not intended for direct consumption unless you are building a custom frontend. Install [@rhinostone/swig](https://www.npmjs.com/package/@rhinostone/swig) for the default Swig (Jinja2/Django-inspired) flavor, or [@rhinostone/swig-twig](https://www.npmjs.com/package/@rhinostone/swig-twig) for the Twig flavor — both pull this package in as a peer dependency pinned to the exact alpha version.
|
|
7
|
+
|
|
8
|
+
Extracted from `@rhinostone/swig@1.6.0` during the Phase 1 carve (see #T14 in [ROADMAP.md](https://github.com/gina-io/swig/blob/develop/ROADMAP.md)).
|
|
9
|
+
|
|
10
|
+
What lives here
|
|
11
|
+
---------------
|
|
12
|
+
|
|
13
|
+
* **IR types and helpers** (`lib/ir.js`) — the intermediate representation emitted by frontend parsers and consumed by the backend.
|
|
14
|
+
* **Backend** (`lib/backend.js`) — lowers IR to compiled JavaScript source via `new Function(...)`.
|
|
15
|
+
* **Engine wiring** (`lib/engine.js`) — `engine.install(self, frontend)` glues a frontend (tags, filters, parser, lexer) onto the shared runtime.
|
|
16
|
+
* **Expression-level TokenParser** (`lib/tokenparser.js`) — IR emission for inline expressions, shared across frontends.
|
|
17
|
+
* **Runtime primitives** (`lib/utils.js`, `lib/security.js`, `lib/cache.js`, `lib/loaders/`, `lib/filters.js`, `lib/dateformatter.js`, `lib/tokentypes.js`).
|
|
18
|
+
|
|
19
|
+
Consumers
|
|
20
|
+
---------
|
|
21
|
+
|
|
22
|
+
* [@rhinostone/swig](https://www.npmjs.com/package/@rhinostone/swig) — default Swig flavor.
|
|
23
|
+
* [@rhinostone/swig-twig](https://www.npmjs.com/package/@rhinostone/swig-twig) — Twig parity frontend.
|
|
24
|
+
|
|
25
|
+
Versioning
|
|
26
|
+
----------
|
|
27
|
+
|
|
28
|
+
During the alpha cycle, every frontend that depends on `@rhinostone/swig-core` pins the **exact** alpha version (no caret, no tilde). The IR ABI is not stable until `2.0.0` stable ships. Do not upgrade `swig-core` independently of the frontend that consumes it.
|
|
29
|
+
|
|
30
|
+
Repository
|
|
31
|
+
----------
|
|
32
|
+
|
|
33
|
+
Source lives in the `@rhinostone/swig` monorepo: [gina-io/swig/packages/swig-core](https://github.com/gina-io/swig/tree/develop/packages/swig-core). File issues and PRs at [gina-io/swig](https://github.com/gina-io/swig).
|
|
34
|
+
|
|
35
|
+
License
|
|
36
|
+
-------
|
|
37
|
+
|
|
38
|
+
MIT. See [LICENSE](https://github.com/gina-io/swig/blob/develop/LICENSE) in the monorepo root.
|
package/lib/backend.js
CHANGED
|
@@ -22,8 +22,6 @@ var utils = require('./utils'),
|
|
|
22
22
|
* is unchanged. Built-in tags migrate per session by returning IR nodes
|
|
23
23
|
* directly. The `new Function(...)` wrapper stays with the native
|
|
24
24
|
* frontend (filename-aware error attribution, per the seam rule).
|
|
25
|
-
*
|
|
26
|
-
* See .claude/architecture/multi-flavor-ir.md § Phase 2.
|
|
27
25
|
*/
|
|
28
26
|
|
|
29
27
|
/*!
|
|
@@ -157,8 +155,7 @@ exports.compile = function (template, parents, options, blockName) {
|
|
|
157
155
|
// dot+bracket) stay on the transitional string fragment — the
|
|
158
156
|
// bracket-lvalue contract is a cross-flavor design call and is
|
|
159
157
|
// deferred. The frontend's set-tag parse handler retains its own
|
|
160
|
-
// _dangerousProps guards on every LHS path segment
|
|
161
|
-
// duplication invariant in .claude/security.md.
|
|
158
|
+
// _dangerousProps guards on every LHS path segment.
|
|
162
159
|
// `value` is an IRExpr node (Session 14b) — backward-compat string
|
|
163
160
|
// fallback preserved for userland setTag tags that may still hand
|
|
164
161
|
// in a raw JS fragment. Emits `<target> <op> <value>;`.
|
|
@@ -463,16 +460,15 @@ exports.compile = function (template, parents, options, blockName) {
|
|
|
463
460
|
* The emitter enforces the CVE-2023-25345 blocklist on every {@link
|
|
464
461
|
* IRVarRef} path segment and every string-literal {@link IRAccess} key,
|
|
465
462
|
* mirroring the guards on the frontend's TokenParser + tag-parse paths.
|
|
466
|
-
* The frontend-side guards stay live
|
|
467
|
-
*
|
|
463
|
+
* The frontend-side guards stay live; the duplicate is intentional
|
|
464
|
+
* defense-in-depth during the migration.
|
|
468
465
|
*
|
|
469
466
|
* `deps` is an optional injection hook:
|
|
470
467
|
* - `deps.dangerousProps` — override the security blocklist. Defaults
|
|
471
468
|
* to `require('./security').dangerousProps`.
|
|
472
469
|
* - `deps.throwError(msg, line, filename)` — override the throw shape.
|
|
473
470
|
* Defaults to `utils.throwError`, matching the seam rule for
|
|
474
|
-
* filename-opaque attribution
|
|
475
|
-
* .claude/architecture/multi-flavor-ir.md § Filename-awareness seam).
|
|
471
|
+
* filename-opaque attribution.
|
|
476
472
|
*
|
|
477
473
|
* @param {object} node IR expression node (any IRExpr shape).
|
|
478
474
|
* @param {object} [deps] Optional dependency overrides.
|
package/lib/cache.js
CHANGED
|
@@ -6,8 +6,7 @@
|
|
|
6
6
|
* without closure state. The native Swig constructor wires its inline
|
|
7
7
|
* `self.cache` + `self.options.cache` through these at each call site.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* strings; the cache layer has no filename awareness.
|
|
9
|
+
* Cache keys are opaque strings; the cache layer has no filename awareness.
|
|
11
10
|
*/
|
|
12
11
|
|
|
13
12
|
/**
|
package/lib/engine.js
CHANGED
|
@@ -24,9 +24,8 @@ function efn() { return ''; }
|
|
|
24
24
|
* not know whether the loader's `resolve` output is a file path, a URL, or
|
|
25
25
|
* a Memcached key.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* filename to compile errors) stays in the frontend.
|
|
27
|
+
* Filename-aware code (utils.throwError wrapping, the engine's try/catch
|
|
28
|
+
* that attaches a filename to compile errors) stays in the frontend.
|
|
30
29
|
*/
|
|
31
30
|
|
|
32
31
|
/**
|
package/lib/filters.js
CHANGED
|
@@ -6,8 +6,7 @@ var utils = require('./utils');
|
|
|
6
6
|
* Phase 1 carve — `iterateFilter` and the `.safe` flag convention live
|
|
7
7
|
* here so every flavor's filter catalog (native Swig, Twig, Jinja2,
|
|
8
8
|
* Django) picks up identical recursion + autoescape-bypass semantics.
|
|
9
|
-
* Filter catalogs themselves stay per-flavor.
|
|
10
|
-
* .claude/architecture/multi-flavor-ir.md.
|
|
9
|
+
* Filter catalogs themselves stay per-flavor.
|
|
11
10
|
*/
|
|
12
11
|
|
|
13
12
|
/**
|
package/lib/index.js
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Phase 1 scaffold. Subsequent commits move security guards, loader
|
|
6
6
|
* contract, filter infra, cache, and the JS-codegen backend in from
|
|
7
|
-
* @rhinostone/swig.
|
|
8
|
-
* the full design.
|
|
7
|
+
* @rhinostone/swig.
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
exports.utils = require('./utils');
|
package/lib/ir.js
CHANGED
|
@@ -9,9 +9,6 @@
|
|
|
9
9
|
* Every frontend (native Swig, Twig, Jinja2, Django) must lower its
|
|
10
10
|
* parse tree into these shapes. Constructs that cannot lower cleanly
|
|
11
11
|
* must throw at parse time — no silent partial behavior.
|
|
12
|
-
*
|
|
13
|
-
* See .claude/architecture/multi-flavor-ir.md for the full design doc,
|
|
14
|
-
* trade-offs considered, and open questions.
|
|
15
12
|
*/
|
|
16
13
|
|
|
17
14
|
/**
|
|
@@ -268,8 +265,7 @@
|
|
|
268
265
|
* functions, and built-in tags not yet migrated to real IR nodes. The
|
|
269
266
|
* backend concatenates `js` verbatim into the compiled template body.
|
|
270
267
|
*
|
|
271
|
-
* Transitional per the Phase 2 layering decision (hybrid / option iii)
|
|
272
|
-
* see .claude/architecture/multi-flavor-ir.md.
|
|
268
|
+
* Transitional per the Phase 2 layering decision (hybrid / option iii).
|
|
273
269
|
*
|
|
274
270
|
* @typedef {Object} IRLegacyJS
|
|
275
271
|
* @property {'LegacyJS'} type
|
|
@@ -445,8 +441,7 @@
|
|
|
445
441
|
*
|
|
446
442
|
* No consumers yet — this commit introduces the schema surface only.
|
|
447
443
|
* Subsequent sessions will migrate the native frontend's token-tree
|
|
448
|
-
* production over to these shapes.
|
|
449
|
-
* .claude/architecture/multi-flavor-ir.md.
|
|
444
|
+
* production over to these shapes.
|
|
450
445
|
* ------------------------------------------------------------------ */
|
|
451
446
|
|
|
452
447
|
/*!
|
package/lib/security.js
CHANGED
|
@@ -12,9 +12,6 @@
|
|
|
12
12
|
* Kept as a shared constant so any future tag that assigns to `_ctx.*`
|
|
13
13
|
* or otherwise exposes an identifier as a property key picks up the
|
|
14
14
|
* full blocklist without drift across copies.
|
|
15
|
-
*
|
|
16
|
-
* See .claude/security.md for the full attack-vector table and the
|
|
17
|
-
* tags/parser checkpoints that consume this list.
|
|
18
15
|
*/
|
|
19
16
|
|
|
20
17
|
/**
|
package/lib/tokenparser.js
CHANGED
|
@@ -17,15 +17,13 @@ var utils = require('./utils'),
|
|
|
17
17
|
* Filter catalogs stay per-flavor — the caller passes its own
|
|
18
18
|
* `filters` map at construction. The `.safe` autoescape-bypass check
|
|
19
19
|
* is preserved verbatim and is the sole gate for the final `e` filter
|
|
20
|
-
* tail-injection.
|
|
21
|
-
* default XSS protection.
|
|
20
|
+
* tail-injection.
|
|
22
21
|
*
|
|
23
22
|
* Error attribution (`utils.throwError(msg, line, filename)`) stays
|
|
24
23
|
* intact: the filename is passed in at construction as an opaque
|
|
25
24
|
* label and used only inside thrown-error messages. TokenParser does
|
|
26
25
|
* not resolve, read, or path-manipulate it — so filename-awareness
|
|
27
|
-
* never crosses the seam back into frontend code.
|
|
28
|
-
* .claude/architecture/multi-flavor-ir.md § Filename-awareness seam.
|
|
26
|
+
* never crosses the seam back into frontend code.
|
|
29
27
|
*/
|
|
30
28
|
|
|
31
29
|
// CVE-2023-25345: prototype-chain properties that must never appear as
|
|
@@ -33,7 +31,6 @@ var utils = require('./utils'),
|
|
|
33
31
|
// gives compiled template code access to Object.prototype (__proto__),
|
|
34
32
|
// Object (constructor), or Function (constructor.constructor), which
|
|
35
33
|
// enables arbitrary code execution inside the new Function(...) body.
|
|
36
|
-
// See .claude/security.md.
|
|
37
34
|
var _dangerousProps = require('./security').dangerousProps;
|
|
38
35
|
|
|
39
36
|
var _reserved = ['break', 'case', 'catch', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'finally', 'for', 'function', 'if', 'in', 'instanceof', 'new', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with'];
|
|
@@ -412,8 +409,7 @@ TokenParser.prototype = {
|
|
|
412
409
|
* VAR segments, DOTKEY matches, STRING-inside-BRACKETOPEN values,
|
|
413
410
|
* FUNCTION / FUNCTIONEMPTY callee names) are mirrored verbatim from
|
|
414
411
|
* {@link TokenParser#parseToken}. Both layers stay live during the
|
|
415
|
-
* migration
|
|
416
|
-
* across layers — DO NOT dedup`.
|
|
412
|
+
* migration.
|
|
417
413
|
*
|
|
418
414
|
* Parses until end of tokens or an un-nested top-level FILTER /
|
|
419
415
|
* FILTEREMPTY token (filter pipes are an Output-site concern —
|
|
@@ -687,8 +683,7 @@ TokenParser.prototype = {
|
|
|
687
683
|
* segments, STRING-in-BRACKETOPEN, DOTKEY) stay live in {@link
|
|
688
684
|
* TokenParser#parseToken} and {@link TokenParser#parseExpr}. Fallback
|
|
689
685
|
* path re-runs through them via `self.parse()`; IR path hits the
|
|
690
|
-
* parseExpr copies.
|
|
691
|
-
* duplicated across layers — DO NOT dedup`.
|
|
686
|
+
* parseExpr copies.
|
|
692
687
|
*
|
|
693
688
|
* @param {object[]} tokens LexerToken[] — the full {{ … }} token stream.
|
|
694
689
|
* @return {object} IROutput IR node.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rhinostone/swig-core",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.8",
|
|
4
4
|
"description": "Shared IR, backend, and runtime for the @rhinostone/swig family of template engines. First publish at 2.0.0-alpha.3 — see @rhinostone/swig #T14 (Phase 1 carve).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"template",
|