@rhinostone/swig 2.5.3 → 2.7.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/HISTORY.md +70 -56
- package/README.md +11 -4
- package/ROADMAP.md +13 -2
- package/dist/swig.js +70 -12
- package/dist/swig.min.js +16 -2
- package/dist/swig.min.js.map +1 -1
- package/lib/filters.js +23 -1
- package/lib/swig.js +1 -1
- package/package.json +2 -3
package/HISTORY.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
[2.7.0](https://github.com/gina-io/swig/tree/v2.7.0) / 2026-06-07
|
|
2
|
+
-----------------------------------------------------------------
|
|
3
|
+
|
|
4
|
+
* **Added** `@rhinostone/swig-django` is a new Django Template Language frontend for the `@rhinostone/swig-core` engine — the fourth flavor in the multi-flavor family alongside the native, Twig, and Jinja2 frontends. It renders real Django templates: the full built-in tag set (`for`/`empty` with `forloop`, `if`/`elif`/`else`, `block`/`extends`/`include`, `with`, `autoescape`, `spaceless`, `comment`, `verbatim`, `cycle`, `firstof`), ~42 built-in filters with colon-argument syntax such as `{{ value|date:"Y-m-d" }}`, a Django-faithful variable resolver (auto-calls callable attributes while honoring `alters_data`/`do_not_call_in_templates`, numeric indexing like `{{ list.0 }}`, and `.keys`/`.values`/`.items` dict iteration), and async loader support via `renderFile(path, locals, cb)`. Installs as `npm install @rhinostone/swig-django`.
|
|
5
|
+
|
|
6
|
+
* **Fixed** De-linked the dead `paularmstrong/swig` issue and pull-request references in `HISTORY.md` and the matching `.changes/v*.md` fragments (2026-06-01 link-health scan, gina-io/gina#33). The upstream repository remains but its issue tracker is disabled, so the inline `[gh-N](...)` links are now plain `gh-N` text — every reference is preserved for historical traceability, and the fragments are edited in lockstep with `HISTORY.md` to keep `changie merge` idempotent. Documentation / changelog only; no runtime or API change.
|
|
7
|
+
|
|
8
|
+
[2.6.0](https://github.com/gina-io/swig/tree/v2.6.0) / 2026-06-03
|
|
9
|
+
-----------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
* **Changed** The native `json` / `json_encode` filters now HTML-escape their output (`<`, `>`, `&`, `'`) and are marked safe, so `{{ data|json }}` renders valid JSON that is safe to embed directly inside a `<script>` block instead of `"`-escaped text. `url_encode` is now marked safe as well — its output never contains HTML-significant characters. `url_decode` is unchanged — its output remains autoescaped, since decoded content can contain HTML.
|
|
12
|
+
|
|
13
|
+
* **Changed** Replaced the `expect.js` test-assertion dev dependency with a small in-repo shim (`tests/lib/expect.js`) that reproduces the subset of its API the suite uses, with verified behavioral parity. Dev-only — the published packages are unaffected (their only runtime dependency remains `@rhinostone/swig-core`), and the full test suite plus the CVE-2023-25345 regressions pass unchanged. `npm audit` was already clean (`expect.js` is zero-dependency and advisory-free), so this is dependency-hygiene polish, not a security fix.
|
|
14
|
+
|
|
1
15
|
[2.5.3](https://github.com/gina-io/swig/tree/v2.5.3) / 2026-06-02
|
|
2
16
|
-----------------------------------------------------------------
|
|
3
17
|
|
|
@@ -320,29 +334,29 @@ Migrating from v0.x.x? The upstream wiki has since been deleted; see the individ
|
|
|
320
334
|
[1.0.0-pre3](https://github.com/paularmstrong/swig/tree/v1.0.0-pre3) / 2013-08-20
|
|
321
335
|
---------------------------------------------------------------------------------
|
|
322
336
|
|
|
323
|
-
* **Changed** Allow tags at block-level if specified.
|
|
324
|
-
* **Fixed** `swig.compileFile` runs callback template is found in cache.
|
|
325
|
-
* **Fixed** Accidental modification of Swig Options Object.
|
|
326
|
-
* **Fixed** Preserve forward-slashes in text chunks.
|
|
337
|
+
* **Changed** Allow tags at block-level if specified. gh-289
|
|
338
|
+
* **Fixed** `swig.compileFile` runs callback template is found in cache. gh-291
|
|
339
|
+
* **Fixed** Accidental modification of Swig Options Object. gh-287
|
|
340
|
+
* **Fixed** Preserve forward-slashes in text chunks. gh-285
|
|
327
341
|
|
|
328
342
|
[1.0.0-pre2](https://github.com/paularmstrong/swig/tree/v1.0.0-pre2) / 2013-08-18
|
|
329
343
|
---------------------------------------------------------------------------------
|
|
330
344
|
|
|
331
345
|
* **Changed** Binary: Allow --method-name to be a shortcut for --wrap-start var setting.
|
|
332
346
|
* **Changed** Make reverse filter an alias for `sort(true)`.
|
|
333
|
-
* **Added** Allow asyncronous `compileFile` and `renderFile` operations.
|
|
347
|
+
* **Added** Allow asyncronous `compileFile` and `renderFile` operations. gh-283
|
|
334
348
|
* **Added** Filter: `sort`.
|
|
335
|
-
* **Added** Allow {% end[tag] tokens... %}.
|
|
349
|
+
* **Added** Allow {% end[tag] tokens... %}. gh-278
|
|
336
350
|
* **Added** Built source map for minified browser source.
|
|
337
|
-
* **Added** Contextual support for object method calls.
|
|
338
|
-
* **Added** `parser.on('start'|'end'...` options.
|
|
339
|
-
* **Added** Allow object prototypal inheritance.
|
|
340
|
-
* **Fixed** Prevent circular extends.
|
|
341
|
-
* **Fixed** Throw an error if reserved word is used as var.
|
|
342
|
-
* **Fixed** Add filename to errors if possible.
|
|
343
|
-
* **Fixed** Filters work over arrays/objects if possible.
|
|
344
|
-
* **Fixed** Allow {% parent %} to work in middle parent templates.
|
|
345
|
-
* **Fixed** Allow newlines in tags/vars/comments.
|
|
351
|
+
* **Added** Contextual support for object method calls. gh-275
|
|
352
|
+
* **Added** `parser.on('start'|'end'...` options. gh-274
|
|
353
|
+
* **Added** Allow object prototypal inheritance. gh-273
|
|
354
|
+
* **Fixed** Prevent circular extends. gh-282
|
|
355
|
+
* **Fixed** Throw an error if reserved word is used as var. gh-276
|
|
356
|
+
* **Fixed** Add filename to errors if possible. gh-280
|
|
357
|
+
* **Fixed** Filters work over arrays/objects if possible. gh-259
|
|
358
|
+
* **Fixed** Allow {% parent %} to work in middle parent templates. gh-277
|
|
359
|
+
* **Fixed** Allow newlines in tags/vars/comments. gh-272
|
|
346
360
|
|
|
347
361
|
[1.0.0-pre1](https://github.com/paularmstrong/swig/tree/v1.0.0-pre1) / 2013-08-14
|
|
348
362
|
---------------------------------------------------------------------------------
|
|
@@ -363,57 +377,57 @@ Migrating from v0.x.x? The upstream wiki has since been deleted; see the individ
|
|
|
363
377
|
* **Changed** allow `_`, `$` to start var names in templates.
|
|
364
378
|
* **Changed** Documentation is auto-generated from jsdoc comments in-files.
|
|
365
379
|
* **Added** Ability to set custom var/tag/comment controls (`{{`, `}}`, etc, can be customized).
|
|
366
|
-
* **Added** Variable/string concatenation
|
|
380
|
+
* **Added** Variable/string concatenation gh-135.
|
|
367
381
|
* **Added** Binary application for `compile`, `run`, and `render` (Lets you pre-compile templates into JS functions for client-side delivery).
|
|
368
382
|
* **Fixed** Lots.
|
|
369
383
|
|
|
370
384
|
[0.14.0](https://github.com/paularmstrong/swig/tree/v0.14.0) / 2013-06-08
|
|
371
385
|
-------------------------------------------------------------------------
|
|
372
386
|
|
|
373
|
-
* **Added** Allow executing functions from within templates
|
|
374
|
-
* **Added** New `spaceless` tag
|
|
375
|
-
* **Fixed** bug when attempting to loop over nested vars with `for`.
|
|
387
|
+
* **Added** Allow executing functions from within templates gh-182
|
|
388
|
+
* **Added** New `spaceless` tag gh-193
|
|
389
|
+
* **Fixed** bug when attempting to loop over nested vars with `for`. gh-232
|
|
376
390
|
|
|
377
391
|
[0.13.5](https://github.com/paularmstrong/swig/tree/v0.13.5) / 2013-01-29
|
|
378
392
|
-------------------------------------------------------------------------
|
|
379
393
|
|
|
380
|
-
* **Fixed** date filter output for 'O' when time-zone offset is negative
|
|
394
|
+
* **Fixed** date filter output for 'O' when time-zone offset is negative gh-185
|
|
381
395
|
|
|
382
396
|
[0.13.4](https://github.com/paularmstrong/swig/tree/v0.13.4) / 2012-12-19
|
|
383
397
|
-------------------------------------------------------------------------
|
|
384
398
|
|
|
385
|
-
* **Fixed** Runaway loop on missing template
|
|
386
|
-
* **Fixed** Allow variables in if tag conditionals to have filters with arguments
|
|
399
|
+
* **Fixed** Runaway loop on missing template gh-162 gh-165
|
|
400
|
+
* **Fixed** Allow variables in if tag conditionals to have filters with arguments gh-167
|
|
387
401
|
|
|
388
402
|
[0.13.3](https://github.com/paularmstrong/swig/tree/v0.13.3) / 2012-12-07
|
|
389
403
|
-------------------------------------------------------------------------
|
|
390
404
|
|
|
391
|
-
* **Added** Support % (modulus) in if tags
|
|
392
|
-
* **Added** Support multi-root via array
|
|
405
|
+
* **Added** Support % (modulus) in if tags gh-155
|
|
406
|
+
* **Added** Support multi-root via array gh-143
|
|
393
407
|
|
|
394
408
|
[0.13.2](https://github.com/paularmstrong/swig/tree/v0.13.2) / 2012-10-28
|
|
395
409
|
-------------------------------------------------------------------------
|
|
396
410
|
|
|
397
|
-
* **Changed** Allow variables, filters, arguments to span lines
|
|
398
|
-
* **Changed** Throw Errors when using undefined filters
|
|
399
|
-
* **Fixed** compiling files from absolute paths
|
|
400
|
-
* **Fixed** Prevent global variables from being used before context variables
|
|
411
|
+
* **Changed** Allow variables, filters, arguments to span lines gh-122
|
|
412
|
+
* **Changed** Throw Errors when using undefined filters gh-115
|
|
413
|
+
* **Fixed** compiling files from absolute paths gh-103
|
|
414
|
+
* **Fixed** Prevent global variables from being used before context variables gh-117
|
|
401
415
|
|
|
402
416
|
[Documentation](https://github.com/paularmstrong/swig/tree/v0.13.2/docs)
|
|
403
417
|
|
|
404
418
|
[0.13.1](https://github.com/paularmstrong/swig/tree/v0.13.1) / 2012-10-28
|
|
405
419
|
-------------------------------------------------------------------------
|
|
406
420
|
|
|
407
|
-
* **Fixed** Macros should be preserved when using inheritence
|
|
408
|
-
* **Fixed** bug in parent tag logic
|
|
409
|
-
* **Fixed** Error messaging when parent block failed compilation
|
|
421
|
+
* **Fixed** Macros should be preserved when using inheritence gh-132 ([nsaun](https://github.com/nsaun))
|
|
422
|
+
* **Fixed** bug in parent tag logic gh-130
|
|
423
|
+
* **Fixed** Error messaging when parent block failed compilation gh-129 ([nsaun](https://github.com/nsaun))
|
|
410
424
|
|
|
411
425
|
[Documentation](https://github.com/paularmstrong/swig/tree/v0.13.1/docs)
|
|
412
426
|
|
|
413
427
|
[0.13.0](https://github.com/paularmstrong/swig/tree/v0.13.0) / 2012-10-20
|
|
414
428
|
-------------------------------------------------------------------------
|
|
415
429
|
|
|
416
|
-
* **Added** Support for nested blocks!
|
|
430
|
+
* **Added** Support for nested blocks! gh-64 gh-129 ([nsaun](https://github.com/nsaun))
|
|
417
431
|
* **Changed** Removed the `parentBlock` argument from tags.
|
|
418
432
|
* **Fixed** Object keys may now contain dots
|
|
419
433
|
|
|
@@ -442,28 +456,28 @@ Migrating from v0.x.x? The upstream wiki has since been deleted; see the individ
|
|
|
442
456
|
[0.11.2](https://github.com/paularmstrong/swig/tree/v0.11.2) / 2012-04-10
|
|
443
457
|
-------------------------------------------------------------------------
|
|
444
458
|
|
|
445
|
-
* **Fixed** Update support for underscore@1.3.3
|
|
459
|
+
* **Fixed** Update support for underscore@1.3.3 gh-70 gh-71
|
|
446
460
|
|
|
447
461
|
[Documentation](https://github.com/paularmstrong/swig/tree/v0.11.2/docs)
|
|
448
462
|
|
|
449
463
|
[0.11.1](https://github.com/paularmstrong/swig/tree/v0.11.1) / 2012-04-01
|
|
450
464
|
-------------------------------------------------------------------------
|
|
451
465
|
|
|
452
|
-
* **Fixed** Duplicate (string) tokens were being removed when extending a base template.
|
|
466
|
+
* **Fixed** Duplicate (string) tokens were being removed when extending a base template. gh-67
|
|
453
467
|
|
|
454
468
|
[Documentation](https://github.com/paularmstrong/swig/tree/v0.11.1/docs)
|
|
455
469
|
|
|
456
470
|
[0.11.0](https://github.com/paularmstrong/swig/tree/v0.11.0) / 2012-02-27
|
|
457
471
|
-------------------------------------------------------------------------
|
|
458
472
|
|
|
459
|
-
* **Added** Support for Windows style paths
|
|
473
|
+
* **Added** Support for Windows style paths gh-57
|
|
460
474
|
* **Added** `ignore missing` tokens to include tag
|
|
461
475
|
* **Changed** include tag `with context` to only work if `context` is an object
|
|
462
476
|
* **Changed** `autoescape` tag controls no longer 'yes' or 'no'. Use `true` and `false`
|
|
463
477
|
* **Changed** parser is now passed into tags as an argument
|
|
464
478
|
* **Changed** don't require passing context object when rendering template
|
|
465
|
-
* **Fixed** dateformats `N` and `w`
|
|
466
|
-
* **Fixed** number changing to string after add filter or set from variable
|
|
479
|
+
* **Fixed** dateformats `N` and `w` gh-59
|
|
480
|
+
* **Fixed** number changing to string after add filter or set from variable gh-53 gh-58
|
|
467
481
|
* **Fixed** speed decrease caused by loop.cycle fixed
|
|
468
482
|
* **Fixed** Ensure set tag bubbles through extends and blocks
|
|
469
483
|
|
|
@@ -472,22 +486,22 @@ Migrating from v0.x.x? The upstream wiki has since been deleted; see the individ
|
|
|
472
486
|
[0.10.0](https://github.com/paularmstrong/swig/tree/v0.10.0) / 2012-02-13
|
|
473
487
|
-------------------------------------------------------------------------
|
|
474
488
|
|
|
475
|
-
* **Added** loop.index0, loop.revindex, loop.revindex0, and loop.cycle
|
|
476
|
-
* **Added** init config `extensions` for 3rd party extension access in custom tags
|
|
477
|
-
* **Added** Whitespace Control
|
|
478
|
-
* **Changed** The `empty` tag in `for` loops is now `else`
|
|
479
|
-
* **Changed** `forloop` vars to `loop` closes
|
|
480
|
-
* **Fixed** `include` tag's `with` and `only` args documentation
|
|
489
|
+
* **Added** loop.index0, loop.revindex, loop.revindex0, and loop.cycle gh-48
|
|
490
|
+
* **Added** init config `extensions` for 3rd party extension access in custom tags gh-44
|
|
491
|
+
* **Added** Whitespace Control gh-46
|
|
492
|
+
* **Changed** The `empty` tag in `for` loops is now `else` gh-49
|
|
493
|
+
* **Changed** `forloop` vars to `loop` closes gh-47
|
|
494
|
+
* **Fixed** `include` tag's `with` and `only` args documentation gh-50
|
|
481
495
|
|
|
482
496
|
[Documentation](https://github.com/paularmstrong/swig/tree/v0.10.0/docs)
|
|
483
497
|
|
|
484
498
|
[0.9.4](https://github.com/paularmstrong/swig/tree/v0.9.4) / 2012-02-07
|
|
485
499
|
-----------------------------------------------------------------------
|
|
486
500
|
|
|
487
|
-
* **Fixed** `parent` tag would not render when called within tags
|
|
488
|
-
* **Fixed** Documentation for forloop.index & forloop.key
|
|
489
|
-
* **Fixed** Errors when using `include` inside base template `block` tags
|
|
490
|
-
* **Fixed** Allow `set` tag to set values to numbers
|
|
501
|
+
* **Fixed** `parent` tag would not render when called within tags gh-41
|
|
502
|
+
* **Fixed** Documentation for forloop.index & forloop.key gh-42
|
|
503
|
+
* **Fixed** Errors when using `include` inside base template `block` tags gh-43
|
|
504
|
+
* **Fixed** Allow `set` tag to set values to numbers gh-45
|
|
491
505
|
* **Fixed** `set` tag for booleans using too many checks
|
|
492
506
|
|
|
493
507
|
[Documentation](https://github.com/paularmstrong/swig/tree/v0.9.4/docs)
|
|
@@ -495,21 +509,21 @@ Migrating from v0.x.x? The upstream wiki has since been deleted; see the individ
|
|
|
495
509
|
[0.9.3](https://github.com/paularmstrong/swig/tree/v0.9.3) / 2012-01-28
|
|
496
510
|
-----------------------------------------------------------------------
|
|
497
511
|
|
|
498
|
-
* **Fixed** Allow object and array values to be accessed via context variables
|
|
512
|
+
* **Fixed** Allow object and array values to be accessed via context variables gh-40
|
|
499
513
|
|
|
500
514
|
[Documentation](https://github.com/paularmstrong/swig/tree/v0.9.3/docs)
|
|
501
515
|
|
|
502
516
|
[0.9.2](https://github.com/paularmstrong/swig/tree/v0.9.2) / 2012-01-23
|
|
503
517
|
-----------------------------------------------------------------------
|
|
504
518
|
|
|
505
|
-
* **Fixed** Correctly reset autoescape after closing an autoescape tag.
|
|
519
|
+
* **Fixed** Correctly reset autoescape after closing an autoescape tag. gh-39
|
|
506
520
|
|
|
507
521
|
[Documentation](https://github.com/paularmstrong/swig/tree/v0.9.2/docs)
|
|
508
522
|
|
|
509
523
|
[0.9.1](https://github.com/paularmstrong/swig/tree/v0.9.1) / 2012-01-18
|
|
510
524
|
-----------------------------------------------------------------------
|
|
511
525
|
|
|
512
|
-
* **Fixed** Allow multi-line tags and comments.
|
|
526
|
+
* **Fixed** Allow multi-line tags and comments. gh-30
|
|
513
527
|
|
|
514
528
|
[Documentation](https://github.com/paularmstrong/swig/tree/v0.9.1/docs)
|
|
515
529
|
|
|
@@ -517,7 +531,7 @@ Migrating from v0.x.x? The upstream wiki has since been deleted; see the individ
|
|
|
517
531
|
-----------------------------------------------------------------------
|
|
518
532
|
|
|
519
533
|
* **Added** DateZ license to browser header, use link to underscore license.
|
|
520
|
-
* **Added** Timezone support in `date` filter
|
|
534
|
+
* **Added** Timezone support in `date` filter gh-27.
|
|
521
535
|
* **Added** New `raw` tag.
|
|
522
536
|
* **Changed** Swig is no longer node 0.4 compatible.
|
|
523
537
|
* **Fixed** Filter `date('f')` for 10am times.
|
|
@@ -539,18 +553,18 @@ Migrating from v0.x.x? The upstream wiki has since been deleted; see the individ
|
|
|
539
553
|
* **Changed** `swig.init()` will clear template cache.
|
|
540
554
|
* **Changed** `swig.init()` is now optional for browser mode with no custom settings.
|
|
541
555
|
* **Changed** Development dependencies are be more lenient.
|
|
542
|
-
* **Fixed** Parser will properly preserver '\' escaping.
|
|
556
|
+
* **Fixed** Parser will properly preserver '\' escaping. gh-24
|
|
543
557
|
* **Fixed** Rewrote tag argument parsing for proper space handling.
|
|
544
|
-
* **Fixed** Rewrote filter argument parsing.
|
|
545
|
-
* **Fixed** Allow pipe `|` characters in filter arguments.
|
|
558
|
+
* **Fixed** Rewrote filter argument parsing. gh-23
|
|
559
|
+
* **Fixed** Allow pipe `|` characters in filter arguments. gh-22
|
|
546
560
|
|
|
547
561
|
[Documentation](https://github.com/paularmstrong/swig/tree/v0.8.0/docs)
|
|
548
562
|
|
|
549
563
|
[0.7.0](https://github.com/paularmstrong/swig/tree/v0.7.0) / 2011-10-05
|
|
550
564
|
-----------------------------------------------------------------------
|
|
551
565
|
|
|
552
|
-
* **Added** `make browser` will build Swig for use in major browsers.
|
|
553
|
-
* **Changed** Allow overriding `escape` filters.
|
|
566
|
+
* **Added** `make browser` will build Swig for use in major browsers. gh-3
|
|
567
|
+
* **Changed** Allow overriding `escape` filters. gh-19
|
|
554
568
|
|
|
555
569
|
[Documentation](https://github.com/paularmstrong/swig/tree/v0.7.0/docs)
|
|
556
570
|
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Swig
|
|
2
2
|
====
|
|
3
3
|
|
|
4
|
-
[](https://github.com/gina-io/swig/actions/workflows/ci.yml) [](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,
|
|
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](https://github.com/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
|
|
|
@@ -13,6 +13,8 @@ Swig is a **Jinja2/Django-inspired** template engine for node.js and browsers. T
|
|
|
13
13
|
|
|
14
14
|
> **Coming from Python Jinja2?** Install [@rhinostone/swig-jinja2](https://www.npmjs.com/package/@rhinostone/swig-jinja2) — a dedicated Jinja2-syntax frontend (near-subset) with closer parity than porting to native Swig syntax here.
|
|
15
15
|
|
|
16
|
+
> **Coming from Django?** Install [@rhinostone/swig-django](https://www.npmjs.com/package/@rhinostone/swig-django) — a dedicated Django Template Language frontend that renders real Django templates, with far closer parity than native Swig syntax here.
|
|
17
|
+
|
|
16
18
|
Workspace packages
|
|
17
19
|
------------------
|
|
18
20
|
|
|
@@ -21,6 +23,7 @@ Workspace packages
|
|
|
21
23
|
| [`@rhinostone/swig`](https://www.npmjs.com/package/@rhinostone/swig) | Native Swig syntax (Jinja2/Django-inspired). Drop-in for `@rhinostone/swig@1.x` consumers. | Upgrading from `@rhinostone/swig@1.x`, or starting fresh with Swig syntax. |
|
|
22
24
|
| [`@rhinostone/swig-twig`](https://www.npmjs.com/package/@rhinostone/swig-twig) | Twig-syntax frontend with closer Twig parity. | Migrating from PHP Twig, or writing new templates in Twig syntax. |
|
|
23
25
|
| [`@rhinostone/swig-jinja2`](https://www.npmjs.com/package/@rhinostone/swig-jinja2) | Python Jinja2-syntax frontend (near-subset). | Migrating from Python Jinja2, or writing new templates in Jinja2 syntax. |
|
|
26
|
+
| [`@rhinostone/swig-django`](https://www.npmjs.com/package/@rhinostone/swig-django) | Django Template Language frontend (real DTL). | Migrating from Django, or writing new templates in Django syntax. |
|
|
24
27
|
| [`@rhinostone/swig-core`](https://www.npmjs.com/package/@rhinostone/swig-core) | Shared IR, backend, and runtime primitives. | Building a custom flavor frontend. Otherwise pulled in transitively. |
|
|
25
28
|
|
|
26
29
|
Each frontend pins the matching `@rhinostone/swig-core` version exactly (no caret, no tilde) — frontends and the core release in lockstep on every cut.
|
|
@@ -68,6 +71,10 @@ For Python Jinja2 syntax:
|
|
|
68
71
|
|
|
69
72
|
npm install @rhinostone/swig-jinja2
|
|
70
73
|
|
|
74
|
+
For Django syntax:
|
|
75
|
+
|
|
76
|
+
npm install @rhinostone/swig-django
|
|
77
|
+
|
|
71
78
|
Documentation
|
|
72
79
|
-------------
|
|
73
80
|
|
|
@@ -116,7 +123,7 @@ Migrating from `@rhinostone/swig@1.x`
|
|
|
116
123
|
|
|
117
124
|
`@rhinostone/swig@2.x` is **drop-in for `1.x` consumers** — `swig.compileFile`, `swig.renderFile`, `swig.setFilter`, `swig.setTag`, and the rest of the public API are unchanged. The internal carve into [@rhinostone/swig-core](https://www.npmjs.com/package/@rhinostone/swig-core) is transparent (test gate during the alpha cycle: byte-identical compiled output against the `1.x` test suite).
|
|
118
125
|
|
|
119
|
-
`2.0.0` also ships [@rhinostone/swig-twig](https://www.npmjs.com/package/@rhinostone/swig-twig), a sibling Twig-syntax frontend
|
|
126
|
+
`2.0.0` also ships [@rhinostone/swig-twig](https://www.npmjs.com/package/@rhinostone/swig-twig), a sibling Twig-syntax frontend; `2.5.0` adds [@rhinostone/swig-jinja2](https://www.npmjs.com/package/@rhinostone/swig-jinja2) for Python Jinja2 syntax; and `2.7.0` adds [@rhinostone/swig-django](https://www.npmjs.com/package/@rhinostone/swig-django) for Django Template Language syntax. Switching is opt-in — your existing `@rhinostone/swig` install keeps working.
|
|
120
127
|
|
|
121
128
|
Migrating from Jinja2 or Django
|
|
122
129
|
-------------------------------
|
|
@@ -139,7 +146,7 @@ How it works
|
|
|
139
146
|
|
|
140
147
|
Swig reads template files and translates them into cached JavaScript functions. The pipeline is: parse → emit IR → lower IR to JS source → `new Function(...)`. At render time, the compiled function runs against a context object to produce the output string.
|
|
141
148
|
|
|
142
|
-
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)) 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.
|
|
149
|
+
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.
|
|
143
150
|
|
|
144
151
|
License
|
|
145
152
|
-------
|
package/ROADMAP.md
CHANGED
|
@@ -15,13 +15,24 @@ _No near-term scheduled items. See [Future (post-2.0)](#future-post-20) for upco
|
|
|
15
15
|
| Status | Item |
|
|
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
|
-
| Planned |
|
|
19
|
-
| Planned | Test framework migration. Replace mocha 1.x + expect.js with `node:test` + `node:assert/strict`, add a modern browser-test harness (the legacy phantomjs runner has been removed), swap blanket for `c8`. (The Node engines bump is upstream-driven by gina and is being treated as done.) |
|
|
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.) |
|
|
20
19
|
|
|
21
20
|
---
|
|
22
21
|
|
|
23
22
|
## Completed
|
|
24
23
|
|
|
24
|
+
### v2.7.0 (June 2026)
|
|
25
|
+
|
|
26
|
+
- Added `@rhinostone/swig-django`, a Django Template Language frontend on the shared `@rhinostone/swig-core` engine — the fourth dialect in the multi-flavor family alongside native swig, `@rhinostone/swig-twig`, and `@rhinostone/swig-jinja2`. It renders real Django templates: 15 tags (`if` / `elif` / `else`, `for` / `empty` with `forloop`, `block`, `extends`, `include`, `with`, `autoescape`, `spaceless`, `comment`, `verbatim`, `cycle`, `firstof`), 42 built-in filters with Django's colon-argument syntax (`{{ value|date:"Y-m-d" }}`), and a Django-faithful variable resolver — callable attributes are auto-called (honoring `alters_data` / `do_not_call_in_templates`), numeric indexing works (`{{ list.0 }}`), and dicts iterate via `.keys` / `.values` / `.items`. Async loader support via `renderFileAsync` / `compileFileAsync` and `renderFile(path, locals, cb)` against an async loader. Autoescape and the CVE-2023-25345 guards are inherited from `@rhinostone/swig-core`. Every tag and filter was cross-checked against Django 5.2; the behavioural differences and explicit non-goals (no `{% load %}` / custom tag libraries, no `{% url %}` / `{% static %}` / `{% csrf_token %}` / `{% trans %}` / `{% blocktrans %}` framework infrastructure, no whitespace-control, no `is` tests) are documented in the Django templating guide.
|
|
27
|
+
- All five packages (`@rhinostone/swig`, `@rhinostone/swig-core`, `@rhinostone/swig-twig`, `@rhinostone/swig-jinja2`, `@rhinostone/swig-django`) released in lockstep at `2.7.0`. `@rhinostone/swig-core` gained an additive opt-in loop-context flag and a variable-resolver primitive (`_utils.resolve`) consumed by the Django frontend; native swig, Twig, and Jinja2 are functionally unchanged (proven byte-identical compiled output).
|
|
28
|
+
- Documentation housekeeping: de-linked the dead `paularmstrong/swig` issue and pull-request references in `HISTORY.md` (the upstream issue tracker is disabled), preserving every reference as plain text.
|
|
29
|
+
|
|
30
|
+
### v2.6.0 (June 2026)
|
|
31
|
+
|
|
32
|
+
- The native `json` / `json_encode` filters now HTML-escape their output (`<`, `>`, `&`, and `'` are emitted as `\u00XX` escapes) and are marked safe, so `{{ data|json }}` renders valid JSON that can be embedded directly inside a `<script>` block instead of `"`-escaped text. `url_encode` is now marked safe as well — its output never contains HTML-significant characters. `url_decode` is deliberately unchanged: its output stays autoescaped, since decoded content can contain HTML. Only the native `@rhinostone/swig` flavor changes here — `@rhinostone/swig-twig` keeps `json_encode` / `url_encode` unescaped (Twig-faithful), and `@rhinostone/swig-jinja2` keeps its `tojson` (safe) / `urlencode` (not safe) split (Jinja2-faithful).
|
|
33
|
+
- Replaced the `expect.js` test-assertion dev dependency with a small in-repo shim, with verified behavioral parity. Dev-only — the published packages are unaffected (their only runtime dependency remains `@rhinostone/swig-core`), and the full test suite plus the CVE-2023-25345 regressions pass unchanged.
|
|
34
|
+
- All four packages (`@rhinostone/swig`, `@rhinostone/swig-core`, `@rhinostone/swig-twig`, `@rhinostone/swig-jinja2`) released in lockstep at `2.6.0`. The functional change is confined to native `@rhinostone/swig`'s filters; the `@rhinostone/swig-core`, `@rhinostone/swig-twig`, and `@rhinostone/swig-jinja2` runtimes are functionally identical to `2.5.3`.
|
|
35
|
+
|
|
25
36
|
### v2.5.3 (June 2026)
|
|
26
37
|
|
|
27
38
|
- Dynamic `{% extends %}` on the synchronous render path now throws a clear error pointing to the async render path (`renderFile` with `loader.async === true`), instead of a generic "template not found" / "no filename" error. Dynamic extends has always required the async render path; this only improves the diagnostic. Applies to all flavors via the shared engine.
|
package/dist/swig.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Swig v2.
|
|
1
|
+
/*! Swig v2.7.0 | https://github.com/gina-io/swig | @license https://github.com/gina-io/swig/blob/master/LICENSE */
|
|
2
2
|
/*! DateZ (c) 2011 Tomo Universalis | @license https://github.com/ocrybit/DateZ/blob/master/LISENCE */
|
|
3
3
|
(() => {
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -6,9 +6,17 @@
|
|
|
6
6
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
// packages/swig-core/lib/security.js
|
|
10
|
+
var require_security = __commonJS({
|
|
11
|
+
"packages/swig-core/lib/security.js"(exports) {
|
|
12
|
+
exports.dangerousProps = ["__proto__", "constructor", "prototype"];
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
9
16
|
// packages/swig-core/lib/utils.js
|
|
10
17
|
var require_utils = __commonJS({
|
|
11
18
|
"packages/swig-core/lib/utils.js"(exports) {
|
|
19
|
+
var security = require_security();
|
|
12
20
|
var isArray;
|
|
13
21
|
exports.strip = function(input) {
|
|
14
22
|
return input.replace(/^\s+|\s+$/g, "");
|
|
@@ -201,6 +209,40 @@
|
|
|
201
209
|
exports.coerceOutput = function(v) {
|
|
202
210
|
return v === null || v === void 0 ? "" : v;
|
|
203
211
|
};
|
|
212
|
+
function resolveSeg(obj, seg) {
|
|
213
|
+
var val;
|
|
214
|
+
if (obj === null || obj === void 0) {
|
|
215
|
+
return void 0;
|
|
216
|
+
}
|
|
217
|
+
if (security.dangerousProps.indexOf(seg) !== -1) {
|
|
218
|
+
return void 0;
|
|
219
|
+
}
|
|
220
|
+
val = obj[seg];
|
|
221
|
+
if (typeof val === "function") {
|
|
222
|
+
if (val.alters_data === true) {
|
|
223
|
+
return void 0;
|
|
224
|
+
}
|
|
225
|
+
if (val.do_not_call_in_templates === true) {
|
|
226
|
+
return val;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
return val.call(obj);
|
|
230
|
+
} catch (e) {
|
|
231
|
+
return void 0;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return val;
|
|
235
|
+
}
|
|
236
|
+
exports.resolve = function(obj, path) {
|
|
237
|
+
var cur = obj, i;
|
|
238
|
+
for (i = 0; i < path.length; i += 1) {
|
|
239
|
+
cur = resolveSeg(cur, path[i]);
|
|
240
|
+
if (cur === null || cur === void 0) {
|
|
241
|
+
return cur;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return cur;
|
|
245
|
+
};
|
|
204
246
|
}
|
|
205
247
|
});
|
|
206
248
|
|
|
@@ -1004,8 +1046,13 @@
|
|
|
1004
1046
|
return input;
|
|
1005
1047
|
};
|
|
1006
1048
|
exports.json = function(input, indent) {
|
|
1007
|
-
|
|
1049
|
+
var json = JSON.stringify(input, null, indent || 0);
|
|
1050
|
+
if (typeof json !== "string") {
|
|
1051
|
+
return json;
|
|
1052
|
+
}
|
|
1053
|
+
return json.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/'/g, "\\u0027");
|
|
1008
1054
|
};
|
|
1055
|
+
exports.json.safe = true;
|
|
1009
1056
|
exports.json_encode = exports.json;
|
|
1010
1057
|
exports.last = function(input) {
|
|
1011
1058
|
if (typeof input === "object" && !utils.isArray(input)) {
|
|
@@ -1115,6 +1162,7 @@
|
|
|
1115
1162
|
}
|
|
1116
1163
|
return encodeURIComponent(input);
|
|
1117
1164
|
};
|
|
1165
|
+
exports.url_encode.safe = true;
|
|
1118
1166
|
exports.url_decode = function(input) {
|
|
1119
1167
|
var out = iterateFilter.apply(exports.url_decode, arguments);
|
|
1120
1168
|
if (out !== void 0) {
|
|
@@ -1223,13 +1271,6 @@
|
|
|
1223
1271
|
}
|
|
1224
1272
|
});
|
|
1225
1273
|
|
|
1226
|
-
// packages/swig-core/lib/security.js
|
|
1227
|
-
var require_security = __commonJS({
|
|
1228
|
-
"packages/swig-core/lib/security.js"(exports) {
|
|
1229
|
-
exports.dangerousProps = ["__proto__", "constructor", "prototype"];
|
|
1230
|
-
}
|
|
1231
|
-
});
|
|
1232
|
-
|
|
1233
1274
|
// lib/tags/for.js
|
|
1234
1275
|
var require_for = __commonJS({
|
|
1235
1276
|
"lib/tags/for.js"(exports) {
|
|
@@ -1430,7 +1471,7 @@
|
|
|
1430
1471
|
return;
|
|
1431
1472
|
}
|
|
1432
1473
|
if (node.type === "For") {
|
|
1433
|
-
var forVal = node.value, forKey = node.key, forIterable, forBodyJS = "", ctxloopcache = ("_ctx.__loopcache" + Math.random()).replace(/\./g, ""), ctx = "_ctx.", ctxloop = "_ctx.
|
|
1474
|
+
var forVal = node.value, forKey = node.key, forIterable, forBodyJS = "", ctxloopcache = ("_ctx.__loopcache" + Math.random()).replace(/\./g, ""), ctx = "_ctx.", loopName = node.loopName || "loop", ctxloop = "_ctx." + loopName, loopFields = node.loopFields || {}, fIndex = loopFields.index || "index", fIndex0 = loopFields.index0 || "index0", fRevindex = loopFields.revindex || "revindex", fRevindex0 = loopFields.revindex0 || "revindex0", parentloopJS = node.loopParent ? ", parentloop: " + ctxloopcache + "." + loopName : "";
|
|
1434
1475
|
if (node.iterable && typeof node.iterable === "object" && typeof node.iterable.type === "string") {
|
|
1435
1476
|
forIterable = exports.emitExpr(node.iterable);
|
|
1436
1477
|
} else {
|
|
@@ -1461,7 +1502,7 @@
|
|
|
1461
1502
|
});
|
|
1462
1503
|
forEmptyCheck = " if (!__l || !__len) {\n" + forEmptyJS + " return;\n }\n";
|
|
1463
1504
|
}
|
|
1464
|
-
out += "(function () {\n var __l = " + forIterable + ', __len = (_utils.isArray(__l) || typeof __l === "string") ? __l.length : _utils.keys(__l).length;\n' + forEmptyCheck + " var " + ctxloopcache + " = {
|
|
1505
|
+
out += "(function () {\n var __l = " + forIterable + ', __len = (_utils.isArray(__l) || typeof __l === "string") ? __l.length : _utils.keys(__l).length;\n' + forEmptyCheck + " var " + ctxloopcache + " = { " + loopName + ": " + ctxloop + ", " + forVal + ": " + ctx + forVal + ", " + forKey + ": " + ctx + forKey + " };\n " + ctxloop + " = { first: false, " + fIndex + ": 1, " + fIndex0 + ": 0, " + fRevindex + ": __len, " + fRevindex0 + ": __len - 1, length: __len, last: false" + parentloopJS + " };\n _utils.each(__l, function (" + forVal + ", " + forKey + ") {\n " + ctx + forVal + " = " + forVal + ";\n " + ctx + forKey + " = " + forKey + ";\n " + ctxloop + ".key = " + forKey + ";\n " + ctxloop + ".first = (" + ctxloop + "." + fIndex0 + " === 0);\n " + ctxloop + ".last = (" + ctxloop + "." + fRevindex0 + " === 0);\n " + forBodyJS + " " + ctxloop + "." + fIndex + " += 1; " + ctxloop + "." + fIndex0 + " += 1; " + ctxloop + "." + fRevindex + " -= 1; " + ctxloop + "." + fRevindex0 + " -= 1;\n });\n " + ctxloop + " = " + ctxloopcache + "." + loopName + ";\n " + ctx + forVal + " = " + ctxloopcache + "." + forVal + ";\n " + ctx + forKey + " = " + ctxloopcache + "." + forKey + ";\n " + ctxloopcache + " = undefined;\n})();\n";
|
|
1465
1506
|
return;
|
|
1466
1507
|
}
|
|
1467
1508
|
if (node.type === "Macro") {
|
|
@@ -1829,6 +1870,9 @@
|
|
|
1829
1870
|
utils.each(node.path, function(segment) {
|
|
1830
1871
|
checkDangerousSegment(segment, d, node);
|
|
1831
1872
|
});
|
|
1873
|
+
if (node.resolve) {
|
|
1874
|
+
return "_utils.resolve(_ctx, " + JSON.stringify(node.path) + ")";
|
|
1875
|
+
}
|
|
1832
1876
|
return checkMatchExpr(node.path);
|
|
1833
1877
|
}
|
|
1834
1878
|
function emitVarRefExists(node, d) {
|
|
@@ -4705,7 +4749,7 @@
|
|
|
4705
4749
|
var loaders = require_loaders2();
|
|
4706
4750
|
var preWalker = require_pre_walker();
|
|
4707
4751
|
var engine = require_engine();
|
|
4708
|
-
exports.version = "2.
|
|
4752
|
+
exports.version = "2.7.0";
|
|
4709
4753
|
var defaultOptions = {
|
|
4710
4754
|
autoescape: true,
|
|
4711
4755
|
varControls: ["{{", "}}"],
|
|
@@ -4925,6 +4969,20 @@
|
|
|
4925
4969
|
window.swig = swig;
|
|
4926
4970
|
}
|
|
4927
4971
|
})();
|
|
4972
|
+
/*!
|
|
4973
|
+
* Resolve a single path segment against a value, Django-style. A plain
|
|
4974
|
+
* property access (`obj[seg]`) covers dictionary, attribute, and array /
|
|
4975
|
+
* string index lookup in one step (JS string-keys an array / string by a
|
|
4976
|
+
* numeric segment, so `["a"][0]` and `["a"]["0"]` both yield the element).
|
|
4977
|
+
* A callable leaf is auto-called with no arguments, bound to its receiver,
|
|
4978
|
+
* honoring Django's opt-outs: `fn.alters_data === true` is not called and
|
|
4979
|
+
* yields `undefined` (Django renders nothing for data-altering callables);
|
|
4980
|
+
* `fn.do_not_call_in_templates === true` is returned uncalled. A call that
|
|
4981
|
+
* throws (e.g. a method that needs arguments) yields `undefined`, mirroring
|
|
4982
|
+
* Django's `string_if_invalid` fallback on a failed auto-call. The
|
|
4983
|
+
* `_dangerousProps` segments are rejected at runtime as defense-in-depth.
|
|
4984
|
+
* @private
|
|
4985
|
+
*/
|
|
4928
4986
|
/*!
|
|
4929
4987
|
* Attach `loc` to the node if provided, skipping the assignment otherwise
|
|
4930
4988
|
* so consumers can tell "no source location available" from
|