@rhinostone/swig 2.7.3 → 2.8.1
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/HISTORY.md +31 -1
- package/README.md +25 -10
- package/ROADMAP.md +9 -0
- package/bin/swig.js +34 -1
- package/context7.json +17 -0
- package/dist/swig.js +76 -7
- package/dist/swig.min.js +15 -4
- package/dist/swig.min.js.map +1 -1
- package/dist/swig.runtime.js +1523 -0
- package/dist/swig.runtime.min.js +45 -0
- package/dist/swig.runtime.min.js.map +1 -0
- package/lib/runtime.js +83 -0
- package/lib/swig.js +27 -1
- package/package.json +3 -3
- package/runtime.js +3 -0
package/HISTORY.md
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
[2.8.1](https://github.com/gina-io/swig/tree/v2.8.1) / 2026-10-20
|
|
2
|
+
-----------------------------------------------------------------
|
|
3
|
+
|
|
4
|
+
* **Added** A "What's new in v<version>" section at the top of the README, regenerated from the release notes at every cut
|
|
5
|
+
|
|
6
|
+
* **Fixed** Refreshed stale README claims — the benchmark summary now reflects the latest measured run (swig leads on all five workloads; the previous table predated the 2.2.0 escape-filter rewrite and showed nunjucks ahead on simple var output), and the security bullet no longer pins an exact CVE regression-test count nor conflates the prototype-chain hardening with CVE-2023-25345 (a directory-traversal fix)
|
|
7
|
+
|
|
8
|
+
* **Fixed** README completeness pass against the 2.x release line — the feature list now covers the async-loader render path (callback dispatch with loader.async, dynamic extends included) and the single-runtime-dependency posture, the "How it works" section explains the runtime-only strict-CSP path, and the Jinja2/Django migration notes no longer list the default filter as absent (it has been built into native swig since upstream 2014)
|
|
9
|
+
|
|
10
|
+
* **Fixed** The v0.9.0 changelog entry's PHP date-format citation now links to the canonical `https://www.php.net/manual/en/function.date.php` instead of the plain-HTTP `http://php.net/date` it had carried since upstream. The old form still resolves — it 301s through to exactly that URL — so this is the same destination over HTTPS, with the link text unchanged; it removes the last plain-HTTP link a link-health scan can flag in this repo (reported twice, gina-io/gina#51 and #58). Documentation only; no runtime or API change.
|
|
11
|
+
|
|
12
|
+
* **Fixed** The bundled DateZ attribution banner now points at `https://github.com/tn0ms/DateZ`, following the fourth rename of that upstream account. The previous `tomoio` form still resolves — GitHub 301s a renamed account through to its current owner — so this is the same destination, with the misspelled upstream `LISENCE` filename preserved because that is genuinely how the file is named there. The banner ships in every `dist/` artifact, so the corrected URL reaches consumers at the next publish. Documentation only; no runtime or API change.
|
|
13
|
+
|
|
14
|
+
* **Changed** Upgraded the ESLint dev dependency from 9.x, which its maintainers no longer support, to 10.x. The flat-config eslint.config.js carries over unchanged, and make lint reports the same results as under 9.x. ESLint 10 raises the lint toolchain Node floor to ^20.19.0, ^22.13.0 or >=24, which affects development and CI only; the published package is unaffected and its only runtime dependency remains @rhinostone/swig-core.
|
|
15
|
+
|
|
16
|
+
[2.8.0](https://github.com/gina-io/swig/tree/v2.8.0) / 2026-08-02
|
|
17
|
+
-----------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
* **Added** AOT template registration — swig.register(path, fn) and swig.registerBundle(map) on every flavor store pre-compiled templates in the template cache under loader-normalized keys, wrapped in the locals-binding call shape include expects (priming via swig.run cached the raw template function, which crashed included partials that used variables or filters); swig compile --recursive --register emits a self-registering browser bundle
|
|
20
|
+
|
|
21
|
+
* **Added** Runtime-only browser build — dist/swig.runtime.js and dist/swig.runtime.min.js execute pre-compiled template bundles (register, registerBundle, run, compileFile, renderFile with the native filter catalog and a root-based memory loader) and ship no parser and no new Function, so pages under a strict Content-Security-Policy without unsafe-eval can render AOT-compiled templates; also exposed to Node as require("@rhinostone/swig/runtime")
|
|
22
|
+
|
|
23
|
+
* **Fixed** Updated the stale `/docs/swig` and `/docs/swig/migration` documentation links to their canonical `/docs/templating/swig` and `/docs/templating/swig/migration` paths in `README.md` (4 occurrences) and `CONTRIBUTING.md` (1 occurrence) (2026-06-15 link-health scan, gina-io/gina#39). The old URLs still resolved via the 301 redirects added in the 2026-04-23 Templating umbrella restructure, but were no longer canonical and kept accruing redirect hops. Documentation only; no runtime or API change.
|
|
24
|
+
|
|
25
|
+
* **Fixed** Refreshed two stale external URLs flagged by the 2026-06-22 / 2026-06-29 link-health scans (gina-io/gina#43). `README.md` now links to `https://expressjs.com/` instead of the plain-HTTP `http://expressjs.com/` (the domain 301-redirects HTTP to HTTPS). `browser/comments.js` updates the DateZ `@license` URL from `github.com/ocrybit/DateZ` to `github.com/tomoio/DateZ` — the upstream GitHub user was renamed again (same class as the v2.0.0 `TomoUniversalis` to `ocrybit` rename); the target repo keeps the upstream `LISENCE` spelling. Documentation and comment-only; no runtime or API change.
|
|
26
|
+
|
|
27
|
+
* **Fixed** De-linked the two remaining live references to the archived `paularmstrong/swig` repository (GitHub topic `unmaintained-dont-use`, last released 2014), flagged by the 2026-07-06 link-health scan (gina-io/gina#47). `README.md` no longer links the "abandoned `paularmstrong/swig`" mention to the archived repo root, and `CONTRIBUTING.md` de-links the "Pull Request 273" historical example (a PR on the archived repo, where contributions are disabled) — both references are preserved as plain text for traceability, matching the de-linking house style from the gina-io/gina#18 and #33 link-health fixes. Documentation only; no runtime or API change.
|
|
28
|
+
|
|
29
|
+
* **Fixed** Several template-cache paths failed silently and now report the problem. The memory loader rejects a climbing path when a basepath is set, instead of resolving it to a different template than the one requested — with a root basepath it clamped and rendered the wrong file, mirroring the confinement the filesystem loader already enforces. Passing a filepath to run stores the same locals-binding wrapper, under the same resolved key, that a cache lookup expects, so a template primed that way is usable by a later include rather than crashing on the raw function argument shape. The memory cache no longer reads inherited object members, so an identifier-style loader cannot resolve a name such as constructor to a cache hit. Both minified bundles now point at the source map filename that is actually written.
|
|
30
|
+
|
|
1
31
|
[2.7.3](https://github.com/gina-io/swig/tree/v2.7.3) / 2026-06-10
|
|
2
32
|
-----------------------------------------------------------------
|
|
3
33
|
|
|
@@ -550,7 +580,7 @@ Migrating from v0.x.x? The upstream wiki has since been deleted; see the individ
|
|
|
550
580
|
* **Added** New `raw` tag.
|
|
551
581
|
* **Changed** Swig is no longer node 0.4 compatible.
|
|
552
582
|
* **Fixed** Filter `date('f')` for 10am times.
|
|
553
|
-
* **Fixed** Filter `date('r')` returns in UTC date format. This is more correct tospec RFC2822, per [php.net/date](
|
|
583
|
+
* **Fixed** Filter `date('r')` returns in UTC date format. This is more correct tospec RFC2822, per [php.net/date](https://www.php.net/manual/en/function.date.php).
|
|
554
584
|
* **Fixed** Filter `add` when adding numbers/numbers+strings together.
|
|
555
585
|
* **Fixed** Tests for error messages that changed in node >0.6.0.
|
|
556
586
|
|
package/README.md
CHANGED
|
@@ -3,11 +3,11 @@ Swig
|
|
|
3
3
|
|
|
4
4
|
[](https://github.com/gina-io/swig/actions/workflows/ci.yml) [](https://www.npmjs.com/package/@rhinostone/swig) [](https://www.npmjs.com/package/@rhinostone/swig) [](https://socket.dev/npm/package/@rhinostone/swig)
|
|
5
5
|
|
|
6
|
-
> **Multi-flavor template engine** for Node.js and browsers — native Swig syntax (Jinja2/Django-inspired), Twig syntax, Python Jinja2 syntax, and Django Template Language syntax via dedicated frontends sharing one IR backend. [gina-io/swig](https://github.com/gina-io/swig) started as a maintained continuation of the abandoned
|
|
6
|
+
> **Multi-flavor template engine** for Node.js and browsers — native Swig syntax (Jinja2/Django-inspired), Twig syntax, Python Jinja2 syntax, and Django Template Language syntax via dedicated frontends sharing one IR backend. [gina-io/swig](https://github.com/gina-io/swig) started as a maintained continuation of the abandoned `paularmstrong/swig` (last released 2014) and is now a standalone project. Security and bug fixes ship here.
|
|
7
7
|
|
|
8
8
|
> **Part of the [Gina](https://github.com/gina-io/gina) ecosystem.** This is the built-in template engine for [Gina](https://gina.io) ([npm](https://www.npmjs.com/package/gina)), a Node.js MVC framework with HTTP/2, multi-bundle architecture, and scope-based data isolation.
|
|
9
9
|
|
|
10
|
-
Swig is a **Jinja2/Django-inspired** template engine for node.js and browsers. The syntax will feel familiar to Jinja2 and Django users, but Swig is **not drop-in compatible** with either — porting templates from an existing project requires a handful of changes. See the [Migration Guide](https://gina.io/docs/swig/migration) for the full parity list and workaround patterns.
|
|
10
|
+
Swig is a **Jinja2/Django-inspired** template engine for node.js and browsers. The syntax will feel familiar to Jinja2 and Django users, but Swig is **not drop-in compatible** with either — porting templates from an existing project requires a handful of changes. See the [Migration Guide](https://gina.io/docs/templating/swig/migration) for the full parity list and workaround patterns.
|
|
11
11
|
|
|
12
12
|
> **Coming from Twig?** Install [@rhinostone/swig-twig](https://www.npmjs.com/package/@rhinostone/swig-twig) instead — a dedicated Twig-syntax frontend with closer parity than working around incompatibilities here.
|
|
13
13
|
|
|
@@ -32,15 +32,28 @@ Features
|
|
|
32
32
|
--------
|
|
33
33
|
|
|
34
34
|
* Available for node.js **and** major web browsers.
|
|
35
|
-
*
|
|
35
|
+
* **Strict-CSP browser runtime** — AOT-compile templates ahead of time (`swig compile --recursive --register`) and render them with the runtime-only bundle (`dist/swig.runtime.min.js`, ~22 KB) — no parser, no `new Function`, no `unsafe-eval` required.
|
|
36
|
+
* [Express](https://expressjs.com/) compatible.
|
|
37
|
+
* **Async template loading** — back the loader with S3 / Redis / HTTP / any callback API: set `loader.async = true` and pass a callback to `renderFile` / `compileFile`; `extends` / `include` / `import` resolve at render time, dynamic `{% extends %}` paths included.
|
|
36
38
|
* Object-Oriented template inheritance.
|
|
37
39
|
* Apply filters and transformations to output in your templates.
|
|
38
|
-
* **
|
|
40
|
+
* **Security-hardened** — prototype-pollution vectors (`__proto__` / `constructor` / `prototype`) blocked at parser, tag-side, and IR-emission layers; template-driven file reads outside the loader root rejected (CVE-2023-25345 directory traversal, patched in 2.7.1). Dedicated CVE regression suite in [`tests/regressions.test.js`](./tests/regressions.test.js).
|
|
39
41
|
* Automatically escapes all variable output (HTML by default; configurable per-call).
|
|
40
42
|
* Lots of iteration and conditionals supported.
|
|
41
|
-
* Robust without the bloat.
|
|
43
|
+
* Robust without the bloat — a single runtime dependency ([`@rhinostone/swig-core`](https://www.npmjs.com/package/@rhinostone/swig-core)).
|
|
42
44
|
* Extendable and customizable — register custom filters, tags, and loaders per-instance.
|
|
43
45
|
|
|
46
|
+
What's new in v2.8.1
|
|
47
|
+
--------------------
|
|
48
|
+
|
|
49
|
+
* **Fixed — README corrections now on npm.** The 2.8.0 README listed CVE-2023-25345 under the prototype-chain hardening; it is the directory-traversal fix, and the security bullet now says so without pinning an exact CVE regression-test count. The benchmark summary reflects the latest measured run (swig leads on all five workloads; the previous table predated the 2.2.0 escape-filter rewrite and showed nunjucks ahead on simple var output). The Jinja2/Django migration notes no longer list the `default` filter as absent — it has been built into native swig since upstream 2014.
|
|
50
|
+
* **Fixed — README completeness for the 2.x line.** The feature list now covers the async-loader render path (callback dispatch with `loader.async`, dynamic `extends` included) and the single-runtime-dependency posture, and "How it works" explains the runtime-only strict-CSP path.
|
|
51
|
+
* **Added — a "What's new" section.** The README now opens with a section like this one, regenerated from the release notes at every cut.
|
|
52
|
+
* **Fixed — attribution and changelog links.** The bundled DateZ attribution banner points at `https://github.com/tn0ms/DateZ` after the upstream account's rename, and the v0.9.0 changelog's PHP date-format citation links to the canonical HTTPS `php.net` page. Both old links still resolved; documentation only, no runtime or API change.
|
|
53
|
+
* **Changed — ESLint 10 for development.** ESLint 9 is no longer supported by its maintainers, so the lint toolchain moves to ESLint 10 with the flat config unchanged. Running `make lint` now needs Node ^20.19.0, ^22.13.0 or >=24; the published package and its runtime Node floor are unaffected.
|
|
54
|
+
|
|
55
|
+
See the full [HISTORY.md](./HISTORY.md) and [ROADMAP.md](./ROADMAP.md).
|
|
56
|
+
|
|
44
57
|
Benchmarks
|
|
45
58
|
----------
|
|
46
59
|
|
|
@@ -50,13 +63,13 @@ Benchmarks
|
|
|
50
63
|
cd benchmarks && npm install && node render.js
|
|
51
64
|
```
|
|
52
65
|
|
|
53
|
-
In production-typical settings (autoescape on), `@rhinostone/swig`
|
|
66
|
+
In production-typical settings (autoescape on), `@rhinostone/swig` leads Nunjucks on all five workload shapes — from ~1.8× on simple variable output to 3.5–5.5× on iteration-heavy templates (median of 3 runs, 2026-08). See [`benchmarks/README.md`](./benchmarks/README.md) for the methodology, the full result table, and how to reproduce on your own hardware.
|
|
54
67
|
|
|
55
68
|
Need Help? Have Questions? Comments?
|
|
56
69
|
------------------------------------
|
|
57
70
|
|
|
58
71
|
* File an issue at [gina-io/swig/issues](https://github.com/gina-io/swig/issues).
|
|
59
|
-
* Swig v0.x → v1.x migration notes — the original upstream wiki has been deleted; see `HISTORY.md` entries around v1.0.0 for the individual breaking changes. For porting *from Jinja2 or Django* into Swig, see the [Migration Guide](https://gina.io/docs/swig/migration).
|
|
72
|
+
* Swig v0.x → v1.x migration notes — the original upstream wiki has been deleted; see `HISTORY.md` entries around v1.0.0 for the individual breaking changes. For porting *from Jinja2 or Django* into Swig, see the [Migration Guide](https://gina.io/docs/templating/swig/migration).
|
|
60
73
|
|
|
61
74
|
Installation
|
|
62
75
|
------------
|
|
@@ -78,7 +91,7 @@ For Django syntax:
|
|
|
78
91
|
Documentation
|
|
79
92
|
-------------
|
|
80
93
|
|
|
81
|
-
User-facing documentation lives in the Gina Docusaurus site under the [Swig Template Engine](https://gina.io/docs/swig) section, maintained in [gina-io/docs](https://github.com/gina-io/docs) at `docs/templating/swig/`. The JSDoc blocks in `lib/swig.js`, `lib/filters.js`, `lib/tags/`, and `lib/loaders/` remain the canonical source-of-truth for the public API and are mirrored into the Docusaurus pages.
|
|
94
|
+
User-facing documentation lives in the Gina Docusaurus site under the [Swig Template Engine](https://gina.io/docs/templating/swig) section, maintained in [gina-io/docs](https://github.com/gina-io/docs) at `docs/templating/swig/`. The JSDoc blocks in `lib/swig.js`, `lib/filters.js`, `lib/tags/`, and `lib/loaders/` remain the canonical source-of-truth for the public API and are mirrored into the Docusaurus pages.
|
|
82
95
|
|
|
83
96
|
Basic Example
|
|
84
97
|
-------------
|
|
@@ -137,9 +150,9 @@ Swig is *inspired by* Jinja2 and Django, not a drop-in replacement. Common pitfa
|
|
|
137
150
|
* **`{% with x=1 %}` → `{% set x = 1 %}`**, and no block-form `{% set %}…{% endset %}`.
|
|
138
151
|
* **No `{% from "f" import x %}` — use `{% import "f" as ns %}` + `ns.x` instead**.
|
|
139
152
|
* **Method calls require parens** — Django auto-invokes `x.get_absolute_url`; Swig needs `x.get_absolute_url()`.
|
|
140
|
-
*
|
|
153
|
+
* **Many Jinja2 filters are absent** — `truncate`, `tojson`, `round`, `int`, `float`, `map`, `select`, `batch`, `trim`, etc. (`default` **is** built in). Register missing ones via `swig.setFilter(name, fn)` — or use [@rhinostone/swig-jinja2](https://www.npmjs.com/package/@rhinostone/swig-jinja2), which ships 39 of them natively.
|
|
141
154
|
|
|
142
|
-
Full parity tables and workaround patterns: **[Migration Guide](https://gina.io/docs/swig/migration)**.
|
|
155
|
+
Full parity tables and workaround patterns: **[Migration Guide](https://gina.io/docs/templating/swig/migration)**.
|
|
143
156
|
|
|
144
157
|
How it works
|
|
145
158
|
------------
|
|
@@ -148,6 +161,8 @@ Swig reads template files and translates them into cached JavaScript functions.
|
|
|
148
161
|
|
|
149
162
|
In `2.x`, frontend parsers (native Swig syntax in [@rhinostone/swig](https://www.npmjs.com/package/@rhinostone/swig), Twig syntax in [@rhinostone/swig-twig](https://www.npmjs.com/package/@rhinostone/swig-twig), Python Jinja2 syntax in [@rhinostone/swig-jinja2](https://www.npmjs.com/package/@rhinostone/swig-jinja2), Django Template Language syntax in [@rhinostone/swig-django](https://www.npmjs.com/package/@rhinostone/swig-django)) emit a shared intermediate representation. The backend in [@rhinostone/swig-core](https://www.npmjs.com/package/@rhinostone/swig-core) lowers IR to JS. New flavors plug in at the frontend without touching the runtime.
|
|
150
163
|
|
|
164
|
+
For strict-CSP targets the compile pipeline can be skipped entirely: AOT-compile templates with the CLI (`swig compile --recursive --register`) and execute the resulting bundle with the runtime-only build (`dist/swig.runtime.min.js`), which contains no parser and no `new Function`.
|
|
165
|
+
|
|
151
166
|
License
|
|
152
167
|
-------
|
|
153
168
|
|
package/ROADMAP.md
CHANGED
|
@@ -16,11 +16,20 @@ _No near-term scheduled items. See [Future (post-2.0)](#future-post-20) for upco
|
|
|
16
16
|
| --- | --- |
|
|
17
17
|
| Planned | Async parse path for the remaining dynamic targets — runtime-resolved `{% import %}` / `{% from %}` paths on the async-codegen branch. Static-target async dispatch shipped in 2.2.0; dynamic `{% extends %}` shipped in 2.5.2 (native + Twig + Jinja2) and dynamic `{% include %}` paths already resolve (the include path has always been an expression). Dynamic `import` / `from` are on hold pending consumer demand. |
|
|
18
18
|
| Planned | Modern browser-test harness. The legacy phantomjs runner was removed; a replacement (e.g. Playwright or JSDOM) has not yet landed, so browser parity is verified in the interim via the production build plus a symbol grep. (The `mocha` → `node:test` runner migration, the `expect.js` → in-repo assertion-shim swap, and the `blanket` → `node:test` built-in coverage migration have all shipped.) |
|
|
19
|
+
| On demand | Ahead-of-time browser support for the Twig, Jinja2, and Django frontends, in two parts that ship together. Runtime-only bundles: the shared runtime factory already supports them, and each flavor needs its own bundle entry and build target, since filter catalogs are per-flavor. A flavor-aware compile step: `swig compile --register` compiles with the native parser only, so flavor templates are precompiled programmatically until it lands. The native `dist/swig.runtime.min.js` shipped in 2.8.0; the flavor pieces land when a consumer needs them. |
|
|
19
20
|
|
|
20
21
|
---
|
|
21
22
|
|
|
22
23
|
## Completed
|
|
23
24
|
|
|
25
|
+
### v2.8.0 (August 2026)
|
|
26
|
+
|
|
27
|
+
- Added ahead-of-time template registration: `swig.register(path, fn)` and `swig.registerBundle(map)` place pre-compiled template functions into the template cache under loader-normalized keys, so `include`, `compileFile`, and `renderFile` resolve them without a compiler. Available on all four frontends (native, Twig, Jinja2, Django). `swig compile --recursive --register` emits a bundle that registers itself on load and also exports the template map, so a page needs no per-template wiring.
|
|
28
|
+
- Added a runtime-only browser build — `dist/swig.runtime.js` and `dist/swig.runtime.min.js` (22 KB minified, roughly a quarter of the full bundle). It executes pre-compiled bundles with the full filter catalog but ships no lexer, parser, or code generator, and therefore contains no `new Function`: pages served under a strict Content-Security-Policy without `unsafe-eval` can render AOT-compiled templates. Every compiling entry point throws a clear error naming the CLI workflow instead of failing obscurely. Also available to Node as `require('@rhinostone/swig/runtime')`.
|
|
29
|
+
- Fixed several template-cache paths that failed silently. A registered template no longer shadows a loadable `{% extends %}` parent — the parent's source is still read through the loader, where previously registering a layout could make a child that rendered correctly start rendering an empty string with no error. The memory loader rejects a climbing path when a `basepath` is set rather than resolving it to a different template than the one requested. Passing a filepath to `run` now caches an entry a later `include` can actually use. The memory cache no longer treats inherited object members as cache hits. `register` reports a disabled cache for every falsy `cache` value, and `registerBundle` validates a whole bundle before committing any of it, so a malformed map cannot leave the registry half-populated.
|
|
30
|
+
- Fixed browser integration for the new bundles: a self-registering bundle finds the engine whether it is a bare global or a property of the global object (under AMD there is no bare binding, so every template previously went unregistered in silence) and warns when no engine is present; loading a runtime bundle twice, or after the full build, no longer discards existing registrations or replaces a compiler-capable instance; and the runtime entry no longer assumes `window`, so it loads in a worker. Both minified bundles now reference the source map filename that is actually written.
|
|
31
|
+
- All five packages (`@rhinostone/swig`, `@rhinostone/swig-core`, `@rhinostone/swig-twig`, `@rhinostone/swig-jinja2`, `@rhinostone/swig-django`) released in lockstep at `2.8.0`. Lockstep is now required rather than conventional: the frontends reference modules inside `@rhinostone/swig-core` that exist only from this version forward.
|
|
32
|
+
|
|
24
33
|
### v2.7.3 (June 2026)
|
|
25
34
|
|
|
26
35
|
- Fixed a filter-chain argument leak in the expression parsers: the per-filter argument accumulator was function-scoped and never reset between iterations, so an arg-bearing filter earlier in a chain leaked its arguments onto a later no-arg filter (e.g. `x|default("y")|upper` lowered `upper` with the leaked `["y"]`). Affected nested and parenthesized chains in the shared core parser and all filter chains in the Twig and Jinja2 frontends; the Django frontend already shipped the per-iteration reset. Runtime impact ranged from harmless (filters that ignore extra positional arguments) to wrong output (a no-arg filter consuming a leaked positional argument).
|
package/bin/swig.js
CHANGED
|
@@ -27,6 +27,7 @@ var FLAGS = {
|
|
|
27
27
|
'm': { alias: 'minify', boolean: true },
|
|
28
28
|
'r': { alias: 'recursive' },
|
|
29
29
|
'ext': {},
|
|
30
|
+
'register': { boolean: true },
|
|
30
31
|
'filters': {},
|
|
31
32
|
'tags': {},
|
|
32
33
|
'options': {},
|
|
@@ -313,6 +314,10 @@ function validate(argv) {
|
|
|
313
314
|
}
|
|
314
315
|
}
|
|
315
316
|
|
|
317
|
+
if (argv.register && !argv.r) {
|
|
318
|
+
throw new Error('--register requires --recursive; it emits a self-registering bundle of the compiled directory.');
|
|
319
|
+
}
|
|
320
|
+
|
|
316
321
|
if (argv.ext && !argv.r) {
|
|
317
322
|
throw new Error('--ext is only meaningful with --recursive.');
|
|
318
323
|
}
|
|
@@ -344,6 +349,7 @@ function usage() {
|
|
|
344
349
|
' -m, --minify Minify compiled functions with terser',
|
|
345
350
|
' -r, --recursive Recursively compile every template in <dir> into a single AOT bundle module.',
|
|
346
351
|
' --ext Comma-separated list of file extensions to include when using --recursive (e.g. ".html,.swig"). Defaults to no filter.',
|
|
352
|
+
' --register With --recursive: emit a self-registering bundle (calls swig.registerBundle when a global swig is present; also exports the template map).',
|
|
347
353
|
' --filters Custom filters as a CommonJS-style file',
|
|
348
354
|
' --tags Custom tags as a CommonJS-style file',
|
|
349
355
|
' --options Customize Swig\'s Options from a CommonJS-style file',
|
|
@@ -418,6 +424,13 @@ function walkSync(dir) {
|
|
|
418
424
|
* resolution still happens at render time through the consumer's loader. The
|
|
419
425
|
* bundle is not a closed module against inheritance chains.
|
|
420
426
|
*
|
|
427
|
+
* With <code>--register</code> the bundle self-registers instead: it calls
|
|
428
|
+
* <code>swig.registerBundle(map)</code> against a global <code>swig</code>
|
|
429
|
+
* when one is present (a browser page that loaded the swig bundle first),
|
|
430
|
+
* and still exports the raw map for Node-side consumers. Run the command
|
|
431
|
+
* from the template root (<code>swig compile -r . --register</code>) so
|
|
432
|
+
* baked include paths line up with the bundle's registration keys.
|
|
433
|
+
*
|
|
421
434
|
* @param {string} dir Directory to walk.
|
|
422
435
|
* @return {undefined} Writes to stdout or to <code>argv.o</code>.
|
|
423
436
|
* @private
|
|
@@ -452,7 +465,27 @@ function bundleRecursive(dir) {
|
|
|
452
465
|
parts.push(JSON.stringify(key) + ': ' + tpl);
|
|
453
466
|
});
|
|
454
467
|
|
|
455
|
-
|
|
468
|
+
if (argv.register) {
|
|
469
|
+
// The engine may be reachable as a bare global (classic script tag) or
|
|
470
|
+
// only as a property of the global object — under AMD the browser entry
|
|
471
|
+
// registers a named module and never creates the bare binding, so a
|
|
472
|
+
// bare-identifier lookup alone would silently register nothing.
|
|
473
|
+
output = 'var __swigTemplates = {\n' + parts.join(',\n') + '\n};\n' +
|
|
474
|
+
'if (typeof module !== "undefined" && module.exports) {\n' +
|
|
475
|
+
' module.exports = __swigTemplates;\n' +
|
|
476
|
+
'}\n' +
|
|
477
|
+
'var __swigEngine = (typeof swig !== "undefined" && swig) ||\n' +
|
|
478
|
+
' (typeof window !== "undefined" && window.swig) ||\n' +
|
|
479
|
+
' (typeof globalThis !== "undefined" && globalThis.swig) || null;\n' +
|
|
480
|
+
'if (__swigEngine && __swigEngine.registerBundle) {\n' +
|
|
481
|
+
' __swigEngine.registerBundle(__swigTemplates);\n' +
|
|
482
|
+
'} else if (typeof console !== "undefined" && console.warn) {\n' +
|
|
483
|
+
' console.warn("swig: no engine found; ' + parts.length + ' pre-compiled template(s) were not registered. ' +
|
|
484
|
+
'Load swig before this bundle, or call swig.registerBundle(__swigTemplates) yourself.");\n' +
|
|
485
|
+
'}\n';
|
|
486
|
+
} else {
|
|
487
|
+
output = 'module.exports = {\n' + parts.join(',\n') + '\n};\n';
|
|
488
|
+
}
|
|
456
489
|
|
|
457
490
|
if (argv.m) {
|
|
458
491
|
output = loadTerser().minify_sync(output).code;
|
package/context7.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"url": "https://context7.com/gina-io/swig",
|
|
3
|
+
"public_key": "pk_MGD4ulFUKUHnxmzFGuWc8",
|
|
4
|
+
"projectTitle": "Rhinostone Swig",
|
|
5
|
+
"description": "Rhinostone Swig is a simple, powerful, and extendable template engine for Node.js and browsers, supporting native Swig, Twig, Python Jinja2, and Django Template Language syntaxes.",
|
|
6
|
+
"branch": "develop",
|
|
7
|
+
"excludeFolders": [
|
|
8
|
+
".changes"
|
|
9
|
+
],
|
|
10
|
+
"excludeFiles": [
|
|
11
|
+
"HISTORY.md",
|
|
12
|
+
"ROADMAP.md",
|
|
13
|
+
"AUTHORS",
|
|
14
|
+
"ISSUE_TEMPLATE.md",
|
|
15
|
+
"PULL_REQUEST_TEMPLATE.md"
|
|
16
|
+
]
|
|
17
|
+
}
|
package/dist/swig.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
/*! Swig v2.
|
|
2
|
-
/*! DateZ (c) 2011 Tomo Universalis | @license https://github.com/
|
|
1
|
+
/*! Swig v2.8.1 | https://github.com/gina-io/swig | @license https://github.com/gina-io/swig/blob/master/LICENSE */
|
|
2
|
+
/*! DateZ (c) 2011 Tomo Universalis | @license https://github.com/tn0ms/DateZ/blob/master/LISENCE */
|
|
3
3
|
(() => {
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __commonJS = (cb, mod) => function __require() {
|
|
6
|
-
|
|
6
|
+
try {
|
|
7
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
8
|
+
} catch (e) {
|
|
9
|
+
throw mod = 0, e;
|
|
10
|
+
}
|
|
7
11
|
};
|
|
8
12
|
|
|
9
13
|
// packages/swig-core/lib/security.js
|
|
@@ -4176,6 +4180,9 @@
|
|
|
4176
4180
|
basepath = basepath ? path.normalize(basepath) : null;
|
|
4177
4181
|
ret.resolve = function(to, from) {
|
|
4178
4182
|
if (basepath) {
|
|
4183
|
+
if (/^\.\.([\\/]|$)/.test(path.normalize(to))) {
|
|
4184
|
+
utils.throwError('Template "' + to + '" resolves outside the loader root "' + basepath + '".');
|
|
4185
|
+
}
|
|
4179
4186
|
from = basepath;
|
|
4180
4187
|
} else {
|
|
4181
4188
|
from = from ? path.dirname(from) : "/";
|
|
@@ -4376,7 +4383,7 @@
|
|
|
4376
4383
|
return;
|
|
4377
4384
|
}
|
|
4378
4385
|
if (engineCache === "memory") {
|
|
4379
|
-
return memoryStore[key];
|
|
4386
|
+
return Object.prototype.hasOwnProperty.call(memoryStore, key) ? memoryStore[key] : void 0;
|
|
4380
4387
|
}
|
|
4381
4388
|
return engineCache.get(key);
|
|
4382
4389
|
};
|
|
@@ -4475,7 +4482,10 @@
|
|
|
4475
4482
|
}
|
|
4476
4483
|
parentFile = parentFile || options.filename;
|
|
4477
4484
|
parentFile = deps.resolve(parentName, parentFile);
|
|
4478
|
-
parent = deps.cacheGet(parentFile, options)
|
|
4485
|
+
parent = deps.cacheGet(parentFile, options);
|
|
4486
|
+
if (!parent || !parent.tokens) {
|
|
4487
|
+
parent = deps.parseFile(parentFile, utils.extend({}, options, { filename: parentFile }));
|
|
4488
|
+
}
|
|
4479
4489
|
parentName = parent.parent;
|
|
4480
4490
|
if (parentFiles.indexOf(parentFile) !== -1) {
|
|
4481
4491
|
throw new Error('Illegal circular extends of "' + parentFile + '".');
|
|
@@ -4526,6 +4536,52 @@
|
|
|
4526
4536
|
self.cache = {};
|
|
4527
4537
|
}
|
|
4528
4538
|
};
|
|
4539
|
+
function buildRegistered(fn) {
|
|
4540
|
+
var context = getLocals(), contextLength = utils.keys(context).length;
|
|
4541
|
+
return function registered(locals, blocks) {
|
|
4542
|
+
var lcls;
|
|
4543
|
+
if (locals && contextLength) {
|
|
4544
|
+
lcls = utils.extend({}, context, locals);
|
|
4545
|
+
} else if (locals && !contextLength) {
|
|
4546
|
+
lcls = locals;
|
|
4547
|
+
} else if (!locals && contextLength) {
|
|
4548
|
+
lcls = context;
|
|
4549
|
+
} else {
|
|
4550
|
+
lcls = {};
|
|
4551
|
+
}
|
|
4552
|
+
return fn(self, lcls, filters, utils, efn, blocks);
|
|
4553
|
+
};
|
|
4554
|
+
}
|
|
4555
|
+
function validateRegistration(path, fn) {
|
|
4556
|
+
if (typeof path !== "string" || !path.length) {
|
|
4557
|
+
throw new Error("Template registration path must be a non-empty string.");
|
|
4558
|
+
}
|
|
4559
|
+
if (typeof fn !== "function") {
|
|
4560
|
+
throw new Error('Registered template for "' + path + '" is not a function. Pass a pre-compiled template function.');
|
|
4561
|
+
}
|
|
4562
|
+
if (!self.options.cache) {
|
|
4563
|
+
throw new Error("Cannot register templates while caching is disabled (cache: " + self.options.cache + "). The registry is the compile cache.");
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
self.register = function(path, fn) {
|
|
4567
|
+
validateRegistration(path, fn);
|
|
4568
|
+
cacheSet(self.options.loader.resolve(path), {}, buildRegistered(fn));
|
|
4569
|
+
return self;
|
|
4570
|
+
};
|
|
4571
|
+
self.registerBundle = function(map) {
|
|
4572
|
+
var entries = [];
|
|
4573
|
+
if (!map || typeof map !== "object") {
|
|
4574
|
+
throw new Error("Template bundle must be an object of { path: templateFn } pairs.");
|
|
4575
|
+
}
|
|
4576
|
+
utils.each(map, function(fn, path) {
|
|
4577
|
+
entries.push([path, fn]);
|
|
4578
|
+
validateRegistration(path, fn);
|
|
4579
|
+
});
|
|
4580
|
+
utils.each(entries, function(entry) {
|
|
4581
|
+
self.register(entry[0], entry[1]);
|
|
4582
|
+
});
|
|
4583
|
+
return self;
|
|
4584
|
+
};
|
|
4529
4585
|
self.setFilter = function(name, method) {
|
|
4530
4586
|
if (typeof method !== "function") {
|
|
4531
4587
|
throw new Error('Filter "' + name + '" is not a valid function.');
|
|
@@ -4739,7 +4795,7 @@
|
|
|
4739
4795
|
self.run = function(tpl, locals, filepath) {
|
|
4740
4796
|
var context = getLocals({ locals });
|
|
4741
4797
|
if (filepath) {
|
|
4742
|
-
cacheSet(filepath, {}, tpl);
|
|
4798
|
+
cacheSet(self.options.loader.resolve(filepath), {}, buildRegistered(tpl));
|
|
4743
4799
|
}
|
|
4744
4800
|
return tpl(self, context, filters, utils, efn);
|
|
4745
4801
|
};
|
|
@@ -4758,7 +4814,7 @@
|
|
|
4758
4814
|
var loaders = require_loaders2();
|
|
4759
4815
|
var preWalker = require_pre_walker();
|
|
4760
4816
|
var engine = require_engine();
|
|
4761
|
-
exports.version = "2.
|
|
4817
|
+
exports.version = "2.8.1";
|
|
4762
4818
|
var defaultOptions = {
|
|
4763
4819
|
autoescape: true,
|
|
4764
4820
|
varControls: ["{{", "}}"],
|
|
@@ -4963,6 +5019,8 @@
|
|
|
4963
5019
|
exports.renderFile = defaultInstance.renderFile;
|
|
4964
5020
|
exports.renderFileAsync = defaultInstance.renderFileAsync;
|
|
4965
5021
|
exports.run = defaultInstance.run;
|
|
5022
|
+
exports.register = defaultInstance.register;
|
|
5023
|
+
exports.registerBundle = defaultInstance.registerBundle;
|
|
4966
5024
|
exports.invalidateCache = defaultInstance.invalidateCache;
|
|
4967
5025
|
exports.loaders = loaders;
|
|
4968
5026
|
}
|
|
@@ -5126,6 +5184,17 @@
|
|
|
5126
5184
|
* chunk, returning the trimmed tag body (e.g. `extends "x.html"`).
|
|
5127
5185
|
* @private
|
|
5128
5186
|
*/
|
|
5187
|
+
/*!
|
|
5188
|
+
* Wrap a pre-compiled template function in the locals-binding call shape
|
|
5189
|
+
* cached entries have — include-emitted code invokes them as
|
|
5190
|
+
* `(context)`, so the raw five-argument function is the wrong shape.
|
|
5191
|
+
* @private
|
|
5192
|
+
*/
|
|
5193
|
+
/*!
|
|
5194
|
+
* Shared argument checks for register / registerBundle, so a bundle can be
|
|
5195
|
+
* validated in full before any entry is committed.
|
|
5196
|
+
* @private
|
|
5197
|
+
*/
|
|
5129
5198
|
/*!
|
|
5130
5199
|
* Export methods publicly
|
|
5131
5200
|
*/
|