@rhinostone/swig-django 2.9.1 → 2.9.3

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/lib/index.js CHANGED
@@ -32,7 +32,7 @@ exports.name = 'django';
32
32
  *
33
33
  * @type {String}
34
34
  */
35
- exports.version = "2.9.1";
35
+ exports.version = "2.9.3";
36
36
 
37
37
  /**
38
38
  * Expression-level parser — Pratt-style recursive descent that consumes
package/lib/tags/for.js CHANGED
@@ -14,6 +14,14 @@
14
14
  * fields and expose the enclosing loop, so nested-loop safety and the
15
15
  * for-empty handling come for free.
16
16
  *
17
+ * Binding follows Django through the opt-in `mapKeys` and `unpack` flags: one
18
+ * name over a dict binds each key, and two names over a list unpack each item
19
+ * into its first and second values (a list's items, a string's characters, a
20
+ * mapping's keys). An item that does not hold two values binds what it holds,
21
+ * a missing value rendering empty, where Django raises. Two names over a
22
+ * mapping bind the key and the value, a swig extension (Django iterates the
23
+ * keys and unpacks each one) that the `.items` rewrite relies on.
24
+ *
17
25
  * Loop variable names must be bare identifiers — dotted paths are rejected
18
26
  * at parse time, and the CVE-2023-25345 `_dangerousProps` guard runs on
19
27
  * every bound name (key and val).
@@ -77,9 +85,8 @@ function resolveVar(path) {
77
85
  *
78
86
  * - `.keys` → `_utils.keys(obj)` (a plain key array; a one-name `for`
79
87
  * binds each key).
80
- * - `.values` → the object itself; swig's one-name object iteration binds
81
- * each VALUE (`_utils.each(obj, fn(value, key))`), matching
82
- * Django.
88
+ * - `.values` → `_utils.values(obj)` (a plain value array; a one-name `for`
89
+ * binds each value).
83
90
  * - `.items` → the object itself, walked with the two-name form, so swig
84
91
  * binds key + value per entry exactly as Django's two-name
85
92
  * `{% for k, v in d.items %}`. The one-name tuple form is
@@ -113,11 +120,14 @@ function rewriteDictIterable(it, hasTwoNames, line, filename) {
113
120
  // (emitFnCall reads a callee path directly, never through emitVarRef).
114
121
  return ir.fnCall(ir.varRef(['_utils', 'keys']), [resolveVar(objPath)]);
115
122
  }
123
+ if (lastSeg === 'values') {
124
+ return ir.fnCall(ir.varRef(['_utils', 'values']), [resolveVar(objPath)]);
125
+ }
116
126
  if (lastSeg === 'items' && !hasTwoNames) {
117
127
  utils.throwError('The "items" pseudo-method in a "for" tag requires two loop variables — use {% for key, value in ' + objPath.join('.') + '.items %}', line, filename);
118
128
  }
119
- // `.values` (one-name → value) and `.items` (two-name → key, value) both
120
- // walk the object directly through swig's object iteration.
129
+ // `.items` walks the object directly: two names over a mapping bind the key
130
+ // and the value.
121
131
  return resolveVar(objPath);
122
132
  }
123
133
 
@@ -210,8 +220,8 @@ exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
210
220
 
211
221
  /**
212
222
  * Emit an IRFor node, splitting `content` at an `{% empty %}` marker into
213
- * the loop body and the empty body, and setting the Django `forloop`
214
- * opt-in flags. The backend's `For` branch owns the loopcache +
223
+ * the loop body and the empty body, and setting the Django `forloop` and
224
+ * binding opt-in flags. The backend's `For` branch owns the loopcache +
215
225
  * `_utils.each` scaffolding.
216
226
  *
217
227
  * @return {object} IRFor node.
@@ -258,5 +268,12 @@ exports.compile = function (compiler, args, content, parents, options, blockName
258
268
  node.loopName = 'forloop';
259
269
  node.loopFields = DJANGO_LOOP_FIELDS;
260
270
  node.loopParent = true;
271
+ // Django binding (see the IRFor typedef): two names unpack each item of a
272
+ // list, one name binds each key of a dict.
273
+ if (args.length === 2) {
274
+ node.unpack = true;
275
+ } else {
276
+ node.mapKeys = true;
277
+ }
261
278
  return node;
262
279
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhinostone/swig-django",
3
- "version": "2.9.1",
3
+ "version": "2.9.3",
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.9.1"
25
+ "@rhinostone/swig-core": "2.9.3"
26
26
  },
27
27
  "publishConfig": {
28
28
  "access": "public"