@barefootjs/xslate 0.33.2 → 0.33.4

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.
@@ -187373,7 +187373,7 @@ function isAriaBooleanAttr(name) {
187373
187373
  }
187374
187374
 
187375
187375
  // src/adapter/xslate-adapter.ts
187376
- import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
187376
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml, resolveJsxChildrenProp } from "@barefootjs/shared";
187377
187377
 
187378
187378
  // src/adapter/lib/constants.ts
187379
187379
  var XSLATE_TEMPLATE_PRIMITIVES = {
@@ -187397,39 +187397,6 @@ function kolonHashKey(name) {
187397
187397
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : `'${escapeKolonSingleQuoted(name)}'`;
187398
187398
  }
187399
187399
 
187400
- // src/adapter/lib/ir-scope.ts
187401
- function resolveJsxChildrenProp(props) {
187402
- const prop = props.find((p) => p.name === "children");
187403
- if (!prop)
187404
- return [];
187405
- if (prop.value.kind !== "jsx-children")
187406
- return [];
187407
- return prop.value.children;
187408
- }
187409
- function collectRootScopeNodes(node) {
187410
- const out = new Set;
187411
- const visit = (n) => {
187412
- if (!n)
187413
- return;
187414
- if (n.type === "element") {
187415
- out.add(n);
187416
- return;
187417
- }
187418
- if (n.type === "if-statement") {
187419
- const s = n;
187420
- visit(s.consequent);
187421
- visit(s.alternate);
187422
- return;
187423
- }
187424
- if (n.type === "fragment") {
187425
- for (const c of n.children)
187426
- visit(c);
187427
- }
187428
- };
187429
- visit(node);
187430
- return out;
187431
- }
187432
-
187433
187400
  // src/adapter/expr/array-method.ts
187434
187401
  import {
187435
187402
  serializeParsedExpr,
@@ -188282,11 +188249,9 @@ class XslateAdapter extends BaseAdapter {
188282
188249
  templatesPerComponent = true;
188283
188250
  templatePrimitives = XSLATE_PRIMITIVE_EMIT_MAP;
188284
188251
  componentName = "";
188285
- rootScopeNodes = new Set;
188286
188252
  options;
188287
188253
  errors = [];
188288
188254
  inLoop = false;
188289
- currentLoopKeyDepth = 0;
188290
188255
  propsObjectName = null;
188291
188256
  propsParams = [];
188292
188257
  booleanTypedProps = new Set;
@@ -188322,7 +188287,6 @@ class XslateAdapter extends BaseAdapter {
188322
188287
  if (!options?.siblingTemplatesRegistered) {
188323
188288
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName));
188324
188289
  }
188325
- this.rootScopeNodes = collectRootScopeNodes(ir.root);
188326
188290
  const templateBody = ir.root.type === "if-statement" ? this.renderIfStatement(ir.root) : this.renderNode(ir.root);
188327
188291
  const scriptReg = options?.skipScriptRegistration ? "" : this.generateScriptRegistrations(ir, options?.scriptBaseName, options?.scriptAssets, options?.preloadAssets);
188328
188292
  const ctxSeed = generateContextConsumerSeed(ir);
@@ -188443,8 +188407,14 @@ class XslateAdapter extends BaseAdapter {
188443
188407
  if (element.needsScope) {
188444
188408
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
188445
188409
  }
188446
- if (this.rootScopeNodes.has(element) && element.needsScope || element.carriesDataKey) {
188447
- hydrationAttrs += ` <: $bf.data_key_attr() | mark_raw :>`;
188410
+ if (element.keyAttr) {
188411
+ if (element.keyAttr.value !== undefined) {
188412
+ const lowered = emitAttrValue({ kind: "expression", expr: element.keyAttr.value }, this.elementAttrEmitter, element.keyAttr.name);
188413
+ if (lowered)
188414
+ hydrationAttrs += ` ${lowered}`;
188415
+ } else {
188416
+ hydrationAttrs += ` <: $bf.data_key_attr() | mark_raw :>`;
188417
+ }
188448
188418
  }
188449
188419
  if (element.slotId) {
188450
188420
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
@@ -188650,13 +188620,10 @@ ${whenTrue}
188650
188620
  }
188651
188621
  const prevInLoop = this.inLoop;
188652
188622
  this.inLoop = true;
188653
- const prevLoopKeyDepth = this.currentLoopKeyDepth;
188654
- this.currentLoopKeyDepth = loop.depth;
188655
188623
  const prevScope = this.scope;
188656
188624
  this.scope = prevScope.enterLoopRow(loop);
188657
188625
  const preambleLines = (loop.preamble?.declarations ?? []).map((d) => `: my $${d.name} = ${this.convertExpressionToKolon(d.raw, d.valueParsed)};`);
188658
188626
  const childrenUnderLoop = this.renderChildren(loop.children);
188659
- this.currentLoopKeyDepth = prevLoopKeyDepth;
188660
188627
  this.inLoop = prevInLoop;
188661
188628
  const bodyChildren = loop.bodyIsItemConditional && loop.key ? `<: $bf.comment("loop-i:" ~ ${this.convertExpressionToKolon(loop.key)}) | mark_raw :>
188662
188629
  ${childrenUnderLoop}` : childrenUnderLoop;
@@ -188929,14 +188896,9 @@ ${name}="<: ${val} :>"
188929
188896
  continue;
188930
188897
  if (isDangerousInnerHtmlAttr(attr))
188931
188898
  continue;
188932
- let attrName;
188933
- if (attr.name === "className")
188934
- attrName = "class";
188935
- else if (attr.name === "key") {
188936
- const depth = this.currentLoopKeyDepth;
188937
- attrName = depth > 0 ? `data-key-${depth}` : "data-key";
188938
- } else
188939
- attrName = attr.name;
188899
+ if (attr.name === "key")
188900
+ continue;
188901
+ const attrName = attr.name === "className" ? "class" : attr.name;
188940
188902
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
188941
188903
  if (lowered)
188942
188904
  parts.push(lowered);
@@ -36,22 +36,9 @@ export declare class XslateAdapter extends BaseAdapter implements IRNodeEmitter<
36
36
  */
37
37
  templatePrimitives: TemplatePrimitiveRegistry;
38
38
  private componentName;
39
- /** Component root scope element(s) — each carries `data-key` for a keyed loop
40
- * item (set by the child renderer from the JSX `key` prop). A plain element
41
- * root is one node; an `if-statement` (early-return) root contributes the
42
- * top element of every branch. */
43
- private rootScopeNodes;
44
39
  private options;
45
40
  private errors;
46
41
  private inLoop;
47
- /**
48
- * `IRLoop.depth` of the loop currently being rendered (save/restore
49
- * around `renderChildren(loop.children)`, mirroring `inLoop` above).
50
- * `renderAttributes` reads this to derive the `key` → `data-key`/
51
- * `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
52
- * re-derived here (#2168 nested-loop-outer-binding).
53
- */
54
- private currentLoopKeyDepth;
55
42
  /**
56
43
  * SolidJS-style props identifier (`function(props: P)`) and the
57
44
  * analyzer-extracted prop names. Stashed at `generate()` entry so the
@@ -1 +1 @@
1
- {"version":3,"file":"xslate-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/xslate-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"xslate-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/xslate-adapter.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -187373,7 +187373,7 @@ function isAriaBooleanAttr(name) {
187373
187373
  }
187374
187374
 
187375
187375
  // src/adapter/xslate-adapter.ts
187376
- import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from "@barefootjs/shared";
187376
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml, resolveJsxChildrenProp } from "@barefootjs/shared";
187377
187377
 
187378
187378
  // src/adapter/lib/constants.ts
187379
187379
  var XSLATE_TEMPLATE_PRIMITIVES = {
@@ -187397,39 +187397,6 @@ function kolonHashKey(name) {
187397
187397
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : `'${escapeKolonSingleQuoted(name)}'`;
187398
187398
  }
187399
187399
 
187400
- // src/adapter/lib/ir-scope.ts
187401
- function resolveJsxChildrenProp(props) {
187402
- const prop = props.find((p) => p.name === "children");
187403
- if (!prop)
187404
- return [];
187405
- if (prop.value.kind !== "jsx-children")
187406
- return [];
187407
- return prop.value.children;
187408
- }
187409
- function collectRootScopeNodes(node) {
187410
- const out = new Set;
187411
- const visit = (n) => {
187412
- if (!n)
187413
- return;
187414
- if (n.type === "element") {
187415
- out.add(n);
187416
- return;
187417
- }
187418
- if (n.type === "if-statement") {
187419
- const s = n;
187420
- visit(s.consequent);
187421
- visit(s.alternate);
187422
- return;
187423
- }
187424
- if (n.type === "fragment") {
187425
- for (const c of n.children)
187426
- visit(c);
187427
- }
187428
- };
187429
- visit(node);
187430
- return out;
187431
- }
187432
-
187433
187400
  // src/adapter/expr/array-method.ts
187434
187401
  import {
187435
187402
  serializeParsedExpr,
@@ -188282,11 +188249,9 @@ class XslateAdapter extends BaseAdapter {
188282
188249
  templatesPerComponent = true;
188283
188250
  templatePrimitives = XSLATE_PRIMITIVE_EMIT_MAP;
188284
188251
  componentName = "";
188285
- rootScopeNodes = new Set;
188286
188252
  options;
188287
188253
  errors = [];
188288
188254
  inLoop = false;
188289
- currentLoopKeyDepth = 0;
188290
188255
  propsObjectName = null;
188291
188256
  propsParams = [];
188292
188257
  booleanTypedProps = new Set;
@@ -188322,7 +188287,6 @@ class XslateAdapter extends BaseAdapter {
188322
188287
  if (!options?.siblingTemplatesRegistered) {
188323
188288
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName));
188324
188289
  }
188325
- this.rootScopeNodes = collectRootScopeNodes(ir.root);
188326
188290
  const templateBody = ir.root.type === "if-statement" ? this.renderIfStatement(ir.root) : this.renderNode(ir.root);
188327
188291
  const scriptReg = options?.skipScriptRegistration ? "" : this.generateScriptRegistrations(ir, options?.scriptBaseName, options?.scriptAssets, options?.preloadAssets);
188328
188292
  const ctxSeed = generateContextConsumerSeed(ir);
@@ -188443,8 +188407,14 @@ class XslateAdapter extends BaseAdapter {
188443
188407
  if (element.needsScope) {
188444
188408
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
188445
188409
  }
188446
- if (this.rootScopeNodes.has(element) && element.needsScope || element.carriesDataKey) {
188447
- hydrationAttrs += ` <: $bf.data_key_attr() | mark_raw :>`;
188410
+ if (element.keyAttr) {
188411
+ if (element.keyAttr.value !== undefined) {
188412
+ const lowered = emitAttrValue({ kind: "expression", expr: element.keyAttr.value }, this.elementAttrEmitter, element.keyAttr.name);
188413
+ if (lowered)
188414
+ hydrationAttrs += ` ${lowered}`;
188415
+ } else {
188416
+ hydrationAttrs += ` <: $bf.data_key_attr() | mark_raw :>`;
188417
+ }
188448
188418
  }
188449
188419
  if (element.slotId) {
188450
188420
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
@@ -188650,13 +188620,10 @@ ${whenTrue}
188650
188620
  }
188651
188621
  const prevInLoop = this.inLoop;
188652
188622
  this.inLoop = true;
188653
- const prevLoopKeyDepth = this.currentLoopKeyDepth;
188654
- this.currentLoopKeyDepth = loop.depth;
188655
188623
  const prevScope = this.scope;
188656
188624
  this.scope = prevScope.enterLoopRow(loop);
188657
188625
  const preambleLines = (loop.preamble?.declarations ?? []).map((d) => `: my $${d.name} = ${this.convertExpressionToKolon(d.raw, d.valueParsed)};`);
188658
188626
  const childrenUnderLoop = this.renderChildren(loop.children);
188659
- this.currentLoopKeyDepth = prevLoopKeyDepth;
188660
188627
  this.inLoop = prevInLoop;
188661
188628
  const bodyChildren = loop.bodyIsItemConditional && loop.key ? `<: $bf.comment("loop-i:" ~ ${this.convertExpressionToKolon(loop.key)}) | mark_raw :>
188662
188629
  ${childrenUnderLoop}` : childrenUnderLoop;
@@ -188929,14 +188896,9 @@ ${name}="<: ${val} :>"
188929
188896
  continue;
188930
188897
  if (isDangerousInnerHtmlAttr(attr))
188931
188898
  continue;
188932
- let attrName;
188933
- if (attr.name === "className")
188934
- attrName = "class";
188935
- else if (attr.name === "key") {
188936
- const depth = this.currentLoopKeyDepth;
188937
- attrName = depth > 0 ? `data-key-${depth}` : "data-key";
188938
- } else
188939
- attrName = attr.name;
188899
+ if (attr.name === "key")
188900
+ continue;
188901
+ const attrName = attr.name === "className" ? "class" : attr.name;
188940
188902
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
188941
188903
  if (lowered)
188942
188904
  parts.push(lowered);
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) {
@@ -3133,11 +3142,14 @@ var KEYWORDS_AND_GLOBALS = new Set([
3133
3142
  // ../jsx/src/ir-to-client-js/walk-prop-accesses.ts
3134
3143
  import ts14 from "typescript";
3135
3144
 
3145
+ // ../jsx/src/ir-to-client-js/imports.ts
3146
+ import ts16 from "typescript";
3147
+
3136
3148
  // ../jsx/src/value-references.ts
3137
3149
  import ts15 from "typescript";
3138
3150
 
3139
3151
  // ../jsx/src/relocate.ts
3140
- import ts16 from "typescript";
3152
+ import ts17 from "typescript";
3141
3153
 
3142
3154
  // ../jsx/src/lowering-registry.ts
3143
3155
  var plugins = [];
@@ -3354,10 +3366,10 @@ function matchSearchParamsMethodCall(callee, args, localNames) {
3354
3366
  }
3355
3367
 
3356
3368
  // ../jsx/src/ir-to-client-js/prune-unused-prop-extractions.ts
3357
- import ts17 from "typescript";
3369
+ import ts18 from "typescript";
3358
3370
 
3359
3371
  // ../jsx/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts
3360
- import ts18 from "typescript";
3372
+ import ts19 from "typescript";
3361
3373
  var NO_PREAMBLE = {
3362
3374
  lazySafe: true,
3363
3375
  facts: { declaredNames: new Set, freeNames: new Set }
@@ -3407,7 +3419,7 @@ var INERT_BINDING_GLOBALS = new Set([
3407
3419
  ]);
3408
3420
 
3409
3421
  // ../jsx/src/ir-to-client-js/emit-reactive.ts
3410
- import ts19 from "typescript";
3422
+ import ts20 from "typescript";
3411
3423
 
3412
3424
  // ../jsx/src/ir-to-client-js/control-flow/stringify/event-delegation.ts
3413
3425
  var NON_BUBBLING_EVENTS = new Set([
@@ -3422,7 +3434,7 @@ var NON_BUBBLING_EVENTS = new Set([
3422
3434
  ]);
3423
3435
 
3424
3436
  // ../jsx/src/ir-to-client-js/rewrite-props-object.ts
3425
- import ts20 from "typescript";
3437
+ import ts21 from "typescript";
3426
3438
 
3427
3439
  // ../jsx/src/ir-to-client-js/source-map.ts
3428
3440
  var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -3513,15 +3525,15 @@ class SourceMapGenerator {
3513
3525
  }
3514
3526
 
3515
3527
  // ../jsx/src/preprocess-inline-jsx-callbacks.ts
3516
- import ts21 from "typescript";
3528
+ import ts22 from "typescript";
3517
3529
 
3518
3530
  // ../jsx/src/ssr-defaults.ts
3519
- import ts22 from "typescript";
3531
+ import ts23 from "typescript";
3520
3532
  var UNRESOLVED = Symbol("unresolved");
3521
3533
  var NO_RETURN = Symbol("no-return");
3522
3534
 
3523
3535
  // ../jsx/src/augment-inherited-props.ts
3524
- import ts23 from "typescript";
3536
+ import ts24 from "typescript";
3525
3537
  function collectContextConsumers(metadata) {
3526
3538
  const constants = metadata.localConstants ?? [];
3527
3539
  const contextDefaults = new Map;
@@ -3553,47 +3565,47 @@ function collectContextConsumers(metadata) {
3553
3565
  }
3554
3566
  function parseUseContextArg(source) {
3555
3567
  const expr = parseSingleExpression(source);
3556
- if (!expr || !ts23.isCallExpression(expr))
3568
+ if (!expr || !ts24.isCallExpression(expr))
3557
3569
  return null;
3558
- if (!ts23.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
3570
+ if (!ts24.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
3559
3571
  return null;
3560
3572
  if (expr.arguments.length !== 1)
3561
3573
  return null;
3562
3574
  const arg = expr.arguments[0];
3563
- return ts23.isIdentifier(arg) ? arg.text : null;
3575
+ return ts24.isIdentifier(arg) ? arg.text : null;
3564
3576
  }
3565
3577
  function parseCreateContextDefault(source) {
3566
3578
  const expr = parseSingleExpression(source);
3567
- if (!expr || !ts23.isCallExpression(expr))
3579
+ if (!expr || !ts24.isCallExpression(expr))
3568
3580
  return null;
3569
3581
  if (expr.arguments.length === 0)
3570
3582
  return null;
3571
3583
  const arg = expr.arguments[0];
3572
- if (ts23.isStringLiteral(arg) || ts23.isNoSubstitutionTemplateLiteral(arg))
3584
+ if (ts24.isStringLiteral(arg) || ts24.isNoSubstitutionTemplateLiteral(arg))
3573
3585
  return arg.text;
3574
- if (ts23.isNumericLiteral(arg))
3586
+ if (ts24.isNumericLiteral(arg))
3575
3587
  return Number(arg.text);
3576
- if (arg.kind === ts23.SyntaxKind.TrueKeyword)
3588
+ if (arg.kind === ts24.SyntaxKind.TrueKeyword)
3577
3589
  return true;
3578
- if (arg.kind === ts23.SyntaxKind.FalseKeyword)
3590
+ if (arg.kind === ts24.SyntaxKind.FalseKeyword)
3579
3591
  return false;
3580
3592
  return null;
3581
3593
  }
3582
3594
  function isObjectLiteralCreateContextDefault(source) {
3583
3595
  const expr = parseSingleExpression(source);
3584
- if (!expr || !ts23.isCallExpression(expr))
3596
+ if (!expr || !ts24.isCallExpression(expr))
3585
3597
  return false;
3586
3598
  if (expr.arguments.length === 0)
3587
3599
  return false;
3588
- return ts23.isObjectLiteralExpression(expr.arguments[0]);
3600
+ return ts24.isObjectLiteralExpression(expr.arguments[0]);
3589
3601
  }
3590
3602
  function parseSingleExpression(source) {
3591
- const sf = ts23.createSourceFile("__ctx.ts", `(${source})`, ts23.ScriptTarget.Latest, false);
3603
+ const sf = ts24.createSourceFile("__ctx.ts", `(${source})`, ts24.ScriptTarget.Latest, false);
3592
3604
  const stmt = sf.statements[0];
3593
- if (!stmt || !ts23.isExpressionStatement(stmt))
3605
+ if (!stmt || !ts24.isExpressionStatement(stmt))
3594
3606
  return null;
3595
3607
  let e = stmt.expression;
3596
- while (ts23.isParenthesizedExpression(e))
3608
+ while (ts24.isParenthesizedExpression(e))
3597
3609
  e = e.expression;
3598
3610
  return e;
3599
3611
  }
@@ -3618,25 +3630,25 @@ function augmentInheritedPropAccesses(ir) {
3618
3630
  const pinCoalesceLiterals = (s) => {
3619
3631
  if (!s || !s.includes(propsObj))
3620
3632
  return;
3621
- const sf = ts23.createSourceFile("__aug.ts", `(${s})`, ts23.ScriptTarget.Latest, false);
3633
+ const sf = ts24.createSourceFile("__aug.ts", `(${s})`, ts24.ScriptTarget.Latest, false);
3622
3634
  const visit = (n) => {
3623
- 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)) {
3624
3636
  let left = n.left;
3625
- while (ts23.isParenthesizedExpression(left))
3637
+ while (ts24.isParenthesizedExpression(left))
3626
3638
  left = left.expression;
3627
- 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) {
3628
3640
  const name = left.name.text;
3629
3641
  let right = n.right;
3630
- while (ts23.isParenthesizedExpression(right))
3642
+ while (ts24.isParenthesizedExpression(right))
3631
3643
  right = right.expression;
3632
- if (ts23.isPrefixUnaryExpression(right))
3644
+ if (ts24.isPrefixUnaryExpression(right))
3633
3645
  right = right.operand;
3634
- 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;
3635
3647
  if (kind && !coalesceLiteralTypes.has(name))
3636
3648
  coalesceLiteralTypes.set(name, kind);
3637
3649
  }
3638
3650
  }
3639
- ts23.forEachChild(n, visit);
3651
+ ts24.forEachChild(n, visit);
3640
3652
  };
3641
3653
  visit(sf);
3642
3654
  };
@@ -3747,33 +3759,33 @@ function augmentInheritedPropAccesses(ir) {
3747
3759
  }
3748
3760
  }
3749
3761
  function parseStaticStringConst(source) {
3750
- 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);
3751
3763
  const stmt = sf.statements[0];
3752
- if (!stmt || !ts23.isVariableStatement(stmt))
3764
+ if (!stmt || !ts24.isVariableStatement(stmt))
3753
3765
  return null;
3754
3766
  let init = stmt.declarationList.declarations[0]?.initializer;
3755
- while (init && ts23.isParenthesizedExpression(init))
3767
+ while (init && ts24.isParenthesizedExpression(init))
3756
3768
  init = init.expression;
3757
3769
  if (!init)
3758
3770
  return null;
3759
- if (ts23.isStringLiteral(init) || ts23.isNoSubstitutionTemplateLiteral(init)) {
3771
+ if (ts24.isStringLiteral(init) || ts24.isNoSubstitutionTemplateLiteral(init)) {
3760
3772
  return init.text;
3761
3773
  }
3762
3774
  return evalStringArrayJoin(source);
3763
3775
  }
3764
3776
  function evalTemplateOfStringConsts(source, resolved) {
3765
- 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);
3766
3778
  const stmt = sf.statements[0];
3767
- if (!stmt || !ts23.isVariableStatement(stmt))
3779
+ if (!stmt || !ts24.isVariableStatement(stmt))
3768
3780
  return null;
3769
3781
  let init = stmt.declarationList.declarations[0]?.initializer;
3770
- while (init && ts23.isParenthesizedExpression(init))
3782
+ while (init && ts24.isParenthesizedExpression(init))
3771
3783
  init = init.expression;
3772
- if (!init || !ts23.isTemplateExpression(init))
3784
+ if (!init || !ts24.isTemplateExpression(init))
3773
3785
  return null;
3774
3786
  let out = init.head.text;
3775
3787
  for (const span of init.templateSpans) {
3776
- if (!ts23.isIdentifier(span.expression))
3788
+ if (!ts24.isIdentifier(span.expression))
3777
3789
  return null;
3778
3790
  const value = resolved.get(span.expression.text);
3779
3791
  if (value === undefined)
@@ -3806,30 +3818,30 @@ function lookupStaticRecordLiteral(objectName, key, constants, isShadowed) {
3806
3818
  const constInfo = (constants ?? []).find((c) => c.name === objectName && c.isModule);
3807
3819
  if (constInfo?.value === undefined)
3808
3820
  return null;
3809
- 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);
3810
3822
  if (sf.statements.length !== 1)
3811
3823
  return null;
3812
3824
  const stmt = sf.statements[0];
3813
- if (!ts23.isExpressionStatement(stmt))
3825
+ if (!ts24.isExpressionStatement(stmt))
3814
3826
  return null;
3815
3827
  let parsed = stmt.expression;
3816
- while (ts23.isParenthesizedExpression(parsed))
3828
+ while (ts24.isParenthesizedExpression(parsed))
3817
3829
  parsed = parsed.expression;
3818
- if (!ts23.isObjectLiteralExpression(parsed))
3830
+ if (!ts24.isObjectLiteralExpression(parsed))
3819
3831
  return null;
3820
3832
  for (const prop of parsed.properties) {
3821
- if (!ts23.isPropertyAssignment(prop))
3833
+ if (!ts24.isPropertyAssignment(prop))
3822
3834
  continue;
3823
3835
  const name = prop.name;
3824
- 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;
3825
3837
  if (propKey !== key)
3826
3838
  continue;
3827
3839
  let v = prop.initializer;
3828
- while (ts23.isParenthesizedExpression(v))
3840
+ while (ts24.isParenthesizedExpression(v))
3829
3841
  v = v.expression;
3830
- if (ts23.isNumericLiteral(v))
3842
+ if (ts24.isNumericLiteral(v))
3831
3843
  return { kind: "number", text: v.text };
3832
- if (ts23.isStringLiteral(v) || ts23.isNoSubstitutionTemplateLiteral(v)) {
3844
+ if (ts24.isStringLiteral(v) || ts24.isNoSubstitutionTemplateLiteral(v)) {
3833
3845
  return { kind: "string", text: v.text };
3834
3846
  }
3835
3847
  return null;
@@ -3837,28 +3849,28 @@ function lookupStaticRecordLiteral(objectName, key, constants, isShadowed) {
3837
3849
  return null;
3838
3850
  }
3839
3851
  function evalStringArrayJoin(source) {
3840
- 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);
3841
3853
  const stmt = sf.statements[0];
3842
- if (!stmt || !ts23.isVariableStatement(stmt))
3854
+ if (!stmt || !ts24.isVariableStatement(stmt))
3843
3855
  return null;
3844
3856
  let node = stmt.declarationList.declarations[0]?.initializer;
3845
- while (node && ts23.isParenthesizedExpression(node))
3857
+ while (node && ts24.isParenthesizedExpression(node))
3846
3858
  node = node.expression;
3847
- if (!node || !ts23.isCallExpression(node))
3859
+ if (!node || !ts24.isCallExpression(node))
3848
3860
  return null;
3849
3861
  const callee = node.expression;
3850
- if (!ts23.isPropertyAccessExpression(callee))
3862
+ if (!ts24.isPropertyAccessExpression(callee))
3851
3863
  return null;
3852
3864
  if (callee.name.text !== "join")
3853
3865
  return null;
3854
3866
  let recv = callee.expression;
3855
- while (ts23.isParenthesizedExpression(recv))
3867
+ while (ts24.isParenthesizedExpression(recv))
3856
3868
  recv = recv.expression;
3857
- if (!ts23.isArrayLiteralExpression(recv))
3869
+ if (!ts24.isArrayLiteralExpression(recv))
3858
3870
  return null;
3859
3871
  const parts = [];
3860
3872
  for (const el of recv.elements) {
3861
- if (ts23.isStringLiteral(el) || ts23.isNoSubstitutionTemplateLiteral(el)) {
3873
+ if (ts24.isStringLiteral(el) || ts24.isNoSubstitutionTemplateLiteral(el)) {
3862
3874
  parts.push(el.text);
3863
3875
  } else {
3864
3876
  return null;
@@ -3867,7 +3879,7 @@ function evalStringArrayJoin(source) {
3867
3879
  let sep = ",";
3868
3880
  if (node.arguments.length >= 1) {
3869
3881
  const arg = node.arguments[0];
3870
- if (ts23.isStringLiteral(arg) || ts23.isNoSubstitutionTemplateLiteral(arg))
3882
+ if (ts24.isStringLiteral(arg) || ts24.isNoSubstitutionTemplateLiteral(arg))
3871
3883
  sep = arg.text;
3872
3884
  else
3873
3885
  return null;
@@ -3875,11 +3887,11 @@ function evalStringArrayJoin(source) {
3875
3887
  return parts.join(sep);
3876
3888
  }
3877
3889
  function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
3878
- if (!ts23.isElementAccessExpression(val))
3890
+ if (!ts24.isElementAccessExpression(val))
3879
3891
  return null;
3880
3892
  const obj = val.expression;
3881
3893
  const arg = val.argumentExpression;
3882
- if (!ts23.isIdentifier(obj) || !ts23.isIdentifier(arg))
3894
+ if (!ts24.isIdentifier(obj) || !ts24.isIdentifier(arg))
3883
3895
  return null;
3884
3896
  let indexPropName;
3885
3897
  let defaultKey;
@@ -3895,35 +3907,35 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
3895
3907
  const constInfo = localConstants.find((c) => c.name === obj.text && c.isModule);
3896
3908
  if (constInfo?.value === undefined)
3897
3909
  return null;
3898
- 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);
3899
3911
  if (sf.statements.length !== 1)
3900
3912
  return null;
3901
3913
  const stmt = sf.statements[0];
3902
- if (!ts23.isExpressionStatement(stmt))
3914
+ if (!ts24.isExpressionStatement(stmt))
3903
3915
  return null;
3904
3916
  let parsed = stmt.expression;
3905
- while (ts23.isParenthesizedExpression(parsed))
3917
+ while (ts24.isParenthesizedExpression(parsed))
3906
3918
  parsed = parsed.expression;
3907
- if (!ts23.isObjectLiteralExpression(parsed))
3919
+ if (!ts24.isObjectLiteralExpression(parsed))
3908
3920
  return null;
3909
3921
  const entries = [];
3910
3922
  for (const prop of parsed.properties) {
3911
- if (!ts23.isPropertyAssignment(prop))
3923
+ if (!ts24.isPropertyAssignment(prop))
3912
3924
  return null;
3913
3925
  let key;
3914
- if (ts23.isIdentifier(prop.name)) {
3926
+ if (ts24.isIdentifier(prop.name)) {
3915
3927
  key = prop.name.text;
3916
- } else if (ts23.isStringLiteral(prop.name) || ts23.isNoSubstitutionTemplateLiteral(prop.name)) {
3928
+ } else if (ts24.isStringLiteral(prop.name) || ts24.isNoSubstitutionTemplateLiteral(prop.name)) {
3917
3929
  key = prop.name.text;
3918
3930
  } else {
3919
3931
  return null;
3920
3932
  }
3921
3933
  let v = prop.initializer;
3922
- while (ts23.isParenthesizedExpression(v))
3934
+ while (ts24.isParenthesizedExpression(v))
3923
3935
  v = v.expression;
3924
- if (ts23.isNumericLiteral(v)) {
3936
+ if (ts24.isNumericLiteral(v)) {
3925
3937
  entries.push({ key, value: { kind: "number", text: v.text } });
3926
- } else if (ts23.isStringLiteral(v) || ts23.isNoSubstitutionTemplateLiteral(v)) {
3938
+ } else if (ts24.isStringLiteral(v) || ts24.isNoSubstitutionTemplateLiteral(v)) {
3927
3939
  entries.push({ key, value: { kind: "string", text: v.text } });
3928
3940
  } else {
3929
3941
  return null;
@@ -4012,7 +4024,7 @@ function computeSsrSeedPlan(metadata) {
4012
4024
  // ../jsx/src/rich-type-refusal.ts
4013
4025
  var EMPTY_BINDINGS2 = new Map;
4014
4026
  // ../jsx/src/shared-program.ts
4015
- import ts25 from "typescript";
4027
+ import ts26 from "typescript";
4016
4028
  // ../jsx/src/adapters/interface.ts
4017
4029
  class BaseAdapter {
4018
4030
  renderChildren(children) {
@@ -4294,7 +4306,7 @@ class JsxAdapter extends BaseAdapter {
4294
4306
  }
4295
4307
 
4296
4308
  // ../jsx/src/adapters/template-imports.ts
4297
- import ts26 from "typescript";
4309
+ import ts27 from "typescript";
4298
4310
  var CLIENT_PACKAGE_SOURCES = new Set([
4299
4311
  "@barefootjs/client",
4300
4312
  "@barefootjs/client/runtime"
@@ -5094,7 +5106,7 @@ function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
5094
5106
  };
5095
5107
  }
5096
5108
  // ../jsx/src/combine-client-js.ts
5097
- import ts27 from "typescript";
5109
+ import ts28 from "typescript";
5098
5110
  // ../jsx/src/loop-destructure.ts
5099
5111
  function isLowerableLoopDestructure(loop) {
5100
5112
  const bindings = loop.paramBindings;
@@ -5234,9 +5246,9 @@ function escapeRe(s) {
5234
5246
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5235
5247
  }
5236
5248
  // ../jsx/src/debug.ts
5237
- import ts28 from "typescript";
5238
- // ../jsx/src/profiler.ts
5239
5249
  import ts29 from "typescript";
5250
+ // ../jsx/src/profiler.ts
5251
+ import ts30 from "typescript";
5240
5252
 
5241
5253
  // ../jsx/src/index.ts
5242
5254
  registerBuiltinLoweringPlugins();
@@ -5315,39 +5327,6 @@ function kolonHashKey(name) {
5315
5327
  return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : `'${escapeKolonSingleQuoted(name)}'`;
5316
5328
  }
5317
5329
 
5318
- // src/adapter/lib/ir-scope.ts
5319
- function resolveJsxChildrenProp(props) {
5320
- const prop = props.find((p) => p.name === "children");
5321
- if (!prop)
5322
- return [];
5323
- if (prop.value.kind !== "jsx-children")
5324
- return [];
5325
- return prop.value.children;
5326
- }
5327
- function collectRootScopeNodes(node) {
5328
- const out = new Set;
5329
- const visit = (n) => {
5330
- if (!n)
5331
- return;
5332
- if (n.type === "element") {
5333
- out.add(n);
5334
- return;
5335
- }
5336
- if (n.type === "if-statement") {
5337
- const s = n;
5338
- visit(s.consequent);
5339
- visit(s.alternate);
5340
- return;
5341
- }
5342
- if (n.type === "fragment") {
5343
- for (const c of n.children)
5344
- visit(c);
5345
- }
5346
- };
5347
- visit(node);
5348
- return out;
5349
- }
5350
-
5351
5330
  // src/adapter/expr/array-method.ts
5352
5331
  function renderArrayMethod(method, object, args, emit) {
5353
5332
  switch (method) {
@@ -6014,7 +5993,7 @@ function collectImportedLoopChildComponentErrors(ir, componentName) {
6014
5993
  }
6015
5994
 
6016
5995
  // src/adapter/spread/spread-codegen.ts
6017
- import ts30 from "typescript";
5996
+ import ts31 from "typescript";
6018
5997
  function conditionalSpreadToKolon(ctx, expr) {
6019
5998
  if (!expr || expr.kind !== "conditional")
6020
5999
  return null;
@@ -6071,7 +6050,7 @@ function recordIndexAccessToKolon(ctx, val) {
6071
6050
  if (val.kind !== "index-access" || val.object.kind !== "identifier" || val.index.kind !== "identifier") {
6072
6051
  return null;
6073
6052
  }
6074
- const tsVal = ts30.factory.createElementAccessExpression(ts30.factory.createIdentifier(val.object.name), ts30.factory.createIdentifier(val.index.name));
6053
+ const tsVal = ts31.factory.createElementAccessExpression(ts31.factory.createIdentifier(val.object.name), ts31.factory.createIdentifier(val.index.name));
6075
6054
  const parsed = parseRecordIndexAccess(tsVal, ctx.localConstants ?? [], ctx.propsParams);
6076
6055
  if (!parsed)
6077
6056
  return null;
@@ -6179,11 +6158,9 @@ class XslateAdapter extends BaseAdapter {
6179
6158
  templatesPerComponent = true;
6180
6159
  templatePrimitives = XSLATE_PRIMITIVE_EMIT_MAP;
6181
6160
  componentName = "";
6182
- rootScopeNodes = new Set;
6183
6161
  options;
6184
6162
  errors = [];
6185
6163
  inLoop = false;
6186
- currentLoopKeyDepth = 0;
6187
6164
  propsObjectName = null;
6188
6165
  propsParams = [];
6189
6166
  booleanTypedProps = new Set;
@@ -6219,7 +6196,6 @@ class XslateAdapter extends BaseAdapter {
6219
6196
  if (!options?.siblingTemplatesRegistered) {
6220
6197
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName));
6221
6198
  }
6222
- this.rootScopeNodes = collectRootScopeNodes(ir.root);
6223
6199
  const templateBody = ir.root.type === "if-statement" ? this.renderIfStatement(ir.root) : this.renderNode(ir.root);
6224
6200
  const scriptReg = options?.skipScriptRegistration ? "" : this.generateScriptRegistrations(ir, options?.scriptBaseName, options?.scriptAssets, options?.preloadAssets);
6225
6201
  const ctxSeed = generateContextConsumerSeed(ir);
@@ -6340,8 +6316,14 @@ class XslateAdapter extends BaseAdapter {
6340
6316
  if (element.needsScope) {
6341
6317
  hydrationAttrs += ` ${this.renderScopeMarker("")}`;
6342
6318
  }
6343
- if (this.rootScopeNodes.has(element) && element.needsScope || element.carriesDataKey) {
6344
- hydrationAttrs += ` <: $bf.data_key_attr() | mark_raw :>`;
6319
+ if (element.keyAttr) {
6320
+ if (element.keyAttr.value !== undefined) {
6321
+ const lowered = emitAttrValue({ kind: "expression", expr: element.keyAttr.value }, this.elementAttrEmitter, element.keyAttr.name);
6322
+ if (lowered)
6323
+ hydrationAttrs += ` ${lowered}`;
6324
+ } else {
6325
+ hydrationAttrs += ` <: $bf.data_key_attr() | mark_raw :>`;
6326
+ }
6345
6327
  }
6346
6328
  if (element.slotId) {
6347
6329
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
@@ -6547,13 +6529,10 @@ ${whenTrue}
6547
6529
  }
6548
6530
  const prevInLoop = this.inLoop;
6549
6531
  this.inLoop = true;
6550
- const prevLoopKeyDepth = this.currentLoopKeyDepth;
6551
- this.currentLoopKeyDepth = loop.depth;
6552
6532
  const prevScope = this.scope;
6553
6533
  this.scope = prevScope.enterLoopRow(loop);
6554
6534
  const preambleLines = (loop.preamble?.declarations ?? []).map((d) => `: my $${d.name} = ${this.convertExpressionToKolon(d.raw, d.valueParsed)};`);
6555
6535
  const childrenUnderLoop = this.renderChildren(loop.children);
6556
- this.currentLoopKeyDepth = prevLoopKeyDepth;
6557
6536
  this.inLoop = prevInLoop;
6558
6537
  const bodyChildren = loop.bodyIsItemConditional && loop.key ? `<: $bf.comment("loop-i:" ~ ${this.convertExpressionToKolon(loop.key)}) | mark_raw :>
6559
6538
  ${childrenUnderLoop}` : childrenUnderLoop;
@@ -6826,14 +6805,9 @@ ${name}="<: ${val} :>"
6826
6805
  continue;
6827
6806
  if (isDangerousInnerHtmlAttr(attr))
6828
6807
  continue;
6829
- let attrName;
6830
- if (attr.name === "className")
6831
- attrName = "class";
6832
- else if (attr.name === "key") {
6833
- const depth = this.currentLoopKeyDepth;
6834
- attrName = depth > 0 ? `data-key-${depth}` : "data-key";
6835
- } else
6836
- attrName = attr.name;
6808
+ if (attr.name === "key")
6809
+ continue;
6810
+ const attrName = attr.name === "className" ? "class" : attr.name;
6837
6811
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
6838
6812
  if (lowered)
6839
6813
  parts.push(lowered);
@@ -1,5 +1,5 @@
1
1
  package BarefootJS::Backend::Xslate;
2
- our $VERSION = "0.33.1";
2
+ our $VERSION = "0.33.3";
3
3
  use strict;
4
4
  use warnings;
5
5
  use utf8;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/xslate",
3
- "version": "0.33.2",
3
+ "version": "0.33.4",
4
4
  "description": "Text::Xslate (Kolon) adapter for BarefootJS — compiles IR to .tx templates and ships the Xslate rendering backend; runs under any PSGI/Plack app",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -55,7 +55,7 @@
55
55
  "directory": "packages/adapter-xslate"
56
56
  },
57
57
  "dependencies": {
58
- "@barefootjs/shared": "0.33.2"
58
+ "@barefootjs/shared": "0.33.4"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "@barefootjs/jsx": ">=0.2.0",
@@ -72,9 +72,9 @@
72
72
  },
73
73
  "devDependencies": {
74
74
  "@barefootjs/adapter-tests": "0.1.0",
75
- "@barefootjs/jsx": "0.33.2",
76
- "@barefootjs/vite": "0.33.2",
77
- "@barefootjs/client": "0.33.2",
75
+ "@barefootjs/jsx": "0.33.4",
76
+ "@barefootjs/vite": "0.33.4",
77
+ "@barefootjs/client": "0.33.4",
78
78
  "typescript": "^5.0.0",
79
79
  "vite": "^6.0.0"
80
80
  }
@@ -85,15 +85,11 @@ import {
85
85
  import { isAriaBooleanAttr, isBooleanResultExpr } from './boolean-result.ts'
86
86
  import ts from 'typescript'
87
87
  import type { ParsedExpr, LoweringMatcher } from '@barefootjs/jsx'
88
- import { BF_SLOT, BF_COND, BF_REGION, escapeHtml } from '@barefootjs/shared'
88
+ import { BF_SLOT, BF_COND, BF_REGION, escapeHtml, resolveJsxChildrenProp } from '@barefootjs/shared'
89
89
 
90
90
  import type { XslateRenderCtx } from './lib/types.ts'
91
91
  import { XSLATE_PRIMITIVE_EMIT_MAP } from './lib/constants.ts'
92
92
  import { kolonHashKey, escapeKolonSingleQuoted } from './lib/kolon-naming.ts'
93
- import {
94
- resolveJsxChildrenProp,
95
- collectRootScopeNodes,
96
- } from './lib/ir-scope.ts'
97
93
  import { renderSortMethod, renderSortEval } from './expr/array-method.ts'
98
94
  import { staticValueToKolon } from './lib/static-value.ts'
99
95
  import { XslateFilterEmitter, XslateTopLevelEmitter } from './expr/emitters.ts'
@@ -174,22 +170,9 @@ export class XslateAdapter extends BaseAdapter implements IRNodeEmitter<XslateRe
174
170
  templatePrimitives: TemplatePrimitiveRegistry = XSLATE_PRIMITIVE_EMIT_MAP
175
171
 
176
172
  private componentName: string = ''
177
- /** Component root scope element(s) — each carries `data-key` for a keyed loop
178
- * item (set by the child renderer from the JSX `key` prop). A plain element
179
- * root is one node; an `if-statement` (early-return) root contributes the
180
- * top element of every branch. */
181
- private rootScopeNodes: Set<IRNode> = new Set()
182
173
  private options: Required<XslateAdapterOptions>
183
174
  private errors: CompilerError[] = []
184
175
  private inLoop: boolean = false
185
- /**
186
- * `IRLoop.depth` of the loop currently being rendered (save/restore
187
- * around `renderChildren(loop.children)`, mirroring `inLoop` above).
188
- * `renderAttributes` reads this to derive the `key` → `data-key`/
189
- * `data-key-N` suffix — the depth is IR-computed (jsx-to-ir.ts), not
190
- * re-derived here (#2168 nested-loop-outer-binding).
191
- */
192
- private currentLoopKeyDepth = 0
193
176
  /**
194
177
  * SolidJS-style props identifier (`function(props: P)`) and the
195
178
  * analyzer-extracted prop names. Stashed at `generate()` entry so the
@@ -317,7 +300,6 @@ export class XslateAdapter extends BaseAdapter implements IRNodeEmitter<XslateRe
317
300
  this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName))
318
301
  }
319
302
 
320
- this.rootScopeNodes = collectRootScopeNodes(ir.root)
321
303
  const templateBody = ir.root.type === 'if-statement'
322
304
  ? this.renderIfStatement(ir.root as IRIfStatement)
323
305
  : this.renderNode(ir.root)
@@ -559,18 +541,30 @@ export class XslateAdapter extends BaseAdapter implements IRNodeEmitter<XslateRe
559
541
  if (element.needsScope) {
560
542
  hydrationAttrs += ` ${this.renderScopeMarker('')}`
561
543
  }
562
- // A root scope element carries `data-key` for a keyed loop item (set on the
563
- // bf instance by the child renderer from the JSX `key` prop); non-keyed
564
- // renders add nothing. Mirrors Hono stamping data-key on each loop item's
565
- // root, including early-return (if-statement) roots. (#1297)
566
- // `carriesDataKey` is an INDEPENDENT reason to emit, not a refinement of
567
- // the root-scope test above (#2732): a fragment root clears `needsScope` on
568
- // its wrapped child and moves the other hydration markers onto the
569
- // `<!--bf-scope:-->` comment, but `data-key` must stay a real attribute —
570
- // the client's `mapArray` adopt loop reads `primaryEl.dataset.key`, never
571
- // the comment. Mirrors the same branch in hono-adapter.ts.
572
- if ((this.rootScopeNodes.has(element) && element.needsScope) || element.carriesDataKey) {
573
- hydrationAttrs += ` <: $bf.data_key_attr() | mark_raw :>`
544
+ // #2753: `element.keyAttr` is the ONE IR-resolved decision for this
545
+ // element's row-key attribute replacing both the `carriesDataKey`
546
+ // boolean and this adapter's own `rootScopeNodes`/`needsScope` check
547
+ // (mechanism 2: a render-root relay, including the #2732 fragment-root
548
+ // case) and the separate `currentLoopKeyDepth`-driven rewrite of a
549
+ // literal `key` attribute this adapter used to do in `renderAttributes`
550
+ // (mechanism 1: a `.map()` row root compiled inline here).
551
+ if (element.keyAttr) {
552
+ if (element.keyAttr.value !== undefined) {
553
+ const lowered = emitAttrValue(
554
+ { kind: 'expression', expr: element.keyAttr.value },
555
+ this.elementAttrEmitter,
556
+ element.keyAttr.name,
557
+ )
558
+ if (lowered) hydrationAttrs += ` ${lowered}`
559
+ } else {
560
+ // Relay: this element is one of THIS component's own render roots,
561
+ // carrying whatever key its OWN caller supplies at runtime (the `bf`
562
+ // instance's `data_key` field) when this component is itself
563
+ // invoked as a caller's keyed loop row. Mirrors Hono stamping
564
+ // data-key on each loop item's root, including early-return
565
+ // (if-statement) roots (#1297), and #2732's fragment-root case.
566
+ hydrationAttrs += ` <: $bf.data_key_attr() | mark_raw :>`
567
+ }
574
568
  }
575
569
  if (element.slotId) {
576
570
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`
@@ -922,8 +916,6 @@ export class XslateAdapter extends BaseAdapter implements IRNodeEmitter<XslateRe
922
916
 
923
917
  const prevInLoop = this.inLoop
924
918
  this.inLoop = true
925
- const prevLoopKeyDepth = this.currentLoopKeyDepth
926
- this.currentLoopKeyDepth = loop.depth
927
919
  // Re-render children now that inLoop is set (so nested components use the
928
920
  // loop-child naming convention). renderedChildren above was computed with
929
921
  // the previous flag; recompute under the loop flag.
@@ -965,7 +957,6 @@ export class XslateAdapter extends BaseAdapter implements IRNodeEmitter<XslateRe
965
957
  d => `: my $${d.name} = ${this.convertExpressionToKolon(d.raw, d.valueParsed)};`,
966
958
  )
967
959
  const childrenUnderLoop = this.renderChildren(loop.children)
968
- this.currentLoopKeyDepth = prevLoopKeyDepth
969
960
  this.inLoop = prevInLoop
970
961
  void renderedChildren
971
962
 
@@ -1518,14 +1509,15 @@ export class XslateAdapter extends BaseAdapter implements IRNodeEmitter<XslateRe
1518
1509
  // literal never reaches the generic object-literal BF101 refusal
1519
1510
  // (which would double-report alongside the purpose-built one).
1520
1511
  if (isDangerousInnerHtmlAttr(attr)) continue
1512
+ // `key` never renders as a literal HTML attribute — #2753 resolves it
1513
+ // onto `element.keyAttr` (name AND value) at IR-build time, emitted
1514
+ // separately in `renderElement`. A stray `key` that jsx-to-ir.ts did
1515
+ // NOT recognize as a loop row root (so `keyAttr` was never set) has no
1516
+ // defined rendering outside a `.map()` anyway; drop it rather than
1517
+ // leak an invalid `key="..."` HTML attribute.
1518
+ if (attr.name === 'key') continue
1521
1519
  // Rewrite JSX special-prop names to their HTML-attribute counterparts.
1522
- let attrName: string
1523
- if (attr.name === 'className') attrName = 'class'
1524
- else if (attr.name === 'key') {
1525
- const depth = this.currentLoopKeyDepth
1526
- attrName = depth > 0 ? `data-key-${depth}` : 'data-key'
1527
- }
1528
- else attrName = attr.name
1520
+ const attrName = attr.name === 'className' ? 'class' : attr.name
1529
1521
  const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName)
1530
1522
  if (lowered) parts.push(lowered)
1531
1523
  }
@@ -1,29 +0,0 @@
1
- /**
2
- * IR traversal helpers for the Text::Xslate (Kolon) template adapter.
3
- *
4
- * Extracted from `xslate-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 Mojo
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. Narrowed via the
17
- * AttrValue `kind` discriminator so adapter code stays type-safe if the IR
18
- * shape evolves.
19
- */
20
- export declare function resolveJsxChildrenProp(props: readonly IRProp[]): IRNode[];
21
- /**
22
- * Collect the component's root scope element node(s) — the elements that
23
- * become the rendered root and so carry `data-key` for a keyed loop item. A
24
- * plain element root is itself; an `if-statement` (early-return) root
25
- * contributes the top element of each branch, since exactly one renders at
26
- * runtime. (#1297)
27
- */
28
- export declare function collectRootScopeNodes(node: IRNode): Set<IRNode>;
29
- //# 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;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAKzE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAiB/D"}
@@ -1,53 +0,0 @@
1
- /**
2
- * IR traversal helpers for the Text::Xslate (Kolon) template adapter.
3
- *
4
- * Extracted from `xslate-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 Mojo
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. Narrowed via the
19
- * AttrValue `kind` discriminator so adapter code stays type-safe if the IR
20
- * shape evolves.
21
- */
22
- export function resolveJsxChildrenProp(props: readonly IRProp[]): IRNode[] {
23
- const prop = props.find(p => p.name === 'children')
24
- if (!prop) return []
25
- if (prop.value.kind !== 'jsx-children') return []
26
- return prop.value.children
27
- }
28
-
29
- /**
30
- * Collect the component's root scope element node(s) — the elements that
31
- * become the rendered root and so carry `data-key` for a keyed loop item. A
32
- * plain element root is itself; an `if-statement` (early-return) root
33
- * contributes the top element of each branch, since exactly one renders at
34
- * runtime. (#1297)
35
- */
36
- export function collectRootScopeNodes(node: IRNode): Set<IRNode> {
37
- const out = new Set<IRNode>()
38
- const visit = (n: IRNode | null): void => {
39
- if (!n) return
40
- if (n.type === 'element') { out.add(n); return }
41
- if (n.type === 'if-statement') {
42
- const s = n as IRIfStatement
43
- visit(s.consequent)
44
- visit(s.alternate)
45
- return
46
- }
47
- if (n.type === 'fragment') {
48
- for (const c of (n as IRFragment).children) visit(c)
49
- }
50
- }
51
- visit(node)
52
- return out
53
- }