@rhinostone/swig 2.8.3 → 2.9.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 +16 -0
- package/README.md +7 -5
- package/ROADMAP.md +15 -1
- package/dist/swig.js +111 -9
- package/dist/swig.min.js +32 -2
- package/dist/swig.min.js.map +1 -1
- package/dist/swig.runtime.js +67 -2
- package/dist/swig.runtime.min.js +15 -2
- package/dist/swig.runtime.min.js.map +1 -1
- package/lib/parser.js +30 -2
- package/lib/runtime.js +1 -1
- package/lib/swig.js +2 -2
- package/lib/tags/if.js +2 -1
- package/package.json +2 -2
package/HISTORY.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
[2.9.0](https://github.com/gina-io/swig/tree/v2.9.0) / 2026-10-23
|
|
2
|
+
-----------------------------------------------------------------
|
|
3
|
+
|
|
4
|
+
* **Added** An OpenSSF Scorecard badge at the top of the README of each of the five packages. It shows the score that a Scorecard workflow publishes for the repository on every push to develop and every week, and links to the Scorecard report. Documentation only; no runtime or API change.
|
|
5
|
+
|
|
6
|
+
* **Added** swig-twig, swig-jinja2 and swig-django: the `not in` operator, as Twig, Jinja2 and Django have it. `x not in y` negates `x in y` and binds at the same level; where Django cannot run the test it is false, as Django gives it, and where Jinja2 raises swig gives true. Until now `x not in y` failed to parse. Native swig still rejects `not in`.
|
|
7
|
+
|
|
8
|
+
* **Fixed** swig-twig, swig-jinja2 and swig-django: the `in` operator tests membership as Twig, Jinja2 and Django do. In Jinja2 and Django a list holds an equal item, a string a substring and a mapping a key; in Twig a list or a mapping holds a value, compared loosely, and a string holds a string or number as a substring. Until now `in` was the JavaScript operator, which tests keys: `3 in [1, 2, 3]` was false, `0 in [5]` true, and `"b" in "abc"` threw. A test the reference engine cannot run (a number or null container, for instance) is false instead of an error. Native swig keeps the JavaScript `in`.
|
|
9
|
+
|
|
10
|
+
[2.8.4](https://github.com/gina-io/swig/tree/v2.8.4) / 2026-10-21
|
|
11
|
+
-----------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
* **Fixed** A filename option that is not a string, such as a number, compiles again with import, from and extends on the async render path, as it did in 2.8.2, and include, which failed with a number in 2.8.2 as well, now compiles too. 2.8.3 escaped the filename as a string before writing it into the compiled template, so a number failed with value.replace is not a function; the filename is now converted to a string first. All four frontends.
|
|
14
|
+
|
|
15
|
+
* **Fixed** The autoescape option of @rhinostone/swig now takes the name of an escape strategy, as documented: autoescape set to 'js' gives JavaScript escaping and 'html' HTML escaping. The option was written into the compiled template as code, so these values failed with ReferenceError: js is not defined, and an autoescape option built from untrusted input could run as code in the template. A value already wrapped in quotes keeps working, and a value holding a double quote or a line break is now rejected with an error. The Twig, Jinja2 and Django frontends already passed the option to the escape filter as a string.
|
|
16
|
+
|
|
1
17
|
[2.8.3](https://github.com/gina-io/swig/tree/v2.8.3) / 2026-10-21
|
|
2
18
|
-----------------------------------------------------------------
|
|
3
19
|
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Swig
|
|
2
2
|
====
|
|
3
3
|
|
|
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)
|
|
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) [](https://scorecard.dev/viewer/?uri=github.com/gina-io/swig)
|
|
5
5
|
|
|
6
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
|
|
|
@@ -32,7 +32,7 @@ Features
|
|
|
32
32
|
--------
|
|
33
33
|
|
|
34
34
|
* Available for node.js **and** major web browsers.
|
|
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`, ~
|
|
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`, ~24 KB) — no parser, no `new Function`, no `unsafe-eval` required.
|
|
36
36
|
* [Express](https://expressjs.com/) compatible.
|
|
37
37
|
* **Async template loading** — back the loader with S3 / Redis / HTTP / any callback API: set `loader.async = true` and pass a callback to `renderFile`, or to `compileFile` with `codegenMode: 'async'` in its options, whose compiled function returns a promise of `{ output, exports }`; `extends` / `include` / `import` resolve at render time, dynamic `{% extends %}` paths included.
|
|
38
38
|
* Object-Oriented template inheritance.
|
|
@@ -43,12 +43,14 @@ Features
|
|
|
43
43
|
* Robust without the bloat — a single runtime dependency ([`@rhinostone/swig-core`](https://www.npmjs.com/package/@rhinostone/swig-core)).
|
|
44
44
|
* Extendable and customizable — register custom filters, tags, and loaders per-instance.
|
|
45
45
|
|
|
46
|
-
What's new in v2.
|
|
46
|
+
What's new in v2.9.0
|
|
47
47
|
--------------------
|
|
48
48
|
|
|
49
|
-
> **
|
|
49
|
+
> **Behaviour change in `@rhinostone/swig-twig`, `@rhinostone/swig-jinja2` and `@rhinostone/swig-django`.** `x in list` now tests the list's items, not its indices, and `x in "text"` a substring, as Twig, Jinja2 and Django do. In swig-twig, `x in mapping` now tests the mapping's values, as Twig does: write `x in mapping|keys` to test a key. Native `@rhinostone/swig` is unchanged.
|
|
50
50
|
|
|
51
|
-
* **
|
|
51
|
+
* **Fixed — `in` tests membership in the Twig, Jinja2 and Django frontends.** In swig-jinja2 and swig-django a list holds an equal item, a string a substring and a mapping a key; in swig-twig a list or a mapping holds a value, compared loosely, and a string holds a string or number as a substring. Until now `in` was the JavaScript operator, which tests keys: `3 in [1, 2, 3]` was false, `0 in [5]` true, and `"b" in "abc"` threw. A test the reference engine cannot run (a number or null container, for instance) is false instead of an error. Native `@rhinostone/swig` keeps the JavaScript `in`, which tests an object's keys or an array's indices.
|
|
52
|
+
* **Added — `not in` in the Twig, Jinja2 and Django frontends.** `x not in y` negates `x in y` and binds at the same level. Where Django cannot run the test it is false, as in Django; where Jinja2 raises, swig gives true. Until now `x not in y` failed to parse. Native `@rhinostone/swig` still rejects `not in`.
|
|
53
|
+
* **Added — an OpenSSF Scorecard badge** at the top of the README of each of the five packages. It shows the score that a Scorecard workflow publishes for the repository on every push to develop and every week, and links to the Scorecard report. Documentation only; no runtime or API change.
|
|
52
54
|
|
|
53
55
|
See the full [HISTORY.md](./HISTORY.md) and [ROADMAP.md](./ROADMAP.md).
|
|
54
56
|
|
package/ROADMAP.md
CHANGED
|
@@ -10,10 +10,11 @@ For bug reports and feature requests, file an issue at [gina-io/swig](https://gi
|
|
|
10
10
|
|
|
11
11
|
| Status | Item |
|
|
12
12
|
| --- | --- |
|
|
13
|
-
| Planned | `in` tests membership in all four flavors: `x in list` tests the elements and `x in "text"` the substring. Today `in` tests an object's keys or an array's indices, so `3 in [1, 2, 3]` is false. |
|
|
14
13
|
| Planned | swig-jinja2 parity: loop filters (`{% for x in xs if cond %}`), `not(x)` written without a space, the `none` / `True` / `False` constants, for-loops over a mapping and over a list of pairs as Jinja2 iterates them, and the builtin filters `attr`, `filesizeformat`, `forceescape`, `items`, `string` and `urlize`. |
|
|
15
14
|
| Planned | swig-django: two-variable for-loops over a list of pairs unpack each pair, as Django does. |
|
|
16
15
|
| Planned | Unknown filter and test names rejected when a Twig, Jinja2 or Django template compiles; today the error comes only at render, or never when the branch does not run. Native swig already rejects an unknown filter. |
|
|
16
|
+
| Planned | swig-django: `not` binds below the comparisons and `in`, as in Django; today `not x == y` reads as `(not x) == y`. |
|
|
17
|
+
| Planned | swig-jinja2 and swig-django: `==` and `!=` compare as Python does; today they are JavaScript's loose operators, so `1 == "1"` is true and `[1] == [1]` false. |
|
|
17
18
|
|
|
18
19
|
## Future (post-2.0)
|
|
19
20
|
|
|
@@ -27,6 +28,19 @@ For bug reports and feature requests, file an issue at [gina-io/swig](https://gi
|
|
|
27
28
|
|
|
28
29
|
## Completed
|
|
29
30
|
|
|
31
|
+
### v2.9.0 (October 2026)
|
|
32
|
+
|
|
33
|
+
- `in` tests membership in swig-twig, swig-jinja2 and swig-django, as Twig, Jinja2 and Django do: a list holds an item, a string a substring, and a mapping a key (Jinja2, Django) or a value (Twig). Native swig keeps the JavaScript `in`, which tests an object's keys or an array's indices.
|
|
34
|
+
- `not in` in swig-twig, swig-jinja2 and swig-django.
|
|
35
|
+
- An OpenSSF Scorecard badge on the README of each package.
|
|
36
|
+
- All five packages released in lockstep at `2.9.0`.
|
|
37
|
+
|
|
38
|
+
### v2.8.4 (October 2026)
|
|
39
|
+
|
|
40
|
+
- Fixed the `autoescape` option of `@rhinostone/swig` set to an escape strategy name: `'js'` and `'html'` work as documented. The value was written into the compiled template as code, so these failed with a ReferenceError and an option built from untrusted input could run as code; a value holding a double quote or a line break is now rejected with an error.
|
|
41
|
+
- Fixed a `filename` option that is not a string, such as a number, which 2.8.3 no longer compiled with `import`, `from` and `extends` on the async render path; `include` now compiles with a number too. All four frontends.
|
|
42
|
+
- All five packages released in lockstep at `2.8.4`.
|
|
43
|
+
|
|
30
44
|
### v2.8.3 (October 2026)
|
|
31
45
|
|
|
32
46
|
- Security: the template compiler escapes the including template's filename before writing it into the compiled function body, so a double quote in a `filename` option can no longer break out of the generated string literal (GHSA-q89c). Escaping is centralized in the code generator, which also fixes a latent broken-JS bug for backslash paths through `import`, `from` and `extends`.
|
package/dist/swig.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Swig v2.
|
|
1
|
+
/*! Swig v2.9.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/tn0ms/DateZ/blob/master/LISENCE */
|
|
3
3
|
(() => {
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -250,6 +250,58 @@
|
|
|
250
250
|
}
|
|
251
251
|
return cur;
|
|
252
252
|
};
|
|
253
|
+
function sameItem(a, b, twig) {
|
|
254
|
+
if (a === b) {
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
if (!twig) {
|
|
258
|
+
return (a === null || a === void 0) && (b === null || b === void 0);
|
|
259
|
+
}
|
|
260
|
+
if (a !== null && typeof a === "object" || b !== null && typeof b === "object" || typeof a === "function" || typeof b === "function") {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
return a == b;
|
|
264
|
+
}
|
|
265
|
+
function membership(needle, haystack, twig) {
|
|
266
|
+
var keys, i;
|
|
267
|
+
if (typeof haystack === "string") {
|
|
268
|
+
if (typeof needle === "string" || twig && typeof needle === "number") {
|
|
269
|
+
return haystack.indexOf(String(needle)) !== -1;
|
|
270
|
+
}
|
|
271
|
+
return twig ? false : null;
|
|
272
|
+
}
|
|
273
|
+
if (haystack === null || typeof haystack !== "object") {
|
|
274
|
+
return twig ? false : null;
|
|
275
|
+
}
|
|
276
|
+
if (exports.isArray(haystack)) {
|
|
277
|
+
for (i = 0; i < haystack.length; i += 1) {
|
|
278
|
+
if (sameItem(needle, haystack[i], twig)) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
if (twig) {
|
|
285
|
+
keys = Object.keys(haystack);
|
|
286
|
+
for (i = 0; i < keys.length; i += 1) {
|
|
287
|
+
if (sameItem(needle, haystack[keys[i]], true)) {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
if (typeof needle === "string" || typeof needle === "number") {
|
|
294
|
+
return Object.prototype.hasOwnProperty.call(haystack, String(needle));
|
|
295
|
+
}
|
|
296
|
+
return needle !== null && typeof needle === "object" ? null : false;
|
|
297
|
+
}
|
|
298
|
+
exports.isIn = function(needle, haystack, mode, negate) {
|
|
299
|
+
var found = membership(needle, haystack, mode === "twig");
|
|
300
|
+
if (found === null) {
|
|
301
|
+
return negate === true && mode !== "django";
|
|
302
|
+
}
|
|
303
|
+
return negate === true ? !found : found;
|
|
304
|
+
};
|
|
253
305
|
}
|
|
254
306
|
});
|
|
255
307
|
|
|
@@ -1618,7 +1670,7 @@
|
|
|
1618
1670
|
} else {
|
|
1619
1671
|
incSelector = "_utils.extend({}, _ctx, " + incCtxJS + ")";
|
|
1620
1672
|
}
|
|
1621
|
-
out += (node.ignoreMissing ? " try {\n" : "") + "_output += _swig.compileFile(" + incPathJS + ', {resolveFrom: "' + escapeTextValue(node.resolveFrom || "") + '"})(' + incSelector + ");\n" + (node.ignoreMissing ? "} catch (e) {}\n" : "");
|
|
1673
|
+
out += (node.ignoreMissing ? " try {\n" : "") + "_output += _swig.compileFile(" + incPathJS + ', {resolveFrom: "' + escapeTextValue(String(node.resolveFrom || "")) + '"})(' + incSelector + ");\n" + (node.ignoreMissing ? "} catch (e) {}\n" : "");
|
|
1622
1674
|
return;
|
|
1623
1675
|
}
|
|
1624
1676
|
if (node.type === "IncludeDeferred") {
|
|
@@ -1643,7 +1695,7 @@
|
|
|
1643
1695
|
} else {
|
|
1644
1696
|
incdSelector = "_utils.extend({}, _ctx, " + incdCtxJS + ")";
|
|
1645
1697
|
}
|
|
1646
|
-
var incdOpts = '{resolveFrom: "' + escapeTextValue(node.resolveFrom || "") + '"}';
|
|
1698
|
+
var incdOpts = '{resolveFrom: "' + escapeTextValue(String(node.resolveFrom || "")) + '"}';
|
|
1647
1699
|
out += (node.ignoreMissing ? " try {\n" : "") + "_output += (await (await _swig.getTemplate(" + incdPathJS + ", " + incdOpts + "))(" + incdSelector + ")).output;\n" + (node.ignoreMissing ? "} catch (e) {}\n" : "");
|
|
1648
1700
|
return;
|
|
1649
1701
|
}
|
|
@@ -1657,7 +1709,7 @@
|
|
|
1657
1709
|
} else {
|
|
1658
1710
|
impdPathJS = node.path;
|
|
1659
1711
|
}
|
|
1660
|
-
var impdOpts = '{resolveFrom: "' + escapeTextValue(node.resolveFrom || "") + '"}';
|
|
1712
|
+
var impdOpts = '{resolveFrom: "' + escapeTextValue(String(node.resolveFrom || "")) + '"}';
|
|
1661
1713
|
out += "_ctx." + node.alias + " = (await (await _swig.getTemplate(" + impdPathJS + ", " + impdOpts + "))(_ctx)).exports;\n";
|
|
1662
1714
|
return;
|
|
1663
1715
|
}
|
|
@@ -1678,7 +1730,7 @@
|
|
|
1678
1730
|
} else {
|
|
1679
1731
|
fromdPathJS = node.path;
|
|
1680
1732
|
}
|
|
1681
|
-
var fromdOpts = '{resolveFrom: "' + escapeTextValue(node.resolveFrom || "") + '"}';
|
|
1733
|
+
var fromdOpts = '{resolveFrom: "' + escapeTextValue(String(node.resolveFrom || "")) + '"}';
|
|
1682
1734
|
var fromdBindings = "";
|
|
1683
1735
|
utils.each(fromdImports, function(entry) {
|
|
1684
1736
|
var bindName = entry.alias || entry.name;
|
|
@@ -1715,7 +1767,7 @@
|
|
|
1715
1767
|
} else {
|
|
1716
1768
|
extPathJS = node.path;
|
|
1717
1769
|
}
|
|
1718
|
-
var extOpts = '{resolveFrom: "' + escapeTextValue(node.resolveFrom || "") + '"}';
|
|
1770
|
+
var extOpts = '{resolveFrom: "' + escapeTextValue(String(node.resolveFrom || "")) + '"}';
|
|
1719
1771
|
out += "var _parentTpl = await _swig.getTemplate(" + extPathJS + ", " + extOpts + ");\n";
|
|
1720
1772
|
out += "var _parentResult = await _parentTpl(_ctx, _mergedBlocks);\n";
|
|
1721
1773
|
out += "_output = _parentResult.output;\n";
|
|
@@ -1923,12 +1975,18 @@
|
|
|
1923
1975
|
}
|
|
1924
1976
|
return js;
|
|
1925
1977
|
}
|
|
1978
|
+
function membershipMode(flag) {
|
|
1979
|
+
return flag === "twig" || flag === "django" ? flag : "jinja2";
|
|
1980
|
+
}
|
|
1926
1981
|
function emitBinaryOp(node, d) {
|
|
1927
1982
|
var left = groupOperand(node.left, emitExpr(node.left, d)), right = groupOperand(node.right, emitExpr(node.right, d));
|
|
1928
1983
|
if (isArithmeticOp(node.op)) {
|
|
1929
1984
|
return left + " " + node.op + " " + right;
|
|
1930
1985
|
}
|
|
1931
1986
|
if (node.op === "in") {
|
|
1987
|
+
if (node.membership) {
|
|
1988
|
+
return "_utils.isIn(" + left + ", " + right + ', "' + membershipMode(node.membership) + '"' + (node.negate ? ", true" : "") + ")";
|
|
1989
|
+
}
|
|
1932
1990
|
return left + " in " + right;
|
|
1933
1991
|
}
|
|
1934
1992
|
if (node.op === "~") {
|
|
@@ -3058,9 +3116,23 @@
|
|
|
3058
3116
|
function escapeRegExp(str) {
|
|
3059
3117
|
return str.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, "\\$&");
|
|
3060
3118
|
}
|
|
3119
|
+
function quoteEscapeOption(value) {
|
|
3120
|
+
var quote;
|
|
3121
|
+
if (typeof value !== "string" || value === "") {
|
|
3122
|
+
return value;
|
|
3123
|
+
}
|
|
3124
|
+
quote = value.charAt(0);
|
|
3125
|
+
if (value.length > 1 && (quote === '"' || quote === "'") && value.charAt(value.length - 1) === quote) {
|
|
3126
|
+
value = value.slice(1, -1);
|
|
3127
|
+
}
|
|
3128
|
+
if (/["\n\r\u2028\u2029]/.test(value)) {
|
|
3129
|
+
throw new Error('Option "autoescape" must be true, false or an escape strategy name without double quotes or line breaks.');
|
|
3130
|
+
}
|
|
3131
|
+
return '"' + value + '"';
|
|
3132
|
+
}
|
|
3061
3133
|
exports.parse = function(swig2, source, opts, tags, filters) {
|
|
3062
3134
|
source = source.replace(/\r\n/g, "\n");
|
|
3063
|
-
var escape = opts.autoescape, tagOpen = opts.tagControls[0], tagClose = opts.tagControls[1], varOpen = opts.varControls[0], varClose = opts.varControls[1], escapedTagOpen = escapeRegExp(tagOpen), escapedTagClose = escapeRegExp(tagClose), escapedVarOpen = escapeRegExp(varOpen), escapedVarClose = escapeRegExp(varClose), tagStrip = new RegExp("^" + escapedTagOpen + "-?\\s*|\\s*-?" + escapedTagClose + "$", "g"), tagStripBefore = new RegExp("^" + escapedTagOpen + "-"), tagStripAfter = new RegExp("-" + escapedTagClose + "$"), varStrip = new RegExp("^" + escapedVarOpen + "-?\\s*|\\s*-?" + escapedVarClose + "$", "g"), varStripBefore = new RegExp("^" + escapedVarOpen + "-"), varStripAfter = new RegExp("-" + escapedVarClose + "$"), cmtOpen = opts.cmtControls[0], cmtClose = opts.cmtControls[1], anyChar = "[\\s\\S]*?", splitter = new RegExp(
|
|
3135
|
+
var escape = quoteEscapeOption(opts.autoescape), tagOpen = opts.tagControls[0], tagClose = opts.tagControls[1], varOpen = opts.varControls[0], varClose = opts.varControls[1], escapedTagOpen = escapeRegExp(tagOpen), escapedTagClose = escapeRegExp(tagClose), escapedVarOpen = escapeRegExp(varOpen), escapedVarClose = escapeRegExp(varClose), tagStrip = new RegExp("^" + escapedTagOpen + "-?\\s*|\\s*-?" + escapedTagClose + "$", "g"), tagStripBefore = new RegExp("^" + escapedTagOpen + "-"), tagStripAfter = new RegExp("-" + escapedTagClose + "$"), varStrip = new RegExp("^" + escapedVarOpen + "-?\\s*|\\s*-?" + escapedVarClose + "$", "g"), varStripBefore = new RegExp("^" + escapedVarOpen + "-"), varStripAfter = new RegExp("-" + escapedVarClose + "$"), cmtOpen = opts.cmtControls[0], cmtClose = opts.cmtControls[1], anyChar = "[\\s\\S]*?", splitter = new RegExp(
|
|
3064
3136
|
"(" + escapedTagOpen + anyChar + escapedTagClose + "|" + escapedVarOpen + anyChar + escapedVarClose + "|" + escapeRegExp(cmtOpen) + anyChar + escapeRegExp(cmtClose) + ")"
|
|
3065
3137
|
), line = 1, stack = [], parent = null, parentExpr = null, tokens = [], blocks = {}, inRaw = false, stripNext;
|
|
3066
3138
|
function parseVariable(str, line2) {
|
|
@@ -3080,7 +3152,7 @@
|
|
|
3080
3152
|
if (last && last.name === str.split(/\s+/)[0].replace(/^end/, "") && last.ends) {
|
|
3081
3153
|
switch (last.name) {
|
|
3082
3154
|
case "autoescape":
|
|
3083
|
-
escape = opts.autoescape;
|
|
3155
|
+
escape = quoteEscapeOption(opts.autoescape);
|
|
3084
3156
|
break;
|
|
3085
3157
|
case "raw":
|
|
3086
3158
|
inRaw = false;
|
|
@@ -4822,7 +4894,7 @@
|
|
|
4822
4894
|
var loaders = require_loaders2();
|
|
4823
4895
|
var preWalker = require_pre_walker();
|
|
4824
4896
|
var engine = require_engine();
|
|
4825
|
-
exports.version = "2.
|
|
4897
|
+
exports.version = "2.9.0";
|
|
4826
4898
|
var defaultOptions = {
|
|
4827
4899
|
autoescape: true,
|
|
4828
4900
|
varControls: ["{{", "}}"],
|
|
@@ -5062,6 +5134,19 @@
|
|
|
5062
5134
|
* `_dangerousProps` segments are rejected at runtime as defense-in-depth.
|
|
5063
5135
|
* @private
|
|
5064
5136
|
*/
|
|
5137
|
+
/*!
|
|
5138
|
+
* Item equality for `isIn`. Jinja2 and Django compare with `===` and treat
|
|
5139
|
+
* null and a missing value as equal (Django reads both as None). Twig
|
|
5140
|
+
* compares two scalars with `==`, as swig-twig's `==` does, and anything
|
|
5141
|
+
* else by identity. @private
|
|
5142
|
+
*/
|
|
5143
|
+
/*!
|
|
5144
|
+
* The membership test of `isIn`: true or false, or null when Jinja2 and
|
|
5145
|
+
* Django cannot run it, where Python raises a TypeError: a container that is
|
|
5146
|
+
* not a list, a string or a mapping, a needle that is not a string tested
|
|
5147
|
+
* against a string, or a list or an object tested against a mapping's keys.
|
|
5148
|
+
* Twig runs every test. @private
|
|
5149
|
+
*/
|
|
5065
5150
|
/*!
|
|
5066
5151
|
* Attach `loc` to the node if provided, skipping the assignment otherwise
|
|
5067
5152
|
* so consumers can tell "no source location available" from
|
|
@@ -5163,6 +5248,10 @@
|
|
|
5163
5248
|
* wraps a unary operand, for the positions where its prefix would bind
|
|
5164
5249
|
* differently (`-(-x)`, `(-x)[0]`). @private
|
|
5165
5250
|
*/
|
|
5251
|
+
/*!
|
|
5252
|
+
* The flavor name `emitBinaryOp` writes into an `_utils.isIn` call: one of
|
|
5253
|
+
* the three names `isIn` knows, never the flag's own text. @private
|
|
5254
|
+
*/
|
|
5166
5255
|
/*!
|
|
5167
5256
|
* Emit a function / method invocation. Three callee shapes:
|
|
5168
5257
|
* 1. Single-segment VarRef (`foo(...)`) — FUNCTION-token pattern with
|
|
@@ -5185,6 +5274,19 @@
|
|
|
5185
5274
|
* @return {string}
|
|
5186
5275
|
* @private
|
|
5187
5276
|
*/
|
|
5277
|
+
/*!
|
|
5278
|
+
* Turn a string autoescape option into the string literal that the
|
|
5279
|
+
* autoescape tail writes into the compiled template as the e filter's
|
|
5280
|
+
* strategy argument; the bare option used to be written there as code.
|
|
5281
|
+
* The tail doubles every backslash, so the value is wrapped in double
|
|
5282
|
+
* quotes without escaping it, and a value holding a double quote or a line
|
|
5283
|
+
* break is rejected. One pair of matching outer quotes is removed first,
|
|
5284
|
+
* so a value that was already quoted keeps working. A value that is not a
|
|
5285
|
+
* non-empty string is returned unchanged.
|
|
5286
|
+
* @param {*} value The autoescape option.
|
|
5287
|
+
* @return {*}
|
|
5288
|
+
* @private
|
|
5289
|
+
*/
|
|
5188
5290
|
/*!
|
|
5189
5291
|
* Loop over the source, split via the tag/var/comment regular expression splitter.
|
|
5190
5292
|
* Send each chunk to the appropriate parser.
|