@barefootjs/jinja 0.30.2 → 0.30.4

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.
@@ -188322,6 +188322,7 @@ class JinjaAdapter extends BaseAdapter {
188322
188322
  _loweringMatchers = [];
188323
188323
  localConstants = [];
188324
188324
  staticLoopSourceBoundNames = new Set;
188325
+ loopBoundNames = new Map;
188325
188326
  nullableOptionalProps = new Set;
188326
188327
  constructor(options = {}) {
188327
188328
  super();
@@ -188338,6 +188339,7 @@ class JinjaAdapter extends BaseAdapter {
188338
188339
  this.booleanTypedProps = collectBooleanTypedProps(ir);
188339
188340
  this.localConstants = ir.metadata.localConstants ?? [];
188340
188341
  this.staticLoopSourceBoundNames = collectLoopBoundNames(ir);
188342
+ this.loopBoundNames.clear();
188341
188343
  this.nullableOptionalProps = collectNullableOptionalProps(ir);
188342
188344
  this.stringValueNames = collectStringValueNames(ir);
188343
188345
  this.moduleStringConsts = collectModuleStringConsts(ir.metadata.localConstants);
@@ -188671,7 +188673,35 @@ ${whenTrue}
188671
188673
  const prevLoopKeyDepth = this.currentLoopKeyDepth;
188672
188674
  this.currentLoopKeyDepth = loop.depth;
188673
188675
  const preambleLines = (loop.preamble?.declarations ?? []).map((d) => `{% set ${jinjaIdent(d.name)} = ${this.convertExpressionToJinja(d.raw, d.valueParsed)} %}`);
188676
+ const loopBound = [];
188677
+ if (loop.objectIteration === "entries") {
188678
+ loopBound.push(loop.index ?? param, param);
188679
+ } else if (loop.objectIteration === "keys" || loop.objectIteration === "values") {
188680
+ loopBound.push(param);
188681
+ } else if (loop.iterationShape === "keys") {
188682
+ loopBound.push("__bf_item", param);
188683
+ } else if (supportableDestructure) {
188684
+ loopBound.push("__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name));
188685
+ if (loop.index)
188686
+ loopBound.push(loop.index);
188687
+ } else {
188688
+ loopBound.push(param);
188689
+ if (loop.index)
188690
+ loopBound.push(loop.index);
188691
+ }
188692
+ for (const d of loop.preamble?.declarations ?? [])
188693
+ loopBound.push(d.name);
188694
+ for (const n of loopBound) {
188695
+ this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
188696
+ }
188674
188697
  const childrenUnderLoop = this.renderChildren(loop.children);
188698
+ for (const n of loopBound) {
188699
+ const c = (this.loopBoundNames.get(n) ?? 1) - 1;
188700
+ if (c <= 0)
188701
+ this.loopBoundNames.delete(n);
188702
+ else
188703
+ this.loopBoundNames.set(n, c);
188704
+ }
188675
188705
  this.currentLoopKeyDepth = prevLoopKeyDepth;
188676
188706
  this.inLoop = prevInLoop;
188677
188707
  const bodyChildren = loop.bodyIsItemConditional && loop.key ? `{{ bf.comment("loop-i:" ~ bf.string(${this.convertExpressionToJinja(loop.key)})) | safe }}
@@ -188850,7 +188880,7 @@ ${children}`;
188850
188880
  }
188851
188881
  const bareId = value.expr.trim();
188852
188882
  const normalizedBareId = this.propsObjectName && bareId.startsWith(`${this.propsObjectName}.`) ? bareId.slice(this.propsObjectName.length + 1) : bareId;
188853
- if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId)) {
188883
+ if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId) && !this.isLoopBoundName(normalizedBareId)) {
188854
188884
  const jinja2 = this.convertExpressionToJinja(value.expr);
188855
188885
  const body = this.shouldBoolStr(value.expr, name) ? `${name}="{{ bf.bool_str(${jinja2}) }}"` : `${name}="{{ bf.string(${jinja2}) }}"`;
188856
188886
  return `
@@ -189137,8 +189167,13 @@ Options:
189137
189167
  }
189138
189168
  if (!/^[A-Za-z_$][\w$]*$/.test(bare))
189139
189169
  return false;
189170
+ if (this.isLoopBoundName(bare))
189171
+ return false;
189140
189172
  return this.booleanTypedProps.has(bare);
189141
189173
  }
189174
+ isLoopBoundName(name) {
189175
+ return this.loopBoundNames.has(name);
189176
+ }
189142
189177
  shouldBoolStr(expr, name) {
189143
189178
  if (isExplicitStringCall(expr))
189144
189179
  return false;
@@ -178,6 +178,20 @@ export declare class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<J
178
178
  * `collectLoopBoundNames` use in `collectStringValueNames`.
179
179
  */
180
180
  private staticLoopSourceBoundNames;
181
+ /**
182
+ * Names currently bound by an enclosing loop body — the block-param
183
+ * locals `renderLoop` introduces (item, index, per-binding destructure
184
+ * fields, `.map()` preamble declarations) — ref-counted so nested loops
185
+ * compose. This is the POSITION-ACCURATE twin of the coarse
186
+ * `staticLoopSourceBoundNames` above: that set is a whole-component
187
+ * union used only to guard static-const inlining (safe to over-suppress
188
+ * there — the fallback is just "don't inline"), whereas the boolean-prop
189
+ * and nullable-optional classification sites need to know whether a name
190
+ * is loop-bound AT THIS POSITION, because for those two the coarse
191
+ * fallback would silently mis-render a genuine prop occurrence outside
192
+ * the loop too (#2488).
193
+ */
194
+ private loopBoundNames;
181
195
  /**
182
196
  * Optional, no-default props that are `None` when the caller omits them.
183
197
  * Their bare-reference attribute emission is guarded with a Jinja
@@ -390,6 +404,8 @@ export declare class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<J
390
404
  consequent: string;
391
405
  } | null;
392
406
  isBooleanTypedPropRef(expr: string): boolean;
407
+ /** Position-accurate loop-bound-name check — see `loopBoundNames`'s docstring. */
408
+ private isLoopBoundName;
393
409
  /**
394
410
  * Whether an attribute-value expression should route through
395
411
  * `bf.bool_str` (vs. plain `bf.string`) at its interpolation site.
@@ -1 +1 @@
1
- {"version":3,"file":"jinja-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/jinja-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jinja-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/jinja-adapter.ts"],"names":[],"mappings":"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"}
package/dist/build.js CHANGED
@@ -188322,6 +188322,7 @@ class JinjaAdapter extends BaseAdapter {
188322
188322
  _loweringMatchers = [];
188323
188323
  localConstants = [];
188324
188324
  staticLoopSourceBoundNames = new Set;
188325
+ loopBoundNames = new Map;
188325
188326
  nullableOptionalProps = new Set;
188326
188327
  constructor(options = {}) {
188327
188328
  super();
@@ -188338,6 +188339,7 @@ class JinjaAdapter extends BaseAdapter {
188338
188339
  this.booleanTypedProps = collectBooleanTypedProps(ir);
188339
188340
  this.localConstants = ir.metadata.localConstants ?? [];
188340
188341
  this.staticLoopSourceBoundNames = collectLoopBoundNames(ir);
188342
+ this.loopBoundNames.clear();
188341
188343
  this.nullableOptionalProps = collectNullableOptionalProps(ir);
188342
188344
  this.stringValueNames = collectStringValueNames(ir);
188343
188345
  this.moduleStringConsts = collectModuleStringConsts(ir.metadata.localConstants);
@@ -188671,7 +188673,35 @@ ${whenTrue}
188671
188673
  const prevLoopKeyDepth = this.currentLoopKeyDepth;
188672
188674
  this.currentLoopKeyDepth = loop.depth;
188673
188675
  const preambleLines = (loop.preamble?.declarations ?? []).map((d) => `{% set ${jinjaIdent(d.name)} = ${this.convertExpressionToJinja(d.raw, d.valueParsed)} %}`);
188676
+ const loopBound = [];
188677
+ if (loop.objectIteration === "entries") {
188678
+ loopBound.push(loop.index ?? param, param);
188679
+ } else if (loop.objectIteration === "keys" || loop.objectIteration === "values") {
188680
+ loopBound.push(param);
188681
+ } else if (loop.iterationShape === "keys") {
188682
+ loopBound.push("__bf_item", param);
188683
+ } else if (supportableDestructure) {
188684
+ loopBound.push("__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name));
188685
+ if (loop.index)
188686
+ loopBound.push(loop.index);
188687
+ } else {
188688
+ loopBound.push(param);
188689
+ if (loop.index)
188690
+ loopBound.push(loop.index);
188691
+ }
188692
+ for (const d of loop.preamble?.declarations ?? [])
188693
+ loopBound.push(d.name);
188694
+ for (const n of loopBound) {
188695
+ this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
188696
+ }
188674
188697
  const childrenUnderLoop = this.renderChildren(loop.children);
188698
+ for (const n of loopBound) {
188699
+ const c = (this.loopBoundNames.get(n) ?? 1) - 1;
188700
+ if (c <= 0)
188701
+ this.loopBoundNames.delete(n);
188702
+ else
188703
+ this.loopBoundNames.set(n, c);
188704
+ }
188675
188705
  this.currentLoopKeyDepth = prevLoopKeyDepth;
188676
188706
  this.inLoop = prevInLoop;
188677
188707
  const bodyChildren = loop.bodyIsItemConditional && loop.key ? `{{ bf.comment("loop-i:" ~ bf.string(${this.convertExpressionToJinja(loop.key)})) | safe }}
@@ -188850,7 +188880,7 @@ ${children}`;
188850
188880
  }
188851
188881
  const bareId = value.expr.trim();
188852
188882
  const normalizedBareId = this.propsObjectName && bareId.startsWith(`${this.propsObjectName}.`) ? bareId.slice(this.propsObjectName.length + 1) : bareId;
188853
- if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId)) {
188883
+ if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId) && !this.isLoopBoundName(normalizedBareId)) {
188854
188884
  const jinja2 = this.convertExpressionToJinja(value.expr);
188855
188885
  const body = this.shouldBoolStr(value.expr, name) ? `${name}="{{ bf.bool_str(${jinja2}) }}"` : `${name}="{{ bf.string(${jinja2}) }}"`;
188856
188886
  return `
@@ -189137,8 +189167,13 @@ Options:
189137
189167
  }
189138
189168
  if (!/^[A-Za-z_$][\w$]*$/.test(bare))
189139
189169
  return false;
189170
+ if (this.isLoopBoundName(bare))
189171
+ return false;
189140
189172
  return this.booleanTypedProps.has(bare);
189141
189173
  }
189174
+ isLoopBoundName(name) {
189175
+ return this.loopBoundNames.has(name);
189176
+ }
189142
189177
  shouldBoolStr(expr, name) {
189143
189178
  if (isExplicitStringCall(expr))
189144
189179
  return false;
package/dist/index.js CHANGED
@@ -188322,6 +188322,7 @@ class JinjaAdapter extends BaseAdapter {
188322
188322
  _loweringMatchers = [];
188323
188323
  localConstants = [];
188324
188324
  staticLoopSourceBoundNames = new Set;
188325
+ loopBoundNames = new Map;
188325
188326
  nullableOptionalProps = new Set;
188326
188327
  constructor(options = {}) {
188327
188328
  super();
@@ -188338,6 +188339,7 @@ class JinjaAdapter extends BaseAdapter {
188338
188339
  this.booleanTypedProps = collectBooleanTypedProps(ir);
188339
188340
  this.localConstants = ir.metadata.localConstants ?? [];
188340
188341
  this.staticLoopSourceBoundNames = collectLoopBoundNames(ir);
188342
+ this.loopBoundNames.clear();
188341
188343
  this.nullableOptionalProps = collectNullableOptionalProps(ir);
188342
188344
  this.stringValueNames = collectStringValueNames(ir);
188343
188345
  this.moduleStringConsts = collectModuleStringConsts(ir.metadata.localConstants);
@@ -188671,7 +188673,35 @@ ${whenTrue}
188671
188673
  const prevLoopKeyDepth = this.currentLoopKeyDepth;
188672
188674
  this.currentLoopKeyDepth = loop.depth;
188673
188675
  const preambleLines = (loop.preamble?.declarations ?? []).map((d) => `{% set ${jinjaIdent(d.name)} = ${this.convertExpressionToJinja(d.raw, d.valueParsed)} %}`);
188676
+ const loopBound = [];
188677
+ if (loop.objectIteration === "entries") {
188678
+ loopBound.push(loop.index ?? param, param);
188679
+ } else if (loop.objectIteration === "keys" || loop.objectIteration === "values") {
188680
+ loopBound.push(param);
188681
+ } else if (loop.iterationShape === "keys") {
188682
+ loopBound.push("__bf_item", param);
188683
+ } else if (supportableDestructure) {
188684
+ loopBound.push("__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name));
188685
+ if (loop.index)
188686
+ loopBound.push(loop.index);
188687
+ } else {
188688
+ loopBound.push(param);
188689
+ if (loop.index)
188690
+ loopBound.push(loop.index);
188691
+ }
188692
+ for (const d of loop.preamble?.declarations ?? [])
188693
+ loopBound.push(d.name);
188694
+ for (const n of loopBound) {
188695
+ this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
188696
+ }
188674
188697
  const childrenUnderLoop = this.renderChildren(loop.children);
188698
+ for (const n of loopBound) {
188699
+ const c = (this.loopBoundNames.get(n) ?? 1) - 1;
188700
+ if (c <= 0)
188701
+ this.loopBoundNames.delete(n);
188702
+ else
188703
+ this.loopBoundNames.set(n, c);
188704
+ }
188675
188705
  this.currentLoopKeyDepth = prevLoopKeyDepth;
188676
188706
  this.inLoop = prevInLoop;
188677
188707
  const bodyChildren = loop.bodyIsItemConditional && loop.key ? `{{ bf.comment("loop-i:" ~ bf.string(${this.convertExpressionToJinja(loop.key)})) | safe }}
@@ -188850,7 +188880,7 @@ ${children}`;
188850
188880
  }
188851
188881
  const bareId = value.expr.trim();
188852
188882
  const normalizedBareId = this.propsObjectName && bareId.startsWith(`${this.propsObjectName}.`) ? bareId.slice(this.propsObjectName.length + 1) : bareId;
188853
- if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId)) {
188883
+ if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId) && !this.isLoopBoundName(normalizedBareId)) {
188854
188884
  const jinja2 = this.convertExpressionToJinja(value.expr);
188855
188885
  const body = this.shouldBoolStr(value.expr, name) ? `${name}="{{ bf.bool_str(${jinja2}) }}"` : `${name}="{{ bf.string(${jinja2}) }}"`;
188856
188886
  return `
@@ -189137,8 +189167,13 @@ Options:
189137
189167
  }
189138
189168
  if (!/^[A-Za-z_$][\w$]*$/.test(bare))
189139
189169
  return false;
189170
+ if (this.isLoopBoundName(bare))
189171
+ return false;
189140
189172
  return this.booleanTypedProps.has(bare);
189141
189173
  }
189174
+ isLoopBoundName(name) {
189175
+ return this.loopBoundNames.has(name);
189176
+ }
189142
189177
  shouldBoolStr(expr, name) {
189143
189178
  if (isExplicitStringCall(expr))
189144
189179
  return false;
@@ -189228,7 +189263,6 @@ var conformancePins = {
189228
189263
  var renderDivergences = {
189229
189264
  "aliased-destructured-prop": "aliased destructured prop `{ n: count }` loses its rename — template vars, ssr-defaults, and the props bridge all key off the local name, so the prop is always undefined (https://github.com/piconic-ai/barefootjs/issues/2460)",
189230
189265
  "composite-row-child-aliased-prop": "same #2460 defect as `aliased-destructured-prop`, inside a keyed `.map()` loop row: the nested child's renamed prop (`{ n: count }`) is always undefined, so both rows render an empty count (https://github.com/piconic-ai/barefootjs/issues/2460)",
189231
- "loop-param-shadows-bool-prop": 'a .map() param sharing a boolean prop\'s name is routed through the bool lowering in attribute position (renders "true"/"false" instead of the row string) — `collectBooleanTypedProps` lacks the `collectLoopBoundNames` subtraction its sibling `collectStringValueNames` got in #2236 (https://github.com/piconic-ai/barefootjs/issues/2488)',
189232
189266
  "loop-param-shadows-spread-const": "a .map() param shadowing an object const is resolved by `emitSpread` against `localConstants` with no loop-shadow check, so every row spreads the OUTER const instead of the row value (https://github.com/piconic-ai/barefootjs/issues/2489)"
189233
189267
  };
189234
189268
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBA0B/B,CAAA"}
1
+ {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAwB/B,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/jinja",
3
- "version": "0.30.2",
3
+ "version": "0.30.4",
4
4
  "description": "Jinja2 adapter for BarefootJS — compiles IR to .jinja templates and ships the Python BarefootJS rendering runtime; runs under any Python web framework (Flask, etc.)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -53,14 +53,14 @@
53
53
  "directory": "packages/adapter-jinja"
54
54
  },
55
55
  "dependencies": {
56
- "@barefootjs/shared": "0.30.2"
56
+ "@barefootjs/shared": "0.30.4"
57
57
  },
58
58
  "peerDependencies": {
59
59
  "@barefootjs/jsx": ">=0.2.0"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@barefootjs/adapter-tests": "0.1.0",
63
- "@barefootjs/jsx": "0.30.2",
63
+ "@barefootjs/jsx": "0.30.4",
64
64
  "typescript": "^5.0.0"
65
65
  }
66
66
  }
@@ -285,6 +285,21 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
285
285
  */
286
286
  private staticLoopSourceBoundNames: Set<string> = new Set()
287
287
 
288
+ /**
289
+ * Names currently bound by an enclosing loop body — the block-param
290
+ * locals `renderLoop` introduces (item, index, per-binding destructure
291
+ * fields, `.map()` preamble declarations) — ref-counted so nested loops
292
+ * compose. This is the POSITION-ACCURATE twin of the coarse
293
+ * `staticLoopSourceBoundNames` above: that set is a whole-component
294
+ * union used only to guard static-const inlining (safe to over-suppress
295
+ * there — the fallback is just "don't inline"), whereas the boolean-prop
296
+ * and nullable-optional classification sites need to know whether a name
297
+ * is loop-bound AT THIS POSITION, because for those two the coarse
298
+ * fallback would silently mis-render a genuine prop occurrence outside
299
+ * the loop too (#2488).
300
+ */
301
+ private loopBoundNames: Map<string, number> = new Map()
302
+
288
303
  /**
289
304
  * Optional, no-default props that are `None` when the caller omits them.
290
305
  * Their bare-reference attribute emission is guarded with a Jinja
@@ -318,6 +333,7 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
318
333
  this.booleanTypedProps = collectBooleanTypedProps(ir)
319
334
  this.localConstants = ir.metadata.localConstants ?? []
320
335
  this.staticLoopSourceBoundNames = collectLoopBoundNames(ir)
336
+ this.loopBoundNames.clear()
321
337
  this.nullableOptionalProps = collectNullableOptionalProps(ir)
322
338
  this.stringValueNames = collectStringValueNames(ir)
323
339
  this.moduleStringConsts = collectModuleStringConsts(ir.metadata.localConstants)
@@ -911,7 +927,39 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
911
927
  const preambleLines = (loop.preamble?.declarations ?? []).map(
912
928
  d => `{% set ${jinjaIdent(d.name)} = ${this.convertExpressionToJinja(d.raw, d.valueParsed)} %}`,
913
929
  )
930
+ // Names this loop binds in body scope, for the position-accurate
931
+ // `loopBoundNames` guard (#2488) — mirrors the ERB adapter's `loopBound`
932
+ // derivation, adapted to what THIS renderLoop actually binds: the
933
+ // for-header target(s) (`loopVar` / `objectIteration` key+value /
934
+ // `iterationShape === 'keys'`'s `param`), each `indexLocalLines` name
935
+ // (explicit index, or a destructure binding), and the `.map()`
936
+ // preamble's declared locals. Ref-counted so nested loops compose.
937
+ const loopBound: string[] = []
938
+ if (loop.objectIteration === 'entries') {
939
+ loopBound.push(loop.index ?? param, param)
940
+ } else if (loop.objectIteration === 'keys' || loop.objectIteration === 'values') {
941
+ loopBound.push(param)
942
+ } else if (loop.iterationShape === 'keys') {
943
+ // The header still binds the throwaway loop var; `param` is only the
944
+ // index alias set beneath it (#2488).
945
+ loopBound.push('__bf_item', param)
946
+ } else if (supportableDestructure) {
947
+ loopBound.push('__bf_item', ...(loop.paramBindings ?? []).map(b => b.name))
948
+ if (loop.index) loopBound.push(loop.index)
949
+ } else {
950
+ loopBound.push(param)
951
+ if (loop.index) loopBound.push(loop.index)
952
+ }
953
+ for (const d of loop.preamble?.declarations ?? []) loopBound.push(d.name)
954
+ for (const n of loopBound) {
955
+ this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1)
956
+ }
914
957
  const childrenUnderLoop = this.renderChildren(loop.children)
958
+ for (const n of loopBound) {
959
+ const c = (this.loopBoundNames.get(n) ?? 1) - 1
960
+ if (c <= 0) this.loopBoundNames.delete(n)
961
+ else this.loopBoundNames.set(n, c)
962
+ }
915
963
  this.currentLoopKeyDepth = prevLoopKeyDepth
916
964
  this.inLoop = prevInLoop
917
965
  void renderedChildren
@@ -1303,7 +1351,11 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
1303
1351
  !isBooleanAttr(name) &&
1304
1352
  !value.presenceOrUndefined &&
1305
1353
  /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) &&
1306
- this.nullableOptionalProps.has(normalizedBareId)
1354
+ this.nullableOptionalProps.has(normalizedBareId) &&
1355
+ // Inside a `.map()` callback, a param that shadows a nullable
1356
+ // optional prop's name is the row binding, not the prop — skip the
1357
+ // spurious "is defined and is not none" guard (#2488).
1358
+ !this.isLoopBoundName(normalizedBareId)
1307
1359
  ) {
1308
1360
  const jinja = this.convertExpressionToJinja(value.expr)
1309
1361
  const body = this.shouldBoolStr(value.expr, name)
@@ -1806,9 +1858,18 @@ export class JinjaAdapter extends BaseAdapter implements IRNodeEmitter<JinjaRend
1806
1858
  bare = bare.slice(this.propsObjectName.length + 1)
1807
1859
  }
1808
1860
  if (!/^[A-Za-z_$][\w$]*$/.test(bare)) return false
1861
+ // Inside a `.map()` callback, a param that shares a boolean prop's name
1862
+ // is the ROW binding, not the prop — route it through plain string
1863
+ // emission instead of `bf.bool_str` (#2488).
1864
+ if (this.isLoopBoundName(bare)) return false
1809
1865
  return this.booleanTypedProps.has(bare)
1810
1866
  }
1811
1867
 
1868
+ /** Position-accurate loop-bound-name check — see `loopBoundNames`'s docstring. */
1869
+ private isLoopBoundName(name: string): boolean {
1870
+ return this.loopBoundNames.has(name)
1871
+ }
1872
+
1812
1873
  /**
1813
1874
  * Whether an attribute-value expression should route through
1814
1875
  * `bf.bool_str` (vs. plain `bf.string`) at its interpolation site.
@@ -36,8 +36,6 @@ export const renderDivergences: RenderDivergences = {
36
36
  // #2482 audit follow-ups: loop-scope holes in per-adapter name
37
37
  // classification. Graduate by applying the loop-bound-name guards
38
38
  // described in each issue and deleting the line.
39
- 'loop-param-shadows-bool-prop':
40
- 'a .map() param sharing a boolean prop\'s name is routed through the bool lowering in attribute position (renders "true"/"false" instead of the row string) — `collectBooleanTypedProps` lacks the `collectLoopBoundNames` subtraction its sibling `collectStringValueNames` got in #2236 (https://github.com/piconic-ai/barefootjs/issues/2488)',
41
39
  'loop-param-shadows-spread-const':
42
40
  'a .map() param shadowing an object const is resolved by `emitSpread` against `localConstants` with no loop-shadow check, so every row spreads the OUTER const instead of the row value (https://github.com/piconic-ai/barefootjs/issues/2489)',
43
41
  }