@barefootjs/mojolicious 0.31.4 → 0.31.6

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.
@@ -25,7 +25,8 @@ import {
25
25
  dangerousInnerHtmlMetacharViolation,
26
26
  dangerousInnerHtmlDiagnostic,
27
27
  resolveStaticLoopSource,
28
- derivesScopeFromSlot
28
+ derivesScopeFromSlot,
29
+ BindingScope
29
30
  } from "@barefootjs/jsx";
30
31
 
31
32
  // src/adapter/boolean-result.ts
@@ -1054,7 +1055,7 @@ class MojoAdapter extends BaseAdapter {
1054
1055
  _loweringMatchers = [];
1055
1056
  moduleStringConsts = new Map;
1056
1057
  localConstants = [];
1057
- loopBoundNames = new Map;
1058
+ scope = BindingScope.EMPTY;
1058
1059
  nullableOptionalProps = new Set;
1059
1060
  constructor(options = {}) {
1060
1061
  super();
@@ -1076,7 +1077,7 @@ class MojoAdapter extends BaseAdapter {
1076
1077
  this._searchParamsLocals = searchParamsLocalNames(ir.metadata);
1077
1078
  this._loweringMatchers = prepareLoweringMatchers(ir.metadata);
1078
1079
  this.localConstants = ir.metadata.localConstants ?? [];
1079
- this.loopBoundNames.clear();
1080
+ this.scope = BindingScope.EMPTY;
1080
1081
  this.errors = [];
1081
1082
  this.childrenCaptureCounter = 0;
1082
1083
  if (!options?.siblingTemplatesRegistered) {
@@ -1111,7 +1112,7 @@ class MojoAdapter extends BaseAdapter {
1111
1112
  }
1112
1113
  if (!/^[A-Za-z_$][\w$]*$/.test(bare))
1113
1114
  return false;
1114
- if (this.loopBoundNames.has(bare))
1115
+ if (this.scope.isBound(bare))
1115
1116
  return false;
1116
1117
  return this.booleanTypedProps.has(bare);
1117
1118
  }
@@ -1129,7 +1130,7 @@ class MojoAdapter extends BaseAdapter {
1129
1130
  };
1130
1131
  }
1131
1132
  resolveLiteralConst(name) {
1132
- if (this.loopBoundNames?.has?.(name))
1133
+ if (this.scope.isBound(name))
1133
1134
  return null;
1134
1135
  const c = (this.localConstants ?? []).find((lc) => lc.name === name);
1135
1136
  if (c?.value === undefined)
@@ -1143,15 +1144,13 @@ class MojoAdapter extends BaseAdapter {
1143
1144
  return null;
1144
1145
  }
1145
1146
  resolveStaticRecordLiteral(objectName, key) {
1146
- if (this.loopBoundNames?.has?.(objectName))
1147
- return null;
1148
- const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
1147
+ const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants, (name) => this.scope.isBound(name));
1149
1148
  if (!hit)
1150
1149
  return null;
1151
1150
  return hit.kind === "number" ? hit.text : `'${hit.text.replace(/[\\']/g, (m) => `\\${m}`)}'`;
1152
1151
  }
1153
1152
  resolveModuleStringConst(name) {
1154
- if (this.loopBoundNames.has(name))
1153
+ if (this.scope.isBound(name))
1155
1154
  return null;
1156
1155
  const value = this.moduleStringConsts.get(name);
1157
1156
  if (value === undefined)
@@ -1416,7 +1415,7 @@ ${whenTrue}
1416
1415
  });
1417
1416
  }
1418
1417
  const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
1419
- isNameShadowed: (name) => this.loopBoundNames.has(name)
1418
+ isNameShadowed: this.scope.asShadowPredicate()
1420
1419
  });
1421
1420
  const staticArray = staticItems !== null ? staticValueToPerl(staticItems) : null;
1422
1421
  const arrayName = loop.array.trim();
@@ -1429,7 +1428,8 @@ ${whenTrue}
1429
1428
  message: `Loop array \`${arrayName}\` is a local computed value (\`${arrayConst.value}\`) that the Mojo adapter cannot bind as a template variable — only numeric/string-literal locals inline at their use site.`,
1430
1429
  loc: loop.loc ?? { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
1431
1430
  suggestion: {
1432
- message: "Pre-compute the array server-side and pass it as a prop, or mark the loop position as @client-only so it runs in JS on the client."
1431
+ message: "Pre-compute the array server-side and pass it as a prop, or mark the loop position as @client-only so it runs in JS on the client.",
1432
+ escape: [{ kind: "prop-precompute" }, { kind: "client-directive" }]
1433
1433
  }
1434
1434
  });
1435
1435
  }
@@ -1443,13 +1443,9 @@ ${whenTrue}
1443
1443
  }
1444
1444
  const param = loop.param;
1445
1445
  const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
1446
- const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
1447
1446
  const preambleDecls = loop.preamble?.declarations ?? [];
1448
- for (const d of preambleDecls)
1449
- loopBound.push(d.name);
1450
- for (const n of loopBound) {
1451
- this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
1452
- }
1447
+ const prevScope = this.scope;
1448
+ this.scope = prevScope.enterLoopRow(loop);
1453
1449
  const prevLoopKeyDepth = this.currentLoopKeyDepth;
1454
1450
  this.currentLoopKeyDepth = loop.depth;
1455
1451
  const renderedChildren = this.renderChildren(loop.children);
@@ -1459,13 +1455,7 @@ ${renderedChildren}` : renderedChildren;
1459
1455
  const lines = [];
1460
1456
  lines.push(`<%== bf->comment("loop:${loop.markerId}") %>`);
1461
1457
  if (sortedHoist && loop.sortComparator) {
1462
- for (const n of loopBound) {
1463
- const c = (this.loopBoundNames.get(n) ?? 1) - 1;
1464
- if (c <= 0)
1465
- this.loopBoundNames.delete(n);
1466
- else
1467
- this.loopBoundNames.set(n, c);
1468
- }
1458
+ this.scope = prevScope;
1469
1459
  const sortEmit = (e) => this.convertExpressionToPerl("", e);
1470
1460
  const sortArrow = loop.sortComparator.arrow;
1471
1461
  let sorted = null;
@@ -1481,9 +1471,7 @@ ${renderedChildren}` : renderedChildren;
1481
1471
  this._recordExprBF101(`.sort(...) loop comparator is not lowerable to a template sort`, `Pre-sort the array in the route handler, or mark the loop @client-only.`);
1482
1472
  sorted = rawArray;
1483
1473
  }
1484
- for (const n of loopBound) {
1485
- this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
1486
- }
1474
+ this.scope = prevScope.enterLoopRow(loop);
1487
1475
  lines.push(`% my $${sortedHoist} = ${sorted};`);
1488
1476
  }
1489
1477
  if (loop.objectIteration) {
@@ -1532,13 +1520,7 @@ ${renderedChildren}` : renderedChildren;
1532
1520
  lines.push(...preambleLines);
1533
1521
  lines.push(children);
1534
1522
  }
1535
- for (const n of loopBound) {
1536
- const c = (this.loopBoundNames.get(n) ?? 1) - 1;
1537
- if (c <= 0)
1538
- this.loopBoundNames.delete(n);
1539
- else
1540
- this.loopBoundNames.set(n, c);
1541
- }
1523
+ this.scope = prevScope;
1542
1524
  lines.push(`% }`);
1543
1525
  lines.push(`<%== bf->comment("/loop:${loop.markerId}") %>`);
1544
1526
  return lines.join(`
@@ -1651,7 +1633,7 @@ ${children}`;
1651
1633
  }
1652
1634
  const bareId = value.expr.trim();
1653
1635
  const normalizedBareId = this.propsObjectName && bareId.startsWith(`${this.propsObjectName}.`) ? bareId.slice(this.propsObjectName.length + 1) : bareId;
1654
- if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId) && !this.loopBoundNames.has(normalizedBareId)) {
1636
+ if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId) && !this.scope.isBound(normalizedBareId)) {
1655
1637
  const perl2 = this.convertExpressionToPerl(value.expr);
1656
1638
  const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr) ? `${name}="<%= bf->bool_str(${perl2}) %>"` : `${name}="<%= ${perl2} %>"`;
1657
1639
  return `<% if (defined ${perl2}) { %>${body}<% } %>`;
@@ -1694,7 +1676,7 @@ ${children}`;
1694
1676
  if (ternaryHashref !== null) {
1695
1677
  return `<%== bf->spread_attrs(${ternaryHashref}) %>`;
1696
1678
  }
1697
- if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.loopBoundNames.has(trimmed)) {
1679
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.scope.isBound(trimmed)) {
1698
1680
  const localConst = this.localConstants.find((c) => c.name === trimmed && !c.isModule);
1699
1681
  if (localConst?.value !== undefined) {
1700
1682
  const initTrimmed = localConst.value.trim();
@@ -103,14 +103,22 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
103
103
  */
104
104
  private localConstants;
105
105
  /**
106
- * Names currently bound by an enclosing loop body — the `my $<param>` and
107
- * `my $<index>` bindings `renderLoop` introduces — ref-counted so nested
108
- * loops compose. `resolveModuleStringConst` consults this so a loop
106
+ * The one canonical, position-accurate "names bound by an enclosing loop
107
+ * callback" service (#2482 Stage 2)replaces the ref-counted
108
+ * `Map<string, number>` this adapter used to push/pop around
109
+ * `renderLoop`'s body (the `my $<param>` / `my $<index>` bindings it
110
+ * introduces). `resolveModuleStringConst` consults this so a loop
109
111
  * variable whose name happens to match a module string const is NOT
110
112
  * inlined as the const literal (mirrors the Go adapter's loop-param /
111
- * loop-var shadowing guards). (#1749 review)
113
+ * loop-var shadowing guards). (#1749 review) Threaded via
114
+ * save/restore-by-reference around `renderChildren(loop.children)` in
115
+ * `renderLoop` (immutable — no ref-count bookkeeping), mirroring the
116
+ * Stage 1a/1b `ctx.scope` precedent in `jsx-to-ir.ts`. `IRLoop` already
117
+ * structurally satisfies `LoopBindingSource`
118
+ * (`param`/`index`/`paramBindings`/`preamble`), so `renderLoop` passes
119
+ * the loop node straight to `enterLoopRow`.
112
120
  */
113
- private loopBoundNames;
121
+ private scope;
114
122
  /**
115
123
  * Prop names whose value is `undef` in the template body when the caller
116
124
  * omits them — so a bare-reference attribute should be dropped rather
@@ -164,13 +172,12 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
164
172
  * function-scope consts never reach the per-render stash, so a bare
165
173
  * `$totalPages` faults under strict mode.
166
174
  *
167
- * The `loopBoundNames` guard also covers the #2221 hazard (a loop
168
- * callback's own param shadowing this outer const's name): unlike the
169
- * Twig-family adapters' coarse, whole-component `collectLoopBoundNames(ir)`
170
- * static set, this adapter's `loopBoundNames` is a LIVE ref-counted map
171
- * `renderLoop` populates/depopulates as it descends/ascends into each
172
- * loop body (#1749) — so it's already scope-precise for this call site;
173
- * no separate `staticLoopSourceBoundNames`-style field is needed here.
175
+ * The threaded scope's shadow guard also covers the #2221 hazard (a loop
176
+ * callback's own param shadowing this outer const's name): `this.scope`
177
+ * is position-accurate (#2482 Stage 2) — pushed/popped by reference as
178
+ * `renderLoop` descends/ascends into each loop body so it's already
179
+ * scope-precise for this call site; no separate coarse whole-component
180
+ * field is needed here.
174
181
  */
175
182
  private resolveLiteralConst;
176
183
  private resolveStaticRecordLiteral;
@@ -1 +1 @@
1
- {"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eAmL7B,CAAA"}
1
+ {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eA4L7B,CAAA"}
package/dist/index.js CHANGED
@@ -25,7 +25,8 @@ import {
25
25
  dangerousInnerHtmlMetacharViolation,
26
26
  dangerousInnerHtmlDiagnostic,
27
27
  resolveStaticLoopSource,
28
- derivesScopeFromSlot
28
+ derivesScopeFromSlot,
29
+ BindingScope
29
30
  } from "@barefootjs/jsx";
30
31
 
31
32
  // src/adapter/boolean-result.ts
@@ -1054,7 +1055,7 @@ class MojoAdapter extends BaseAdapter {
1054
1055
  _loweringMatchers = [];
1055
1056
  moduleStringConsts = new Map;
1056
1057
  localConstants = [];
1057
- loopBoundNames = new Map;
1058
+ scope = BindingScope.EMPTY;
1058
1059
  nullableOptionalProps = new Set;
1059
1060
  constructor(options = {}) {
1060
1061
  super();
@@ -1076,7 +1077,7 @@ class MojoAdapter extends BaseAdapter {
1076
1077
  this._searchParamsLocals = searchParamsLocalNames(ir.metadata);
1077
1078
  this._loweringMatchers = prepareLoweringMatchers(ir.metadata);
1078
1079
  this.localConstants = ir.metadata.localConstants ?? [];
1079
- this.loopBoundNames.clear();
1080
+ this.scope = BindingScope.EMPTY;
1080
1081
  this.errors = [];
1081
1082
  this.childrenCaptureCounter = 0;
1082
1083
  if (!options?.siblingTemplatesRegistered) {
@@ -1111,7 +1112,7 @@ class MojoAdapter extends BaseAdapter {
1111
1112
  }
1112
1113
  if (!/^[A-Za-z_$][\w$]*$/.test(bare))
1113
1114
  return false;
1114
- if (this.loopBoundNames.has(bare))
1115
+ if (this.scope.isBound(bare))
1115
1116
  return false;
1116
1117
  return this.booleanTypedProps.has(bare);
1117
1118
  }
@@ -1129,7 +1130,7 @@ class MojoAdapter extends BaseAdapter {
1129
1130
  };
1130
1131
  }
1131
1132
  resolveLiteralConst(name) {
1132
- if (this.loopBoundNames?.has?.(name))
1133
+ if (this.scope.isBound(name))
1133
1134
  return null;
1134
1135
  const c = (this.localConstants ?? []).find((lc) => lc.name === name);
1135
1136
  if (c?.value === undefined)
@@ -1143,15 +1144,13 @@ class MojoAdapter extends BaseAdapter {
1143
1144
  return null;
1144
1145
  }
1145
1146
  resolveStaticRecordLiteral(objectName, key) {
1146
- if (this.loopBoundNames?.has?.(objectName))
1147
- return null;
1148
- const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
1147
+ const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants, (name) => this.scope.isBound(name));
1149
1148
  if (!hit)
1150
1149
  return null;
1151
1150
  return hit.kind === "number" ? hit.text : `'${hit.text.replace(/[\\']/g, (m) => `\\${m}`)}'`;
1152
1151
  }
1153
1152
  resolveModuleStringConst(name) {
1154
- if (this.loopBoundNames.has(name))
1153
+ if (this.scope.isBound(name))
1155
1154
  return null;
1156
1155
  const value = this.moduleStringConsts.get(name);
1157
1156
  if (value === undefined)
@@ -1416,7 +1415,7 @@ ${whenTrue}
1416
1415
  });
1417
1416
  }
1418
1417
  const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
1419
- isNameShadowed: (name) => this.loopBoundNames.has(name)
1418
+ isNameShadowed: this.scope.asShadowPredicate()
1420
1419
  });
1421
1420
  const staticArray = staticItems !== null ? staticValueToPerl(staticItems) : null;
1422
1421
  const arrayName = loop.array.trim();
@@ -1429,7 +1428,8 @@ ${whenTrue}
1429
1428
  message: `Loop array \`${arrayName}\` is a local computed value (\`${arrayConst.value}\`) that the Mojo adapter cannot bind as a template variable — only numeric/string-literal locals inline at their use site.`,
1430
1429
  loc: loop.loc ?? { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
1431
1430
  suggestion: {
1432
- message: "Pre-compute the array server-side and pass it as a prop, or mark the loop position as @client-only so it runs in JS on the client."
1431
+ message: "Pre-compute the array server-side and pass it as a prop, or mark the loop position as @client-only so it runs in JS on the client.",
1432
+ escape: [{ kind: "prop-precompute" }, { kind: "client-directive" }]
1433
1433
  }
1434
1434
  });
1435
1435
  }
@@ -1443,13 +1443,9 @@ ${whenTrue}
1443
1443
  }
1444
1444
  const param = loop.param;
1445
1445
  const indexVar = loop.iterationShape === "keys" ? `$${param}` : loop.index ? `$${loop.index}` : "$_i";
1446
- const loopBound = loop.objectIteration === "entries" ? [param, loop.index ?? "_k"] : loop.objectIteration === "keys" || loop.objectIteration === "values" || loop.iterationShape === "keys" ? [param] : supportableDestructure ? ["__bf_item", ...(loop.paramBindings ?? []).map((b) => b.name), loop.index ?? "_i"] : [param, loop.index ?? "_i"];
1447
1446
  const preambleDecls = loop.preamble?.declarations ?? [];
1448
- for (const d of preambleDecls)
1449
- loopBound.push(d.name);
1450
- for (const n of loopBound) {
1451
- this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
1452
- }
1447
+ const prevScope = this.scope;
1448
+ this.scope = prevScope.enterLoopRow(loop);
1453
1449
  const prevLoopKeyDepth = this.currentLoopKeyDepth;
1454
1450
  this.currentLoopKeyDepth = loop.depth;
1455
1451
  const renderedChildren = this.renderChildren(loop.children);
@@ -1459,13 +1455,7 @@ ${renderedChildren}` : renderedChildren;
1459
1455
  const lines = [];
1460
1456
  lines.push(`<%== bf->comment("loop:${loop.markerId}") %>`);
1461
1457
  if (sortedHoist && loop.sortComparator) {
1462
- for (const n of loopBound) {
1463
- const c = (this.loopBoundNames.get(n) ?? 1) - 1;
1464
- if (c <= 0)
1465
- this.loopBoundNames.delete(n);
1466
- else
1467
- this.loopBoundNames.set(n, c);
1468
- }
1458
+ this.scope = prevScope;
1469
1459
  const sortEmit = (e) => this.convertExpressionToPerl("", e);
1470
1460
  const sortArrow = loop.sortComparator.arrow;
1471
1461
  let sorted = null;
@@ -1481,9 +1471,7 @@ ${renderedChildren}` : renderedChildren;
1481
1471
  this._recordExprBF101(`.sort(...) loop comparator is not lowerable to a template sort`, `Pre-sort the array in the route handler, or mark the loop @client-only.`);
1482
1472
  sorted = rawArray;
1483
1473
  }
1484
- for (const n of loopBound) {
1485
- this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
1486
- }
1474
+ this.scope = prevScope.enterLoopRow(loop);
1487
1475
  lines.push(`% my $${sortedHoist} = ${sorted};`);
1488
1476
  }
1489
1477
  if (loop.objectIteration) {
@@ -1532,13 +1520,7 @@ ${renderedChildren}` : renderedChildren;
1532
1520
  lines.push(...preambleLines);
1533
1521
  lines.push(children);
1534
1522
  }
1535
- for (const n of loopBound) {
1536
- const c = (this.loopBoundNames.get(n) ?? 1) - 1;
1537
- if (c <= 0)
1538
- this.loopBoundNames.delete(n);
1539
- else
1540
- this.loopBoundNames.set(n, c);
1541
- }
1523
+ this.scope = prevScope;
1542
1524
  lines.push(`% }`);
1543
1525
  lines.push(`<%== bf->comment("/loop:${loop.markerId}") %>`);
1544
1526
  return lines.join(`
@@ -1651,7 +1633,7 @@ ${children}`;
1651
1633
  }
1652
1634
  const bareId = value.expr.trim();
1653
1635
  const normalizedBareId = this.propsObjectName && bareId.startsWith(`${this.propsObjectName}.`) ? bareId.slice(this.propsObjectName.length + 1) : bareId;
1654
- if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId) && !this.loopBoundNames.has(normalizedBareId)) {
1636
+ if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId) && !this.scope.isBound(normalizedBareId)) {
1655
1637
  const perl2 = this.convertExpressionToPerl(value.expr);
1656
1638
  const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr) ? `${name}="<%= bf->bool_str(${perl2}) %>"` : `${name}="<%= ${perl2} %>"`;
1657
1639
  return `<% if (defined ${perl2}) { %>${body}<% } %>`;
@@ -1694,7 +1676,7 @@ ${children}`;
1694
1676
  if (ternaryHashref !== null) {
1695
1677
  return `<%== bf->spread_attrs(${ternaryHashref}) %>`;
1696
1678
  }
1697
- if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.loopBoundNames.has(trimmed)) {
1679
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.scope.isBound(trimmed)) {
1698
1680
  const localConst = this.localConstants.find((c) => c.name === trimmed && !c.isModule);
1699
1681
  if (localConst?.value !== undefined) {
1700
1682
  const initTrimmed = localConst.value.trim();
@@ -1930,14 +1912,20 @@ var conformancePins = {
1930
1912
  "tag-cloud": [{ code: "BF021", severity: "error" }],
1931
1913
  "preamble-cells": [{ code: "BF021", severity: "error" }],
1932
1914
  "static-array-from-props": [
1933
- { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2321" }
1915
+ {
1916
+ code: "BF101",
1917
+ severity: "error",
1918
+ issue: "https://github.com/piconic-ai/barefootjs/issues/2321"
1919
+ }
1934
1920
  ],
1935
1921
  "static-array-from-props-with-component": [
1936
- { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2321" }
1937
- ],
1938
- "filter-nested-find-predicate": [
1939
- { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2320" }
1922
+ {
1923
+ code: "BF101",
1924
+ severity: "error",
1925
+ issue: "https://github.com/piconic-ai/barefootjs/issues/2321"
1926
+ }
1940
1927
  ],
1928
+ "filter-nested-find-predicate": [{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2320" }],
1941
1929
  "date-method-uncatalogued": [{ code: "BF021", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2356" }]
1942
1930
  };
1943
1931
  // src/render-divergences.ts