@rhinostone/swig-django 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-django
2
2
  =======================
3
3
 
4
- [![NPM version](https://img.shields.io/npm/v/@rhinostone/swig-django.svg?style=flat)](https://www.npmjs.com/package/@rhinostone/swig-django) [![Socket Badge](https://socket.dev/api/badge/npm/package/@rhinostone/swig-django)](https://socket.dev/npm/package/@rhinostone/swig-django)
4
+ [![NPM version](https://img.shields.io/npm/v/@rhinostone/swig-django.svg?style=flat)](https://www.npmjs.com/package/@rhinostone/swig-django) [![Socket Badge](https://socket.dev/api/badge/npm/package/@rhinostone/swig-django)](https://socket.dev/npm/package/@rhinostone/swig-django) [![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
  Django Template Language 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
@@ -559,7 +559,10 @@ exports.floatformat = function (input, arg) {
559
559
  if (arg === undefined || arg === null || arg === '') {
560
560
  p = -1;
561
561
  } else {
562
- a = String(arg).replace(/[gu]+$/, '');
562
+ // A /[gu]+$/ regex takes quadratic time on a long run of g or u not at the
563
+ // end; trimEnd removes the same characters in linear time.
564
+ // a = String(arg).replace(/[gu]+$/, '');
565
+ a = utils.trimEnd(String(arg), 'gu');
563
566
  p = parseInt(a, 10);
564
567
  if (isNaN(p)) {
565
568
  return String(input);
@@ -885,7 +888,10 @@ exports.phone2numeric = function (input) {
885
888
  * case. Returns undefined where Python raises ValueError. @private
886
889
  */
887
890
  function pyFloat(s) {
888
- var t = s.replace(/^\s+|\s+$/g, '');
891
+ // A trailing \s+$ takes quadratic time on a long run of whitespace inside
892
+ // the value; utils.strip gives the same output in linear time.
893
+ // var t = s.replace(/^\s+|\s+$/g, '');
894
+ var t = utils.strip(s);
889
895
  if (/^[+-]?(inf|infinity)$/i.test(t)) {
890
896
  return (t.charAt(0) === '-') ? -Infinity : Infinity;
891
897
  }
@@ -1050,7 +1056,10 @@ exports.slugify = function (input) {
1050
1056
  value = value.normalize('NFKD').replace(/[\u0300-\u036f]/g, '');
1051
1057
  }
1052
1058
  value = value.toLowerCase().replace(/[^\w\s-]/g, '');
1053
- return value.replace(/[-\s]+/g, '-').replace(/^[-_]+|[-_]+$/g, '');
1059
+ // A trailing [-_]+$ takes quadratic time on a long run of _ inside the
1060
+ // value; trimEnd removes the same characters in linear time.
1061
+ // return value.replace(/[-\s]+/g, '-').replace(/^[-_]+|[-_]+$/g, '');
1062
+ return utils.trimEnd(value.replace(/[-\s]+/g, '-').replace(/^[-_]+/, ''), '-_');
1054
1063
  };
1055
1064
 
1056
1065
  /**
@@ -1064,7 +1073,11 @@ exports.slugify = function (input) {
1064
1073
  * @return {string}
1065
1074
  */
1066
1075
  exports.striptags = function (input) {
1067
- return String(input).replace(/(<([^>]+)>)/ig, '');
1076
+ // This regex takes time quadratic in the input length on many < with no >
1077
+ // after them (about 5 s for 100 000 characters); stripTags gives the same
1078
+ // output in linear time.
1079
+ // return String(input).replace(/(<([^>]+)>)/ig, '');
1080
+ return utils.stripTags(String(input));
1068
1081
  };
1069
1082
 
1070
1083
  /**
@@ -1175,7 +1188,10 @@ exports.truncatewords = function (input, arg) {
1175
1188
  if (n <= 0) {
1176
1189
  return '';
1177
1190
  }
1178
- words = String(input).replace(/^\s+|\s+$/g, '').split(/\s+/);
1191
+ // A trailing \s+$ takes quadratic time on a long run of whitespace inside
1192
+ // the value; utils.strip gives the same output in linear time.
1193
+ // words = String(input).replace(/^\s+|\s+$/g, '').split(/\s+/);
1194
+ words = utils.strip(String(input)).split(/\s+/);
1179
1195
  if (words.length === 1 && words[0] === '') {
1180
1196
  return '';
1181
1197
  }
package/lib/index.js CHANGED
@@ -32,7 +32,7 @@ exports.name = 'django';
32
32
  *
33
33
  * @type {String}
34
34
  */
35
- exports.version = "2.8.4";
35
+ exports.version = "2.9.1";
36
36
 
37
37
  /**
38
38
  * Expression-level parser — Pratt-style recursive descent that consumes
package/lib/lexer.js CHANGED
@@ -115,7 +115,7 @@ var rules = [
115
115
  {
116
116
  type: TYPES.COMPARATOR,
117
117
  regex: [
118
- /^(===|==|\!==|\!=|<=|<|>=|>|in\s)\s*/
118
+ /^(===|==|\!==|\!=|<=|<|>=|>|not\s+in\s|in\s)\s*/
119
119
  ],
120
120
  idx: 1
121
121
  },
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 Django'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 = 'django';
39
+ if (negate) {
40
+ node.negate = true;
41
+ }
42
+ return node;
43
+ }
44
+
24
45
  /**
25
46
  * Build a VarRef IR node flagged for Django-style runtime resolution. The
26
47
  * `resolve` flag makes swig-core's emitVarRef emit `_utils.resolve(_ctx,
@@ -135,6 +156,9 @@ exports.parseExpr = function (tokens, filters, _posOut) {
135
156
  if (m === '===' || m === '!==' || m === '==' || m === '!=') {
136
157
  return { op: m, prec: 3 };
137
158
  }
159
+ if (/^not\s+in$/.test(m)) {
160
+ return { op: 'not in', prec: 4 };
161
+ }
138
162
  return { op: m, prec: 4 };
139
163
  }
140
164
  // Django `is` / `is not` are *identity* comparisons (not Jinja2 test
@@ -453,7 +477,7 @@ exports.parseExpr = function (tokens, filters, _posOut) {
453
477
  continue;
454
478
  }
455
479
  var right = parseExpression(info.prec + 1);
456
- left = ir.binaryOp(info.op, left, right);
480
+ left = (info.op === 'in' || info.op === 'not in') ? membership(left, right, info.op === 'not in') : ir.binaryOp(info.op, left, right);
457
481
  }
458
482
  return left;
459
483
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhinostone/swig-django",
3
- "version": "2.8.4",
3
+ "version": "2.9.1",
4
4
  "description": "Django-syntax frontend for the @rhinostone/swig-core template engine. Part of the @rhinostone/swig multi-flavor family.",
5
5
  "keywords": [
6
6
  "template",
@@ -22,7 +22,7 @@
22
22
  "node": ">=12"
23
23
  },
24
24
  "peerDependencies": {
25
- "@rhinostone/swig-core": "2.8.4"
25
+ "@rhinostone/swig-core": "2.9.1"
26
26
  },
27
27
  "publishConfig": {
28
28
  "access": "public"