@barefootjs/mojolicious 0.33.1 → 0.33.3

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.
@@ -84,7 +84,7 @@ function isAriaBooleanAttr(name) {
84
84
  }
85
85
 
86
86
  // src/adapter/mojo-adapter.ts
87
- import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
87
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml, resolveJsxChildrenProp } from "@barefootjs/shared";
88
88
 
89
89
  // src/adapter/lib/constants.ts
90
90
  var MOJO_TEMPLATE_PRIMITIVES = {
@@ -112,39 +112,6 @@ function perlIdentifierFromMarkerId(markerId) {
112
112
  return markerId.replace(/[^a-zA-Z0-9]/g, (ch) => ch === "_" ? "__" : `_x${ch.charCodeAt(0).toString(16)}`);
113
113
  }
114
114
 
115
- // src/adapter/lib/ir-scope.ts
116
- function resolveJsxChildrenProp(props) {
117
- const prop = props.find((p) => p.name === "children");
118
- if (!prop)
119
- return [];
120
- if (prop.value.kind !== "jsx-children")
121
- return [];
122
- return prop.value.children;
123
- }
124
- function collectRootScopeNodes(node) {
125
- const out = new Set;
126
- const visit = (n) => {
127
- if (!n)
128
- return;
129
- if (n.type === "element") {
130
- out.add(n);
131
- return;
132
- }
133
- if (n.type === "if-statement") {
134
- const s = n;
135
- visit(s.consequent);
136
- visit(s.alternate);
137
- return;
138
- }
139
- if (n.type === "fragment") {
140
- for (const c of n.children)
141
- visit(c);
142
- }
143
- };
144
- visit(node);
145
- return out;
146
- }
147
-
148
115
  // src/adapter/expr/array-method.ts
149
116
  import {
150
117
  serializeParsedExpr,
@@ -1051,10 +1018,8 @@ class MojoAdapter extends BaseAdapter {
1051
1018
  templatesPerComponent = true;
1052
1019
  templatePrimitives = MOJO_PRIMITIVE_EMIT_MAP;
1053
1020
  componentName = "";
1054
- rootScopeNodes = new Set;
1055
1021
  options;
1056
1022
  errors = [];
1057
- currentLoopKeyDepth = 0;
1058
1023
  propsObjectName = null;
1059
1024
  propsParams = [];
1060
1025
  booleanTypedProps = new Set;
@@ -1092,7 +1057,6 @@ class MojoAdapter extends BaseAdapter {
1092
1057
  if (!options?.siblingTemplatesRegistered) {
1093
1058
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName));
1094
1059
  }
1095
- this.rootScopeNodes = collectRootScopeNodes(ir.root);
1096
1060
  const templateBody = ir.root.type === "if-statement" ? this.renderIfStatement(ir.root) : this.renderNode(ir.root);
1097
1061
  const scriptReg = options?.skipScriptRegistration ? "" : this.generateScriptRegistrations(ir, options?.scriptBaseName, options?.scriptAssets, options?.preloadAssets);
1098
1062
  const ctxSeed = generateContextConsumerSeed(ir);
@@ -1273,8 +1237,14 @@ class MojoAdapter extends BaseAdapter {
1273
1237
  if (element.needsScope) {
1274
1238
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
1275
1239
  }
1276
- if (this.rootScopeNodes.has(element) && element.needsScope) {
1277
- hydrationAttrs += ` <%== bf->data_key_attr %>`;
1240
+ if (element.keyAttr) {
1241
+ if (element.keyAttr.value !== undefined) {
1242
+ const lowered = emitAttrValue({ kind: "expression", expr: element.keyAttr.value }, this.elementAttrEmitter, element.keyAttr.name);
1243
+ if (lowered)
1244
+ hydrationAttrs += ` ${lowered}`;
1245
+ } else {
1246
+ hydrationAttrs += ` <%== bf->data_key_attr %>`;
1247
+ }
1278
1248
  }
1279
1249
  if (element.slotId) {
1280
1250
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
@@ -1455,10 +1425,7 @@ ${whenTrue}
1455
1425
  const preambleDecls = loop.preamble?.declarations ?? [];
1456
1426
  const prevScope = this.scope;
1457
1427
  this.scope = prevScope.enterLoopRow(loop);
1458
- const prevLoopKeyDepth = this.currentLoopKeyDepth;
1459
- this.currentLoopKeyDepth = loop.depth;
1460
1428
  const renderedChildren = this.renderChildren(loop.children);
1461
- this.currentLoopKeyDepth = prevLoopKeyDepth;
1462
1429
  const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
1463
1430
  ${renderedChildren}` : renderedChildren;
1464
1431
  const lines = [];
@@ -1724,14 +1691,9 @@ ${children}`;
1724
1691
  continue;
1725
1692
  if (isDangerousInnerHtmlAttr(attr))
1726
1693
  continue;
1727
- let attrName;
1728
- if (attr.name === "className")
1729
- attrName = "class";
1730
- else if (attr.name === "key") {
1731
- const depth = this.currentLoopKeyDepth;
1732
- attrName = depth > 0 ? `data-key-${depth}` : "data-key";
1733
- } else
1734
- attrName = attr.name;
1694
+ if (attr.name === "key")
1695
+ continue;
1696
+ const attrName = attr.name === "className" ? "class" : attr.name;
1735
1697
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
1736
1698
  if (lowered)
1737
1699
  parts.push(lowered);
@@ -26,22 +26,8 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
26
26
  */
27
27
  templatePrimitives: TemplatePrimitiveRegistry;
28
28
  private componentName;
29
- /** The component's root scope element(s) — each carries `data-key` for a
30
- * keyed loop item (set by the child renderer from the JSX `key` prop). A
31
- * plain element root is a single node; an `if-statement` (early-return) root
32
- * contributes the top element of every branch, since any one of them can be
33
- * the rendered root at runtime. */
34
- private rootScopeNodes;
35
29
  private options;
36
30
  private errors;
37
- /**
38
- * `IRLoop.depth` of the loop currently being rendered (save/restore
39
- * around `renderChildren(loop.children)`, mirroring `inLoop` above).
40
- * `renderAttributes` reads this to derive the `key` → `data-key`/
41
- * `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
42
- * re-derived here (#2168 nested-loop-outer-binding).
43
- */
44
- private currentLoopKeyDepth;
45
31
  /**
46
32
  * SolidJS-style props identifier (`function(props: P)`) and the
47
33
  * analyzer-extracted prop names. Stashed at `generate()` entry so
@@ -1 +1 @@
1
- {"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -84,7 +84,7 @@ function isAriaBooleanAttr(name) {
84
84
  }
85
85
 
86
86
  // src/adapter/mojo-adapter.ts
87
- import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
87
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml, resolveJsxChildrenProp } from "@barefootjs/shared";
88
88
 
89
89
  // src/adapter/lib/constants.ts
90
90
  var MOJO_TEMPLATE_PRIMITIVES = {
@@ -112,39 +112,6 @@ function perlIdentifierFromMarkerId(markerId) {
112
112
  return markerId.replace(/[^a-zA-Z0-9]/g, (ch) => ch === "_" ? "__" : `_x${ch.charCodeAt(0).toString(16)}`);
113
113
  }
114
114
 
115
- // src/adapter/lib/ir-scope.ts
116
- function resolveJsxChildrenProp(props) {
117
- const prop = props.find((p) => p.name === "children");
118
- if (!prop)
119
- return [];
120
- if (prop.value.kind !== "jsx-children")
121
- return [];
122
- return prop.value.children;
123
- }
124
- function collectRootScopeNodes(node) {
125
- const out = new Set;
126
- const visit = (n) => {
127
- if (!n)
128
- return;
129
- if (n.type === "element") {
130
- out.add(n);
131
- return;
132
- }
133
- if (n.type === "if-statement") {
134
- const s = n;
135
- visit(s.consequent);
136
- visit(s.alternate);
137
- return;
138
- }
139
- if (n.type === "fragment") {
140
- for (const c of n.children)
141
- visit(c);
142
- }
143
- };
144
- visit(node);
145
- return out;
146
- }
147
-
148
115
  // src/adapter/expr/array-method.ts
149
116
  import {
150
117
  serializeParsedExpr,
@@ -1051,10 +1018,8 @@ class MojoAdapter extends BaseAdapter {
1051
1018
  templatesPerComponent = true;
1052
1019
  templatePrimitives = MOJO_PRIMITIVE_EMIT_MAP;
1053
1020
  componentName = "";
1054
- rootScopeNodes = new Set;
1055
1021
  options;
1056
1022
  errors = [];
1057
- currentLoopKeyDepth = 0;
1058
1023
  propsObjectName = null;
1059
1024
  propsParams = [];
1060
1025
  booleanTypedProps = new Set;
@@ -1092,7 +1057,6 @@ class MojoAdapter extends BaseAdapter {
1092
1057
  if (!options?.siblingTemplatesRegistered) {
1093
1058
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName));
1094
1059
  }
1095
- this.rootScopeNodes = collectRootScopeNodes(ir.root);
1096
1060
  const templateBody = ir.root.type === "if-statement" ? this.renderIfStatement(ir.root) : this.renderNode(ir.root);
1097
1061
  const scriptReg = options?.skipScriptRegistration ? "" : this.generateScriptRegistrations(ir, options?.scriptBaseName, options?.scriptAssets, options?.preloadAssets);
1098
1062
  const ctxSeed = generateContextConsumerSeed(ir);
@@ -1273,8 +1237,14 @@ class MojoAdapter extends BaseAdapter {
1273
1237
  if (element.needsScope) {
1274
1238
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
1275
1239
  }
1276
- if (this.rootScopeNodes.has(element) && element.needsScope) {
1277
- hydrationAttrs += ` <%== bf->data_key_attr %>`;
1240
+ if (element.keyAttr) {
1241
+ if (element.keyAttr.value !== undefined) {
1242
+ const lowered = emitAttrValue({ kind: "expression", expr: element.keyAttr.value }, this.elementAttrEmitter, element.keyAttr.name);
1243
+ if (lowered)
1244
+ hydrationAttrs += ` ${lowered}`;
1245
+ } else {
1246
+ hydrationAttrs += ` <%== bf->data_key_attr %>`;
1247
+ }
1278
1248
  }
1279
1249
  if (element.slotId) {
1280
1250
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
@@ -1455,10 +1425,7 @@ ${whenTrue}
1455
1425
  const preambleDecls = loop.preamble?.declarations ?? [];
1456
1426
  const prevScope = this.scope;
1457
1427
  this.scope = prevScope.enterLoopRow(loop);
1458
- const prevLoopKeyDepth = this.currentLoopKeyDepth;
1459
- this.currentLoopKeyDepth = loop.depth;
1460
1428
  const renderedChildren = this.renderChildren(loop.children);
1461
- this.currentLoopKeyDepth = prevLoopKeyDepth;
1462
1429
  const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
1463
1430
  ${renderedChildren}` : renderedChildren;
1464
1431
  const lines = [];
@@ -1724,14 +1691,9 @@ ${children}`;
1724
1691
  continue;
1725
1692
  if (isDangerousInnerHtmlAttr(attr))
1726
1693
  continue;
1727
- let attrName;
1728
- if (attr.name === "className")
1729
- attrName = "class";
1730
- else if (attr.name === "key") {
1731
- const depth = this.currentLoopKeyDepth;
1732
- attrName = depth > 0 ? `data-key-${depth}` : "data-key";
1733
- } else
1734
- attrName = attr.name;
1694
+ if (attr.name === "key")
1695
+ continue;
1696
+ const attrName = attr.name === "className" ? "class" : attr.name;
1735
1697
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
1736
1698
  if (lowered)
1737
1699
  parts.push(lowered);
@@ -1941,7 +1903,9 @@ var conformancePins = {
1941
1903
  "jsx-element-prop-array": [{ code: "BF021", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2667" }]
1942
1904
  };
1943
1905
  // src/render-divergences.ts
1944
- var renderDivergences = {};
1906
+ var renderDivergences = {
1907
+ "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)."
1908
+ };
1945
1909
  export {
1946
1910
  renderDivergences,
1947
1911
  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;AAQxD,eAAO,MAAM,iBAAiB,EAAE,iBAAsB,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;AAkBxD,eAAO,MAAM,iBAAiB,EAAE,iBAG/B,CAAA"}
package/dist/vite.js CHANGED
@@ -5,7 +5,7 @@ import { barefoot as coreBarefoot } from "@barefootjs/vite";
5
5
  import { devModuleUrl, loadManifest, resolveDevOrigin, resolveScriptAssets, toPosixRelative } from "@barefootjs/vite";
6
6
 
7
7
  // ../jsx/src/compiler.ts
8
- import ts24 from "typescript";
8
+ import ts25 from "typescript";
9
9
 
10
10
  // ../jsx/src/analyzer.ts
11
11
  import ts9 from "typescript";
@@ -2082,6 +2082,15 @@ function isBooleanAttr(name) {
2082
2082
  function escapeHtml(text) {
2083
2083
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2084
2084
  }
2085
+ // ../shared/src/jsx-children-prop.ts
2086
+ function resolveJsxChildrenProp(props) {
2087
+ const prop = props.find((p) => p.name === "children");
2088
+ if (!prop)
2089
+ return [];
2090
+ if (prop.value.kind !== "jsx-children")
2091
+ return [];
2092
+ return prop.value.children ?? [];
2093
+ }
2085
2094
  // ../jsx/src/ir-to-client-js/csr-substitute.ts
2086
2095
  import ts4 from "typescript";
2087
2096
  function extractFreeIdentifiersFromText(text) {
@@ -3008,6 +3017,10 @@ var EMPTY_BOUND = new Set;
3008
3017
  var constInitializerCache = new WeakMap;
3009
3018
  var functionInfoExprCache = new WeakMap;
3010
3019
 
3020
+ // ../jsx/src/ir-to-client-js/prop-handling.ts
3021
+ var _localConstantValuesCache = new WeakMap;
3022
+ var _restSpreadNamesCache = new WeakMap;
3023
+
3011
3024
  // ../jsx/src/ir-to-client-js/control-flow/stringify/template-parse.ts
3012
3025
  var SVG_ROOT_TAGS = new Set([
3013
3026
  "svg",
@@ -3129,11 +3142,14 @@ var KEYWORDS_AND_GLOBALS = new Set([
3129
3142
  // ../jsx/src/ir-to-client-js/walk-prop-accesses.ts
3130
3143
  import ts14 from "typescript";
3131
3144
 
3145
+ // ../jsx/src/ir-to-client-js/imports.ts
3146
+ import ts16 from "typescript";
3147
+
3132
3148
  // ../jsx/src/value-references.ts
3133
3149
  import ts15 from "typescript";
3134
3150
 
3135
3151
  // ../jsx/src/relocate.ts
3136
- import ts16 from "typescript";
3152
+ import ts17 from "typescript";
3137
3153
 
3138
3154
  // ../jsx/src/lowering-registry.ts
3139
3155
  var plugins = [];
@@ -3350,10 +3366,10 @@ function matchSearchParamsMethodCall(callee, args, localNames) {
3350
3366
  }
3351
3367
 
3352
3368
  // ../jsx/src/ir-to-client-js/prune-unused-prop-extractions.ts
3353
- import ts17 from "typescript";
3369
+ import ts18 from "typescript";
3354
3370
 
3355
3371
  // ../jsx/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts
3356
- import ts18 from "typescript";
3372
+ import ts19 from "typescript";
3357
3373
  var NO_PREAMBLE = {
3358
3374
  lazySafe: true,
3359
3375
  facts: { declaredNames: new Set, freeNames: new Set }
@@ -3403,7 +3419,7 @@ var INERT_BINDING_GLOBALS = new Set([
3403
3419
  ]);
3404
3420
 
3405
3421
  // ../jsx/src/ir-to-client-js/emit-reactive.ts
3406
- import ts19 from "typescript";
3422
+ import ts20 from "typescript";
3407
3423
 
3408
3424
  // ../jsx/src/ir-to-client-js/control-flow/stringify/event-delegation.ts
3409
3425
  var NON_BUBBLING_EVENTS = new Set([
@@ -3418,7 +3434,7 @@ var NON_BUBBLING_EVENTS = new Set([
3418
3434
  ]);
3419
3435
 
3420
3436
  // ../jsx/src/ir-to-client-js/rewrite-props-object.ts
3421
- import ts20 from "typescript";
3437
+ import ts21 from "typescript";
3422
3438
 
3423
3439
  // ../jsx/src/ir-to-client-js/source-map.ts
3424
3440
  var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -3509,15 +3525,15 @@ class SourceMapGenerator {
3509
3525
  }
3510
3526
 
3511
3527
  // ../jsx/src/preprocess-inline-jsx-callbacks.ts
3512
- import ts21 from "typescript";
3528
+ import ts22 from "typescript";
3513
3529
 
3514
3530
  // ../jsx/src/ssr-defaults.ts
3515
- import ts22 from "typescript";
3531
+ import ts23 from "typescript";
3516
3532
  var UNRESOLVED = Symbol("unresolved");
3517
3533
  var NO_RETURN = Symbol("no-return");
3518
3534
 
3519
3535
  // ../jsx/src/augment-inherited-props.ts
3520
- import ts23 from "typescript";
3536
+ import ts24 from "typescript";
3521
3537
  function collectContextConsumers(metadata) {
3522
3538
  const constants = metadata.localConstants ?? [];
3523
3539
  const contextDefaults = new Map;
@@ -3549,47 +3565,47 @@ function collectContextConsumers(metadata) {
3549
3565
  }
3550
3566
  function parseUseContextArg(source) {
3551
3567
  const expr = parseSingleExpression(source);
3552
- if (!expr || !ts23.isCallExpression(expr))
3568
+ if (!expr || !ts24.isCallExpression(expr))
3553
3569
  return null;
3554
- if (!ts23.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
3570
+ if (!ts24.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
3555
3571
  return null;
3556
3572
  if (expr.arguments.length !== 1)
3557
3573
  return null;
3558
3574
  const arg = expr.arguments[0];
3559
- return ts23.isIdentifier(arg) ? arg.text : null;
3575
+ return ts24.isIdentifier(arg) ? arg.text : null;
3560
3576
  }
3561
3577
  function parseCreateContextDefault(source) {
3562
3578
  const expr = parseSingleExpression(source);
3563
- if (!expr || !ts23.isCallExpression(expr))
3579
+ if (!expr || !ts24.isCallExpression(expr))
3564
3580
  return null;
3565
3581
  if (expr.arguments.length === 0)
3566
3582
  return null;
3567
3583
  const arg = expr.arguments[0];
3568
- if (ts23.isStringLiteral(arg) || ts23.isNoSubstitutionTemplateLiteral(arg))
3584
+ if (ts24.isStringLiteral(arg) || ts24.isNoSubstitutionTemplateLiteral(arg))
3569
3585
  return arg.text;
3570
- if (ts23.isNumericLiteral(arg))
3586
+ if (ts24.isNumericLiteral(arg))
3571
3587
  return Number(arg.text);
3572
- if (arg.kind === ts23.SyntaxKind.TrueKeyword)
3588
+ if (arg.kind === ts24.SyntaxKind.TrueKeyword)
3573
3589
  return true;
3574
- if (arg.kind === ts23.SyntaxKind.FalseKeyword)
3590
+ if (arg.kind === ts24.SyntaxKind.FalseKeyword)
3575
3591
  return false;
3576
3592
  return null;
3577
3593
  }
3578
3594
  function isObjectLiteralCreateContextDefault(source) {
3579
3595
  const expr = parseSingleExpression(source);
3580
- if (!expr || !ts23.isCallExpression(expr))
3596
+ if (!expr || !ts24.isCallExpression(expr))
3581
3597
  return false;
3582
3598
  if (expr.arguments.length === 0)
3583
3599
  return false;
3584
- return ts23.isObjectLiteralExpression(expr.arguments[0]);
3600
+ return ts24.isObjectLiteralExpression(expr.arguments[0]);
3585
3601
  }
3586
3602
  function parseSingleExpression(source) {
3587
- const sf = ts23.createSourceFile("__ctx.ts", `(${source})`, ts23.ScriptTarget.Latest, false);
3603
+ const sf = ts24.createSourceFile("__ctx.ts", `(${source})`, ts24.ScriptTarget.Latest, false);
3588
3604
  const stmt = sf.statements[0];
3589
- if (!stmt || !ts23.isExpressionStatement(stmt))
3605
+ if (!stmt || !ts24.isExpressionStatement(stmt))
3590
3606
  return null;
3591
3607
  let e = stmt.expression;
3592
- while (ts23.isParenthesizedExpression(e))
3608
+ while (ts24.isParenthesizedExpression(e))
3593
3609
  e = e.expression;
3594
3610
  return e;
3595
3611
  }
@@ -3614,25 +3630,25 @@ function augmentInheritedPropAccesses(ir) {
3614
3630
  const pinCoalesceLiterals = (s) => {
3615
3631
  if (!s || !s.includes(propsObj))
3616
3632
  return;
3617
- const sf = ts23.createSourceFile("__aug.ts", `(${s})`, ts23.ScriptTarget.Latest, false);
3633
+ const sf = ts24.createSourceFile("__aug.ts", `(${s})`, ts24.ScriptTarget.Latest, false);
3618
3634
  const visit = (n) => {
3619
- if (ts23.isBinaryExpression(n) && (n.operatorToken.kind === ts23.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts23.SyntaxKind.BarBarToken)) {
3635
+ if (ts24.isBinaryExpression(n) && (n.operatorToken.kind === ts24.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts24.SyntaxKind.BarBarToken)) {
3620
3636
  let left = n.left;
3621
- while (ts23.isParenthesizedExpression(left))
3637
+ while (ts24.isParenthesizedExpression(left))
3622
3638
  left = left.expression;
3623
- if (ts23.isPropertyAccessExpression(left) && ts23.isIdentifier(left.expression) && left.expression.text === propsObj) {
3639
+ if (ts24.isPropertyAccessExpression(left) && ts24.isIdentifier(left.expression) && left.expression.text === propsObj) {
3624
3640
  const name = left.name.text;
3625
3641
  let right = n.right;
3626
- while (ts23.isParenthesizedExpression(right))
3642
+ while (ts24.isParenthesizedExpression(right))
3627
3643
  right = right.expression;
3628
- if (ts23.isPrefixUnaryExpression(right))
3644
+ if (ts24.isPrefixUnaryExpression(right))
3629
3645
  right = right.operand;
3630
- const kind = ts23.isNumericLiteral(right) ? "number" : right.kind === ts23.SyntaxKind.TrueKeyword || right.kind === ts23.SyntaxKind.FalseKeyword ? "boolean" : ts23.isStringLiteralLike(right) ? "string" : null;
3646
+ const kind = ts24.isNumericLiteral(right) ? "number" : right.kind === ts24.SyntaxKind.TrueKeyword || right.kind === ts24.SyntaxKind.FalseKeyword ? "boolean" : ts24.isStringLiteralLike(right) ? "string" : null;
3631
3647
  if (kind && !coalesceLiteralTypes.has(name))
3632
3648
  coalesceLiteralTypes.set(name, kind);
3633
3649
  }
3634
3650
  }
3635
- ts23.forEachChild(n, visit);
3651
+ ts24.forEachChild(n, visit);
3636
3652
  };
3637
3653
  visit(sf);
3638
3654
  };
@@ -3743,33 +3759,33 @@ function augmentInheritedPropAccesses(ir) {
3743
3759
  }
3744
3760
  }
3745
3761
  function parseStaticStringConst(source) {
3746
- const sf = ts23.createSourceFile("__const.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
3762
+ const sf = ts24.createSourceFile("__const.ts", `const __x = (${source});`, ts24.ScriptTarget.Latest, false);
3747
3763
  const stmt = sf.statements[0];
3748
- if (!stmt || !ts23.isVariableStatement(stmt))
3764
+ if (!stmt || !ts24.isVariableStatement(stmt))
3749
3765
  return null;
3750
3766
  let init = stmt.declarationList.declarations[0]?.initializer;
3751
- while (init && ts23.isParenthesizedExpression(init))
3767
+ while (init && ts24.isParenthesizedExpression(init))
3752
3768
  init = init.expression;
3753
3769
  if (!init)
3754
3770
  return null;
3755
- if (ts23.isStringLiteral(init) || ts23.isNoSubstitutionTemplateLiteral(init)) {
3771
+ if (ts24.isStringLiteral(init) || ts24.isNoSubstitutionTemplateLiteral(init)) {
3756
3772
  return init.text;
3757
3773
  }
3758
3774
  return evalStringArrayJoin(source);
3759
3775
  }
3760
3776
  function evalTemplateOfStringConsts(source, resolved) {
3761
- const sf = ts23.createSourceFile("__const.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
3777
+ const sf = ts24.createSourceFile("__const.ts", `const __x = (${source});`, ts24.ScriptTarget.Latest, false);
3762
3778
  const stmt = sf.statements[0];
3763
- if (!stmt || !ts23.isVariableStatement(stmt))
3779
+ if (!stmt || !ts24.isVariableStatement(stmt))
3764
3780
  return null;
3765
3781
  let init = stmt.declarationList.declarations[0]?.initializer;
3766
- while (init && ts23.isParenthesizedExpression(init))
3782
+ while (init && ts24.isParenthesizedExpression(init))
3767
3783
  init = init.expression;
3768
- if (!init || !ts23.isTemplateExpression(init))
3784
+ if (!init || !ts24.isTemplateExpression(init))
3769
3785
  return null;
3770
3786
  let out = init.head.text;
3771
3787
  for (const span of init.templateSpans) {
3772
- if (!ts23.isIdentifier(span.expression))
3788
+ if (!ts24.isIdentifier(span.expression))
3773
3789
  return null;
3774
3790
  const value = resolved.get(span.expression.text);
3775
3791
  if (value === undefined)
@@ -3802,30 +3818,30 @@ function lookupStaticRecordLiteral(objectName, key, constants, isShadowed) {
3802
3818
  const constInfo = (constants ?? []).find((c) => c.name === objectName && c.isModule);
3803
3819
  if (constInfo?.value === undefined)
3804
3820
  return null;
3805
- const sf = ts23.createSourceFile("__rec.ts", `(${constInfo.value})`, ts23.ScriptTarget.Latest, true);
3821
+ const sf = ts24.createSourceFile("__rec.ts", `(${constInfo.value})`, ts24.ScriptTarget.Latest, true);
3806
3822
  if (sf.statements.length !== 1)
3807
3823
  return null;
3808
3824
  const stmt = sf.statements[0];
3809
- if (!ts23.isExpressionStatement(stmt))
3825
+ if (!ts24.isExpressionStatement(stmt))
3810
3826
  return null;
3811
3827
  let parsed = stmt.expression;
3812
- while (ts23.isParenthesizedExpression(parsed))
3828
+ while (ts24.isParenthesizedExpression(parsed))
3813
3829
  parsed = parsed.expression;
3814
- if (!ts23.isObjectLiteralExpression(parsed))
3830
+ if (!ts24.isObjectLiteralExpression(parsed))
3815
3831
  return null;
3816
3832
  for (const prop of parsed.properties) {
3817
- if (!ts23.isPropertyAssignment(prop))
3833
+ if (!ts24.isPropertyAssignment(prop))
3818
3834
  continue;
3819
3835
  const name = prop.name;
3820
- const propKey = ts23.isIdentifier(name) || ts23.isStringLiteral(name) || ts23.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
3836
+ const propKey = ts24.isIdentifier(name) || ts24.isStringLiteral(name) || ts24.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
3821
3837
  if (propKey !== key)
3822
3838
  continue;
3823
3839
  let v = prop.initializer;
3824
- while (ts23.isParenthesizedExpression(v))
3840
+ while (ts24.isParenthesizedExpression(v))
3825
3841
  v = v.expression;
3826
- if (ts23.isNumericLiteral(v))
3842
+ if (ts24.isNumericLiteral(v))
3827
3843
  return { kind: "number", text: v.text };
3828
- if (ts23.isStringLiteral(v) || ts23.isNoSubstitutionTemplateLiteral(v)) {
3844
+ if (ts24.isStringLiteral(v) || ts24.isNoSubstitutionTemplateLiteral(v)) {
3829
3845
  return { kind: "string", text: v.text };
3830
3846
  }
3831
3847
  return null;
@@ -3833,28 +3849,28 @@ function lookupStaticRecordLiteral(objectName, key, constants, isShadowed) {
3833
3849
  return null;
3834
3850
  }
3835
3851
  function evalStringArrayJoin(source) {
3836
- const sf = ts23.createSourceFile("__join.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
3852
+ const sf = ts24.createSourceFile("__join.ts", `const __x = (${source});`, ts24.ScriptTarget.Latest, false);
3837
3853
  const stmt = sf.statements[0];
3838
- if (!stmt || !ts23.isVariableStatement(stmt))
3854
+ if (!stmt || !ts24.isVariableStatement(stmt))
3839
3855
  return null;
3840
3856
  let node = stmt.declarationList.declarations[0]?.initializer;
3841
- while (node && ts23.isParenthesizedExpression(node))
3857
+ while (node && ts24.isParenthesizedExpression(node))
3842
3858
  node = node.expression;
3843
- if (!node || !ts23.isCallExpression(node))
3859
+ if (!node || !ts24.isCallExpression(node))
3844
3860
  return null;
3845
3861
  const callee = node.expression;
3846
- if (!ts23.isPropertyAccessExpression(callee))
3862
+ if (!ts24.isPropertyAccessExpression(callee))
3847
3863
  return null;
3848
3864
  if (callee.name.text !== "join")
3849
3865
  return null;
3850
3866
  let recv = callee.expression;
3851
- while (ts23.isParenthesizedExpression(recv))
3867
+ while (ts24.isParenthesizedExpression(recv))
3852
3868
  recv = recv.expression;
3853
- if (!ts23.isArrayLiteralExpression(recv))
3869
+ if (!ts24.isArrayLiteralExpression(recv))
3854
3870
  return null;
3855
3871
  const parts = [];
3856
3872
  for (const el of recv.elements) {
3857
- if (ts23.isStringLiteral(el) || ts23.isNoSubstitutionTemplateLiteral(el)) {
3873
+ if (ts24.isStringLiteral(el) || ts24.isNoSubstitutionTemplateLiteral(el)) {
3858
3874
  parts.push(el.text);
3859
3875
  } else {
3860
3876
  return null;
@@ -3863,7 +3879,7 @@ function evalStringArrayJoin(source) {
3863
3879
  let sep = ",";
3864
3880
  if (node.arguments.length >= 1) {
3865
3881
  const arg = node.arguments[0];
3866
- if (ts23.isStringLiteral(arg) || ts23.isNoSubstitutionTemplateLiteral(arg))
3882
+ if (ts24.isStringLiteral(arg) || ts24.isNoSubstitutionTemplateLiteral(arg))
3867
3883
  sep = arg.text;
3868
3884
  else
3869
3885
  return null;
@@ -3871,11 +3887,11 @@ function evalStringArrayJoin(source) {
3871
3887
  return parts.join(sep);
3872
3888
  }
3873
3889
  function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
3874
- if (!ts23.isElementAccessExpression(val))
3890
+ if (!ts24.isElementAccessExpression(val))
3875
3891
  return null;
3876
3892
  const obj = val.expression;
3877
3893
  const arg = val.argumentExpression;
3878
- if (!ts23.isIdentifier(obj) || !ts23.isIdentifier(arg))
3894
+ if (!ts24.isIdentifier(obj) || !ts24.isIdentifier(arg))
3879
3895
  return null;
3880
3896
  let indexPropName;
3881
3897
  let defaultKey;
@@ -3891,35 +3907,35 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
3891
3907
  const constInfo = localConstants.find((c) => c.name === obj.text && c.isModule);
3892
3908
  if (constInfo?.value === undefined)
3893
3909
  return null;
3894
- const sf = ts23.createSourceFile("__rec.ts", `(${constInfo.value})`, ts23.ScriptTarget.Latest, true);
3910
+ const sf = ts24.createSourceFile("__rec.ts", `(${constInfo.value})`, ts24.ScriptTarget.Latest, true);
3895
3911
  if (sf.statements.length !== 1)
3896
3912
  return null;
3897
3913
  const stmt = sf.statements[0];
3898
- if (!ts23.isExpressionStatement(stmt))
3914
+ if (!ts24.isExpressionStatement(stmt))
3899
3915
  return null;
3900
3916
  let parsed = stmt.expression;
3901
- while (ts23.isParenthesizedExpression(parsed))
3917
+ while (ts24.isParenthesizedExpression(parsed))
3902
3918
  parsed = parsed.expression;
3903
- if (!ts23.isObjectLiteralExpression(parsed))
3919
+ if (!ts24.isObjectLiteralExpression(parsed))
3904
3920
  return null;
3905
3921
  const entries = [];
3906
3922
  for (const prop of parsed.properties) {
3907
- if (!ts23.isPropertyAssignment(prop))
3923
+ if (!ts24.isPropertyAssignment(prop))
3908
3924
  return null;
3909
3925
  let key;
3910
- if (ts23.isIdentifier(prop.name)) {
3926
+ if (ts24.isIdentifier(prop.name)) {
3911
3927
  key = prop.name.text;
3912
- } else if (ts23.isStringLiteral(prop.name) || ts23.isNoSubstitutionTemplateLiteral(prop.name)) {
3928
+ } else if (ts24.isStringLiteral(prop.name) || ts24.isNoSubstitutionTemplateLiteral(prop.name)) {
3913
3929
  key = prop.name.text;
3914
3930
  } else {
3915
3931
  return null;
3916
3932
  }
3917
3933
  let v = prop.initializer;
3918
- while (ts23.isParenthesizedExpression(v))
3934
+ while (ts24.isParenthesizedExpression(v))
3919
3935
  v = v.expression;
3920
- if (ts23.isNumericLiteral(v)) {
3936
+ if (ts24.isNumericLiteral(v)) {
3921
3937
  entries.push({ key, value: { kind: "number", text: v.text } });
3922
- } else if (ts23.isStringLiteral(v) || ts23.isNoSubstitutionTemplateLiteral(v)) {
3938
+ } else if (ts24.isStringLiteral(v) || ts24.isNoSubstitutionTemplateLiteral(v)) {
3923
3939
  entries.push({ key, value: { kind: "string", text: v.text } });
3924
3940
  } else {
3925
3941
  return null;
@@ -4008,7 +4024,7 @@ function computeSsrSeedPlan(metadata) {
4008
4024
  // ../jsx/src/rich-type-refusal.ts
4009
4025
  var EMPTY_BINDINGS2 = new Map;
4010
4026
  // ../jsx/src/shared-program.ts
4011
- import ts25 from "typescript";
4027
+ import ts26 from "typescript";
4012
4028
  // ../jsx/src/adapters/interface.ts
4013
4029
  class BaseAdapter {
4014
4030
  renderChildren(children) {
@@ -4290,7 +4306,7 @@ class JsxAdapter extends BaseAdapter {
4290
4306
  }
4291
4307
 
4292
4308
  // ../jsx/src/adapters/template-imports.ts
4293
- import ts26 from "typescript";
4309
+ import ts27 from "typescript";
4294
4310
  var CLIENT_PACKAGE_SOURCES = new Set([
4295
4311
  "@barefootjs/client",
4296
4312
  "@barefootjs/client/runtime"
@@ -5070,7 +5086,7 @@ function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
5070
5086
  };
5071
5087
  }
5072
5088
  // ../jsx/src/combine-client-js.ts
5073
- import ts27 from "typescript";
5089
+ import ts28 from "typescript";
5074
5090
  // ../jsx/src/loop-destructure.ts
5075
5091
  function isLowerableLoopDestructure(loop) {
5076
5092
  const bindings = loop.paramBindings;
@@ -5210,9 +5226,9 @@ function escapeRe(s) {
5210
5226
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5211
5227
  }
5212
5228
  // ../jsx/src/debug.ts
5213
- import ts28 from "typescript";
5214
- // ../jsx/src/profiler.ts
5215
5229
  import ts29 from "typescript";
5230
+ // ../jsx/src/profiler.ts
5231
+ import ts30 from "typescript";
5216
5232
 
5217
5233
  // ../jsx/src/index.ts
5218
5234
  registerBuiltinLoweringPlugins();
@@ -5295,39 +5311,6 @@ function perlIdentifierFromMarkerId(markerId) {
5295
5311
  return markerId.replace(/[^a-zA-Z0-9]/g, (ch) => ch === "_" ? "__" : `_x${ch.charCodeAt(0).toString(16)}`);
5296
5312
  }
5297
5313
 
5298
- // src/adapter/lib/ir-scope.ts
5299
- function resolveJsxChildrenProp(props) {
5300
- const prop = props.find((p) => p.name === "children");
5301
- if (!prop)
5302
- return [];
5303
- if (prop.value.kind !== "jsx-children")
5304
- return [];
5305
- return prop.value.children;
5306
- }
5307
- function collectRootScopeNodes(node) {
5308
- const out = new Set;
5309
- const visit = (n) => {
5310
- if (!n)
5311
- return;
5312
- if (n.type === "element") {
5313
- out.add(n);
5314
- return;
5315
- }
5316
- if (n.type === "if-statement") {
5317
- const s = n;
5318
- visit(s.consequent);
5319
- visit(s.alternate);
5320
- return;
5321
- }
5322
- if (n.type === "fragment") {
5323
- for (const c of n.children)
5324
- visit(c);
5325
- }
5326
- };
5327
- visit(node);
5328
- return out;
5329
- }
5330
-
5331
5314
  // src/adapter/expr/array-method.ts
5332
5315
  function renderArrayMethod(method, object, args, emit) {
5333
5316
  switch (method) {
@@ -6044,7 +6027,7 @@ function collectImportedLoopChildComponentErrors(ir, componentName) {
6044
6027
  }
6045
6028
 
6046
6029
  // src/adapter/spread/spread-codegen.ts
6047
- import ts30 from "typescript";
6030
+ import ts31 from "typescript";
6048
6031
  function conditionalSpreadToPerl(ctx, expr) {
6049
6032
  if (!expr || expr.kind !== "conditional")
6050
6033
  return null;
@@ -6101,7 +6084,7 @@ function recordIndexAccessToPerl(ctx, val) {
6101
6084
  if (val.kind !== "index-access" || val.object.kind !== "identifier" || val.index.kind !== "identifier") {
6102
6085
  return null;
6103
6086
  }
6104
- const tsVal = ts30.factory.createElementAccessExpression(ts30.factory.createIdentifier(val.object.name), ts30.factory.createIdentifier(val.index.name));
6087
+ const tsVal = ts31.factory.createElementAccessExpression(ts31.factory.createIdentifier(val.object.name), ts31.factory.createIdentifier(val.index.name));
6105
6088
  const parsed = parseRecordIndexAccess(tsVal, ctx.localConstants, ctx.propsParams);
6106
6089
  if (!parsed)
6107
6090
  return null;
@@ -6210,10 +6193,8 @@ class MojoAdapter extends BaseAdapter {
6210
6193
  templatesPerComponent = true;
6211
6194
  templatePrimitives = MOJO_PRIMITIVE_EMIT_MAP;
6212
6195
  componentName = "";
6213
- rootScopeNodes = new Set;
6214
6196
  options;
6215
6197
  errors = [];
6216
- currentLoopKeyDepth = 0;
6217
6198
  propsObjectName = null;
6218
6199
  propsParams = [];
6219
6200
  booleanTypedProps = new Set;
@@ -6251,7 +6232,6 @@ class MojoAdapter extends BaseAdapter {
6251
6232
  if (!options?.siblingTemplatesRegistered) {
6252
6233
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName));
6253
6234
  }
6254
- this.rootScopeNodes = collectRootScopeNodes(ir.root);
6255
6235
  const templateBody = ir.root.type === "if-statement" ? this.renderIfStatement(ir.root) : this.renderNode(ir.root);
6256
6236
  const scriptReg = options?.skipScriptRegistration ? "" : this.generateScriptRegistrations(ir, options?.scriptBaseName, options?.scriptAssets, options?.preloadAssets);
6257
6237
  const ctxSeed = generateContextConsumerSeed(ir);
@@ -6432,8 +6412,14 @@ class MojoAdapter extends BaseAdapter {
6432
6412
  if (element.needsScope) {
6433
6413
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
6434
6414
  }
6435
- if (this.rootScopeNodes.has(element) && element.needsScope) {
6436
- hydrationAttrs += ` <%== bf->data_key_attr %>`;
6415
+ if (element.keyAttr) {
6416
+ if (element.keyAttr.value !== undefined) {
6417
+ const lowered = emitAttrValue({ kind: "expression", expr: element.keyAttr.value }, this.elementAttrEmitter, element.keyAttr.name);
6418
+ if (lowered)
6419
+ hydrationAttrs += ` ${lowered}`;
6420
+ } else {
6421
+ hydrationAttrs += ` <%== bf->data_key_attr %>`;
6422
+ }
6437
6423
  }
6438
6424
  if (element.slotId) {
6439
6425
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
@@ -6614,10 +6600,7 @@ ${whenTrue}
6614
6600
  const preambleDecls = loop.preamble?.declarations ?? [];
6615
6601
  const prevScope = this.scope;
6616
6602
  this.scope = prevScope.enterLoopRow(loop);
6617
- const prevLoopKeyDepth = this.currentLoopKeyDepth;
6618
- this.currentLoopKeyDepth = loop.depth;
6619
6603
  const renderedChildren = this.renderChildren(loop.children);
6620
- this.currentLoopKeyDepth = prevLoopKeyDepth;
6621
6604
  const children = loop.bodyIsItemConditional && loop.key ? `<%== bf->comment("loop-i:" . ${this.convertExpressionToPerl(loop.key)}) %>
6622
6605
  ${renderedChildren}` : renderedChildren;
6623
6606
  const lines = [];
@@ -6883,14 +6866,9 @@ ${children}`;
6883
6866
  continue;
6884
6867
  if (isDangerousInnerHtmlAttr(attr))
6885
6868
  continue;
6886
- let attrName;
6887
- if (attr.name === "className")
6888
- attrName = "class";
6889
- else if (attr.name === "key") {
6890
- const depth = this.currentLoopKeyDepth;
6891
- attrName = depth > 0 ? `data-key-${depth}` : "data-key";
6892
- } else
6893
- attrName = attr.name;
6869
+ if (attr.name === "key")
6870
+ continue;
6871
+ const attrName = attr.name === "className" ? "class" : attr.name;
6894
6872
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
6895
6873
  if (lowered)
6896
6874
  parts.push(lowered);
@@ -1,5 +1,5 @@
1
1
  package BarefootJS::Backend::Mojo;
2
- our $VERSION = "0.33.0";
2
+ our $VERSION = "0.33.2";
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.0";
2
+ our $VERSION = "0.33.2";
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.0";
2
+ our $VERSION = "0.33.2";
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.1",
3
+ "version": "0.33.3",
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.1"
55
+ "@barefootjs/shared": "0.33.3"
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.1",
74
- "@barefootjs/vite": "0.33.1",
75
- "@barefootjs/client": "0.33.1",
73
+ "@barefootjs/jsx": "0.33.3",
74
+ "@barefootjs/vite": "0.33.3",
75
+ "@barefootjs/client": "0.33.3",
76
76
  "vite": "^6.0.0"
77
77
  }
78
78
  }
@@ -70,15 +70,11 @@ import {
70
70
  } from '@barefootjs/jsx'
71
71
  import { isAriaBooleanAttr, isBooleanResultExpr } from './boolean-result.ts'
72
72
  import type { ParsedExpr, LoweringMatcher } from '@barefootjs/jsx'
73
- import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from '@barefootjs/shared'
73
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml, resolveJsxChildrenProp } from '@barefootjs/shared'
74
74
 
75
75
  import type { MojoRenderCtx } from './lib/types.ts'
76
76
  import { MOJO_PRIMITIVE_EMIT_MAP } from './lib/constants.ts'
77
77
  import { perlHashKey, perlIdentifierFromMarkerId } from './lib/perl-naming.ts'
78
- import {
79
- resolveJsxChildrenProp,
80
- collectRootScopeNodes,
81
- } from './lib/ir-scope.ts'
82
78
  import { renderSortMethod, renderSortEval } from './expr/array-method.ts'
83
79
  import { staticValueToPerl } from './lib/static-value.ts'
84
80
  import { MojoFilterEmitter, MojoTopLevelEmitter } from './expr/emitters.ts'
@@ -162,22 +158,8 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
162
158
  templatePrimitives: TemplatePrimitiveRegistry = MOJO_PRIMITIVE_EMIT_MAP
163
159
 
164
160
  private componentName: string = ''
165
- /** The component's root scope element(s) — each carries `data-key` for a
166
- * keyed loop item (set by the child renderer from the JSX `key` prop). A
167
- * plain element root is a single node; an `if-statement` (early-return) root
168
- * contributes the top element of every branch, since any one of them can be
169
- * the rendered root at runtime. */
170
- private rootScopeNodes: Set<IRNode> = new Set()
171
161
  private options: Required<MojoAdapterOptions>
172
162
  private errors: CompilerError[] = []
173
- /**
174
- * `IRLoop.depth` of the loop currently being rendered (save/restore
175
- * around `renderChildren(loop.children)`, mirroring `inLoop` above).
176
- * `renderAttributes` reads this to derive the `key` → `data-key`/
177
- * `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
178
- * re-derived here (#2168 nested-loop-outer-binding).
179
- */
180
- private currentLoopKeyDepth = 0
181
163
  /**
182
164
  * SolidJS-style props identifier (`function(props: P)`) and the
183
165
  * analyzer-extracted prop names. Stashed at `generate()` entry so
@@ -322,7 +304,6 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
322
304
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName))
323
305
  }
324
306
 
325
- this.rootScopeNodes = collectRootScopeNodes(ir.root)
326
307
  const templateBody = ir.root.type === 'if-statement'
327
308
  ? this.renderIfStatement(ir.root as IRIfStatement)
328
309
  : this.renderNode(ir.root)
@@ -672,13 +653,30 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
672
653
  if (element.needsScope) {
673
654
  hydrationAttrs += ` ${this.renderScopeMarker('')}`
674
655
  }
675
- // A root scope element carries `data-key` for a keyed loop item emitted
676
- // from the bf instance (the child renderer sets it from the JSX `key`
677
- // prop), so a non-keyed render adds nothing. Mirrors Hono stamping
678
- // data-key on each loop item's scope root, including early-return
679
- // (if-statement) roots where every branch's top element qualifies. (#1297)
680
- if (this.rootScopeNodes.has(element) && element.needsScope) {
681
- hydrationAttrs += ` <%== bf->data_key_attr %>`
656
+ // #2753: `element.keyAttr` is the ONE IR-resolved decision for this
657
+ // element's row-key attribute replacing both the `carriesDataKey`
658
+ // boolean and this adapter's own `rootScopeNodes`/`needsScope` check
659
+ // (mechanism 2: a render-root relay, including the #2732 fragment-root
660
+ // case) and the separate `currentLoopKeyDepth`-driven rewrite of a
661
+ // literal `key` attribute this adapter used to do in `renderAttributes`
662
+ // (mechanism 1: a `.map()` row root compiled inline here).
663
+ if (element.keyAttr) {
664
+ if (element.keyAttr.value !== undefined) {
665
+ const lowered = emitAttrValue(
666
+ { kind: 'expression', expr: element.keyAttr.value },
667
+ this.elementAttrEmitter,
668
+ element.keyAttr.name,
669
+ )
670
+ if (lowered) hydrationAttrs += ` ${lowered}`
671
+ } else {
672
+ // Relay: this element is one of THIS component's own render roots,
673
+ // carrying whatever key its OWN caller supplies at runtime (the `bf`
674
+ // instance's `data_key` field) when this component is itself
675
+ // invoked as a caller's keyed loop row. Mirrors Hono stamping
676
+ // data-key on each loop item's root, including early-return
677
+ // (if-statement) roots (#1297), and #2732's fragment-root case.
678
+ hydrationAttrs += ` <%== bf->data_key_attr %>`
679
+ }
682
680
  }
683
681
  if (element.slotId) {
684
682
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`
@@ -975,10 +973,7 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
975
973
  const preambleDecls = loop.preamble?.declarations ?? []
976
974
  const prevScope = this.scope
977
975
  this.scope = prevScope.enterLoopRow(loop)
978
- const prevLoopKeyDepth = this.currentLoopKeyDepth
979
- this.currentLoopKeyDepth = loop.depth
980
976
  const renderedChildren = this.renderChildren(loop.children)
981
- this.currentLoopKeyDepth = prevLoopKeyDepth
982
977
 
983
978
  // Whole-item conditional (#1665): prepend an always-present
984
979
  // `<!--bf-loop-i:KEY-->` anchor before each item's (possibly empty)
@@ -1601,20 +1596,15 @@ export class MojoAdapter extends BaseAdapter implements IRNodeEmitter<MojoRender
1601
1596
  // literal never reaches the generic object-literal BF101 refusal
1602
1597
  // (which would double-report alongside the purpose-built one).
1603
1598
  if (isDangerousInnerHtmlAttr(attr)) continue
1604
- // Rewrite JSX special-prop names to their HTML-attribute
1605
- // counterparts (#1475). `className` `class` was already
1606
- // wired in; the `key` `data-key` rewrite matches the
1607
- // canonical Hono attribute name the client runtime
1608
- // reconciles against. Hono SSR strips raw `key` via its JSX
1609
- // runtime; the Mojo template path has no such layer so the
1610
- // rewrite happens at attribute-emit time.
1611
- let attrName: string
1612
- if (attr.name === 'className') attrName = 'class'
1613
- else if (attr.name === 'key') {
1614
- const depth = this.currentLoopKeyDepth
1615
- attrName = depth > 0 ? `data-key-${depth}` : 'data-key'
1616
- }
1617
- else attrName = attr.name
1599
+ // `key` never renders as a literal HTML attribute — #2753 resolves it
1600
+ // onto `element.keyAttr` (name AND value) at IR-build time, emitted
1601
+ // separately in `renderElement`. A stray `key` that jsx-to-ir.ts did
1602
+ // NOT recognize as a loop row root (so `keyAttr` was never set) has no
1603
+ // defined rendering outside a `.map()` anyway; drop it rather than
1604
+ // leak an invalid `key="..."` HTML attribute.
1605
+ if (attr.name === 'key') continue
1606
+ // Rewrite JSX special-prop names to their HTML-attribute counterparts (#1475).
1607
+ const attrName = attr.name === 'className' ? 'class' : attr.name
1618
1608
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName)
1619
1609
  if (lowered) parts.push(lowered)
1620
1610
  }
@@ -15,4 +15,17 @@ 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
- export const renderDivergences: RenderDivergences = {}
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
+ }
@@ -1,33 +0,0 @@
1
- /**
2
- * IR traversal helpers for the Mojolicious EP template adapter.
3
- *
4
- * Extracted from `mojo-adapter.ts` (domain-module refactor, issue #2018
5
- * track D). Pure functions over the IR tree — no adapter instance state.
6
- *
7
- * SHARED CANDIDATE: the bodies here are byte-identical to the Xslate
8
- * adapter's `lib/ir-scope.ts`. They are adapter-agnostic (no Perl/Kolon
9
- * specifics), so they are the obvious first extraction into a shared
10
- * Perl-family codegen module once one exists — the groundwork for the
11
- * future Perl evaluator integration (issue #2018 track D). Kept per-adapter
12
- * for now, matching the repo convention (the Go adapter keeps its own copy).
13
- */
14
- import type { IRNode, IRProp } from '@barefootjs/jsx';
15
- /**
16
- * Find the `children` prop's `jsx-children` payload (#1326). Narrowed
17
- * via the AttrValue `kind` discriminator so adapter code stays type-
18
- * safe if the IR shape evolves — adding a new AttrValue variant or
19
- * renaming `children` to `jsxChildren` becomes a TS compile error
20
- * here instead of silently dropping the children at runtime.
21
- */
22
- export declare function resolveJsxChildrenProp(props: readonly IRProp[]): IRNode[];
23
- /**
24
- * Collect the component's root scope element node(s) — the elements that
25
- * become the rendered root and so carry `data-key` for a keyed loop item. A
26
- * plain element root is itself; an `if-statement` (early-return) root
27
- * contributes the top element of each branch (`consequent` + the `alternate`
28
- * chain), since exactly one branch renders at runtime. Non-element branch
29
- * tops (fragments / nested shapes) are walked one level so an
30
- * `if (…) return <A/>` still resolves to `<A>`. (#1297)
31
- */
32
- export declare function collectRootScopeNodes(node: IRNode): Set<IRNode>;
33
- //# sourceMappingURL=ir-scope.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ir-scope.d.ts","sourceRoot":"","sources":["../../../src/adapter/lib/ir-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAA6B,MAAM,iBAAiB,CAAA;AAEhF;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAKzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAiB/D"}
@@ -1,57 +0,0 @@
1
- /**
2
- * IR traversal helpers for the Mojolicious EP template adapter.
3
- *
4
- * Extracted from `mojo-adapter.ts` (domain-module refactor, issue #2018
5
- * track D). Pure functions over the IR tree — no adapter instance state.
6
- *
7
- * SHARED CANDIDATE: the bodies here are byte-identical to the Xslate
8
- * adapter's `lib/ir-scope.ts`. They are adapter-agnostic (no Perl/Kolon
9
- * specifics), so they are the obvious first extraction into a shared
10
- * Perl-family codegen module once one exists — the groundwork for the
11
- * future Perl evaluator integration (issue #2018 track D). Kept per-adapter
12
- * for now, matching the repo convention (the Go adapter keeps its own copy).
13
- */
14
-
15
- import type { IRNode, IRProp, IRIfStatement, IRFragment } from '@barefootjs/jsx'
16
-
17
- /**
18
- * Find the `children` prop's `jsx-children` payload (#1326). Narrowed
19
- * via the AttrValue `kind` discriminator so adapter code stays type-
20
- * safe if the IR shape evolves — adding a new AttrValue variant or
21
- * renaming `children` to `jsxChildren` becomes a TS compile error
22
- * here instead of silently dropping the children at runtime.
23
- */
24
- export function resolveJsxChildrenProp(props: readonly IRProp[]): IRNode[] {
25
- const prop = props.find(p => p.name === 'children')
26
- if (!prop) return []
27
- if (prop.value.kind !== 'jsx-children') return []
28
- return prop.value.children
29
- }
30
-
31
- /**
32
- * Collect the component's root scope element node(s) — the elements that
33
- * become the rendered root and so carry `data-key` for a keyed loop item. A
34
- * plain element root is itself; an `if-statement` (early-return) root
35
- * contributes the top element of each branch (`consequent` + the `alternate`
36
- * chain), since exactly one branch renders at runtime. Non-element branch
37
- * tops (fragments / nested shapes) are walked one level so an
38
- * `if (…) return <A/>` still resolves to `<A>`. (#1297)
39
- */
40
- export function collectRootScopeNodes(node: IRNode): Set<IRNode> {
41
- const out = new Set<IRNode>()
42
- const visit = (n: IRNode | null): void => {
43
- if (!n) return
44
- if (n.type === 'element') { out.add(n); return }
45
- if (n.type === 'if-statement') {
46
- const s = n as IRIfStatement
47
- visit(s.consequent)
48
- visit(s.alternate)
49
- return
50
- }
51
- if (n.type === 'fragment') {
52
- for (const c of (n as IRFragment).children) visit(c)
53
- }
54
- }
55
- visit(node)
56
- return out
57
- }