@barefootjs/mojolicious 0.31.3 → 0.31.5

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();
@@ -1443,13 +1442,9 @@ ${whenTrue}
1443
1442
  }
1444
1443
  const param = loop.param;
1445
1444
  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
1445
  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
- }
1446
+ const prevScope = this.scope;
1447
+ this.scope = prevScope.enterLoopRow(loop);
1453
1448
  const prevLoopKeyDepth = this.currentLoopKeyDepth;
1454
1449
  this.currentLoopKeyDepth = loop.depth;
1455
1450
  const renderedChildren = this.renderChildren(loop.children);
@@ -1459,13 +1454,7 @@ ${renderedChildren}` : renderedChildren;
1459
1454
  const lines = [];
1460
1455
  lines.push(`<%== bf->comment("loop:${loop.markerId}") %>`);
1461
1456
  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
- }
1457
+ this.scope = prevScope;
1469
1458
  const sortEmit = (e) => this.convertExpressionToPerl("", e);
1470
1459
  const sortArrow = loop.sortComparator.arrow;
1471
1460
  let sorted = null;
@@ -1481,9 +1470,7 @@ ${renderedChildren}` : renderedChildren;
1481
1470
  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
1471
  sorted = rawArray;
1483
1472
  }
1484
- for (const n of loopBound) {
1485
- this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
1486
- }
1473
+ this.scope = prevScope.enterLoopRow(loop);
1487
1474
  lines.push(`% my $${sortedHoist} = ${sorted};`);
1488
1475
  }
1489
1476
  if (loop.objectIteration) {
@@ -1532,13 +1519,7 @@ ${renderedChildren}` : renderedChildren;
1532
1519
  lines.push(...preambleLines);
1533
1520
  lines.push(children);
1534
1521
  }
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
- }
1522
+ this.scope = prevScope;
1542
1523
  lines.push(`% }`);
1543
1524
  lines.push(`<%== bf->comment("/loop:${loop.markerId}") %>`);
1544
1525
  return lines.join(`
@@ -1651,7 +1632,7 @@ ${children}`;
1651
1632
  }
1652
1633
  const bareId = value.expr.trim();
1653
1634
  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)) {
1635
+ if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId) && !this.scope.isBound(normalizedBareId)) {
1655
1636
  const perl2 = this.convertExpressionToPerl(value.expr);
1656
1637
  const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr) ? `${name}="<%= bf->bool_str(${perl2}) %>"` : `${name}="<%= ${perl2} %>"`;
1657
1638
  return `<% if (defined ${perl2}) { %>${body}<% } %>`;
@@ -1694,7 +1675,7 @@ ${children}`;
1694
1675
  if (ternaryHashref !== null) {
1695
1676
  return `<%== bf->spread_attrs(${ternaryHashref}) %>`;
1696
1677
  }
1697
- if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.loopBoundNames.has(trimmed)) {
1678
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.scope.isBound(trimmed)) {
1698
1679
  const localConst = this.localConstants.find((c) => c.name === trimmed && !c.isModule);
1699
1680
  if (localConst?.value !== undefined) {
1700
1681
  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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"}
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();
@@ -1443,13 +1442,9 @@ ${whenTrue}
1443
1442
  }
1444
1443
  const param = loop.param;
1445
1444
  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
1445
  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
- }
1446
+ const prevScope = this.scope;
1447
+ this.scope = prevScope.enterLoopRow(loop);
1453
1448
  const prevLoopKeyDepth = this.currentLoopKeyDepth;
1454
1449
  this.currentLoopKeyDepth = loop.depth;
1455
1450
  const renderedChildren = this.renderChildren(loop.children);
@@ -1459,13 +1454,7 @@ ${renderedChildren}` : renderedChildren;
1459
1454
  const lines = [];
1460
1455
  lines.push(`<%== bf->comment("loop:${loop.markerId}") %>`);
1461
1456
  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
- }
1457
+ this.scope = prevScope;
1469
1458
  const sortEmit = (e) => this.convertExpressionToPerl("", e);
1470
1459
  const sortArrow = loop.sortComparator.arrow;
1471
1460
  let sorted = null;
@@ -1481,9 +1470,7 @@ ${renderedChildren}` : renderedChildren;
1481
1470
  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
1471
  sorted = rawArray;
1483
1472
  }
1484
- for (const n of loopBound) {
1485
- this.loopBoundNames.set(n, (this.loopBoundNames.get(n) ?? 0) + 1);
1486
- }
1473
+ this.scope = prevScope.enterLoopRow(loop);
1487
1474
  lines.push(`% my $${sortedHoist} = ${sorted};`);
1488
1475
  }
1489
1476
  if (loop.objectIteration) {
@@ -1532,13 +1519,7 @@ ${renderedChildren}` : renderedChildren;
1532
1519
  lines.push(...preambleLines);
1533
1520
  lines.push(children);
1534
1521
  }
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
- }
1522
+ this.scope = prevScope;
1542
1523
  lines.push(`% }`);
1543
1524
  lines.push(`<%== bf->comment("/loop:${loop.markerId}") %>`);
1544
1525
  return lines.join(`
@@ -1651,7 +1632,7 @@ ${children}`;
1651
1632
  }
1652
1633
  const bareId = value.expr.trim();
1653
1634
  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)) {
1635
+ if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId) && !this.scope.isBound(normalizedBareId)) {
1655
1636
  const perl2 = this.convertExpressionToPerl(value.expr);
1656
1637
  const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr) ? `${name}="<%= bf->bool_str(${perl2}) %>"` : `${name}="<%= ${perl2} %>"`;
1657
1638
  return `<% if (defined ${perl2}) { %>${body}<% } %>`;
@@ -1694,7 +1675,7 @@ ${children}`;
1694
1675
  if (ternaryHashref !== null) {
1695
1676
  return `<%== bf->spread_attrs(${ternaryHashref}) %>`;
1696
1677
  }
1697
- if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.loopBoundNames.has(trimmed)) {
1678
+ if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed) && !this.scope.isBound(trimmed)) {
1698
1679
  const localConst = this.localConstants.find((c) => c.name === trimmed && !c.isModule);
1699
1680
  if (localConst?.value !== undefined) {
1700
1681
  const initTrimmed = localConst.value.trim();