@barefootjs/erb 0.18.5 → 0.18.7

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.
@@ -251,6 +251,13 @@ export declare class ErbAdapter extends BaseAdapter implements IRNodeEmitter<Erb
251
251
  private isClientOnlyContextIdentifier;
252
252
  emitAsync(node: IRAsync, _ctx: ErbRenderCtx, _emit: EmitIRNode<ErbRenderCtx>): string;
253
253
  renderElement(element: IRElement): string;
254
+ /**
255
+ * `dangerouslySetInnerHTML={{ __html: '...' }}` (#2207) — see the Blade
256
+ * adapter's identical helper for the full rationale. `null` means the
257
+ * attribute is absent (caller falls through to normal `renderChildren`);
258
+ * a non-`null` string (possibly `''`) replaces the children outright.
259
+ */
260
+ private renderDangerousInnerHtml;
254
261
  renderExpression(expr: IRExpression): string;
255
262
  /**
256
263
  * True when `expr` is a structural read of the `children` prop: a bare
@@ -1 +1 @@
1
- {"version":3,"file":"erb-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/erb-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"erb-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/erb-adapter.ts"],"names":[],"mappings":"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"}
@@ -39,7 +39,8 @@ export declare class ErbFilterEmitter implements ParsedExprEmitter {
39
39
  private readonly isLoopBoundOuter;
40
40
  private readonly isStringName;
41
41
  private readonly onUnsupported?;
42
- constructor(param: string, localVarMap: Map<string, string>, isLoopBoundOuter?: (n: string) => boolean, isStringName?: (n: string) => boolean, onUnsupported?: ((message: string, reason?: string) => void) | undefined);
42
+ private readonly renderParamAs;
43
+ constructor(param: string, localVarMap: Map<string, string>, isLoopBoundOuter?: (n: string) => boolean, isStringName?: (n: string) => boolean, onUnsupported?: ((message: string, reason?: string) => void) | undefined, renderParamAs?: string);
43
44
  identifier(name: string): string;
44
45
  literal(value: string | number | boolean | null, literalType: LiteralType): string;
45
46
  member(object: ParsedExpr, property: string, _computed: boolean, optional: boolean, emit: (e: ParsedExpr) => string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"emitters.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/emitters.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"emitters.d.ts","sourceRoot":"","sources":["../../../src/adapter/expr/emitters.ts"],"names":[],"mappings":"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"}
@@ -187308,7 +187308,12 @@ import {
187308
187308
  prepareLoweringMatchers,
187309
187309
  queryHrefArgs,
187310
187310
  isValidHelperId,
187311
- sortComparatorFromArrow as sortComparatorFromArrow2
187311
+ sortComparatorFromArrow as sortComparatorFromArrow2,
187312
+ isDangerousInnerHtmlAttr,
187313
+ resolveDangerousInnerHtml,
187314
+ dangerousInnerHtmlMetacharViolation,
187315
+ dangerousInnerHtmlDiagnostic,
187316
+ resolveStaticLoopSource
187312
187317
  } from "@barefootjs/jsx";
187313
187318
 
187314
187319
  // src/adapter/boolean-result.ts
@@ -187693,6 +187698,39 @@ function renderFlatMethod(recv, depth, emit) {
187693
187698
  return `bf.flat(${recv}, ${d})`;
187694
187699
  }
187695
187700
 
187701
+ // src/adapter/lib/static-value.ts
187702
+ function staticValueToRuby(value) {
187703
+ if (value === null || value === undefined)
187704
+ return "nil";
187705
+ if (typeof value === "boolean")
187706
+ return value ? "true" : "false";
187707
+ if (typeof value === "number")
187708
+ return String(value);
187709
+ if (typeof value === "string")
187710
+ return rubyStringLiteral(value);
187711
+ if (Array.isArray(value)) {
187712
+ const items = [];
187713
+ for (const el of value) {
187714
+ const serialized = staticValueToRuby(el);
187715
+ if (serialized === null)
187716
+ return null;
187717
+ items.push(serialized);
187718
+ }
187719
+ return `[${items.join(", ")}]`;
187720
+ }
187721
+ if (typeof value === "object") {
187722
+ const entries = [];
187723
+ for (const [key, val] of Object.entries(value)) {
187724
+ const serialized = staticValueToRuby(val);
187725
+ if (serialized === null)
187726
+ return null;
187727
+ entries.push(`${rubySymbolKey(key)} ${serialized}`);
187728
+ }
187729
+ return `{ ${entries.join(", ")} }`;
187730
+ }
187731
+ return null;
187732
+ }
187733
+
187696
187734
  // src/adapter/expr/emitters.ts
187697
187735
  import {
187698
187736
  emitParsedExpr,
@@ -187738,16 +187776,18 @@ class ErbFilterEmitter {
187738
187776
  isLoopBoundOuter;
187739
187777
  isStringName;
187740
187778
  onUnsupported;
187741
- constructor(param, localVarMap, isLoopBoundOuter = () => false, isStringName = unusedIsStringName, onUnsupported) {
187779
+ renderParamAs;
187780
+ constructor(param, localVarMap, isLoopBoundOuter = () => false, isStringName = unusedIsStringName, onUnsupported, renderParamAs = rubyLocal(param)) {
187742
187781
  this.param = param;
187743
187782
  this.localVarMap = localVarMap;
187744
187783
  this.isLoopBoundOuter = isLoopBoundOuter;
187745
187784
  this.isStringName = isStringName;
187746
187785
  this.onUnsupported = onUnsupported;
187786
+ this.renderParamAs = renderParamAs;
187747
187787
  }
187748
187788
  identifier(name) {
187749
187789
  if (name === this.param)
187750
- return rubyLocal(this.param);
187790
+ return this.renderParamAs;
187751
187791
  const signal = this.localVarMap.get(name);
187752
187792
  if (signal)
187753
187793
  return `v[${rubySymbolLiteral(signal)}]`;
@@ -188547,7 +188587,8 @@ class ErbAdapter extends BaseAdapter {
188547
188587
  renderElement(element) {
188548
188588
  const tag = element.tag;
188549
188589
  const attrs = this.renderAttributes(element);
188550
- const children = this.renderChildren(element.children);
188590
+ const dangerousHtml = this.renderDangerousInnerHtml(element);
188591
+ const children = dangerousHtml !== null ? dangerousHtml : this.renderChildren(element.children);
188551
188592
  let hydrationAttrs = "";
188552
188593
  if (element.needsScope) {
188553
188594
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
@@ -188582,6 +188623,22 @@ class ErbAdapter extends BaseAdapter {
188582
188623
  }
188583
188624
  return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`;
188584
188625
  }
188626
+ renderDangerousInnerHtml(element) {
188627
+ const resolution = resolveDangerousInnerHtml(element);
188628
+ if (!resolution)
188629
+ return null;
188630
+ if (resolution.kind === "dynamic") {
188631
+ this.errors.push(dangerousInnerHtmlDiagnostic(resolution.expr, resolution.loc));
188632
+ return "";
188633
+ }
188634
+ const violation = dangerousInnerHtmlMetacharViolation(resolution.html, this.name);
188635
+ if (violation) {
188636
+ const attr = element.attrs.find(isDangerousInnerHtmlAttr);
188637
+ this.errors.push(dangerousInnerHtmlDiagnostic(`{ __html: ${JSON.stringify(resolution.html)} }`, attr.loc, violation));
188638
+ return "";
188639
+ }
188640
+ return resolution.html;
188641
+ }
188585
188642
  renderExpression(expr) {
188586
188643
  if (expr.clientOnly) {
188587
188644
  if (expr.slotId) {
@@ -188589,7 +188646,7 @@ class ErbAdapter extends BaseAdapter {
188589
188646
  }
188590
188647
  return "";
188591
188648
  }
188592
- const rubyExpr = this.convertExpressionToRuby(expr.expr);
188649
+ const rubyExpr = this.convertExpressionToRuby(expr.expr, expr.parsed);
188593
188650
  const wrapped = this.isChildrenValueExpr(expr) ? rubyExpr : `bf.h(${rubyExpr})`;
188594
188651
  if (expr.slotId) {
188595
188652
  return `<%= bf.text_start("${expr.slotId}") %><%= ${wrapped} %><%= bf.text_end %>`;
@@ -188698,7 +188755,11 @@ ${whenTrue}
188698
188755
  }
188699
188756
  });
188700
188757
  }
188701
- if (loop.arrayParsed?.kind === "identifier") {
188758
+ const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
188759
+ isNameShadowed: (name) => this.loopBoundNames.has(name)
188760
+ });
188761
+ const staticArray = staticItems !== null ? staticValueToRuby(staticItems) : null;
188762
+ if (staticArray === null && loop.arrayParsed?.kind === "identifier") {
188702
188763
  const arrayName = loop.arrayParsed.name;
188703
188764
  const isUnresolvableLocalConst = !this.loopBoundNames.has(arrayName) && this.resolveModuleStringConst(arrayName) === null && this.resolveLiteralConst(arrayName) === null && this.localConstants.some((c) => c.name === arrayName && !c.isModule);
188704
188765
  if (isUnresolvableLocalConst) {
@@ -188708,7 +188769,7 @@ ${whenTrue}
188708
188769
  3. Precompute the value server-side and pass it in as a prop.`);
188709
188770
  }
188710
188771
  }
188711
- const rawArray = this.convertExpressionToRuby(loop.array);
188772
+ const rawArray = staticArray ?? this.convertExpressionToRuby(loop.array);
188712
188773
  let sortedHoist = null;
188713
188774
  let array = rawArray;
188714
188775
  if (loop.sortComparator) {
@@ -188788,7 +188849,9 @@ ${renderedChildren}` : renderedChildren;
188788
188849
  if (loop.filterPredicate) {
188789
188850
  let filterCond;
188790
188851
  if (loop.filterPredicate.predicate) {
188791
- filterCond = this.renderRubyFilterExpr(loop.filterPredicate.predicate, param);
188852
+ const filterOwnParam = loop.filterPredicate.param;
188853
+ const matchParam = filterOwnParam && !filterOwnParam.startsWith("[") && !filterOwnParam.startsWith("{") ? filterOwnParam : param;
188854
+ filterCond = this.renderRubyFilterExpr(loop.filterPredicate.predicate, matchParam, undefined, rubyLocal(param));
188792
188855
  } else {
188793
188856
  filterCond = "true";
188794
188857
  }
@@ -189009,6 +189072,8 @@ ${children}`;
189009
189072
  for (const attr of element.attrs) {
189010
189073
  if (attr.clientOnly)
189011
189074
  continue;
189075
+ if (isDangerousInnerHtmlAttr(attr))
189076
+ continue;
189012
189077
  let attrName;
189013
189078
  if (attr.name === "className")
189014
189079
  attrName = "class";
@@ -189032,8 +189097,8 @@ ${children}`;
189032
189097
  renderCondMarker(condId) {
189033
189098
  return `${BF_COND}="${condId}"`;
189034
189099
  }
189035
- renderRubyFilterExpr(expr, param, localVarMap = new Map) {
189036
- return emitParsedExpr2(expr, new ErbFilterEmitter(param, localVarMap, (n) => this.isLoopBoundName(n), (n) => this._isStringValueName(n), (message, reason) => this._recordExprBF101(message, reason)));
189100
+ renderRubyFilterExpr(expr, param, localVarMap = new Map, renderParamAs) {
189101
+ return emitParsedExpr2(expr, new ErbFilterEmitter(param, localVarMap, (n) => this.isLoopBoundName(n), (n) => this._isStringValueName(n), (message, reason) => this._recordExprBF101(message, reason), renderParamAs));
189037
189102
  }
189038
189103
  convertTemplateLiteralPartsToRuby(literalParts) {
189039
189104
  const parts = [];
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Serialize a compile-time-evaluated JS value (`@barefootjs/jsx`'s
3
+ * `evaluateStaticLiteral`/`resolveStaticLoopSource`, #2208) into a native
4
+ * Ruby literal. Used to inline a fully-static loop source (an inline array
5
+ * literal, or a function-scope local const with a static initializer)
6
+ * directly in the loop-bound expression, rather than requiring a bound
7
+ * template variable.
8
+ *
9
+ * Hash keys render as symbols (`label: 'Alpha'`) to match `item[:label]`,
10
+ * this adapter's existing member-access convention (`rubyLocal`'s
11
+ * companion, `rubySymbolKey`/`rubySymbolLiteral`).
12
+ *
13
+ * Returns `null` for a value this adapter can't represent as a literal —
14
+ * the caller falls back to its existing BF101 refusal instead of guessing.
15
+ */
16
+ export declare function staticValueToRuby(value: unknown): string | null;
17
+ //# sourceMappingURL=static-value.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-value.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/static-value.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAwB/D"}
package/dist/build.js CHANGED
@@ -187308,7 +187308,12 @@ import {
187308
187308
  prepareLoweringMatchers,
187309
187309
  queryHrefArgs,
187310
187310
  isValidHelperId,
187311
- sortComparatorFromArrow as sortComparatorFromArrow2
187311
+ sortComparatorFromArrow as sortComparatorFromArrow2,
187312
+ isDangerousInnerHtmlAttr,
187313
+ resolveDangerousInnerHtml,
187314
+ dangerousInnerHtmlMetacharViolation,
187315
+ dangerousInnerHtmlDiagnostic,
187316
+ resolveStaticLoopSource
187312
187317
  } from "@barefootjs/jsx";
187313
187318
 
187314
187319
  // src/adapter/boolean-result.ts
@@ -187693,6 +187698,39 @@ function renderFlatMethod(recv, depth, emit) {
187693
187698
  return `bf.flat(${recv}, ${d})`;
187694
187699
  }
187695
187700
 
187701
+ // src/adapter/lib/static-value.ts
187702
+ function staticValueToRuby(value) {
187703
+ if (value === null || value === undefined)
187704
+ return "nil";
187705
+ if (typeof value === "boolean")
187706
+ return value ? "true" : "false";
187707
+ if (typeof value === "number")
187708
+ return String(value);
187709
+ if (typeof value === "string")
187710
+ return rubyStringLiteral(value);
187711
+ if (Array.isArray(value)) {
187712
+ const items = [];
187713
+ for (const el of value) {
187714
+ const serialized = staticValueToRuby(el);
187715
+ if (serialized === null)
187716
+ return null;
187717
+ items.push(serialized);
187718
+ }
187719
+ return `[${items.join(", ")}]`;
187720
+ }
187721
+ if (typeof value === "object") {
187722
+ const entries = [];
187723
+ for (const [key, val] of Object.entries(value)) {
187724
+ const serialized = staticValueToRuby(val);
187725
+ if (serialized === null)
187726
+ return null;
187727
+ entries.push(`${rubySymbolKey(key)} ${serialized}`);
187728
+ }
187729
+ return `{ ${entries.join(", ")} }`;
187730
+ }
187731
+ return null;
187732
+ }
187733
+
187696
187734
  // src/adapter/expr/emitters.ts
187697
187735
  import {
187698
187736
  emitParsedExpr,
@@ -187738,16 +187776,18 @@ class ErbFilterEmitter {
187738
187776
  isLoopBoundOuter;
187739
187777
  isStringName;
187740
187778
  onUnsupported;
187741
- constructor(param, localVarMap, isLoopBoundOuter = () => false, isStringName = unusedIsStringName, onUnsupported) {
187779
+ renderParamAs;
187780
+ constructor(param, localVarMap, isLoopBoundOuter = () => false, isStringName = unusedIsStringName, onUnsupported, renderParamAs = rubyLocal(param)) {
187742
187781
  this.param = param;
187743
187782
  this.localVarMap = localVarMap;
187744
187783
  this.isLoopBoundOuter = isLoopBoundOuter;
187745
187784
  this.isStringName = isStringName;
187746
187785
  this.onUnsupported = onUnsupported;
187786
+ this.renderParamAs = renderParamAs;
187747
187787
  }
187748
187788
  identifier(name) {
187749
187789
  if (name === this.param)
187750
- return rubyLocal(this.param);
187790
+ return this.renderParamAs;
187751
187791
  const signal = this.localVarMap.get(name);
187752
187792
  if (signal)
187753
187793
  return `v[${rubySymbolLiteral(signal)}]`;
@@ -188547,7 +188587,8 @@ class ErbAdapter extends BaseAdapter {
188547
188587
  renderElement(element) {
188548
188588
  const tag = element.tag;
188549
188589
  const attrs = this.renderAttributes(element);
188550
- const children = this.renderChildren(element.children);
188590
+ const dangerousHtml = this.renderDangerousInnerHtml(element);
188591
+ const children = dangerousHtml !== null ? dangerousHtml : this.renderChildren(element.children);
188551
188592
  let hydrationAttrs = "";
188552
188593
  if (element.needsScope) {
188553
188594
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
@@ -188582,6 +188623,22 @@ class ErbAdapter extends BaseAdapter {
188582
188623
  }
188583
188624
  return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`;
188584
188625
  }
188626
+ renderDangerousInnerHtml(element) {
188627
+ const resolution = resolveDangerousInnerHtml(element);
188628
+ if (!resolution)
188629
+ return null;
188630
+ if (resolution.kind === "dynamic") {
188631
+ this.errors.push(dangerousInnerHtmlDiagnostic(resolution.expr, resolution.loc));
188632
+ return "";
188633
+ }
188634
+ const violation = dangerousInnerHtmlMetacharViolation(resolution.html, this.name);
188635
+ if (violation) {
188636
+ const attr = element.attrs.find(isDangerousInnerHtmlAttr);
188637
+ this.errors.push(dangerousInnerHtmlDiagnostic(`{ __html: ${JSON.stringify(resolution.html)} }`, attr.loc, violation));
188638
+ return "";
188639
+ }
188640
+ return resolution.html;
188641
+ }
188585
188642
  renderExpression(expr) {
188586
188643
  if (expr.clientOnly) {
188587
188644
  if (expr.slotId) {
@@ -188589,7 +188646,7 @@ class ErbAdapter extends BaseAdapter {
188589
188646
  }
188590
188647
  return "";
188591
188648
  }
188592
- const rubyExpr = this.convertExpressionToRuby(expr.expr);
188649
+ const rubyExpr = this.convertExpressionToRuby(expr.expr, expr.parsed);
188593
188650
  const wrapped = this.isChildrenValueExpr(expr) ? rubyExpr : `bf.h(${rubyExpr})`;
188594
188651
  if (expr.slotId) {
188595
188652
  return `<%= bf.text_start("${expr.slotId}") %><%= ${wrapped} %><%= bf.text_end %>`;
@@ -188698,7 +188755,11 @@ ${whenTrue}
188698
188755
  }
188699
188756
  });
188700
188757
  }
188701
- if (loop.arrayParsed?.kind === "identifier") {
188758
+ const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
188759
+ isNameShadowed: (name) => this.loopBoundNames.has(name)
188760
+ });
188761
+ const staticArray = staticItems !== null ? staticValueToRuby(staticItems) : null;
188762
+ if (staticArray === null && loop.arrayParsed?.kind === "identifier") {
188702
188763
  const arrayName = loop.arrayParsed.name;
188703
188764
  const isUnresolvableLocalConst = !this.loopBoundNames.has(arrayName) && this.resolveModuleStringConst(arrayName) === null && this.resolveLiteralConst(arrayName) === null && this.localConstants.some((c) => c.name === arrayName && !c.isModule);
188704
188765
  if (isUnresolvableLocalConst) {
@@ -188708,7 +188769,7 @@ ${whenTrue}
188708
188769
  3. Precompute the value server-side and pass it in as a prop.`);
188709
188770
  }
188710
188771
  }
188711
- const rawArray = this.convertExpressionToRuby(loop.array);
188772
+ const rawArray = staticArray ?? this.convertExpressionToRuby(loop.array);
188712
188773
  let sortedHoist = null;
188713
188774
  let array = rawArray;
188714
188775
  if (loop.sortComparator) {
@@ -188788,7 +188849,9 @@ ${renderedChildren}` : renderedChildren;
188788
188849
  if (loop.filterPredicate) {
188789
188850
  let filterCond;
188790
188851
  if (loop.filterPredicate.predicate) {
188791
- filterCond = this.renderRubyFilterExpr(loop.filterPredicate.predicate, param);
188852
+ const filterOwnParam = loop.filterPredicate.param;
188853
+ const matchParam = filterOwnParam && !filterOwnParam.startsWith("[") && !filterOwnParam.startsWith("{") ? filterOwnParam : param;
188854
+ filterCond = this.renderRubyFilterExpr(loop.filterPredicate.predicate, matchParam, undefined, rubyLocal(param));
188792
188855
  } else {
188793
188856
  filterCond = "true";
188794
188857
  }
@@ -189009,6 +189072,8 @@ ${children}`;
189009
189072
  for (const attr of element.attrs) {
189010
189073
  if (attr.clientOnly)
189011
189074
  continue;
189075
+ if (isDangerousInnerHtmlAttr(attr))
189076
+ continue;
189012
189077
  let attrName;
189013
189078
  if (attr.name === "className")
189014
189079
  attrName = "class";
@@ -189032,8 +189097,8 @@ ${children}`;
189032
189097
  renderCondMarker(condId) {
189033
189098
  return `${BF_COND}="${condId}"`;
189034
189099
  }
189035
- renderRubyFilterExpr(expr, param, localVarMap = new Map) {
189036
- return emitParsedExpr2(expr, new ErbFilterEmitter(param, localVarMap, (n) => this.isLoopBoundName(n), (n) => this._isStringValueName(n), (message, reason) => this._recordExprBF101(message, reason)));
189100
+ renderRubyFilterExpr(expr, param, localVarMap = new Map, renderParamAs) {
189101
+ return emitParsedExpr2(expr, new ErbFilterEmitter(param, localVarMap, (n) => this.isLoopBoundName(n), (n) => this._isStringValueName(n), (message, reason) => this._recordExprBF101(message, reason), renderParamAs));
189037
189102
  }
189038
189103
  convertTemplateLiteralPartsToRuby(literalParts) {
189039
189104
  const parts = [];
package/dist/index.js CHANGED
@@ -187308,7 +187308,12 @@ import {
187308
187308
  prepareLoweringMatchers,
187309
187309
  queryHrefArgs,
187310
187310
  isValidHelperId,
187311
- sortComparatorFromArrow as sortComparatorFromArrow2
187311
+ sortComparatorFromArrow as sortComparatorFromArrow2,
187312
+ isDangerousInnerHtmlAttr,
187313
+ resolveDangerousInnerHtml,
187314
+ dangerousInnerHtmlMetacharViolation,
187315
+ dangerousInnerHtmlDiagnostic,
187316
+ resolveStaticLoopSource
187312
187317
  } from "@barefootjs/jsx";
187313
187318
 
187314
187319
  // src/adapter/boolean-result.ts
@@ -187693,6 +187698,39 @@ function renderFlatMethod(recv, depth, emit) {
187693
187698
  return `bf.flat(${recv}, ${d})`;
187694
187699
  }
187695
187700
 
187701
+ // src/adapter/lib/static-value.ts
187702
+ function staticValueToRuby(value) {
187703
+ if (value === null || value === undefined)
187704
+ return "nil";
187705
+ if (typeof value === "boolean")
187706
+ return value ? "true" : "false";
187707
+ if (typeof value === "number")
187708
+ return String(value);
187709
+ if (typeof value === "string")
187710
+ return rubyStringLiteral(value);
187711
+ if (Array.isArray(value)) {
187712
+ const items = [];
187713
+ for (const el of value) {
187714
+ const serialized = staticValueToRuby(el);
187715
+ if (serialized === null)
187716
+ return null;
187717
+ items.push(serialized);
187718
+ }
187719
+ return `[${items.join(", ")}]`;
187720
+ }
187721
+ if (typeof value === "object") {
187722
+ const entries = [];
187723
+ for (const [key, val] of Object.entries(value)) {
187724
+ const serialized = staticValueToRuby(val);
187725
+ if (serialized === null)
187726
+ return null;
187727
+ entries.push(`${rubySymbolKey(key)} ${serialized}`);
187728
+ }
187729
+ return `{ ${entries.join(", ")} }`;
187730
+ }
187731
+ return null;
187732
+ }
187733
+
187696
187734
  // src/adapter/expr/emitters.ts
187697
187735
  import {
187698
187736
  emitParsedExpr,
@@ -187738,16 +187776,18 @@ class ErbFilterEmitter {
187738
187776
  isLoopBoundOuter;
187739
187777
  isStringName;
187740
187778
  onUnsupported;
187741
- constructor(param, localVarMap, isLoopBoundOuter = () => false, isStringName = unusedIsStringName, onUnsupported) {
187779
+ renderParamAs;
187780
+ constructor(param, localVarMap, isLoopBoundOuter = () => false, isStringName = unusedIsStringName, onUnsupported, renderParamAs = rubyLocal(param)) {
187742
187781
  this.param = param;
187743
187782
  this.localVarMap = localVarMap;
187744
187783
  this.isLoopBoundOuter = isLoopBoundOuter;
187745
187784
  this.isStringName = isStringName;
187746
187785
  this.onUnsupported = onUnsupported;
187786
+ this.renderParamAs = renderParamAs;
187747
187787
  }
187748
187788
  identifier(name) {
187749
187789
  if (name === this.param)
187750
- return rubyLocal(this.param);
187790
+ return this.renderParamAs;
187751
187791
  const signal = this.localVarMap.get(name);
187752
187792
  if (signal)
187753
187793
  return `v[${rubySymbolLiteral(signal)}]`;
@@ -188547,7 +188587,8 @@ class ErbAdapter extends BaseAdapter {
188547
188587
  renderElement(element) {
188548
188588
  const tag = element.tag;
188549
188589
  const attrs = this.renderAttributes(element);
188550
- const children = this.renderChildren(element.children);
188590
+ const dangerousHtml = this.renderDangerousInnerHtml(element);
188591
+ const children = dangerousHtml !== null ? dangerousHtml : this.renderChildren(element.children);
188551
188592
  let hydrationAttrs = "";
188552
188593
  if (element.needsScope) {
188553
188594
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
@@ -188582,6 +188623,22 @@ class ErbAdapter extends BaseAdapter {
188582
188623
  }
188583
188624
  return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`;
188584
188625
  }
188626
+ renderDangerousInnerHtml(element) {
188627
+ const resolution = resolveDangerousInnerHtml(element);
188628
+ if (!resolution)
188629
+ return null;
188630
+ if (resolution.kind === "dynamic") {
188631
+ this.errors.push(dangerousInnerHtmlDiagnostic(resolution.expr, resolution.loc));
188632
+ return "";
188633
+ }
188634
+ const violation = dangerousInnerHtmlMetacharViolation(resolution.html, this.name);
188635
+ if (violation) {
188636
+ const attr = element.attrs.find(isDangerousInnerHtmlAttr);
188637
+ this.errors.push(dangerousInnerHtmlDiagnostic(`{ __html: ${JSON.stringify(resolution.html)} }`, attr.loc, violation));
188638
+ return "";
188639
+ }
188640
+ return resolution.html;
188641
+ }
188585
188642
  renderExpression(expr) {
188586
188643
  if (expr.clientOnly) {
188587
188644
  if (expr.slotId) {
@@ -188589,7 +188646,7 @@ class ErbAdapter extends BaseAdapter {
188589
188646
  }
188590
188647
  return "";
188591
188648
  }
188592
- const rubyExpr = this.convertExpressionToRuby(expr.expr);
188649
+ const rubyExpr = this.convertExpressionToRuby(expr.expr, expr.parsed);
188593
188650
  const wrapped = this.isChildrenValueExpr(expr) ? rubyExpr : `bf.h(${rubyExpr})`;
188594
188651
  if (expr.slotId) {
188595
188652
  return `<%= bf.text_start("${expr.slotId}") %><%= ${wrapped} %><%= bf.text_end %>`;
@@ -188698,7 +188755,11 @@ ${whenTrue}
188698
188755
  }
188699
188756
  });
188700
188757
  }
188701
- if (loop.arrayParsed?.kind === "identifier") {
188758
+ const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
188759
+ isNameShadowed: (name) => this.loopBoundNames.has(name)
188760
+ });
188761
+ const staticArray = staticItems !== null ? staticValueToRuby(staticItems) : null;
188762
+ if (staticArray === null && loop.arrayParsed?.kind === "identifier") {
188702
188763
  const arrayName = loop.arrayParsed.name;
188703
188764
  const isUnresolvableLocalConst = !this.loopBoundNames.has(arrayName) && this.resolveModuleStringConst(arrayName) === null && this.resolveLiteralConst(arrayName) === null && this.localConstants.some((c) => c.name === arrayName && !c.isModule);
188704
188765
  if (isUnresolvableLocalConst) {
@@ -188708,7 +188769,7 @@ ${whenTrue}
188708
188769
  3. Precompute the value server-side and pass it in as a prop.`);
188709
188770
  }
188710
188771
  }
188711
- const rawArray = this.convertExpressionToRuby(loop.array);
188772
+ const rawArray = staticArray ?? this.convertExpressionToRuby(loop.array);
188712
188773
  let sortedHoist = null;
188713
188774
  let array = rawArray;
188714
188775
  if (loop.sortComparator) {
@@ -188788,7 +188849,9 @@ ${renderedChildren}` : renderedChildren;
188788
188849
  if (loop.filterPredicate) {
188789
188850
  let filterCond;
188790
188851
  if (loop.filterPredicate.predicate) {
188791
- filterCond = this.renderRubyFilterExpr(loop.filterPredicate.predicate, param);
188852
+ const filterOwnParam = loop.filterPredicate.param;
188853
+ const matchParam = filterOwnParam && !filterOwnParam.startsWith("[") && !filterOwnParam.startsWith("{") ? filterOwnParam : param;
188854
+ filterCond = this.renderRubyFilterExpr(loop.filterPredicate.predicate, matchParam, undefined, rubyLocal(param));
188792
188855
  } else {
188793
188856
  filterCond = "true";
188794
188857
  }
@@ -189009,6 +189072,8 @@ ${children}`;
189009
189072
  for (const attr of element.attrs) {
189010
189073
  if (attr.clientOnly)
189011
189074
  continue;
189075
+ if (isDangerousInnerHtmlAttr(attr))
189076
+ continue;
189012
189077
  let attrName;
189013
189078
  if (attr.name === "className")
189014
189079
  attrName = "class";
@@ -189032,8 +189097,8 @@ ${children}`;
189032
189097
  renderCondMarker(condId) {
189033
189098
  return `${BF_COND}="${condId}"`;
189034
189099
  }
189035
- renderRubyFilterExpr(expr, param, localVarMap = new Map) {
189036
- return emitParsedExpr2(expr, new ErbFilterEmitter(param, localVarMap, (n) => this.isLoopBoundName(n), (n) => this._isStringValueName(n), (message, reason) => this._recordExprBF101(message, reason)));
189100
+ renderRubyFilterExpr(expr, param, localVarMap = new Map, renderParamAs) {
189101
+ return emitParsedExpr2(expr, new ErbFilterEmitter(param, localVarMap, (n) => this.isLoopBoundName(n), (n) => this._isStringValueName(n), (message, reason) => this._recordExprBF101(message, reason), renderParamAs));
189037
189102
  }
189038
189103
  convertTemplateLiteralPartsToRuby(literalParts) {
189039
189104
  const parts = [];
@@ -189194,21 +189259,16 @@ Options:
189194
189259
  var erbAdapter = new ErbAdapter;
189195
189260
  // src/conformance-pins.ts
189196
189261
  var conformancePins = {
189197
- "static-array-children": [{ code: "BF103", severity: "error" }],
189198
- "todo-app": [{ code: "BF103", severity: "error" }],
189199
- "todo-app-ssr": [{ code: "BF103", severity: "error" }],
189200
189262
  "static-array-from-props": [
189201
189263
  { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2087" }
189202
189264
  ],
189203
189265
  "static-array-from-props-with-component": [
189204
- { code: "BF103", severity: "error" },
189205
189266
  { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2087" }
189206
189267
  ],
189207
189268
  "filter-nested-find-predicate": [
189208
189269
  { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2038" }
189209
189270
  ],
189210
- "array-map-function-reference": [{ code: "BF101", severity: "error" }],
189211
- "dangerous-inner-html": [{ code: "BF101", severity: "error" }]
189271
+ "dangerous-inner-html-dynamic": [{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2215" }]
189212
189272
  };
189213
189273
  // src/render-divergences.ts
189214
189274
  var renderDivergences = {};
@@ -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,iBAAsB,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,iBAK/B,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/erb",
3
- "version": "0.18.5",
3
+ "version": "0.18.7",
4
4
  "description": "ERB (Embedded Ruby) adapter for BarefootJS — compiles IR to .erb templates and ships the Ruby rendering backend; runs under any Rack app (Sinatra, Rails)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -54,14 +54,14 @@
54
54
  "directory": "packages/adapter-erb"
55
55
  },
56
56
  "dependencies": {
57
- "@barefootjs/shared": "0.18.5"
57
+ "@barefootjs/shared": "0.18.7"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "@barefootjs/jsx": ">=0.2.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@barefootjs/adapter-tests": "0.1.0",
64
- "@barefootjs/jsx": "0.18.5",
64
+ "@barefootjs/jsx": "0.18.7",
65
65
  "typescript": "^5.0.0"
66
66
  }
67
67
  }
@@ -305,6 +305,127 @@ export { Slot }
305
305
  })
306
306
  })
307
307
 
308
+ describe('ErbAdapter - filter().map() predicate matches the FILTER param, not the loop param (#2245)', () => {
309
+ // `todos.filter(t => t.done).map(todo => ...)`: the predicate's own `t`
310
+ // used to be matched against the LOOP's (map's) param `todo` inside
311
+ // `ErbFilterEmitter.identifier()`, so every reference to `t` inside the
312
+ // predicate fell to the `v[:t]` vars-Hash fallback instead of resolving
313
+ // to the loop-bound `todo` local — `v[:t]` is never seeded, and real
314
+ // Ruby raises `NoMethodError: undefined method '[]' for nil` on
315
+ // `v[:t][:done]` at render time (masked in the shipped `todo-app-ssr`
316
+ // corpus by its `'all'`-default filter short-circuiting the buggy
317
+ // branch away — see `filter-wrapper-props-reachable`'s docstring).
318
+ const DIFFERENTLY_NAMED_SOURCE = `
319
+ 'use client'
320
+ import { createSignal } from '@barefootjs/client'
321
+
322
+ type Todo = { id: number; text: string; done: boolean }
323
+
324
+ export function TodoList(props: { initialTodos?: Todo[] }) {
325
+ const [todos] = createSignal<Todo[]>(props.initialTodos ?? [])
326
+ return (
327
+ <ul>
328
+ {todos().filter(t => !t.done).map(todo => (
329
+ <li key={todo.id}>{todo.text}</li>
330
+ ))}
331
+ </ul>
332
+ )
333
+ }
334
+ `
335
+
336
+ function compileToIR(source: string): ComponentIR {
337
+ const result = compileJSX(source.trimStart(), 'test.tsx', {
338
+ adapter: new ErbAdapter(),
339
+ outputIR: true,
340
+ })
341
+ const irFile = result.files.find(f => f.type === 'ir')
342
+ if (!irFile) throw new Error('No IR output')
343
+ return JSON.parse(irFile.content) as ComponentIR
344
+ }
345
+
346
+ test('predicate reference lowers through the loop-bound local, never the filter-param vars-Hash fallback', () => {
347
+ const ir = compileToIR(DIFFERENTLY_NAMED_SOURCE)
348
+ const { template } = new ErbAdapter().generate(ir)
349
+ // The loop-gating `<if>` must reference the loop's actual bound Ruby
350
+ // local (`todo[:done]`, from the MAP callback's param)...
351
+ expect(template).toContain('todo[:done]')
352
+ // ...never the filter callback's own param name resolved as an
353
+ // (unseeded) vars-Hash key — the literal pre-fix bug.
354
+ expect(template).not.toContain('v[:t]')
355
+ })
356
+
357
+ test('same-named filter/map params render byte-identically to the pre-#2245 form (regression pin)', () => {
358
+ const sameNamedSource = DIFFERENTLY_NAMED_SOURCE.replace(
359
+ 'filter(t => !t.done)',
360
+ 'filter(todo => !todo.done)',
361
+ )
362
+ const ir = compileToIR(sameNamedSource)
363
+ const { template } = new ErbAdapter().generate(ir)
364
+ expect(template).toContain('<%- if bf.truthy?(!bf.truthy?(todo[:done])) -%>')
365
+ })
366
+
367
+ test('real Ruby render: reachable predicate on differently-named params renders correctly (pre-fix NoMethodError pin)', async () => {
368
+ // `filter` defaults to `'active'` (never `'all'`) so the predicate
369
+ // branch referencing `t.done` is actually REACHABLE at render time —
370
+ // an `'all'`-style short-circuiting default is exactly what hid this
371
+ // bug in the shipped `todo-app-ssr` fixture. Block-body predicate
372
+ // (folded to one expression by #2040's `foldBlockToExpr` +
373
+ // `predicateTernaryToLogical`) matches the real `TodoAppSSR.tsx` shape.
374
+ const source = `
375
+ 'use client'
376
+ import { createSignal } from '@barefootjs/client'
377
+
378
+ type Todo = { id: number; text: string; done: boolean }
379
+ type Filter = 'all' | 'active'
380
+
381
+ export function TodoList(props: { initialTodos?: Todo[] }) {
382
+ const [todos] = createSignal<Todo[]>(props.initialTodos ?? [])
383
+ const [filter] = createSignal<Filter>('active')
384
+ return (
385
+ <ul>
386
+ {todos().filter(t => {
387
+ const f = filter()
388
+ if (f === 'active') return !t.done
389
+ return true
390
+ }).map(todo => (
391
+ <li key={todo.id}>{todo.text}</li>
392
+ ))}
393
+ </ul>
394
+ )
395
+ }
396
+ `
397
+ let html: string
398
+ try {
399
+ html = await renderErbComponent({
400
+ source: source.trimStart(),
401
+ adapter: new ErbAdapter(),
402
+ props: {
403
+ initialTodos: [
404
+ { id: 1, text: 'Eat breakfast', done: true },
405
+ { id: 2, text: 'Write tests', done: false },
406
+ ],
407
+ },
408
+ })
409
+ } catch (err) {
410
+ if (err instanceof ErbNotAvailableError) {
411
+ console.log('Skipping #2245 filter-param e2e: ruby/erb not available')
412
+ return
413
+ }
414
+ throw err
415
+ }
416
+ // Pre-fix: real Ruby raises `NoMethodError: undefined method '[]' for
417
+ // nil` evaluating `v[:t][:done]` — `renderErbComponent` surfaces that
418
+ // as a thrown "ruby render failed" error, so a `NoMethodError` string
419
+ // anywhere in a caught error would fail this test outright rather than
420
+ // reaching these assertions. Post-fix: only the not-done todo (id 2)
421
+ // survives the 'active' filter.
422
+ expect(html).not.toContain('Eat breakfast')
423
+ expect(html).toContain('Write tests')
424
+ expect(html).toContain('data-key="2"')
425
+ expect(html).not.toContain('data-key="1"')
426
+ })
427
+ })
428
+
308
429
  describe('ErbAdapter - named-slot capture identifier safety (#2168 jsx-element-prop)', () => {
309
430
  // A JSX-valued prop under a hyphenated name (`data-slot`, a valid JSX
310
431
  // attribute name) must not leak into the buffer-slice capture's local
@@ -88,6 +88,11 @@ import {
88
88
  queryHrefArgs,
89
89
  isValidHelperId,
90
90
  sortComparatorFromArrow,
91
+ isDangerousInnerHtmlAttr,
92
+ resolveDangerousInnerHtml,
93
+ dangerousInnerHtmlMetacharViolation,
94
+ dangerousInnerHtmlDiagnostic,
95
+ resolveStaticLoopSource,
91
96
  } from '@barefootjs/jsx'
92
97
  import { isAriaBooleanAttr, isBooleanResultExpr, isExplicitStringCall } from './boolean-result.ts'
93
98
  import type { ParsedExpr, LoweringMatcher, LoopBindingPathSegment } from '@barefootjs/jsx'
@@ -107,6 +112,7 @@ import {
107
112
  collectRootScopeNodes,
108
113
  } from './lib/ir-scope.ts'
109
114
  import { renderSortMethod, renderSortEval } from './expr/array-method.ts'
115
+ import { staticValueToRuby } from './lib/static-value.ts'
110
116
  import { ErbFilterEmitter, ErbTopLevelEmitter } from './expr/emitters.ts'
111
117
  import type { ErbEmitContext, ErbSpreadContext, ErbMemoContext } from './emit-context.ts'
112
118
  import {
@@ -636,7 +642,8 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
636
642
  renderElement(element: IRElement): string {
637
643
  const tag = element.tag
638
644
  const attrs = this.renderAttributes(element)
639
- const children = this.renderChildren(element.children)
645
+ const dangerousHtml = this.renderDangerousInnerHtml(element)
646
+ const children = dangerousHtml !== null ? dangerousHtml : this.renderChildren(element.children)
640
647
 
641
648
  let hydrationAttrs = ''
642
649
  if (element.needsScope) {
@@ -673,6 +680,28 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
673
680
  return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`
674
681
  }
675
682
 
683
+ /**
684
+ * `dangerouslySetInnerHTML={{ __html: '...' }}` (#2207) — see the Blade
685
+ * adapter's identical helper for the full rationale. `null` means the
686
+ * attribute is absent (caller falls through to normal `renderChildren`);
687
+ * a non-`null` string (possibly `''`) replaces the children outright.
688
+ */
689
+ private renderDangerousInnerHtml(element: IRElement): string | null {
690
+ const resolution = resolveDangerousInnerHtml(element)
691
+ if (!resolution) return null
692
+ if (resolution.kind === 'dynamic') {
693
+ this.errors.push(dangerousInnerHtmlDiagnostic(resolution.expr, resolution.loc))
694
+ return ''
695
+ }
696
+ const violation = dangerousInnerHtmlMetacharViolation(resolution.html, this.name)
697
+ if (violation) {
698
+ const attr = element.attrs.find(isDangerousInnerHtmlAttr)!
699
+ this.errors.push(dangerousInnerHtmlDiagnostic(`{ __html: ${JSON.stringify(resolution.html)} }`, attr.loc, violation))
700
+ return ''
701
+ }
702
+ return resolution.html
703
+ }
704
+
676
705
  // ===========================================================================
677
706
  // Expression Rendering
678
707
  // ===========================================================================
@@ -685,7 +714,12 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
685
714
  return ''
686
715
  }
687
716
 
688
- const rubyExpr = this.convertExpressionToRuby(expr.expr)
717
+ // Thread the IR-carried `.parsed` tree through (mirrors go-template's
718
+ // `convertExpressionToGo(expr.expr, classify, expr.parsed)`) so a
719
+ // resolved bare-identifier `.map`/`.filter`/… callback
720
+ // (`resolveCallbackMethodFunctionReferences`, #2206) isn't lost to a
721
+ // fresh, unresolved re-parse of the raw string.
722
+ const rubyExpr = this.convertExpressionToRuby(expr.expr, expr.parsed)
689
723
 
690
724
  // A bare read of the `children` prop (`{children}` / `{props.children}`,
691
725
  // optionally `?? fallback`) is pre-rendered HTML — captured via the
@@ -869,7 +903,23 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
869
903
  // reproduces identically with a non-destructured param, so it is NOT a
870
904
  // destructure-lowering limitation. Surface BF101 honestly instead of
871
905
  // emitting a loop bound that silently crashes / renders empty.
872
- if (loop.arrayParsed?.kind === 'identifier') {
906
+ // #2208: a loop source that is a fully-static array literal — either
907
+ // inline (`[{ label: 'Alpha' }, ...].map(...)`) or a bare identifier
908
+ // bound to a FUNCTION-scope local const whose initializer has no
909
+ // prop/signal/function-call dependency — inlines as a native Ruby
910
+ // array/hash literal below, the same way a module-scope const's value
911
+ // is already seeded. Previously the INLINE shape wasn't gated here at
912
+ // all (this check only ever inspected an `identifier` array source) —
913
+ // it still ended up refusing via `convertExpressionToRuby`'s generic
914
+ // `unsupported` object-literal path (BF101, "Expression not
915
+ // supported"), which is what this loop-specific check now also does
916
+ // deliberately, up front, for both shapes.
917
+ const staticItems = resolveStaticLoopSource(loop.arrayParsed, this.localConstants, {
918
+ isNameShadowed: name => this.loopBoundNames.has(name),
919
+ })
920
+ const staticArray = staticItems !== null ? staticValueToRuby(staticItems) : null
921
+
922
+ if (staticArray === null && loop.arrayParsed?.kind === 'identifier') {
873
923
  const arrayName = loop.arrayParsed.name
874
924
  const isUnresolvableLocalConst =
875
925
  !this.loopBoundNames.has(arrayName) &&
@@ -884,7 +934,7 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
884
934
  }
885
935
  }
886
936
 
887
- const rawArray = this.convertExpressionToRuby(loop.array)
937
+ const rawArray = staticArray ?? this.convertExpressionToRuby(loop.array)
888
938
  // Apply sort if present: hoist the (possibly sorted) array into a Ruby
889
939
  // local BEFORE the index loop, so both the loop bound and the per-item
890
940
  // lookup reference the same materialised array — otherwise a sort
@@ -1040,14 +1090,31 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
1040
1090
  if (loop.filterPredicate) {
1041
1091
  let filterCond: string
1042
1092
  if (loop.filterPredicate.predicate) {
1043
- // The loop's own (possibly destructure-adjusted) param name IS the
1044
- // filter predicate's parameter for rendering purposes — pass it
1045
- // straight through as the filter emitter's bound param. Ruby's
1046
- // named-block-param model makes the Mojo original's regex
1047
- // `$filterParam $loopParam` rename unnecessary here: the emitter
1048
- // just renders every reference to the predicate's own arrow
1049
- // parameter AS `param` from the start.
1050
- filterCond = this.renderRubyFilterExpr(loop.filterPredicate.predicate, param)
1093
+ // The filter predicate's identifiers were parsed against the
1094
+ // FILTER callback's own param (`loop.filterPredicate.param`), which
1095
+ // can differ from the loop's rendered Ruby local (`param`, the MAP
1096
+ // callback's param) whenever the two are named differently —
1097
+ // `todos.filter(t => t.done).map(todo => ...)` (#2245). Ruby's
1098
+ // named-block-param model means there's no Mojo-style regex
1099
+ // `$filterParam $loopParam` TEXT rewrite to do, but the
1100
+ // DISTINCTION it encoded still matters: match identifiers against
1101
+ // the filter's own param, while EMITTING the loop's actual bound
1102
+ // local — `ErbFilterEmitter`'s `renderParamAs` carries that split.
1103
+ // `filterPredicate.param` is only ever a bare identifier in
1104
+ // practice (`extractFilterPredicate` in jsx-to-ir.ts refuses a
1105
+ // destructured filter param outright, leaving `filterPredicate`
1106
+ // unset entirely rather than populating it with pattern text — see
1107
+ // its docstring), but guard defensively instead of relying on that
1108
+ // invariant: a pattern-text param (leading `[`/`{` — the same
1109
+ // prefix check `destructureLoopParam`/#2238 use, NOT an identifier
1110
+ // regex, which would misclassify a Unicode param name) falls back
1111
+ // to `param`, matching pre-#2245 behavior byte-for-byte.
1112
+ const filterOwnParam = loop.filterPredicate.param
1113
+ const matchParam =
1114
+ filterOwnParam && !filterOwnParam.startsWith('[') && !filterOwnParam.startsWith('{')
1115
+ ? filterOwnParam
1116
+ : param
1117
+ filterCond = this.renderRubyFilterExpr(loop.filterPredicate.predicate, matchParam, undefined, rubyLocal(param))
1051
1118
  } else {
1052
1119
  filterCond = 'true'
1053
1120
  }
@@ -1509,6 +1576,12 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
1509
1576
  // the unsupported-expression lowering is never reached for a
1510
1577
  // deferred predicate (no BF101 / BF102).
1511
1578
  if (attr.clientOnly) continue
1579
+ // `dangerouslySetInnerHTML` never renders as an HTML attribute — it's
1580
+ // handled by `renderDangerousInnerHtml` instead, which replaces the
1581
+ // element's children. Skip it here so its `{ __html: ... }` object
1582
+ // literal never reaches the generic object-literal BF101 refusal
1583
+ // (which would double-report alongside the purpose-built one).
1584
+ if (isDangerousInnerHtmlAttr(attr)) continue
1512
1585
  // Rewrite JSX special-prop names to their HTML-attribute
1513
1586
  // counterparts. `className` → `class`; `key` → `data-key` matches
1514
1587
  // the canonical Hono attribute name the client runtime reconciles
@@ -1563,6 +1636,14 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
1563
1636
  expr: ParsedExpr,
1564
1637
  param: string,
1565
1638
  localVarMap: Map<string, string> = new Map(),
1639
+ // See `ErbFilterEmitter`'s constructor docstring (#2245): the Ruby
1640
+ // local to EMIT for a reference to `param`, when it differs from
1641
+ // `param` itself (the loop-gating call site passes the filter
1642
+ // callback's own param as `param` — the name to MATCH — and this as
1643
+ // the loop's actual bound local). Every other caller omits it, so
1644
+ // `ErbFilterEmitter`'s own default (`rubyLocal(param)`) applies and
1645
+ // match/render stay the same value, unchanged from before #2245.
1646
+ renderParamAs?: string,
1566
1647
  ): string {
1567
1648
  return emitParsedExpr(
1568
1649
  expr,
@@ -1572,6 +1653,7 @@ export class ErbAdapter extends BaseAdapter implements IRNodeEmitter<ErbRenderCt
1572
1653
  n => this.isLoopBoundName(n),
1573
1654
  n => this._isStringValueName(n),
1574
1655
  (message, reason) => this._recordExprBF101(message, reason),
1656
+ renderParamAs,
1575
1657
  ),
1576
1658
  )
1577
1659
  }
@@ -106,10 +106,23 @@ export class ErbFilterEmitter implements ParsedExprEmitter {
106
106
  // construction stays possible without an adapter; a missing hook keeps
107
107
  // the old silent-degrade emit.
108
108
  private readonly onUnsupported?: (message: string, reason?: string) => void,
109
+ // The Ruby local to EMIT for a reference matching `this.param` — as
110
+ // opposed to `this.param` itself, which is only the name to MATCH.
111
+ // These two are the SAME value everywhere in this file except the
112
+ // `filter().map()` loop-gating `<if>` (erb-adapter.ts's `renderLoop`,
113
+ // #2245): `todos.filter(t => t.done).map(todo => ...)` parses the
114
+ // predicate against the filter callback's OWN param (`t`), but the
115
+ // Ruby local actually bound by the loop is the MAP callback's param
116
+ // (`todo`) — Ruby has no per-callback block scope there (unlike the
117
+ // real nested `.select { |t| ... }` block `callbackMethod` below
118
+ // builds, where match and render are naturally the same param).
119
+ // Defaults to `rubyLocal(this.param)`, i.e. every other construction
120
+ // site is unaffected.
121
+ private readonly renderParamAs: string = rubyLocal(param),
109
122
  ) {}
110
123
 
111
124
  identifier(name: string): string {
112
- if (name === this.param) return rubyLocal(this.param)
125
+ if (name === this.param) return this.renderParamAs
113
126
  const signal = this.localVarMap.get(name)
114
127
  if (signal) return `v[${rubySymbolLiteral(signal)}]`
115
128
  if (this.isLoopBoundOuter(name)) return rubyLocal(name)
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Serialize a compile-time-evaluated JS value (`@barefootjs/jsx`'s
3
+ * `evaluateStaticLiteral`/`resolveStaticLoopSource`, #2208) into a native
4
+ * Ruby literal. Used to inline a fully-static loop source (an inline array
5
+ * literal, or a function-scope local const with a static initializer)
6
+ * directly in the loop-bound expression, rather than requiring a bound
7
+ * template variable.
8
+ *
9
+ * Hash keys render as symbols (`label: 'Alpha'`) to match `item[:label]`,
10
+ * this adapter's existing member-access convention (`rubyLocal`'s
11
+ * companion, `rubySymbolKey`/`rubySymbolLiteral`).
12
+ *
13
+ * Returns `null` for a value this adapter can't represent as a literal —
14
+ * the caller falls back to its existing BF101 refusal instead of guessing.
15
+ */
16
+
17
+ import { rubyStringLiteral, rubySymbolKey } from './ruby-naming.ts'
18
+
19
+ export function staticValueToRuby(value: unknown): string | null {
20
+ if (value === null || value === undefined) return 'nil'
21
+ if (typeof value === 'boolean') return value ? 'true' : 'false'
22
+ if (typeof value === 'number') return String(value)
23
+ if (typeof value === 'string') return rubyStringLiteral(value)
24
+ if (Array.isArray(value)) {
25
+ const items: string[] = []
26
+ for (const el of value) {
27
+ const serialized = staticValueToRuby(el)
28
+ if (serialized === null) return null
29
+ items.push(serialized)
30
+ }
31
+ return `[${items.join(', ')}]`
32
+ }
33
+ if (typeof value === 'object') {
34
+ const entries: string[] = []
35
+ for (const [key, val] of Object.entries(value as Record<string, unknown>)) {
36
+ const serialized = staticValueToRuby(val)
37
+ if (serialized === null) return null
38
+ entries.push(`${rubySymbolKey(key)} ${serialized}`)
39
+ }
40
+ return `{ ${entries.join(', ')} }`
41
+ }
42
+ return null
43
+ }
@@ -12,16 +12,17 @@
12
12
  import type { ConformancePins } from '@barefootjs/jsx'
13
13
 
14
14
  export const conformancePins: ConformancePins = {
15
- // Sibling-imported child component in a loop body: emits a
16
- // cross-template call needing separate registration. BF103 makes
17
- // the requirement loud (same as mojo).
18
- 'static-array-children': [{ code: 'BF103', severity: 'error' }],
19
- // TodoApp / TodoAppSSR import `TodoItem` from a sibling file and
20
- // call it inside a keyed `.map`. Same BF103 surface as the
21
- // synthetic `static-array-children` above pinned at adapter
22
- // level so the shared-component corpus stays adapter-neutral.
23
- 'todo-app': [{ code: 'BF103', severity: 'error' }],
24
- 'todo-app-ssr': [{ code: 'BF103', severity: 'error' }],
15
+ // `todo-app` / `todo-app-ssr` no longer pinned (#2205) the conformance
16
+ // harness now passes `siblingTemplatesRegistered: true` for fixtures with
17
+ // sibling `components`, matching `bf build`'s real semantics, so the
18
+ // BF103 loop-body cross-template check no longer fires spuriously. (Both
19
+ // fixtures are still skipped on this adapter via `render-divergences.ts`
20
+ // #2209 for an unrelated signal-seeding gap.)
21
+ // `static-array-children` no longer pinned (#2208) — `items`'s
22
+ // array-literal initializer is now recognized as fully-static
23
+ // (`resolveStaticLoopSource`) and inlined as a native Ruby array/hash
24
+ // literal in the loop-bound expression, the same way a module-scope
25
+ // const's value is already seeded.
25
26
  // `static-array-from-props` / `static-array-from-props-with-component`:
26
27
  // the `.map(([emoji, users]) => …)` / `.map(([id, t]) => …)` callback is
27
28
  // a plain array-index destructure (the `.filter(...)` runs on a
@@ -45,8 +46,9 @@ export const conformancePins: ConformancePins = {
45
46
  'static-array-from-props': [
46
47
  { code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2087' },
47
48
  ],
49
+ // BF103 (imported child in the loop body) no longer fires now that the
50
+ // conformance harness passes `siblingTemplatesRegistered: true` (#2205).
48
51
  'static-array-from-props-with-component': [
49
- { code: 'BF103', severity: 'error' },
50
52
  { code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2087' },
51
53
  ],
52
54
  // #2087 Phase B: `isLowerableLoopDestructure` now admits every fixed-
@@ -91,16 +93,14 @@ export const conformancePins: ConformancePins = {
91
93
  // `/* @client */` keyed-map slot-id elision contract only (same as
92
94
  // `todo-app`), not a render or BF101 gap.
93
95
  //
94
- // #2073 follow-up: a function-reference `.map(format)` callback has no
95
- // arrow body to serialize not a CALLBACK_METHODS shape — so the
96
- // UNSUPPORTED_METHODS gate (shared `@barefootjs/jsx` code) refuses it
97
- // with BF101 rather than emitting a broken template. Same pin as
98
- // mojo/xslate.
99
- 'array-map-function-reference': [{ code: 'BF101', severity: 'error' }],
100
- // Edge-case sweep (Priority 12): `dangerouslySetInnerHTML` requires a
101
- // deliberate raw-HTML (unescaped) output affordance in the target
102
- // template language. No lowering exists yet, so the compiler refuses
103
- // the shape loudly instead of emitting entity-escaped markup that
104
- // silently renders tags as text.
105
- 'dangerous-inner-html': [{ code: 'BF101', severity: 'error' }],
96
+ // `array-map-function-reference` no longer pinned — a bare-identifier
97
+ // `.map(format)` callback now resolves one hop to its declaration
98
+ // (`resolveCallbackMethodFunctionReferences`, #2206), the same mechanism
99
+ // #2090 established for `.sort(fnref)`.
100
+ // `dangerous-inner-html` no longer pinned — a compile-time string-literal
101
+ // `dangerouslySetInnerHTML={{ __html: '...' }}` is spliced directly into
102
+ // the template as trusted raw text (`resolveDangerousInnerHtml`, #2207).
103
+ // A dynamic/signal-derived value still refuses with BF101 — see the
104
+ // `dangerous-inner-html-dynamic` fixture/pin below (tracked: #2215).
105
+ 'dangerous-inner-html-dynamic': [{ code: 'BF101', severity: 'error', issue: 'https://github.com/piconic-ai/barefootjs/issues/2215' }],
106
106
  }
@@ -14,4 +14,9 @@
14
14
 
15
15
  import type { RenderDivergences } from '@barefootjs/jsx'
16
16
 
17
- export const renderDivergences: RenderDivergences = {}
17
+ export const renderDivergences: RenderDivergences = {
18
+ // `todo-app` / `todo-app-ssr` no longer diverge (#2209) — the shared
19
+ // `evaluateSignalInit` (`@barefootjs/jsx`, sandboxed real-JS evaluation
20
+ // instead of a fixed regex-shape catalogue) now correctly seeds `todos`
21
+ // from `(props.initialTodos ?? []).map(t => ({ ...t, editing: false }))`.
22
+ }
@@ -36,7 +36,7 @@
36
36
  * regression test.
37
37
  */
38
38
 
39
- import { compileJSX, extractSsrDefaults, importsSearchParams } from '@barefootjs/jsx'
39
+ import { compileJSX, extractSsrDefaults, importsSearchParams, evaluateSignalInit, tryEvaluateSignalInit } from '@barefootjs/jsx'
40
40
  import type { ComponentIR, SsrDefault } from '@barefootjs/jsx'
41
41
  import { mkdir, rm } from 'node:fs/promises'
42
42
  import { resolve } from 'node:path'
@@ -151,8 +151,15 @@ export async function renderErbComponent(options: RenderOptions): Promise<string
151
151
  }
152
152
  }
153
153
 
154
- // Compile parent source.
155
- const result = compileJSX(source, 'component.tsx', { adapter, outputIR: true })
154
+ // Compile parent source. `siblingTemplatesRegistered: Boolean(components)`
155
+ // matches this harness's real behavior every sibling child template is registered
156
+ // alongside the parent before rendering, so a loop-body cross-template
157
+ // call resolves at render time (#2205).
158
+ const result = compileJSX(source, 'component.tsx', {
159
+ adapter,
160
+ outputIR: true,
161
+ siblingTemplatesRegistered: Boolean(components),
162
+ })
156
163
 
157
164
  const errors = result.errors.filter(e => e.severity === 'error')
158
165
  if (errors.length > 0) {
@@ -495,9 +502,9 @@ function buildRubyProps(
495
502
  for (const param of ir.metadata.propsParams) {
496
503
  if (props && param.name in props) continue
497
504
  if (param.defaultValue) {
498
- const value = jsDefaultLiteral(param.defaultValue)
499
- if (value !== undefined) {
500
- obj[param.name] = value
505
+ const result = tryEvaluateSignalInit(param.defaultValue.trim(), props)
506
+ if (result.ok) {
507
+ obj[param.name] = result.value
501
508
  continue
502
509
  }
503
510
  }
@@ -556,133 +563,3 @@ function buildRubyProps(
556
563
  return { obj, needsSearchParams }
557
564
  }
558
565
 
559
- /**
560
- * Best-effort literal evaluation of a prop-destructure default's source
561
- * text (`{ size = 'md' }` → `'md'`), including a `props.x ?? default`
562
- * nullish fallback (handled generically, though destructure defaults
563
- * rarely reference `props`) and delegating to `parseLiteral` for
564
- * everything else. Returns `undefined` for a non-literal (computed)
565
- * default, matching the Perl harnesses' "fall through to undef" behaviour.
566
- */
567
- function jsDefaultLiteral(expr: string): unknown {
568
- const t = expr.trim()
569
- const nullishMatch = t.match(/\?\?\s*(.+)$/)
570
- if (nullishMatch) return jsDefaultLiteral(nullishMatch[1])
571
- if (t.startsWith('props.')) return undefined
572
- const parsed = parseLiteral(t)
573
- return parsed === null && t !== 'null' ? undefined : parsed
574
- }
575
-
576
- /**
577
- * Evaluate a signal initializer expression using provided props.
578
- * Handles: props.initial ?? 0, props.value, literal values.
579
- */
580
- export function evaluateSignalInit(
581
- expr: string,
582
- props?: Record<string, unknown>,
583
- ): unknown {
584
- const nullishMatch = expr.match(/^props\.(\w+)\s*\?\?\s*(.+)$/)
585
- if (nullishMatch) {
586
- const propName = nullishMatch[1]
587
- const defaultExpr = nullishMatch[2].trim()
588
- if (props && propName in props) return props[propName]
589
- return parseLiteral(defaultExpr)
590
- }
591
-
592
- const propsMatch = expr.match(/^props\.(\w+)$/)
593
- if (propsMatch) {
594
- if (props && propsMatch[1] in props) return props[propsMatch[1]]
595
- return null
596
- }
597
-
598
- return parseLiteral(expr)
599
- }
600
-
601
- function parseLiteral(expr: string): unknown {
602
- if (/^-?\d+(\.\d+)?$/.test(expr)) return Number(expr)
603
- if (expr === 'true') return true
604
- if (expr === 'false') return false
605
- if (expr === '[]') return []
606
-
607
- {
608
- const t = expr.trim()
609
- if (t.startsWith('[') && t.endsWith(']')) {
610
- const inner = t.slice(1, -1).trim()
611
- if (!inner) return []
612
- const out: unknown[] = []
613
- for (const seg of splitTopLevelCommas(inner)) {
614
- if (!seg.trim()) continue
615
- const parsed = parseLiteral(seg.trim())
616
- if (parsed === null && seg.trim() !== 'null') return null
617
- out.push(parsed)
618
- }
619
- return out
620
- }
621
- }
622
-
623
- const stringMatch = expr.match(/^(['"])(.*)\1$/s)
624
- if (stringMatch) return unescapeJsString(stringMatch[2])
625
-
626
- const trimmed = expr.trim()
627
- if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
628
- const inner = trimmed.slice(1, -1).trim()
629
- if (!inner) return {}
630
- const obj: Record<string, unknown> = {}
631
- for (const pair of splitTopLevelCommas(inner)) {
632
- if (!pair.trim()) continue
633
- const colonIdx = pair.indexOf(':')
634
- if (colonIdx < 0) return null
635
- let key = pair.slice(0, colonIdx).trim()
636
- const val = pair.slice(colonIdx + 1).trim()
637
- const keyMatch = key.match(/^(['"])(.*)\1$/s)
638
- if (keyMatch) key = unescapeJsString(keyMatch[2])
639
- const parsedVal = parseLiteral(val)
640
- if (parsedVal === null && val !== 'null') return null
641
- obj[key] = parsedVal
642
- }
643
- return obj
644
- }
645
- return null
646
- }
647
-
648
- function splitTopLevelCommas(inner: string): string[] {
649
- const segments: string[] = []
650
- let depth = 0
651
- let start = 0
652
- let quote: string | null = null
653
- for (let i = 0; i < inner.length; i++) {
654
- const c = inner[i]
655
- if (quote) {
656
- if (c === quote) {
657
- let backslashes = 0
658
- for (let j = i - 1; j >= 0 && inner[j] === '\\'; j--) backslashes++
659
- if (backslashes % 2 === 0) quote = null
660
- }
661
- continue
662
- }
663
- if (c === '"' || c === "'") {
664
- quote = c
665
- continue
666
- }
667
- if (c === '{' || c === '[') depth++
668
- else if (c === '}' || c === ']') depth--
669
- else if (c === ',' && depth === 0) {
670
- segments.push(inner.slice(start, i))
671
- start = i + 1
672
- }
673
- }
674
- segments.push(inner.slice(start))
675
- return segments
676
- }
677
-
678
- function unescapeJsString(s: string): string {
679
- return s.replace(/\\(.)/g, (_, c) => {
680
- switch (c) {
681
- case 'n': return '\n'
682
- case 'r': return '\r'
683
- case 't': return '\t'
684
- case '0': return '\0'
685
- default: return c
686
- }
687
- })
688
- }