@barefootjs/mojolicious 0.33.6 → 0.35.0

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.
@@ -17,13 +17,23 @@
17
17
  * genuinely needs it, so the seam documents the real cross-module coupling
18
18
  * rather than re-exposing the whole adapter.
19
19
  */
20
- import type { ParsedExpr, CompilerError, IRMetadata } from '@barefootjs/jsx';
20
+ import type { ParsedExpr, CompilerError, IRMetadata, LoweringMatcher } from '@barefootjs/jsx';
21
21
  export interface MojoEmitContext {
22
22
  /**
23
23
  * (#1922) Local binding names the request-scoped `searchParams()` env signal
24
24
  * is imported under. Non-empty enables the env-signal method-call lowering.
25
25
  */
26
26
  readonly _searchParamsLocals: Set<string>;
27
+ /**
28
+ * Registered lowering-plugin matchers (#2057), bound to this component's
29
+ * metadata at init. Read by `MojoTopLevelEmitter`'s `lowering` seam (#2843)
30
+ * so a registered call — the built-in `queryHref`, or any userland plugin —
31
+ * is recognised no matter where it sits in an expression tree (a ternary
32
+ * branch, a template-literal interpolation, …), not only when it's the call
33
+ * the adapter's own top-level conversion entry point
34
+ * (`convertExpressionToPerl`) is asked to lower directly.
35
+ */
36
+ readonly _loweringMatchers: readonly LoweringMatcher[];
27
37
  /**
28
38
  * Inline a module-scope pure string-literal const by name as the resolved
29
39
  * literal value, or null when the name is not such a const.
@@ -1 +1 @@
1
- {"version":3,"file":"emit-context.d.ts","sourceRoot":"","sources":["../../src/adapter/emit-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAE5E,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAEzC;;;OAGG;IACH,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAErD,mFAAmF;IACnF,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAEhD;;;OAGG;IACH,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAE1E,uEAAuE;IACvE,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IAEzC,wDAAwD;IACxD,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAExD,wEAAwE;IACxE,2BAA2B,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CACrE;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAE9B,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAA;IAEhC,0EAA0E;IAC1E,QAAQ,CAAC,cAAc,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAA;IAErD,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAExC;;;;;;;;;OASG;IACH,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,UAAU,GAAG,MAAM,CAAA;CACtE;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;;;;;;OAYG;IACH,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,MAAM,CAAA;CAClG"}
1
+ {"version":3,"file":"emit-context.d.ts","sourceRoot":"","sources":["../../src/adapter/emit-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAE7F,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAEzC;;;;;;;;OAQG;IACH,QAAQ,CAAC,iBAAiB,EAAE,SAAS,eAAe,EAAE,CAAA;IAEtD;;;OAGG;IACH,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAErD,mFAAmF;IACnF,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAEhD;;;OAGG;IACH,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAE1E,uEAAuE;IACvE,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IAEzC,wDAAwD;IACxD,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAExD,wEAAwE;IACxE,2BAA2B,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAA;CACrE;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAE9B,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,CAAA;IAEhC,0EAA0E;IAC1E,QAAQ,CAAC,cAAc,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAA;IAErD,sEAAsE;IACtE,QAAQ,CAAC,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAExC;;;;;;;;;OASG;IACH,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,UAAU,GAAG,MAAM,CAAA;CACtE;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;;;;;;;OAYG;IACH,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,MAAM,CAAA;CAClG"}
@@ -10,7 +10,7 @@
10
10
  * - `MojoTopLevelEmitter` — top-level / stash context; depends on the
11
11
  * adapter only through the narrow `MojoEmitContext` seam.
12
12
  */
13
- import { type ParsedExprEmitter, type ArrayMethod, type LiteralType, type ParsedExpr, type ObjectLiteralProperty, type FlatDepth, type TemplatePart } from '@barefootjs/jsx';
13
+ import { type ParsedExprEmitter, type ArrayMethod, type LiteralType, type ParsedExpr, type ObjectLiteralProperty, type FlatDepth, type TemplatePart, type LoweringEmitter } from '@barefootjs/jsx';
14
14
  import type { MojoEmitContext } from '../emit-context.ts';
15
15
  export declare class MojoFilterEmitter implements ParsedExprEmitter {
16
16
  private readonly param;
@@ -55,6 +55,16 @@ export declare class MojoFilterEmitter implements ParsedExprEmitter {
55
55
  export declare class MojoTopLevelEmitter implements ParsedExprEmitter {
56
56
  private readonly ctx;
57
57
  constructor(ctx: MojoEmitContext);
58
+ /**
59
+ * Registered-lowering seam (#2843): `emitParsedExpr`'s shared `call` case
60
+ * tries every matcher here BEFORE `call()` itself, so a registered call
61
+ * (the built-in `queryHref`, or any userland plugin) is recognised no
62
+ * matter where it sits in the tree. `render` is what used to live inline
63
+ * in `MojoAdapter.convertExpressionToPerl` before the object-literal
64
+ * support-gate refusal (`checkSupport`'s `call` arm, now itself
65
+ * registry-aware) made the pre-gate special case unnecessary.
66
+ */
67
+ get lowering(): LoweringEmitter;
58
68
  identifier(name: string): string;
59
69
  literal(value: string | number | boolean | null, literalType: LiteralType): string;
60
70
  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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"}
@@ -17,8 +17,6 @@ import {
17
17
  lookupStaticRecordLiteral,
18
18
  searchParamsLocalNames,
19
19
  prepareLoweringMatchers,
20
- queryHrefArgs,
21
- isValidHelperId,
22
20
  sortComparatorFromArrow as sortComparatorFromArrow2,
23
21
  isLowerableLoopDestructure,
24
22
  isDangerousInnerHtmlAttr,
@@ -363,7 +361,9 @@ import {
363
361
  identifierPath,
364
362
  matchSearchParamsMethodCall,
365
363
  sortComparatorFromArrow,
366
- asCallbackMethodCall
364
+ asCallbackMethodCall,
365
+ queryHrefArgs,
366
+ isValidHelperId
367
367
  } from "@barefootjs/jsx";
368
368
 
369
369
  // src/adapter/expr/operand.ts
@@ -525,6 +525,21 @@ class MojoTopLevelEmitter {
525
525
  constructor(ctx) {
526
526
  this.ctx = ctx;
527
527
  }
528
+ get lowering() {
529
+ return {
530
+ matchers: this.ctx._loweringMatchers,
531
+ render: (node, emit) => {
532
+ if (node.kind === "guard-list" && node.helper === "query") {
533
+ const qArgs = queryHrefArgs(node, emit);
534
+ return `bf->query(${qArgs.join(", ")})`;
535
+ }
536
+ if (node.kind === "helper-call" && isValidHelperId(node.helper)) {
537
+ return `bf->${node.helper}(${node.args.map(emit).join(", ")})`;
538
+ }
539
+ return null;
540
+ }
541
+ };
542
+ }
528
543
  identifier(name) {
529
544
  if (name === "undefined" || name === "null")
530
545
  return "undef";
@@ -1102,7 +1117,9 @@ class MojoAdapter extends BaseAdapter {
1102
1117
  return null;
1103
1118
  return {
1104
1119
  condition: exprToString(parsed.test),
1105
- consequent: exprToString(parsed.consequent)
1120
+ consequent: exprToString(parsed.consequent),
1121
+ testParsed: parsed.test,
1122
+ consequentParsed: parsed.consequent
1106
1123
  };
1107
1124
  }
1108
1125
  resolveLiteralConst(name) {
@@ -1429,7 +1446,7 @@ ${whenTrue}
1429
1446
  const prevScope = this.scope;
1430
1447
  this.scope = prevScope.enterLoopRow(loop);
1431
1448
  const renderedChildren = this.renderChildren(loop.children);
1432
- const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
1449
+ const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . bf->escape_comment_key(${this.convertExpressionToPerl(loop.key)})) %>
1433
1450
  ${renderedChildren}` : renderedChildren;
1434
1451
  const lines = [];
1435
1452
  lines.push(`<%== bf->comment("loop:${loop.markerId}") %>`);
@@ -1630,8 +1647,8 @@ ${children}`;
1630
1647
  {
1631
1648
  const m = this.parseUndefinedAlternateTernary(value.expr);
1632
1649
  if (m) {
1633
- const cond = this.convertExpressionToPerl(m.condition);
1634
- const val = this.convertExpressionToPerl(m.consequent);
1650
+ const cond = this.convertExpressionToPerl("", m.testParsed);
1651
+ const val = this.convertExpressionToPerl("", m.consequentParsed);
1635
1652
  return `<% if (${cond}) { %>${name}="<%= ${val} %>"<% } %>`;
1636
1653
  }
1637
1654
  }
@@ -1760,7 +1777,7 @@ ${children}`;
1760
1777
  if (!startsAsObjectLiteral && !hasTaggedTemplate)
1761
1778
  return false;
1762
1779
  const parsed = parseExpression2(expr.trim());
1763
- const support = isSupported(parsed);
1780
+ const support = isSupported(parsed, { loweringMatchers: this._loweringMatchers });
1764
1781
  if (parsed.kind !== "unsupported" && support.supported)
1765
1782
  return false;
1766
1783
  const reason = support.reason ?? (parsed.kind === "unsupported" ? parsed.reason : undefined);
@@ -1780,6 +1797,7 @@ ${reason}` : "";
1780
1797
  get emitCtx() {
1781
1798
  return {
1782
1799
  _searchParamsLocals: this._searchParamsLocals,
1800
+ _loweringMatchers: this._loweringMatchers,
1783
1801
  resolveModuleStringConst: (name) => this.resolveModuleStringConst(name),
1784
1802
  resolveLiteralConst: (name) => this.resolveLiteralConst(name),
1785
1803
  resolveStaticRecordLiteral: (o, k) => this.resolveStaticRecordLiteral(o, k),
@@ -1810,20 +1828,8 @@ ${reason}` : "";
1810
1828
  return "''";
1811
1829
  parsed = parseExpression2(trimmed);
1812
1830
  }
1813
- if (parsed.kind === "call") {
1814
- for (const matcher of this._loweringMatchers) {
1815
- const node = matcher(parsed.callee, parsed.args);
1816
- if (node?.kind === "guard-list" && node.helper === "query") {
1817
- const argsGo = queryHrefArgs(node, (n) => this.renderParsedExprToPerl(n));
1818
- return `bf->query(${argsGo.join(", ")})`;
1819
- }
1820
- if (node?.kind === "helper-call" && isValidHelperId(node.helper)) {
1821
- const argsX = node.args.map((a) => this.renderParsedExprToPerl(a));
1822
- return `bf->${node.helper}(${argsX.join(", ")})`;
1823
- }
1824
- }
1825
- }
1826
- const support = pos === "value" ? isSupportedValue(parsed) : isSupported(parsed);
1831
+ const supportOpts = { loweringMatchers: this._loweringMatchers };
1832
+ const support = pos === "value" ? isSupportedValue(parsed, supportOpts) : isSupported(parsed, supportOpts);
1827
1833
  if (!support.supported) {
1828
1834
  this.errors.push({
1829
1835
  code: "BF101",
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import type { ComponentIR, IRNode, IRElement, IRText, IRExpression, IRConditional, IRLoop, IRComponent, IRFragment, IRSlot, IRIfStatement, IRProvider, IRAsync, TemplatePrimitiveRegistry } from '@barefootjs/jsx';
7
7
  import { BaseAdapter, type AdapterOutput, type AdapterGenerateOptions, type IRNodeEmitter, type EmitIRNode } from '@barefootjs/jsx';
8
+ import type { ParsedExpr } from '@barefootjs/jsx';
8
9
  import type { MojoRenderCtx } from './lib/types.ts';
9
10
  export type { MojoAdapterOptions } from './lib/types.ts';
10
11
  import type { MojoAdapterOptions } from './lib/types.ts';
@@ -162,6 +163,8 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
162
163
  parseUndefinedAlternateTernary(expr: string): {
163
164
  condition: string;
164
165
  consequent: string;
166
+ testParsed: ParsedExpr;
167
+ consequentParsed: ParsedExpr;
165
168
  } | null;
166
169
  /**
167
170
  * Inline a const (any scope) whose initializer is a pure numeric or
@@ -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;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eAsD7B,CAAA"}
1
+ {"version":3,"file":"conformance-pins.d.ts","sourceRoot":"","sources":["../src/conformance-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,eAAO,MAAM,eAAe,EAAE,eA0D7B,CAAA"}
package/dist/index.js CHANGED
@@ -17,8 +17,6 @@ import {
17
17
  lookupStaticRecordLiteral,
18
18
  searchParamsLocalNames,
19
19
  prepareLoweringMatchers,
20
- queryHrefArgs,
21
- isValidHelperId,
22
20
  sortComparatorFromArrow as sortComparatorFromArrow2,
23
21
  isLowerableLoopDestructure,
24
22
  isDangerousInnerHtmlAttr,
@@ -363,7 +361,9 @@ import {
363
361
  identifierPath,
364
362
  matchSearchParamsMethodCall,
365
363
  sortComparatorFromArrow,
366
- asCallbackMethodCall
364
+ asCallbackMethodCall,
365
+ queryHrefArgs,
366
+ isValidHelperId
367
367
  } from "@barefootjs/jsx";
368
368
 
369
369
  // src/adapter/expr/operand.ts
@@ -525,6 +525,21 @@ class MojoTopLevelEmitter {
525
525
  constructor(ctx) {
526
526
  this.ctx = ctx;
527
527
  }
528
+ get lowering() {
529
+ return {
530
+ matchers: this.ctx._loweringMatchers,
531
+ render: (node, emit) => {
532
+ if (node.kind === "guard-list" && node.helper === "query") {
533
+ const qArgs = queryHrefArgs(node, emit);
534
+ return `bf->query(${qArgs.join(", ")})`;
535
+ }
536
+ if (node.kind === "helper-call" && isValidHelperId(node.helper)) {
537
+ return `bf->${node.helper}(${node.args.map(emit).join(", ")})`;
538
+ }
539
+ return null;
540
+ }
541
+ };
542
+ }
528
543
  identifier(name) {
529
544
  if (name === "undefined" || name === "null")
530
545
  return "undef";
@@ -1102,7 +1117,9 @@ class MojoAdapter extends BaseAdapter {
1102
1117
  return null;
1103
1118
  return {
1104
1119
  condition: exprToString(parsed.test),
1105
- consequent: exprToString(parsed.consequent)
1120
+ consequent: exprToString(parsed.consequent),
1121
+ testParsed: parsed.test,
1122
+ consequentParsed: parsed.consequent
1106
1123
  };
1107
1124
  }
1108
1125
  resolveLiteralConst(name) {
@@ -1429,7 +1446,7 @@ ${whenTrue}
1429
1446
  const prevScope = this.scope;
1430
1447
  this.scope = prevScope.enterLoopRow(loop);
1431
1448
  const renderedChildren = this.renderChildren(loop.children);
1432
- const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
1449
+ const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . bf->escape_comment_key(${this.convertExpressionToPerl(loop.key)})) %>
1433
1450
  ${renderedChildren}` : renderedChildren;
1434
1451
  const lines = [];
1435
1452
  lines.push(`<%== bf->comment("loop:${loop.markerId}") %>`);
@@ -1630,8 +1647,8 @@ ${children}`;
1630
1647
  {
1631
1648
  const m = this.parseUndefinedAlternateTernary(value.expr);
1632
1649
  if (m) {
1633
- const cond = this.convertExpressionToPerl(m.condition);
1634
- const val = this.convertExpressionToPerl(m.consequent);
1650
+ const cond = this.convertExpressionToPerl("", m.testParsed);
1651
+ const val = this.convertExpressionToPerl("", m.consequentParsed);
1635
1652
  return `<% if (${cond}) { %>${name}="<%= ${val} %>"<% } %>`;
1636
1653
  }
1637
1654
  }
@@ -1760,7 +1777,7 @@ ${children}`;
1760
1777
  if (!startsAsObjectLiteral && !hasTaggedTemplate)
1761
1778
  return false;
1762
1779
  const parsed = parseExpression2(expr.trim());
1763
- const support = isSupported(parsed);
1780
+ const support = isSupported(parsed, { loweringMatchers: this._loweringMatchers });
1764
1781
  if (parsed.kind !== "unsupported" && support.supported)
1765
1782
  return false;
1766
1783
  const reason = support.reason ?? (parsed.kind === "unsupported" ? parsed.reason : undefined);
@@ -1780,6 +1797,7 @@ ${reason}` : "";
1780
1797
  get emitCtx() {
1781
1798
  return {
1782
1799
  _searchParamsLocals: this._searchParamsLocals,
1800
+ _loweringMatchers: this._loweringMatchers,
1783
1801
  resolveModuleStringConst: (name) => this.resolveModuleStringConst(name),
1784
1802
  resolveLiteralConst: (name) => this.resolveLiteralConst(name),
1785
1803
  resolveStaticRecordLiteral: (o, k) => this.resolveStaticRecordLiteral(o, k),
@@ -1810,20 +1828,8 @@ ${reason}` : "";
1810
1828
  return "''";
1811
1829
  parsed = parseExpression2(trimmed);
1812
1830
  }
1813
- if (parsed.kind === "call") {
1814
- for (const matcher of this._loweringMatchers) {
1815
- const node = matcher(parsed.callee, parsed.args);
1816
- if (node?.kind === "guard-list" && node.helper === "query") {
1817
- const argsGo = queryHrefArgs(node, (n) => this.renderParsedExprToPerl(n));
1818
- return `bf->query(${argsGo.join(", ")})`;
1819
- }
1820
- if (node?.kind === "helper-call" && isValidHelperId(node.helper)) {
1821
- const argsX = node.args.map((a) => this.renderParsedExprToPerl(a));
1822
- return `bf->${node.helper}(${argsX.join(", ")})`;
1823
- }
1824
- }
1825
- }
1826
- const support = pos === "value" ? isSupportedValue(parsed) : isSupported(parsed);
1831
+ const supportOpts = { loweringMatchers: this._loweringMatchers };
1832
+ const support = pos === "value" ? isSupportedValue(parsed, supportOpts) : isSupported(parsed, supportOpts);
1827
1833
  if (!support.supported) {
1828
1834
  this.errors.push({
1829
1835
  code: "BF101",
@@ -1908,10 +1914,7 @@ var conformancePins = {
1908
1914
  "namespace-import-primitive": [{ code: "BF013", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2771" }]
1909
1915
  };
1910
1916
  // src/render-divergences.ts
1911
- var renderDivergences = {
1912
- "children-passthrough-renamed": "A renamed `children` destructure emits the local alias as a template variable; the stash defines only `children`, so the Perl render dies (#2788).",
1913
- "aliased-loop-source": "A `.map()` loop whose source is a local const alias of a signal getter (`const items__alias = items`) dies inside `Mojo::Template::process` on real Mojolicious — the seeded loop data is keyed by the signal's real name (`items`), and the alias hop is never resolved when deciding what to seed under `items__alias`. This is the SSR-side twin of #2778 (fixed for the CSR client-JS template in the same PR that added this fixture) — that fix only touches client-JS emission, not SSR data-seeding. Tracked at https://github.com/piconic-ai/barefootjs/issues/2813; graduate by resolving the alias hop at SSR-seeding time using the same `resolveAliasOrigin`/`resolveGetterAliases` mechanism #2778 introduced, rather than a third alias-hop walker."
1914
- };
1917
+ var renderDivergences = {};
1915
1918
  export {
1916
1919
  renderDivergences,
1917
1920
  mojoAdapter,
@@ -1 +1 @@
1
- {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAkBxD,eAAO,MAAM,iBAAiB,EAAE,iBAK/B,CAAA"}
1
+ {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAQxD,eAAO,MAAM,iBAAiB,EAAE,iBAAsB,CAAA"}
package/dist/vite.js CHANGED
@@ -12,6 +12,41 @@ import ts10 from "typescript";
12
12
 
13
13
  // ../jsx/src/expression-parser.ts
14
14
  import ts from "typescript";
15
+
16
+ // ../jsx/src/lowering-registry.ts
17
+ var plugins = [];
18
+ function registerLoweringPlugin(plugin) {
19
+ const existing = plugins.findIndex((p) => p.name === plugin.name);
20
+ if (existing >= 0)
21
+ plugins[existing] = plugin;
22
+ else
23
+ plugins.push(plugin);
24
+ }
25
+ function prepareLoweringMatchers(metadata) {
26
+ const matchers = [];
27
+ for (const plugin of plugins) {
28
+ const matcher = plugin.prepare(metadata);
29
+ if (matcher)
30
+ matchers.push(matcher);
31
+ }
32
+ return matchers;
33
+ }
34
+ function loweringNodeChildren(node) {
35
+ if (node.kind === "helper-call")
36
+ return [...node.args];
37
+ const children = [node.base];
38
+ for (const t of node.triples) {
39
+ if (t.guard)
40
+ children.push(t.guard);
41
+ children.push(t.value);
42
+ }
43
+ return children;
44
+ }
45
+ function isValidHelperId(helper) {
46
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(helper);
47
+ }
48
+
49
+ // ../jsx/src/expression-parser.ts
15
50
  var UNSUPPORTED_METHODS = new Set([
16
51
  "filter",
17
52
  "map",
@@ -263,7 +298,7 @@ function convertNode(node, raw) {
263
298
  }
264
299
  if (n === undefined || Number.isNaN(n)) {
265
300
  const parsedDepth = convertNode(depthNode, raw);
266
- if (checkSupport(parsedDepth, "rendered").supported) {
301
+ if (checkSupport(parsedDepth, "rendered", []).supported) {
267
302
  depthExpr = parsedDepth;
268
303
  flatDepth = 1;
269
304
  } else {
@@ -1041,13 +1076,13 @@ function getUnaryOperatorString(op) {
1041
1076
  return "unknown";
1042
1077
  }
1043
1078
  }
1044
- function isSupported(expr) {
1045
- return checkSupport(expr, "rendered");
1079
+ function isSupported(expr, opts) {
1080
+ return checkSupport(expr, "rendered", opts?.loweringMatchers ?? []);
1046
1081
  }
1047
- function isSupportedValue(expr) {
1048
- return checkSupport(expr, "value");
1082
+ function isSupportedValue(expr, opts) {
1083
+ return checkSupport(expr, "value", opts?.loweringMatchers ?? []);
1049
1084
  }
1050
- function checkSupport(expr, pos) {
1085
+ function checkSupport(expr, pos, matchers) {
1051
1086
  switch (expr.kind) {
1052
1087
  case "unsupported":
1053
1088
  return { supported: false, reason: expr.reason };
@@ -1056,7 +1091,7 @@ function checkSupport(expr, pos) {
1056
1091
  return { supported: false, reason: "Unsupported syntax: ObjectLiteralExpression" };
1057
1092
  }
1058
1093
  for (const prop of expr.properties) {
1059
- const propSupport = checkSupport(prop.kind === "spread" ? prop.expr : prop.value, pos);
1094
+ const propSupport = checkSupport(prop.kind === "spread" ? prop.expr : prop.value, pos, matchers);
1060
1095
  if (!propSupport.supported)
1061
1096
  return propSupport;
1062
1097
  }
@@ -1071,7 +1106,7 @@ function checkSupport(expr, pos) {
1071
1106
  return { supported: false, reason: "Standalone arrow functions / regex literals are not supported" };
1072
1107
  case "array-literal": {
1073
1108
  for (const el of expr.elements) {
1074
- const elSupport = checkSupport(el, pos);
1109
+ const elSupport = checkSupport(el, pos, matchers);
1075
1110
  if (!elSupport.supported)
1076
1111
  return elSupport;
1077
1112
  }
@@ -1084,28 +1119,39 @@ function checkSupport(expr, pos) {
1084
1119
  reason: `String.prototype.${expr.method} supports only a string pattern + string replacement (the regex form is deferred); use a string pattern or wrap the expression in /* @client */`
1085
1120
  };
1086
1121
  }
1087
- const objSupport = checkSupport(expr.object, pos);
1122
+ const objSupport = checkSupport(expr.object, pos, matchers);
1088
1123
  if (!objSupport.supported)
1089
1124
  return objSupport;
1090
1125
  for (const arg of expr.args) {
1091
- const argSupport = checkSupport(arg, pos);
1126
+ const argSupport = checkSupport(arg, pos, matchers);
1092
1127
  if (!argSupport.supported)
1093
1128
  return argSupport;
1094
1129
  }
1095
1130
  if (expr.method === "flat" && expr.depthExpr) {
1096
- const depthSupport = checkSupport(expr.depthExpr, pos);
1131
+ const depthSupport = checkSupport(expr.depthExpr, pos, matchers);
1097
1132
  if (!depthSupport.supported)
1098
1133
  return depthSupport;
1099
1134
  }
1100
1135
  return { supported: true, level: "L2" };
1101
1136
  }
1102
1137
  case "call": {
1138
+ for (const matcher of matchers) {
1139
+ const node = matcher(expr.callee, expr.args);
1140
+ if (!node)
1141
+ continue;
1142
+ for (const child of loweringNodeChildren(node)) {
1143
+ const childSupport = checkSupport(child, pos, matchers);
1144
+ if (!childSupport.supported)
1145
+ return childSupport;
1146
+ }
1147
+ return { supported: true, level: "L2" };
1148
+ }
1103
1149
  const cb = asCallbackMethodCall(expr);
1104
1150
  if (cb) {
1105
- const objSupport = checkSupport(cb.object, pos);
1151
+ const objSupport = checkSupport(cb.object, pos, matchers);
1106
1152
  if (!objSupport.supported)
1107
1153
  return objSupport;
1108
- const bodySupport = checkSupport(cb.arrow.body, pos);
1154
+ const bodySupport = checkSupport(cb.arrow.body, pos, matchers);
1109
1155
  if (!bodySupport.supported) {
1110
1156
  return {
1111
1157
  supported: false,
@@ -1114,13 +1160,13 @@ function checkSupport(expr, pos) {
1114
1160
  };
1115
1161
  }
1116
1162
  for (const rest of cb.args) {
1117
- const restSupport = checkSupport(rest, pos);
1163
+ const restSupport = checkSupport(rest, pos, matchers);
1118
1164
  if (!restSupport.supported)
1119
1165
  return restSupport;
1120
1166
  }
1121
1167
  return { supported: true, level: "L5" };
1122
1168
  }
1123
- const calleeSupport = checkSupport(expr.callee, pos);
1169
+ const calleeSupport = checkSupport(expr.callee, pos, matchers);
1124
1170
  if (!calleeSupport.supported) {
1125
1171
  return calleeSupport;
1126
1172
  }
@@ -1139,7 +1185,7 @@ function checkSupport(expr, pos) {
1139
1185
  return { supported: true, level: "L1" };
1140
1186
  }
1141
1187
  for (const arg of expr.args) {
1142
- const argSupport = checkSupport(arg, pos);
1188
+ const argSupport = checkSupport(arg, pos, matchers);
1143
1189
  if (!argSupport.supported) {
1144
1190
  return argSupport;
1145
1191
  }
@@ -1147,7 +1193,7 @@ function checkSupport(expr, pos) {
1147
1193
  return { supported: true, level: "L2" };
1148
1194
  }
1149
1195
  case "member": {
1150
- const objSupport = checkSupport(expr.object, pos);
1196
+ const objSupport = checkSupport(expr.object, pos, matchers);
1151
1197
  if (!objSupport.supported) {
1152
1198
  return objSupport;
1153
1199
  }
@@ -1157,19 +1203,19 @@ function checkSupport(expr, pos) {
1157
1203
  return { supported: true, level: "L2" };
1158
1204
  }
1159
1205
  case "index-access": {
1160
- const objSupport = checkSupport(expr.object, pos);
1206
+ const objSupport = checkSupport(expr.object, pos, matchers);
1161
1207
  if (!objSupport.supported)
1162
1208
  return objSupport;
1163
- const indexSupport = checkSupport(expr.index, pos);
1209
+ const indexSupport = checkSupport(expr.index, pos, matchers);
1164
1210
  if (!indexSupport.supported)
1165
1211
  return indexSupport;
1166
1212
  return { supported: true, level: "L2" };
1167
1213
  }
1168
1214
  case "binary": {
1169
- const leftSupport = checkSupport(expr.left, pos);
1215
+ const leftSupport = checkSupport(expr.left, pos, matchers);
1170
1216
  if (!leftSupport.supported)
1171
1217
  return leftSupport;
1172
- const rightSupport = checkSupport(expr.right, pos);
1218
+ const rightSupport = checkSupport(expr.right, pos, matchers);
1173
1219
  if (!rightSupport.supported)
1174
1220
  return rightSupport;
1175
1221
  if (["===", "==", "!==", "!=", ">", "<", ">=", "<="].includes(expr.op)) {
@@ -1181,7 +1227,7 @@ function checkSupport(expr, pos) {
1181
1227
  return { supported: false, reason: `Unknown operator: ${expr.op}` };
1182
1228
  }
1183
1229
  case "unary": {
1184
- const argSupport = checkSupport(expr.argument, pos);
1230
+ const argSupport = checkSupport(expr.argument, pos, matchers);
1185
1231
  if (!argSupport.supported)
1186
1232
  return argSupport;
1187
1233
  if (expr.op === "!") {
@@ -1193,25 +1239,25 @@ function checkSupport(expr, pos) {
1193
1239
  return { supported: false, reason: `Unsupported unary operator: ${expr.op}` };
1194
1240
  }
1195
1241
  case "logical": {
1196
- const leftSupport = checkSupport(expr.left, pos);
1242
+ const leftSupport = checkSupport(expr.left, pos, matchers);
1197
1243
  if (!leftSupport.supported)
1198
1244
  return leftSupport;
1199
1245
  if (expr.op === "??" && expr.right.kind === "object-literal" && expr.right.properties.length === 0) {
1200
1246
  return { supported: true, level: "L4" };
1201
1247
  }
1202
- const rightSupport = checkSupport(expr.right, pos);
1248
+ const rightSupport = checkSupport(expr.right, pos, matchers);
1203
1249
  if (!rightSupport.supported)
1204
1250
  return rightSupport;
1205
1251
  return { supported: true, level: "L4" };
1206
1252
  }
1207
1253
  case "conditional": {
1208
- const testSupport = checkSupport(expr.test, pos);
1254
+ const testSupport = checkSupport(expr.test, pos, matchers);
1209
1255
  if (!testSupport.supported)
1210
1256
  return testSupport;
1211
- const consSupport = checkSupport(expr.consequent, pos);
1257
+ const consSupport = checkSupport(expr.consequent, pos, matchers);
1212
1258
  if (!consSupport.supported)
1213
1259
  return consSupport;
1214
- const altSupport = checkSupport(expr.alternate, pos);
1260
+ const altSupport = checkSupport(expr.alternate, pos, matchers);
1215
1261
  if (!altSupport.supported)
1216
1262
  return altSupport;
1217
1263
  return { supported: true, level: "L4" };
@@ -1219,7 +1265,7 @@ function checkSupport(expr, pos) {
1219
1265
  case "template-literal": {
1220
1266
  for (const part of expr.parts) {
1221
1267
  if (part.type === "expression") {
1222
- const partSupport = checkSupport(part.expr, pos);
1268
+ const partSupport = checkSupport(part.expr, pos, matchers);
1223
1269
  if (!partSupport.supported)
1224
1270
  return partSupport;
1225
1271
  }
@@ -2228,6 +2274,12 @@ class BindingScope {
2228
2274
  }
2229
2275
  }
2230
2276
 
2277
+ // ../jsx/src/ir-to-client-js/safe-html.ts
2278
+ function safeHtml(expr) {
2279
+ return expr;
2280
+ }
2281
+ var EMPTY_MARKUP = safeHtml("''");
2282
+
2231
2283
  // ../jsx/src/ir-to-client-js/html-template.ts
2232
2284
  var VOID_ELEMENTS = new Set([
2233
2285
  "area",
@@ -2514,6 +2566,7 @@ var CLIENT_EXPORTS = new Set([
2514
2566
  "isSSRPortal",
2515
2567
  "findSiblingSlot",
2516
2568
  "cleanupPortalPlaceholder",
2569
+ "trackPosition",
2517
2570
  "createSearchParams",
2518
2571
  "queryHref",
2519
2572
  "formatDate",
@@ -2574,7 +2627,8 @@ var BROWSER_ONLY_CLIENT_APIS = new Set([
2574
2627
  "createPortal",
2575
2628
  "isSSRPortal",
2576
2629
  "findSiblingSlot",
2577
- "cleanupPortalPlaceholder"
2630
+ "cleanupPortalPlaceholder",
2631
+ "trackPosition"
2578
2632
  ]);
2579
2633
  var REACTIVE_PRIMITIVES = new Set([
2580
2634
  "createSignal",
@@ -3173,30 +3227,6 @@ import ts16 from "typescript";
3173
3227
 
3174
3228
  // ../jsx/src/relocate.ts
3175
3229
  import ts18 from "typescript";
3176
-
3177
- // ../jsx/src/lowering-registry.ts
3178
- var plugins = [];
3179
- function registerLoweringPlugin(plugin) {
3180
- const existing = plugins.findIndex((p) => p.name === plugin.name);
3181
- if (existing >= 0)
3182
- plugins[existing] = plugin;
3183
- else
3184
- plugins.push(plugin);
3185
- }
3186
- function prepareLoweringMatchers(metadata) {
3187
- const matchers = [];
3188
- for (const plugin of plugins) {
3189
- const matcher = plugin.prepare(metadata);
3190
- if (matcher)
3191
- matchers.push(matcher);
3192
- }
3193
- return matchers;
3194
- }
3195
- function isValidHelperId(helper) {
3196
- return /^[A-Za-z_][A-Za-z0-9_]*$/.test(helper);
3197
- }
3198
-
3199
- // ../jsx/src/relocate.ts
3200
3230
  var REGISTRY_SAFE_BINDING_KINDS = new Set([
3201
3231
  "global",
3202
3232
  "module-import",
@@ -4664,6 +4694,17 @@ function emitParsedExpr(expr, emitter) {
4664
4694
  case "literal":
4665
4695
  return emitter.literal(expr.value, expr.literalType);
4666
4696
  case "call": {
4697
+ if (emitter.lowering) {
4698
+ for (const matcher of emitter.lowering.matchers) {
4699
+ const node = matcher(expr.callee, expr.args);
4700
+ if (!node)
4701
+ continue;
4702
+ const rendered = emitter.lowering.render(node, emit);
4703
+ if (rendered !== null)
4704
+ return rendered;
4705
+ break;
4706
+ }
4707
+ }
4667
4708
  const cb = asCallbackMethodCall(expr);
4668
4709
  if (cb)
4669
4710
  return emitter.callbackMethod(cb.method, cb.object, cb.arrow, cb.args, emit);
@@ -5727,6 +5768,21 @@ class MojoTopLevelEmitter {
5727
5768
  constructor(ctx) {
5728
5769
  this.ctx = ctx;
5729
5770
  }
5771
+ get lowering() {
5772
+ return {
5773
+ matchers: this.ctx._loweringMatchers,
5774
+ render: (node, emit) => {
5775
+ if (node.kind === "guard-list" && node.helper === "query") {
5776
+ const qArgs = queryHrefArgs(node, emit);
5777
+ return `bf->query(${qArgs.join(", ")})`;
5778
+ }
5779
+ if (node.kind === "helper-call" && isValidHelperId(node.helper)) {
5780
+ return `bf->${node.helper}(${node.args.map(emit).join(", ")})`;
5781
+ }
5782
+ return null;
5783
+ }
5784
+ };
5785
+ }
5730
5786
  identifier(name) {
5731
5787
  if (name === "undefined" || name === "null")
5732
5788
  return "undef";
@@ -6296,7 +6352,9 @@ class MojoAdapter extends BaseAdapter {
6296
6352
  return null;
6297
6353
  return {
6298
6354
  condition: exprToString(parsed.test),
6299
- consequent: exprToString(parsed.consequent)
6355
+ consequent: exprToString(parsed.consequent),
6356
+ testParsed: parsed.test,
6357
+ consequentParsed: parsed.consequent
6300
6358
  };
6301
6359
  }
6302
6360
  resolveLiteralConst(name) {
@@ -6623,7 +6681,7 @@ ${whenTrue}
6623
6681
  const prevScope = this.scope;
6624
6682
  this.scope = prevScope.enterLoopRow(loop);
6625
6683
  const renderedChildren = this.renderChildren(loop.children);
6626
- const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
6684
+ const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . bf->escape_comment_key(${this.convertExpressionToPerl(loop.key)})) %>
6627
6685
  ${renderedChildren}` : renderedChildren;
6628
6686
  const lines = [];
6629
6687
  lines.push(`<%== bf->comment("loop:${loop.markerId}") %>`);
@@ -6824,8 +6882,8 @@ ${children}`;
6824
6882
  {
6825
6883
  const m = this.parseUndefinedAlternateTernary(value.expr);
6826
6884
  if (m) {
6827
- const cond = this.convertExpressionToPerl(m.condition);
6828
- const val = this.convertExpressionToPerl(m.consequent);
6885
+ const cond = this.convertExpressionToPerl("", m.testParsed);
6886
+ const val = this.convertExpressionToPerl("", m.consequentParsed);
6829
6887
  return `<% if (${cond}) { %>${name}="<%= ${val} %>"<% } %>`;
6830
6888
  }
6831
6889
  }
@@ -6954,7 +7012,7 @@ ${children}`;
6954
7012
  if (!startsAsObjectLiteral && !hasTaggedTemplate)
6955
7013
  return false;
6956
7014
  const parsed = parseExpression(expr.trim());
6957
- const support = isSupported(parsed);
7015
+ const support = isSupported(parsed, { loweringMatchers: this._loweringMatchers });
6958
7016
  if (parsed.kind !== "unsupported" && support.supported)
6959
7017
  return false;
6960
7018
  const reason = support.reason ?? (parsed.kind === "unsupported" ? parsed.reason : undefined);
@@ -6974,6 +7032,7 @@ ${reason}` : "";
6974
7032
  get emitCtx() {
6975
7033
  return {
6976
7034
  _searchParamsLocals: this._searchParamsLocals,
7035
+ _loweringMatchers: this._loweringMatchers,
6977
7036
  resolveModuleStringConst: (name) => this.resolveModuleStringConst(name),
6978
7037
  resolveLiteralConst: (name) => this.resolveLiteralConst(name),
6979
7038
  resolveStaticRecordLiteral: (o, k) => this.resolveStaticRecordLiteral(o, k),
@@ -7004,20 +7063,8 @@ ${reason}` : "";
7004
7063
  return "''";
7005
7064
  parsed = parseExpression(trimmed);
7006
7065
  }
7007
- if (parsed.kind === "call") {
7008
- for (const matcher of this._loweringMatchers) {
7009
- const node = matcher(parsed.callee, parsed.args);
7010
- if (node?.kind === "guard-list" && node.helper === "query") {
7011
- const argsGo = queryHrefArgs(node, (n) => this.renderParsedExprToPerl(n));
7012
- return `bf->query(${argsGo.join(", ")})`;
7013
- }
7014
- if (node?.kind === "helper-call" && isValidHelperId(node.helper)) {
7015
- const argsX = node.args.map((a) => this.renderParsedExprToPerl(a));
7016
- return `bf->${node.helper}(${argsX.join(", ")})`;
7017
- }
7018
- }
7019
- }
7020
- const support = pos === "value" ? isSupportedValue(parsed) : isSupported(parsed);
7066
+ const supportOpts = { loweringMatchers: this._loweringMatchers };
7067
+ const support = pos === "value" ? isSupportedValue(parsed, supportOpts) : isSupported(parsed, supportOpts);
7021
7068
  if (!support.supported) {
7022
7069
  this.errors.push({
7023
7070
  code: "BF101",
@@ -1,5 +1,5 @@
1
1
  package BarefootJS::Backend::Mojo;
2
- our $VERSION = "0.33.4";
2
+ our $VERSION = "0.34.0";
3
3
  use Mojo::Base -base, -signatures;
4
4
 
5
5
  use Mojo::ByteStream qw(b);
@@ -1,5 +1,5 @@
1
1
  package Mojolicious::Plugin::BarefootJS::DevReload;
2
- our $VERSION = "0.33.4";
2
+ our $VERSION = "0.34.0";
3
3
  use Mojo::Base 'Mojolicious::Plugin', -signatures;
4
4
 
5
5
  =head1 NAME
@@ -1,5 +1,5 @@
1
1
  package Mojolicious::Plugin::BarefootJS;
2
- our $VERSION = "0.33.4";
2
+ our $VERSION = "0.34.0";
3
3
  use Mojo::Base 'Mojolicious::Plugin', -signatures;
4
4
 
5
5
  use Mojo::File qw(path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/mojolicious",
3
- "version": "0.33.6",
3
+ "version": "0.35.0",
4
4
  "description": "Mojolicious EP template adapter for BarefootJS - generates .html.ep files from IR",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -52,7 +52,7 @@
52
52
  "directory": "packages/adapter-mojolicious"
53
53
  },
54
54
  "dependencies": {
55
- "@barefootjs/shared": "0.33.6"
55
+ "@barefootjs/shared": "0.35.0"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "@barefootjs/jsx": ">=0.2.0",
@@ -70,9 +70,9 @@
70
70
  },
71
71
  "devDependencies": {
72
72
  "@barefootjs/adapter-tests": "0.1.0",
73
- "@barefootjs/jsx": "0.33.6",
74
- "@barefootjs/vite": "0.33.6",
75
- "@barefootjs/client": "0.33.6",
73
+ "@barefootjs/jsx": "0.35.0",
74
+ "@barefootjs/vite": "0.35.0",
75
+ "@barefootjs/client": "0.35.0",
76
76
  "vite": "^6.0.0"
77
77
  }
78
78
  }
@@ -18,7 +18,7 @@
18
18
  * rather than re-exposing the whole adapter.
19
19
  */
20
20
 
21
- import type { ParsedExpr, CompilerError, IRMetadata } from '@barefootjs/jsx'
21
+ import type { ParsedExpr, CompilerError, IRMetadata, LoweringMatcher } from '@barefootjs/jsx'
22
22
 
23
23
  export interface MojoEmitContext {
24
24
  /**
@@ -27,6 +27,17 @@ export interface MojoEmitContext {
27
27
  */
28
28
  readonly _searchParamsLocals: Set<string>
29
29
 
30
+ /**
31
+ * Registered lowering-plugin matchers (#2057), bound to this component's
32
+ * metadata at init. Read by `MojoTopLevelEmitter`'s `lowering` seam (#2843)
33
+ * so a registered call — the built-in `queryHref`, or any userland plugin —
34
+ * is recognised no matter where it sits in an expression tree (a ternary
35
+ * branch, a template-literal interpolation, …), not only when it's the call
36
+ * the adapter's own top-level conversion entry point
37
+ * (`convertExpressionToPerl`) is asked to lower directly.
38
+ */
39
+ readonly _loweringMatchers: readonly LoweringMatcher[]
40
+
30
41
  /**
31
42
  * Inline a module-scope pure string-literal const by name as the resolved
32
43
  * literal value, or null when the name is not such a const.
@@ -27,6 +27,10 @@ import { groupBinaryOperand,
27
27
  matchSearchParamsMethodCall,
28
28
  sortComparatorFromArrow,
29
29
  asCallbackMethodCall,
30
+ type LoweringEmitter,
31
+ type LoweringNode,
32
+ queryHrefArgs,
33
+ isValidHelperId,
30
34
  } from '@barefootjs/jsx'
31
35
 
32
36
  import type { MojoEmitContext } from '../emit-context.ts'
@@ -323,6 +327,35 @@ export class MojoTopLevelEmitter implements ParsedExprEmitter {
323
327
  this.ctx = ctx
324
328
  }
325
329
 
330
+ /**
331
+ * Registered-lowering seam (#2843): `emitParsedExpr`'s shared `call` case
332
+ * tries every matcher here BEFORE `call()` itself, so a registered call
333
+ * (the built-in `queryHref`, or any userland plugin) is recognised no
334
+ * matter where it sits in the tree. `render` is what used to live inline
335
+ * in `MojoAdapter.convertExpressionToPerl` before the object-literal
336
+ * support-gate refusal (`checkSupport`'s `call` arm, now itself
337
+ * registry-aware) made the pre-gate special case unnecessary.
338
+ */
339
+ get lowering(): LoweringEmitter {
340
+ return {
341
+ matchers: this.ctx._loweringMatchers,
342
+ render: (node: LoweringNode, emit: (e: ParsedExpr) => string): string | null => {
343
+ // `query` guard-list — `queryHref`-shaped.
344
+ if (node.kind === 'guard-list' && node.helper === 'query') {
345
+ const qArgs = queryHrefArgs(node, emit)
346
+ return `bf->query(${qArgs.join(', ')})`
347
+ }
348
+ // Generic `helper-call` (#2069) — a userland `LoweringPlugin`'s
349
+ // single runtime-helper invocation; `bf-><helper>(args…)` mirrors
350
+ // the `query` helper's own naming convention.
351
+ if (node.kind === 'helper-call' && isValidHelperId(node.helper)) {
352
+ return `bf->${node.helper}(${node.args.map(emit).join(', ')})`
353
+ }
354
+ return null
355
+ },
356
+ }
357
+ }
358
+
326
359
  identifier(name: string): string {
327
360
  // `undefined` / `null` nested inside a larger expression tree
328
361
  // (#1897, pagination's `props.isActive ? 'page' : undefined`) — the
@@ -56,8 +56,6 @@ import {
56
56
  lookupStaticRecordLiteral,
57
57
  searchParamsLocalNames,
58
58
  prepareLoweringMatchers,
59
- queryHrefArgs,
60
- isValidHelperId,
61
59
  sortComparatorFromArrow,
62
60
  isLowerableLoopDestructure,
63
61
  isDangerousInnerHtmlAttr,
@@ -404,7 +402,7 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
404
402
  */
405
403
  parseUndefinedAlternateTernary(
406
404
  expr: string,
407
- ): { condition: string; consequent: string } | null {
405
+ ): { condition: string; consequent: string; testParsed: ParsedExpr; consequentParsed: ParsedExpr } | null {
408
406
  const parsed = parseExpression(expr.trim())
409
407
  if (parsed?.kind !== 'conditional') return null
410
408
  const alt = parsed.alternate
@@ -412,13 +410,16 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
412
410
  (alt.kind === 'identifier' && (alt.name === 'undefined' || alt.name === 'null')) ||
413
411
  (alt.kind === 'literal' && (alt.value === null || alt.value === undefined))
414
412
  if (!isUndef) return null
415
- // Serialise the parsed sub-expressions back to JS source rather than
416
- // slicing `expr` text `indexOf('?')` / `lastIndexOf(':')` would
417
- // mis-split when the consequent itself contains `?` / `:` inside a
418
- // string or nested ternary (`cond ? 'a:b' : undefined`).
413
+ // `condition`/`consequent` are DEBUG text only (`exprToString` renders an
414
+ // unsupported nested shape like an object literal as non-reparseable
415
+ // `[UNSUPPORTED: …]` text) callers that need to re-lower the
416
+ // sub-expression must use `testParsed`/`consequentParsed` (the actual
417
+ // parsed trees) as `preParsed`, not re-parse these strings.
419
418
  return {
420
419
  condition: exprToString(parsed.test),
421
420
  consequent: exprToString(parsed.consequent),
421
+ testParsed: parsed.test,
422
+ consequentParsed: parsed.consequent,
422
423
  }
423
424
  }
424
425
 
@@ -994,9 +995,13 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
994
995
  // conditional content so the client's `mapArrayAnchored` can hydrate
995
996
  // every SSR-rendered item by its anchor. `bf->comment` prepends `bf-`,
996
997
  // so `"loop-i:" . KEY` yields `<!--bf-loop-i:KEY-->`.
998
+ // `bf->escape_comment_key` (#2795 follow-up) both stringifies the key
999
+ // (JS `String()` semantics, matching the other adapters) and neutralizes
1000
+ // `-` so it can't spell `-->` and close the comment early — see its doc
1001
+ // in `BarefootJS.pm`.
997
1002
  const children =
998
1003
  loop.bodyIsItemConditional && loop.key
999
- ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>\n${renderedChildren}`
1004
+ ? `<%== bf->comment("loop-i:" . bf->escape_comment_key(${this.convertExpressionToPerl(loop.key)})) %>\n${renderedChildren}`
1000
1005
  : renderedChildren
1001
1006
 
1002
1007
  const lines: string[] = []
@@ -1459,8 +1464,8 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
1459
1464
  {
1460
1465
  const m = this.parseUndefinedAlternateTernary(value.expr)
1461
1466
  if (m) {
1462
- const cond = this.convertExpressionToPerl(m.condition)
1463
- const val = this.convertExpressionToPerl(m.consequent)
1467
+ const cond = this.convertExpressionToPerl('', m.testParsed)
1468
+ const val = this.convertExpressionToPerl('', m.consequentParsed)
1464
1469
  return `<% if (${cond}) { %>${name}="<%= ${val} %>"<% } %>`
1465
1470
  }
1466
1471
  }
@@ -1782,7 +1787,7 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
1782
1787
  const hasTaggedTemplate = /[A-Za-z_$][\w$]*\s*`/.test(probe)
1783
1788
  if (!startsAsObjectLiteral && !hasTaggedTemplate) return false
1784
1789
  const parsed = parseExpression(expr.trim())
1785
- const support = isSupported(parsed)
1790
+ const support = isSupported(parsed, { loweringMatchers: this._loweringMatchers })
1786
1791
  if (parsed.kind !== 'unsupported' && support.supported) return false
1787
1792
  // Surface the `isSupported` reason so the diagnostic is as
1788
1793
  // actionable as the Go adapter's BF101 — keeps cross-adapter
@@ -1812,6 +1817,7 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
1812
1817
  private get emitCtx(): MojoEmitContext {
1813
1818
  return {
1814
1819
  _searchParamsLocals: this._searchParamsLocals,
1820
+ _loweringMatchers: this._loweringMatchers,
1815
1821
  resolveModuleStringConst: (name) => this.resolveModuleStringConst(name),
1816
1822
  resolveLiteralConst: (name) => this.resolveLiteralConst(name),
1817
1823
  resolveStaticRecordLiteral: (o, k) => this.resolveStaticRecordLiteral(o, k),
@@ -1868,42 +1874,16 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
1868
1874
  parsed = parseExpression(trimmed)
1869
1875
  }
1870
1876
 
1871
- // Registered call lowerings (#2057) — including the built-in `queryHref`
1872
- // plugin (#2042), which lowers `queryHref(base, { … })` to a neutral
1873
- // `guard-list` on the `query` helper → `bf->query(base, <triples>)`.
1874
- // Recognised before the support gate because the object-literal arg is
1875
- // otherwise `unsupported` (BF101). The `bf->query` helper includes a pair iff
1876
- // its guard is truthy AND its value is a non-empty string (the client's
1877
- // `if (value)`): a plain `key: v` passes guard `1`, a conditional
1878
- // `key: cond ? v : undefined` passes the lowered cond. Only the `query`
1879
- // helper renders to `bf->query`; another guard-list helper must not be
1880
- // silently mis-rendered as a query.
1881
- if (parsed.kind === 'call') {
1882
- for (const matcher of this._loweringMatchers) {
1883
- const node = matcher(parsed.callee, parsed.args)
1884
- if (node?.kind === 'guard-list' && node.helper === 'query') {
1885
- const argsGo = queryHrefArgs(node, n => this.renderParsedExprToPerl(n))
1886
- return `bf->query(${argsGo.join(', ')})`
1887
- }
1888
- // Generic `helper-call` (#2069) — the neutral vocabulary's escape
1889
- // hatch for a userland `LoweringPlugin` that lowers to a single
1890
- // runtime-helper invocation. `bf-><helper>(args…)` mirrors the
1891
- // `query` helper's own naming convention exactly: the framework
1892
- // renders the call, the plugin author registers `<helper>` as a
1893
- // method on their own Mojolicious `bf` helper object — same
1894
- // contract as `bf->query` itself, just not built in.
1895
- if (node?.kind === 'helper-call' && isValidHelperId(node.helper)) {
1896
- const argsX = node.args.map(a => this.renderParsedExprToPerl(a))
1897
- return `bf->${node.helper}(${argsX.join(', ')})`
1898
- }
1899
- }
1900
- }
1901
-
1902
1877
  // `pos` distinguishes a derived-seed RHS (an assignment, checked via
1903
1878
  // `isSupportedValue`) from every other, genuinely rendered call site —
1904
1879
  // the rendered gate would otherwise re-refuse a tree the seed plan
1905
1880
  // already classified `derived` at value position (#2696 review).
1906
- const support = pos === 'value' ? isSupportedValue(parsed) : isSupported(parsed)
1881
+ // Registered call lowerings (#2057) including the built-in `queryHref`
1882
+ // plugin — are consulted by this gate no matter where the call sits in
1883
+ // the tree (`MojoTopLevelEmitter`'s `lowering` seam does the actual
1884
+ // rendering below, via `emitParsedExpr`'s shared `call` case).
1885
+ const supportOpts = { loweringMatchers: this._loweringMatchers }
1886
+ const support = pos === 'value' ? isSupportedValue(parsed, supportOpts) : isSupported(parsed, supportOpts)
1907
1887
  if (!support.supported) {
1908
1888
  this.errors.push({
1909
1889
  code: 'BF101',
@@ -10,6 +10,10 @@
10
10
  import type { ConformancePins } from '@barefootjs/jsx'
11
11
 
12
12
  export const conformancePins: ConformancePins = {
13
+ // #2843: graduated — a registered lowering call inside a ternary
14
+ // attribute branch (or any nested value position) is now recognised via
15
+ // `MojoTopLevelEmitter`'s `lowering` seam + the registry-aware support
16
+ // gate, matching the direct-call attribute path exactly.
13
17
  'filter-typeof-predicate': [{ code: 'BF021', severity: 'error' }],
14
18
  'map-array-builder-body': [{ code: 'BF021', severity: 'error' }],
15
19
  'map-array-builder-escaping': [{ code: 'BF021', severity: 'error' }],
@@ -15,19 +15,4 @@ import type { RenderDivergences } from '@barefootjs/jsx'
15
15
  // at value position and the runtime evaluator's `object-literal` case
16
16
  // now merges it, so the seed classifies `derived` and SSRs identically
17
17
  // to Hono.
18
- // https://github.com/piconic-ai/barefootjs/issues/2788 a renamed
19
- // `children` destructure (`const { children: kids } = props`) interpolates
20
- // the LOCAL alias into the `.html.ep`, and the stash only carries the
21
- // caller-facing `children`, so Perl dies inside `Mojo::Template::process`
22
- // at render time rather than at build. Seven adapters resolve the alias;
23
- // only Mojo does not. Same family as `aliased-destructured-prop`'s
24
- // `{ n: count }` shape, one level in — the reserved children slot.
25
- // Delete this entry when #2788 is fixed: `children-passthrough-renamed`
26
- // already asserts the correct (Hono-generated) output, so it becomes the
27
- // regression test the moment the skip comes off.
28
- export const renderDivergences: RenderDivergences = {
29
- 'children-passthrough-renamed':
30
- 'A renamed `children` destructure emits the local alias as a template variable; the stash defines only `children`, so the Perl render dies (#2788).',
31
- 'aliased-loop-source':
32
- 'A `.map()` loop whose source is a local const alias of a signal getter (`const items__alias = items`) dies inside `Mojo::Template::process` on real Mojolicious — the seeded loop data is keyed by the signal\'s real name (`items`), and the alias hop is never resolved when deciding what to seed under `items__alias`. This is the SSR-side twin of #2778 (fixed for the CSR client-JS template in the same PR that added this fixture) — that fix only touches client-JS emission, not SSR data-seeding. Tracked at https://github.com/piconic-ai/barefootjs/issues/2813; graduate by resolving the alias hop at SSR-seeding time using the same `resolveAliasOrigin`/`resolveGetterAliases` mechanism #2778 introduced, rather than a third alias-hop walker.',
33
- }
18
+ export const renderDivergences: RenderDivergences = {}
@@ -548,6 +548,21 @@ function buildChildDefaultsPerl(ir: ComponentIR): string {
548
548
  `${memo.name} => ${value !== undefined && value !== null ? toPerlLiteral(value) : 'undef'}`,
549
549
  )
550
550
  }
551
+ // #2788: `extractSsrDefaults` can seed an entry under a name that is
552
+ // none of `propsParams` / `signals` / `memos` above — a bare-props-form
553
+ // component's body-level RENAMED destructure (`const { children: kids }
554
+ // = props`) seeds `kids` alongside the caller-facing `children`
555
+ // (`ssr-defaults.ts`). Real production hands `$entry->{ssrDefaults}`
556
+ // straight through to `_derive_stash_from_defaults`, which iterates
557
+ // every key it contains — so any leftover entry here must be added too,
558
+ // or this harness's hand-built stash literal would silently omit a key
559
+ // production's manifest-driven path already provides, mismeasuring
560
+ // this conformance case's real behavior.
561
+ for (const name of Object.keys(ssrDefaults)) {
562
+ if (declared.has(name)) continue
563
+ declared.add(name)
564
+ entries.push(`${name} => ${ssrDefaultEntryToPerl(ssrDefaults[name])}`)
565
+ }
551
566
  return `{${entries.join(', ')}}`
552
567
  }
553
568
 
@@ -758,6 +773,29 @@ function buildPerlProps(
758
773
  }
759
774
  }
760
775
 
776
+ // #2813: `extractSsrDefaults` can seed an entry under a name that is
777
+ // none of `propsParams` / the inherited-attribute scan / `signals` /
778
+ // `memos` above — a bare local-const alias of a signal/memo getter
779
+ // (`const items__alias = items`, `ssr-defaults.ts`) seeds `items__alias`
780
+ // alongside `items`. Production hands the whole manifest-derived stash
781
+ // straight through (`deriveStashFromDefaults` IS the production seeding
782
+ // mechanism here, not a harness stand-in), so any leftover
783
+ // `derivedProps` key must be pushed too, or this harness would silently
784
+ // omit what production actually provides, mismeasuring this
785
+ // conformance case — the same gap `buildChildDefaultsPerl` (above) was
786
+ // already fixed for child components.
787
+ const seededNames = new Set<string>([
788
+ ...ir.metadata.propsParams.map(p => p.name),
789
+ ...(ir.metadata.propsObjectName ? [...collectPropsObjectAccesses(ir, ir.metadata.propsObjectName)] : []),
790
+ ...(restPropsName ? [restPropsName] : []),
791
+ ...ir.metadata.signals.filter(s => !s.envReader).map(s => s.getter),
792
+ ...ir.metadata.memos.map(m => m.name),
793
+ ])
794
+ for (const [name, value] of Object.entries(derivedProps)) {
795
+ if (seededNames.has(name)) continue
796
+ entries.push(`${name} => ${toPerlLiteral(value)}`)
797
+ }
798
+
761
799
  // (#1922) Request-scoped `searchParams()`: bind `$searchParams` to an
762
800
  // empty-query reader via the lazy-loading factory (so the render script
763
801
  // needn't `use BarefootJS::SearchParams`). The conformance harness issues no