@rhinostone/swig-jinja2 2.8.4 → 2.9.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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  @rhinostone/swig-jinja2
2
2
  =======================
3
3
 
4
- [![NPM version](https://img.shields.io/npm/v/@rhinostone/swig-jinja2.svg?style=flat)](https://www.npmjs.com/package/@rhinostone/swig-jinja2) [![Socket Badge](https://socket.dev/api/badge/npm/package/@rhinostone/swig-jinja2)](https://socket.dev/npm/package/@rhinostone/swig-jinja2)
4
+ [![NPM version](https://img.shields.io/npm/v/@rhinostone/swig-jinja2.svg?style=flat)](https://www.npmjs.com/package/@rhinostone/swig-jinja2) [![Socket Badge](https://socket.dev/api/badge/npm/package/@rhinostone/swig-jinja2)](https://socket.dev/npm/package/@rhinostone/swig-jinja2) [![OpenSSF Scorecard](https://img.shields.io/ossf-scorecard/github.com/gina-io/swig?label=openssf%20scorecard&style=flat)](https://scorecard.dev/viewer/?uri=github.com/gina-io/swig)
5
5
 
6
6
  Jinja2-syntax frontend for the [@rhinostone/swig-core](https://www.npmjs.com/package/@rhinostone/swig-core) template engine. Part of the multi-flavor architecture introduced in `2.0.0` — see [ROADMAP.md](https://github.com/gina-io/swig/blob/develop/ROADMAP.md) for the release narrative.
7
7
 
package/lib/filters.js CHANGED
@@ -611,11 +611,16 @@ exports.striptags = function (input) {
611
611
  if (out !== undefined) {
612
612
  return out;
613
613
  }
614
- return String(input)
615
- .replace(/<!--[\s\S]*?-->/g, '')
616
- .replace(/<[^>]*>/g, '')
617
- .replace(/\s+/g, ' ')
618
- .replace(/^\s+|\s+$/g, '');
614
+ // The comment and tag regexes take time quadratic in the input length on
615
+ // many <!-- or < with no --> or > after them, and the trailing \s+$ on a
616
+ // long run of whitespace; the swig-core helpers give the same output in
617
+ // linear time.
618
+ // return String(input)
619
+ // .replace(/<!--[\s\S]*?-->/g, '')
620
+ // .replace(/<[^>]*>/g, '')
621
+ // .replace(/\s+/g, ' ')
622
+ // .replace(/^\s+|\s+$/g, '');
623
+ return utils.strip(utils.stripTags(utils.stripComments(String(input)), true).replace(/\s+/g, ' '));
619
624
  };
620
625
 
621
626
  /**
@@ -645,9 +650,13 @@ exports.trim = function (input, chars) {
645
650
  } else {
646
651
  pattern = '[' + String(chars).replace(/[\\\[\]\^\-]/g, '\\$&') + ']';
647
652
  }
648
- return input
649
- .replace(new RegExp('^' + pattern + '+'), '')
650
- .replace(new RegExp(pattern + '+$'), '');
653
+ // A pattern + '+$' regex takes quadratic time on a long run of trimmed
654
+ // characters inside the string; trimEnd removes the same code units in
655
+ // linear time.
656
+ // return input
657
+ // .replace(new RegExp('^' + pattern + '+'), '')
658
+ // .replace(new RegExp(pattern + '+$'), '');
659
+ return utils.trimEnd(input.replace(new RegExp('^' + pattern + '+'), ''), pattern === '\\s' ? undefined : String(chars));
651
660
  };
652
661
 
653
662
  /**
@@ -753,7 +762,14 @@ function formatGeneral(num, prec) {
753
762
  */
754
763
  function sprintf(fmt, args) {
755
764
  var idx = 0;
756
- return fmt.replace(/%([-+ 0]*)(\d+)?(?:\.(\d+))?([sdiufFeEgGxXoc%])/g, function (m, flags, width, prec, conv) {
765
+ // The flags and the width both take zeros, so on a % followed by a long run
766
+ // of zeros and no conversion this regex backtracked over every split,
767
+ // taking quadratic time. Each lookahead below captures its run once and
768
+ // the backreference consumes it, so nothing backtracks; the matches are
769
+ // the same, except that an absent width is "" instead of undefined, which
770
+ // the callback reads the same way.
771
+ // return fmt.replace(/%([-+ 0]*)(\d+)?(?:\.(\d+))?([sdiufFeEgGxXoc%])/g, function (m, flags, width, prec, conv) {
772
+ return fmt.replace(/%(?=([-+ 0]*))\1(?=(\d*))\2(?:\.(?=(\d+))\3)?([sdiufFeEgGxXoc%])/g, function (m, flags, width, prec, conv) {
757
773
  var arg, hasPrec, sign, s, num, pad;
758
774
 
759
775
  function withSign(value) {
@@ -1179,7 +1195,7 @@ exports.slice = function (input, slices, fill) {
1179
1195
  * and `reverse` flips the order.
1180
1196
  *
1181
1197
  * @example
1182
- * {% for k, v in {"b":2,"a":1}|dictsort %}{{ k }}={{ v }};{% endfor %}
1198
+ * {% for p in {"b":2,"a":1}|dictsort %}{{ p[0] }}={{ p[1] }};{% endfor %}
1183
1199
  * // => a=1;b=2;
1184
1200
  *
1185
1201
  * @param {object} input
package/lib/index.js CHANGED
@@ -27,7 +27,7 @@ exports.name = 'jinja2';
27
27
  *
28
28
  * @type {String}
29
29
  */
30
- exports.version = "2.8.4";
30
+ exports.version = "2.9.1";
31
31
 
32
32
  /**
33
33
  * Expression-level parser — Pratt-style recursive descent that consumes
package/lib/lexer.js CHANGED
@@ -108,7 +108,7 @@ var rules = [
108
108
  {
109
109
  type: TYPES.COMPARATOR,
110
110
  regex: [
111
- /^(===|==|\!==|\!=|<=|<|>=|>|in\s)\s*/
111
+ /^(===|==|\!==|\!=|<=|<|>=|>|not\s+in\s|in\s)\s*/
112
112
  ],
113
113
  idx: 1
114
114
  },
package/lib/parser.js CHANGED
@@ -21,6 +21,27 @@ function escapeRegExp(str) {
21
21
  */
22
22
  var _reserved = ['break', 'case', 'catch', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'finally', 'for', 'function', 'if', 'in', 'instanceof', 'new', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with'];
23
23
 
24
+ /**
25
+ * Build the IR of `left in right`, or of `left not in right` when `negate`
26
+ * is set: a BinaryOp `in` flagged with this flavor's name, so swig-core
27
+ * emits Jinja2's membership test (`_utils.isIn`) instead of the JavaScript
28
+ * `in` operator, which tests keys.
29
+ *
30
+ * @param {object} left The needle.
31
+ * @param {object} right The container.
32
+ * @param {boolean} [negate] The `not in` form.
33
+ * @return {object} IRBinaryOp.
34
+ * @private
35
+ */
36
+ function membership(left, right, negate) {
37
+ var node = ir.binaryOp('in', left, right);
38
+ node.membership = 'jinja2';
39
+ if (negate) {
40
+ node.negate = true;
41
+ }
42
+ return node;
43
+ }
44
+
24
45
  /**
25
46
  * Jinja2 expression parser — Pratt-style recursive descent.
26
47
  *
@@ -110,6 +131,9 @@ exports.parseExpr = function (tokens, filters, _posOut) {
110
131
  if (m === '===' || m === '!==' || m === '==' || m === '!=') {
111
132
  return { op: m, prec: 3 };
112
133
  }
134
+ if (/^not\s+in$/.test(m)) {
135
+ return { op: 'not in', prec: 4 };
136
+ }
113
137
  return { op: m, prec: 4 };
114
138
  }
115
139
  if (tok.type === _t.OPERATOR) {
@@ -468,6 +492,8 @@ exports.parseExpr = function (tokens, filters, _posOut) {
468
492
  // Math.floor(a / b). Matches Python `//` for ints and floats,
469
493
  // including negative operands (floors toward negative infinity).
470
494
  left = ir.fnCall(ir.varRef(['Math', 'floor']), [ir.binaryOp('/', left, right)]);
495
+ } else if (info.op === 'in' || info.op === 'not in') {
496
+ left = membership(left, right, info.op === 'not in');
471
497
  } else {
472
498
  left = ir.binaryOp(info.op, left, right);
473
499
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhinostone/swig-jinja2",
3
- "version": "2.8.4",
3
+ "version": "2.9.1",
4
4
  "description": "Jinja2-syntax frontend for the @rhinostone/swig-core template engine. Part of the @rhinostone/swig multi-flavor family.",
5
5
  "keywords": [
6
6
  "template",
@@ -23,7 +23,7 @@
23
23
  "node": ">=12"
24
24
  },
25
25
  "peerDependencies": {
26
- "@rhinostone/swig-core": "2.8.4"
26
+ "@rhinostone/swig-core": "2.9.1"
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public"