@barefootjs/cli 0.18.5 → 0.18.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +949 -247
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -3397,6 +3397,38 @@ function extractFreeIdentifiersFromText(text) {
3397
3397
  const expr = ts4.isParenthesizedExpression(stmt.expression) ? stmt.expression.expression : stmt.expression;
3398
3398
  return extractFreeIdentifiersFromNode(expr);
3399
3399
  }
3400
+ function extractFreeIdentifiersFromStatementText(text) {
3401
+ if (!text || text.trim().length === 0) return /* @__PURE__ */ new Set();
3402
+ const sf = ts4.createSourceFile(
3403
+ "__free_ids_stmt__.ts",
3404
+ text,
3405
+ ts4.ScriptTarget.Latest,
3406
+ /* setParentNodes */
3407
+ true,
3408
+ ts4.ScriptKind.TS
3409
+ );
3410
+ return extractFreeIdentifiersFromNode(sf);
3411
+ }
3412
+ function extractFreeIdentifiersFromTemplateText(template) {
3413
+ if (!template || template.length === 0) return /* @__PURE__ */ new Set();
3414
+ const sf = ts4.createSourceFile(
3415
+ "__free_ids_template__.ts",
3416
+ `(\`${template}\`);`,
3417
+ ts4.ScriptTarget.Latest,
3418
+ /* setParentNodes */
3419
+ true,
3420
+ ts4.ScriptKind.TS
3421
+ );
3422
+ const stmt = sf.statements[0];
3423
+ if (!stmt || !ts4.isExpressionStatement(stmt)) return /* @__PURE__ */ new Set();
3424
+ const expr = ts4.isParenthesizedExpression(stmt.expression) ? stmt.expression.expression : stmt.expression;
3425
+ if (!ts4.isTemplateExpression(expr)) return /* @__PURE__ */ new Set();
3426
+ const ids = /* @__PURE__ */ new Set();
3427
+ for (const span of expr.templateSpans) {
3428
+ for (const id2 of extractFreeIdentifiersFromNode(span.expression)) ids.add(id2);
3429
+ }
3430
+ return ids;
3431
+ }
3400
3432
  function extractMemoBodyExpr(computation) {
3401
3433
  const arrowMatch = computation.match(/^\(\)\s*=>\s*(.+)$/s);
3402
3434
  if (!arrowMatch) return computation;
@@ -3879,6 +3911,100 @@ function buildLoopSkeletonTemplate(node, safe) {
3879
3911
  return assertNever(node);
3880
3912
  }
3881
3913
  }
3914
+ function skeletonForceCloseGroup(tag) {
3915
+ return SKELETON_PATH_FORCE_CLOSE_GROUPS.findIndex((group) => group.has(tag));
3916
+ }
3917
+ function computeSkeletonSlotPaths(node, safe) {
3918
+ const state2 = { elementPaths: /* @__PURE__ */ new Map(), textMarkerPaths: /* @__PURE__ */ new Map(), bailed: false };
3919
+ walkSkeletonPathNode(node, [], safe, state2, /* @__PURE__ */ new Set());
3920
+ if (state2.bailed) return null;
3921
+ return { elementPaths: state2.elementPaths, textMarkerPaths: state2.textMarkerPaths };
3922
+ }
3923
+ function walkSkeletonPathNode(node, path25, safe, state2, forceCloseAncestors) {
3924
+ if (state2.bailed || node.type !== "element") return;
3925
+ if (SKELETON_PATH_HAZARD_TAGS.has(node.tag)) {
3926
+ state2.bailed = true;
3927
+ return;
3928
+ }
3929
+ const groupIdx = skeletonForceCloseGroup(node.tag);
3930
+ if (groupIdx >= 0 && forceCloseAncestors.has(groupIdx)) {
3931
+ state2.bailed = true;
3932
+ return;
3933
+ }
3934
+ const flatChildren = flattenSkeletonChildren(node.children);
3935
+ if (VOID_ELEMENTS.has(node.tag) && flatChildren.length > 0) {
3936
+ state2.bailed = true;
3937
+ return;
3938
+ }
3939
+ if (node.tag === "tr" && hasForeignTableRowContent(flatChildren)) {
3940
+ state2.bailed = true;
3941
+ return;
3942
+ }
3943
+ if (node.slotId) state2.elementPaths.set(node.slotId, path25);
3944
+ const nextAncestors = groupIdx >= 0 ? /* @__PURE__ */ new Set([...forceCloseAncestors, groupIdx]) : forceCloseAncestors;
3945
+ walkSkeletonPathChildren(flatChildren, path25, safe, state2, nextAncestors);
3946
+ }
3947
+ function hasForeignTableRowContent(children2) {
3948
+ for (const child of children2) {
3949
+ if (child.type === "text") {
3950
+ if (child.value.trim() !== "") return true;
3951
+ continue;
3952
+ }
3953
+ if (child.type === "element" && child.tag !== "td" && child.tag !== "th") {
3954
+ return true;
3955
+ }
3956
+ }
3957
+ return false;
3958
+ }
3959
+ function flattenSkeletonChildren(children2) {
3960
+ const out = [];
3961
+ for (const child of children2) {
3962
+ if (child.type === "fragment") {
3963
+ out.push(...flattenSkeletonChildren(child.children));
3964
+ } else {
3965
+ out.push(child);
3966
+ }
3967
+ }
3968
+ return out;
3969
+ }
3970
+ function walkSkeletonPathChildren(children2, parentPath, safe, state2, forceCloseAncestors) {
3971
+ let idx = 0;
3972
+ let pendingText = false;
3973
+ for (const child of children2) {
3974
+ if (state2.bailed) return;
3975
+ switch (child.type) {
3976
+ case "text": {
3977
+ if (child.value === "") continue;
3978
+ if (!pendingText) idx += 1;
3979
+ pendingText = true;
3980
+ continue;
3981
+ }
3982
+ case "expression": {
3983
+ if (child.expr === "null" || child.expr === "undefined") continue;
3984
+ if (!child.slotId || !safe.reactiveTextSlotIds.has(child.slotId)) {
3985
+ state2.bailed = true;
3986
+ return;
3987
+ }
3988
+ state2.textMarkerPaths.set(child.slotId, [...parentPath, idx]);
3989
+ idx += 2;
3990
+ pendingText = false;
3991
+ continue;
3992
+ }
3993
+ case "element": {
3994
+ walkSkeletonPathNode(child, [...parentPath, idx], safe, state2, forceCloseAncestors);
3995
+ idx += 1;
3996
+ pendingText = false;
3997
+ continue;
3998
+ }
3999
+ case "fragment":
4000
+ continue;
4001
+ // already flattened
4002
+ default:
4003
+ state2.bailed = true;
4004
+ return;
4005
+ }
4006
+ }
4007
+ }
3882
4008
  function irToPlaceholderTemplate(node, restSpreadNames, loopDepth = 0, loopParams) {
3883
4009
  const recurse = (n) => irToPlaceholderTemplate(n, restSpreadNames, loopDepth, loopParams);
3884
4010
  const wrapExpr = (expr) => wrapExprWithLoopParams(expr, loopParams);
@@ -4495,7 +4621,26 @@ function generateCsrTemplateWithOpts(node, opts) {
4495
4621
  return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${keyArg || (slotArg ? ", undefined" : "")}${slotArg})}`;
4496
4622
  }
4497
4623
  case "loop": {
4498
- let childTemplate = node.children.map(recurseInLoop).join("");
4624
+ const boundHere = new Set(opts.loopBoundNames ?? []);
4625
+ if (node.paramBindings && node.paramBindings.length > 0) {
4626
+ for (const b of node.paramBindings) boundHere.add(b.name);
4627
+ } else if (!node.param.startsWith("[") && !node.param.startsWith("{")) {
4628
+ boundHere.add(node.param);
4629
+ }
4630
+ if (node.index) boundHere.add(node.index);
4631
+ const childEnv = {
4632
+ ...env,
4633
+ substitutions: new Map([...env.substitutions].filter(([name2]) => !boundHere.has(name2)))
4634
+ };
4635
+ const recurseInLoopBody = (n) => generateCsrTemplateWithOpts(n, {
4636
+ ...opts,
4637
+ insideLoop: true,
4638
+ loopDepth: loopDepth + 1,
4639
+ inHoistedChildren: false,
4640
+ loopBoundNames: boundHere,
4641
+ csrEnv: childEnv
4642
+ });
4643
+ let childTemplate = node.children.map(recurseInLoopBody).join("");
4499
4644
  if (node.bodyIsItemConditional && node.key) {
4500
4645
  childTemplate = `${itemAnchorTemplate(node.key)}${childTemplate}`;
4501
4646
  }
@@ -4509,7 +4654,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4509
4654
  if (node.flatMapCallback) {
4510
4655
  let body2 = node.flatMapCallback.templateBody ?? node.flatMapCallback.body;
4511
4656
  for (const frag of node.flatMapCallback.fragments) {
4512
- const renderedIr = recurseInLoop(frag.ir);
4657
+ const renderedIr = recurseInLoopBody(frag.ir);
4513
4658
  body2 = body2.replace(frag.placeholder, `\`${renderedIr}\``);
4514
4659
  }
4515
4660
  body2 = applyPropsRewrite(body2, propsObjectName ?? null);
@@ -4551,7 +4696,7 @@ function isSimplePropExpression(expr, propNames) {
4551
4696
  if (expr.includes("()")) return false;
4552
4697
  return true;
4553
4698
  }
4554
- var VOID_ELEMENTS, UNSAFE_TEMPLATE_EXPR;
4699
+ var VOID_ELEMENTS, UNSAFE_TEMPLATE_EXPR, SKELETON_PATH_HAZARD_TAGS, SKELETON_PATH_FORCE_CLOSE_GROUPS;
4555
4700
  var init_html_template = __esm({
4556
4701
  "../jsx/src/ir-to-client-js/html-template.ts"() {
4557
4702
  "use strict";
@@ -4579,6 +4724,33 @@ var init_html_template = __esm({
4579
4724
  "wbr"
4580
4725
  ]);
4581
4726
  UNSAFE_TEMPLATE_EXPR = "undefined";
4727
+ SKELETON_PATH_HAZARD_TAGS = /* @__PURE__ */ new Set([
4728
+ "table",
4729
+ "thead",
4730
+ "tbody",
4731
+ "tfoot",
4732
+ "caption",
4733
+ "colgroup",
4734
+ "col",
4735
+ "select",
4736
+ "optgroup",
4737
+ "p",
4738
+ "pre",
4739
+ "textarea",
4740
+ "listing",
4741
+ "template",
4742
+ "math"
4743
+ // MathML foreign-content: breakout tags pop content back out, same class of hazard as SVG.
4744
+ ]);
4745
+ SKELETON_PATH_FORCE_CLOSE_GROUPS = [
4746
+ /* @__PURE__ */ new Set(["a"]),
4747
+ /* @__PURE__ */ new Set(["button"]),
4748
+ /* @__PURE__ */ new Set(["form"]),
4749
+ /* @__PURE__ */ new Set(["option"]),
4750
+ /* @__PURE__ */ new Set(["h1", "h2", "h3", "h4", "h5", "h6"]),
4751
+ /* @__PURE__ */ new Set(["dd", "dt"]),
4752
+ /* @__PURE__ */ new Set(["li"])
4753
+ ];
4582
4754
  }
4583
4755
  });
4584
4756
 
@@ -5229,6 +5401,7 @@ import fs from "node:fs";
5229
5401
  function needsTypeBasedDetection(source) {
5230
5402
  if (REACTIVE_BRAND_PACKAGES.some((pkg) => source.includes(pkg))) return true;
5231
5403
  if (/\.map\s*\(/.test(source)) return true;
5404
+ if (source.includes("createSelector")) return true;
5232
5405
  return false;
5233
5406
  }
5234
5407
  function findBrandPackageImportLoc(sourceFile, filePath) {
@@ -7518,6 +7691,7 @@ var init_analyzer = __esm({
7518
7691
  "createEffect",
7519
7692
  "createDisposableEffect",
7520
7693
  "createMemo",
7694
+ "createSelector",
7521
7695
  "createRoot",
7522
7696
  "onCleanup",
7523
7697
  "onMount",
@@ -8184,8 +8358,13 @@ function exprHasFunctionCalls(expr) {
8184
8358
  return found;
8185
8359
  }
8186
8360
  function rewriteBarePropRefs2(text, expr, ctx2) {
8187
- const propNames = getDestructuredPropNames(ctx2);
8361
+ let propNames = getDestructuredPropNames(ctx2);
8188
8362
  if (!propNames) return void 0;
8363
+ if (ctx2.loopParams.size > 0) {
8364
+ const filtered = new Set([...propNames].filter((n) => !ctx2.loopParams.has(n)));
8365
+ if (filtered.size === 0) return void 0;
8366
+ propNames = filtered;
8367
+ }
8189
8368
  const extraPropRefs = collectBranchLocalPropRefsViaSubstitution(expr, ctx2);
8190
8369
  return rewriteBarePropRefs(text, expr, propNames, extraPropRefs);
8191
8370
  }
@@ -8339,35 +8518,37 @@ function makeBindingEnv(ctx2) {
8339
8518
  function parseValueExpr(trimmed) {
8340
8519
  return parseExpression(trimmed.startsWith("{") ? `(${trimmed})` : trimmed);
8341
8520
  }
8342
- function attachParsedExpressions(node) {
8521
+ function attachParsedExpressions(node, analyzer, bound = EMPTY_BOUND) {
8522
+ const parse = (trimmed) => resolveCallbackMethodFunctionReferences(parseExpression(trimmed), analyzer, bound);
8523
+ const parseValue = (trimmed) => resolveCallbackMethodFunctionReferences(parseValueExpr(trimmed), analyzer, bound);
8343
8524
  if (node.type === "expression") {
8344
8525
  const trimmed = node.expr.trim();
8345
- if (trimmed) node.parsed = parseExpression(trimmed);
8526
+ if (trimmed) node.parsed = parse(trimmed);
8346
8527
  } else if (node.type === "conditional" || node.type === "if-statement") {
8347
8528
  const trimmed = node.condition.trim();
8348
- if (trimmed) node.parsedCondition = parseExpression(trimmed);
8529
+ if (trimmed) node.parsedCondition = parse(trimmed);
8349
8530
  }
8350
8531
  if (node.type === "element") {
8351
8532
  for (const attr of node.attrs) {
8352
8533
  if (attr.value.kind === "expression") {
8353
8534
  const trimmed = attr.value.expr.trim();
8354
- if (trimmed) attr.value.parsed = parseValueExpr(trimmed);
8535
+ if (trimmed) attr.value.parsed = parseValue(trimmed);
8355
8536
  } else if (attr.value.kind === "spread") {
8356
8537
  const trimmed = attr.value.expr.trim();
8357
- if (trimmed) attr.value.parsed = parseExpression(trimmed);
8538
+ if (trimmed) attr.value.parsed = parse(trimmed);
8358
8539
  }
8359
8540
  }
8360
8541
  } else if (node.type === "component") {
8361
8542
  for (const prop of node.props) {
8362
8543
  if (prop.value.kind === "expression") {
8363
8544
  const trimmed = prop.value.expr.trim();
8364
- if (trimmed) prop.value.parsed = parseValueExpr(trimmed);
8545
+ if (trimmed) prop.value.parsed = parseValue(trimmed);
8365
8546
  }
8366
8547
  }
8367
8548
  } else if (node.type === "provider") {
8368
8549
  if (node.valueProp.value.kind === "expression") {
8369
8550
  const trimmed = node.valueProp.value.expr.trim();
8370
- if (trimmed) node.valueProp.value.parsed = parseValueExpr(trimmed);
8551
+ if (trimmed) node.valueProp.value.parsed = parseValue(trimmed);
8371
8552
  }
8372
8553
  }
8373
8554
  switch (node.type) {
@@ -8375,40 +8556,43 @@ function attachParsedExpressions(node) {
8375
8556
  case "component":
8376
8557
  case "fragment":
8377
8558
  case "provider":
8378
- for (const child of node.children) attachParsedExpressions(child);
8559
+ for (const child of node.children) attachParsedExpressions(child, analyzer, bound);
8379
8560
  break;
8380
8561
  case "async":
8381
- attachParsedExpressions(node.fallback);
8382
- for (const child of node.children) attachParsedExpressions(child);
8562
+ attachParsedExpressions(node.fallback, analyzer, bound);
8563
+ for (const child of node.children) attachParsedExpressions(child, analyzer, bound);
8383
8564
  break;
8384
8565
  case "loop": {
8385
8566
  const trimmedArray = node.array.trim();
8386
- if (trimmedArray) node.arrayParsed = parseExpression(trimmedArray);
8387
- for (const child of node.children) attachParsedExpressions(child);
8567
+ if (trimmedArray) node.arrayParsed = parse(trimmedArray);
8568
+ const loopBound = new Set(bound);
8569
+ loopBound.add(node.param);
8570
+ if (node.index) loopBound.add(node.index);
8571
+ for (const child of node.children) attachParsedExpressions(child, analyzer, loopBound);
8388
8572
  if (node.childComponent) {
8389
- for (const child of node.childComponent.children) attachParsedExpressions(child);
8573
+ for (const child of node.childComponent.children) attachParsedExpressions(child, analyzer, loopBound);
8390
8574
  }
8391
8575
  for (const nested of node.nestedComponents ?? []) {
8392
- for (const child of nested.children) attachParsedExpressions(child);
8576
+ for (const child of nested.children) attachParsedExpressions(child, analyzer, loopBound);
8393
8577
  }
8394
8578
  for (const frag of node.flatMapCallback?.fragments ?? []) {
8395
- attachParsedExpressions(frag.ir);
8579
+ attachParsedExpressions(frag.ir, analyzer, loopBound);
8396
8580
  }
8397
8581
  break;
8398
8582
  }
8399
8583
  case "conditional":
8400
- attachParsedExpressions(node.whenTrue);
8401
- attachParsedExpressions(node.whenFalse);
8584
+ attachParsedExpressions(node.whenTrue, analyzer, bound);
8585
+ attachParsedExpressions(node.whenFalse, analyzer, bound);
8402
8586
  break;
8403
8587
  case "if-statement":
8404
- attachParsedExpressions(node.consequent);
8405
- if (node.alternate) attachParsedExpressions(node.alternate);
8588
+ attachParsedExpressions(node.consequent, analyzer, bound);
8589
+ if (node.alternate) attachParsedExpressions(node.alternate, analyzer, bound);
8406
8590
  break;
8407
8591
  }
8408
8592
  }
8409
8593
  function jsxToIR(analyzer) {
8410
8594
  const root2 = buildIRRoot(analyzer);
8411
- if (root2) attachParsedExpressions(root2);
8595
+ if (root2) attachParsedExpressions(root2, analyzer);
8412
8596
  return root2;
8413
8597
  }
8414
8598
  function buildIRRoot(analyzer) {
@@ -9504,8 +9688,8 @@ function extractSortComparator(callback, _method, ctx2) {
9504
9688
  };
9505
9689
  }
9506
9690
  function resolveSortComparatorIdentifier(name2, ctx2) {
9507
- const constInfo = findLocalConst(name2, ctx2);
9508
- const fnInfo = findLocalFunction(name2, ctx2);
9691
+ const constInfo = findLocalConst(name2, ctx2.analyzer);
9692
+ const fnInfo = findLocalFunction(name2, ctx2.analyzer);
9509
9693
  if (constInfo && fnInfo) return null;
9510
9694
  if (constInfo) {
9511
9695
  const ast = parseConstInitializer(constInfo);
@@ -9517,6 +9701,70 @@ function resolveSortComparatorIdentifier(name2, ctx2) {
9517
9701
  }
9518
9702
  return null;
9519
9703
  }
9704
+ function resolveCallbackMethodFunctionReferenceIdentifier(name2, analyzer) {
9705
+ const constInfo = findLocalConst(name2, analyzer);
9706
+ const fnInfo = findLocalFunction(name2, analyzer);
9707
+ if (constInfo && fnInfo) return null;
9708
+ if (constInfo) {
9709
+ const ast = parseConstInitializer(constInfo);
9710
+ return ast && (ts11.isArrowFunction(ast) || ts11.isFunctionExpression(ast)) ? ast : null;
9711
+ }
9712
+ if (fnInfo) {
9713
+ const ast = parseFunctionInfoAsExpr(fnInfo);
9714
+ return ast && (ts11.isArrowFunction(ast) || ts11.isFunctionExpression(ast)) ? ast : null;
9715
+ }
9716
+ return null;
9717
+ }
9718
+ function resolveCallbackMethodFunctionReferences(expr, analyzer, bound = EMPTY_BOUND) {
9719
+ function visit3(e, bound2) {
9720
+ switch (e.kind) {
9721
+ case "literal":
9722
+ case "identifier":
9723
+ case "regex":
9724
+ case "unsupported":
9725
+ return e;
9726
+ case "call": {
9727
+ const callee = visit3(e.callee, bound2);
9728
+ const args2 = e.args.map((a) => visit3(a, bound2));
9729
+ if (callee.kind === "member" && !callee.computed && CALLBACK_METHODS.has(callee.property) && args2[0]?.kind === "identifier" && !bound2.has(args2[0].name)) {
9730
+ const resolved = resolveCallbackMethodFunctionReferenceIdentifier(args2[0].name, analyzer);
9731
+ const arrow = resolved ? tsNodeToParsedExpr(resolved) : null;
9732
+ if (arrow && arrow.kind === "arrow") args2[0] = arrow;
9733
+ }
9734
+ return { ...e, callee, args: args2 };
9735
+ }
9736
+ case "member":
9737
+ return { ...e, object: visit3(e.object, bound2) };
9738
+ case "index-access":
9739
+ return { ...e, object: visit3(e.object, bound2), index: visit3(e.index, bound2) };
9740
+ case "binary":
9741
+ case "logical":
9742
+ return { ...e, left: visit3(e.left, bound2), right: visit3(e.right, bound2) };
9743
+ case "unary":
9744
+ return { ...e, argument: visit3(e.argument, bound2) };
9745
+ case "conditional":
9746
+ return { ...e, test: visit3(e.test, bound2), consequent: visit3(e.consequent, bound2), alternate: visit3(e.alternate, bound2) };
9747
+ case "template-literal":
9748
+ return { ...e, parts: e.parts.map((p) => p.type === "expression" ? { ...p, expr: visit3(p.expr, bound2) } : p) };
9749
+ case "array-literal":
9750
+ return { ...e, elements: e.elements.map((el) => visit3(el, bound2)) };
9751
+ case "array-method":
9752
+ return {
9753
+ ...e,
9754
+ object: visit3(e.object, bound2),
9755
+ args: e.args.map((a) => visit3(a, bound2)),
9756
+ ...e.method === "flat" && e.depthExpr ? { depthExpr: visit3(e.depthExpr, bound2) } : {}
9757
+ };
9758
+ case "object-literal":
9759
+ return { ...e, properties: e.properties.map((p) => ({ ...p, value: visit3(p.value, bound2) })) };
9760
+ case "arrow": {
9761
+ const inner = e.params.length === 0 ? bound2 : /* @__PURE__ */ new Set([...bound2, ...e.params]);
9762
+ return { ...e, body: visit3(e.body, inner) };
9763
+ }
9764
+ }
9765
+ }
9766
+ return visit3(expr, bound);
9767
+ }
9520
9768
  function extractFilterPredicate(callback, ctx2) {
9521
9769
  if (!ts11.isArrowFunction(callback)) return { result: null };
9522
9770
  if (callback.parameters.length < 1) return { result: null };
@@ -9995,13 +10243,11 @@ function transformMapCall(node, ctx2, isClientOnly = false, method2 = "map") {
9995
10243
  setArray(innerSort.array);
9996
10244
  }
9997
10245
  } else {
9998
- array = ctx2.getJS(filterInfo.array);
9999
- arrayExpr = filterInfo.array;
10246
+ setArray(filterInfo.array);
10000
10247
  }
10001
10248
  }
10002
10249
  } else {
10003
- array = ctx2.getJS(chainSource);
10004
- arrayExpr = chainSource;
10250
+ setArray(chainSource);
10005
10251
  }
10006
10252
  const callback = node.arguments[0];
10007
10253
  let param = "item";
@@ -10561,7 +10807,7 @@ function parseTemplateLiteral(expr, ctx2) {
10561
10807
  }
10562
10808
  function tryResolveTemplateSpanFromConst(expr, ctx2) {
10563
10809
  if (ts11.isIdentifier(expr)) {
10564
- const constInfo = findLocalConst(expr.text, ctx2);
10810
+ const constInfo = findLocalConst(expr.text, ctx2.analyzer);
10565
10811
  if (!constInfo) return null;
10566
10812
  const ast = parseConstInitializer(constInfo);
10567
10813
  if (!ast) return null;
@@ -10572,7 +10818,7 @@ function tryResolveTemplateSpanFromConst(expr, ctx2) {
10572
10818
  }
10573
10819
  if (ts11.isElementAccessExpression(expr)) {
10574
10820
  if (!ts11.isIdentifier(expr.expression)) return null;
10575
- const constInfo = findLocalConst(expr.expression.text, ctx2);
10821
+ const constInfo = findLocalConst(expr.expression.text, ctx2.analyzer);
10576
10822
  if (!constInfo) return null;
10577
10823
  const ast = parseConstInitializer(constInfo);
10578
10824
  if (!ast || !ts11.isObjectLiteralExpression(ast)) return null;
@@ -10594,15 +10840,15 @@ function tryResolveTemplateSpanFromConst(expr, ctx2) {
10594
10840
  }
10595
10841
  return null;
10596
10842
  }
10597
- function findLocalConst(name2, ctx2) {
10598
- const matches = ctx2.analyzer.localConstants.filter((c) => c.name === name2);
10843
+ function findLocalConst(name2, analyzer) {
10844
+ const matches = analyzer.localConstants.filter((c) => c.name === name2);
10599
10845
  if (matches.length === 0) return void 0;
10600
10846
  const fnScoped = matches.filter((c) => !c.isModule);
10601
10847
  const pool = fnScoped.length > 0 ? fnScoped : matches;
10602
10848
  return pool[pool.length - 1];
10603
10849
  }
10604
- function findLocalFunction(name2, ctx2) {
10605
- const matches = ctx2.analyzer.localFunctions.filter((f) => f.name === name2);
10850
+ function findLocalFunction(name2, analyzer) {
10851
+ const matches = analyzer.localFunctions.filter((f) => f.name === name2);
10606
10852
  if (matches.length === 0) return void 0;
10607
10853
  const fnScoped = matches.filter((f) => !f.isModule);
10608
10854
  const pool = fnScoped.length > 0 ? fnScoped : matches;
@@ -10637,7 +10883,8 @@ function hasDynamicTagBinding(name2, sourceFile) {
10637
10883
  return found;
10638
10884
  }
10639
10885
  function tryResolveIdentifierAsTemplateLiteral(ident, ctx2) {
10640
- const constInfo = findLocalConst(ident.text, ctx2);
10886
+ if (ctx2.loopParams.has(ident.text)) return null;
10887
+ const constInfo = findLocalConst(ident.text, ctx2.analyzer);
10641
10888
  if (!constInfo) return null;
10642
10889
  const ast = parseConstInitializer(constInfo);
10643
10890
  if (!ast) return null;
@@ -10729,8 +10976,8 @@ function tryDesugarInterleaveTaggedTemplate(expr, ctx2) {
10729
10976
  return rewritten ?? expr;
10730
10977
  }
10731
10978
  function resolveInterleaveTagIdentifier(name2, ctx2) {
10732
- const constInfo = findLocalConst(name2, ctx2);
10733
- const fnInfo = findLocalFunction(name2, ctx2);
10979
+ const constInfo = findLocalConst(name2, ctx2.analyzer);
10980
+ const fnInfo = findLocalFunction(name2, ctx2.analyzer);
10734
10981
  if (constInfo && fnInfo) return null;
10735
10982
  if (constInfo) {
10736
10983
  const ast = parseConstInitializer(constInfo);
@@ -11229,7 +11476,7 @@ function buildIfStatementChain(analyzer, ctx2) {
11229
11476
  }
11230
11477
  return alternate;
11231
11478
  }
11232
- var CLIENT_DIRECTIVE_INTERIOR_RE2, BLOCK_COMMENT_RE2, constInitializerCache, functionInfoExprCache;
11479
+ var CLIENT_DIRECTIVE_INTERIOR_RE2, BLOCK_COMMENT_RE2, EMPTY_BOUND, constInitializerCache, functionInfoExprCache;
11233
11480
  var init_jsx_to_ir = __esm({
11234
11481
  "../jsx/src/jsx-to-ir.ts"() {
11235
11482
  "use strict";
@@ -11248,6 +11495,7 @@ var init_jsx_to_ir = __esm({
11248
11495
  init_src();
11249
11496
  CLIENT_DIRECTIVE_INTERIOR_RE2 = /^\s*@client\s*$/;
11250
11497
  BLOCK_COMMENT_RE2 = /\/\*([\s\S]*?)\*\//g;
11498
+ EMPTY_BOUND = /* @__PURE__ */ new Set();
11251
11499
  constInitializerCache = /* @__PURE__ */ new WeakMap();
11252
11500
  functionInfoExprCache = /* @__PURE__ */ new WeakMap();
11253
11501
  }
@@ -11480,6 +11728,12 @@ function collectLoopChildEventsWithNesting(node, initialNestingStack = []) {
11480
11728
  depth: scope.nestingStack.length + 1,
11481
11729
  array: l.array,
11482
11730
  param: l.param,
11731
+ // Event-delegation metadata only (see the field comment on
11732
+ // `NestedLoop.index`) — threaded for type-completeness (#2218)
11733
+ // so this record stays a structurally-valid `NestedLoop`, even
11734
+ // though delegated-handler index binding is handled separately
11735
+ // by `indexBindingLine` in `stringify/event-delegation.ts` (#2189).
11736
+ index: l.index,
11483
11737
  key: l.key,
11484
11738
  markerId: l.markerId,
11485
11739
  containerSlotId: scope.lastElementSlotId,
@@ -11576,6 +11830,156 @@ var init_reactivity = __esm({
11576
11830
  }
11577
11831
  });
11578
11832
 
11833
+ // ../jsx/src/ir-to-client-js/control-flow/stringify/template-parse.ts
11834
+ function templateRootIsSvg(template) {
11835
+ const stripped = stripLeadingNonContent(template);
11836
+ const m = stripped.match(/^<\s*([A-Za-z][A-Za-z0-9-]*)/);
11837
+ if (m) {
11838
+ const tag = m[1];
11839
+ if (SVG_ROOT_TAGS.has(tag)) return true;
11840
+ return SVG_ROOT_TAGS.has(tag.toLowerCase());
11841
+ }
11842
+ const branches = extractConditionalBranchTemplates(stripped);
11843
+ if (branches === null || branches.length === 0) return false;
11844
+ return branches.every(templateRootIsSvg);
11845
+ }
11846
+ function multiRootTemplateNeedsSvgWrap(template) {
11847
+ const m = stripLeadingNonContent(template).match(/^<\s*([A-Za-z][A-Za-z0-9-]*)/);
11848
+ if (m && m[1].toLowerCase() === "svg") return false;
11849
+ return templateRootIsSvg(template);
11850
+ }
11851
+ function stripLeadingNonContent(template) {
11852
+ let s = template.trimStart();
11853
+ while (s.startsWith("<!--")) {
11854
+ const end2 = s.indexOf("-->");
11855
+ if (end2 < 0) return s;
11856
+ s = s.slice(end2 + 3).trimStart();
11857
+ }
11858
+ return s;
11859
+ }
11860
+ function extractConditionalBranchTemplates(template) {
11861
+ if (!template.startsWith("${")) return null;
11862
+ const exprEnd = findInterpolationEnd(template, 2);
11863
+ if (exprEnd < 0) return null;
11864
+ const trailing = stripLeadingNonContent(template.slice(exprEnd + 1));
11865
+ if (trailing.length > 0) return null;
11866
+ const expr = template.slice(2, exprEnd);
11867
+ return findTopLevelTemplateLiterals(expr);
11868
+ }
11869
+ function emitTemplateCloneInline(template) {
11870
+ if (templateRootIsSvg(template)) {
11871
+ return `const __tpl = document.createElement('template'); __tpl.innerHTML = \`<svg>${template}</svg>\`; return __tpl.content.firstElementChild.firstElementChild.cloneNode(true)`;
11872
+ }
11873
+ return `const __tpl = document.createElement('template'); __tpl.innerHTML = \`${template}\`; return __tpl.content.firstElementChild.cloneNode(true)`;
11874
+ }
11875
+ function emitHoistedTemplateDecl(lines, indent, tplVar, skeletonTemplate) {
11876
+ const isSvg = templateRootIsSvg(skeletonTemplate);
11877
+ const html = isSvg ? `<svg>${skeletonTemplate}</svg>` : skeletonTemplate;
11878
+ lines.push(`${indent}const ${tplVar} = document.createElement('template')`);
11879
+ lines.push(`${indent}${tplVar}.innerHTML = \`${html}\``);
11880
+ }
11881
+ function hoistedCloneExpr(tplVar, skeletonTemplate) {
11882
+ return templateRootIsSvg(skeletonTemplate) ? `${tplVar}.content.firstElementChild.firstElementChild.cloneNode(true)` : `${tplVar}.content.firstElementChild.cloneNode(true)`;
11883
+ }
11884
+ function emitTemplateCloneLines(template, indent) {
11885
+ if (templateRootIsSvg(template)) {
11886
+ return [
11887
+ `${indent}const __tpl = document.createElement('template')`,
11888
+ `${indent}__tpl.innerHTML = \`<svg>${template}</svg>\``,
11889
+ `${indent}return __tpl.content.firstElementChild.firstElementChild.cloneNode(true)`
11890
+ ];
11891
+ }
11892
+ return [
11893
+ `${indent}const __tpl = document.createElement('template')`,
11894
+ `${indent}__tpl.innerHTML = \`${template}\``,
11895
+ `${indent}return __tpl.content.firstElementChild.cloneNode(true)`
11896
+ ];
11897
+ }
11898
+ function emitLoopItemElementSetup(lines, opts) {
11899
+ const { template, bodyIsMultiRoot, indent, singleRootLayout } = opts;
11900
+ const innerIndent = indent + " ";
11901
+ if (bodyIsMultiRoot) {
11902
+ lines.push(`${indent}let __el, __extras`);
11903
+ lines.push(`${indent}if (__existing) {`);
11904
+ lines.push(`${innerIndent}__el = __existing`);
11905
+ lines.push(`${indent}} else {`);
11906
+ for (const ln of emitMultiRootTemplateCloneLines(template, innerIndent, "__el", "__extras")) {
11907
+ lines.push(ln);
11908
+ }
11909
+ lines.push(`${innerIndent}__el.__bfExtras = __extras`);
11910
+ lines.push(`${indent}}`);
11911
+ return;
11912
+ }
11913
+ if (singleRootLayout === "inline") {
11914
+ const cloneExpr = emitTemplateCloneInline(template);
11915
+ lines.push(`${indent}const __el = __existing ?? (() => { ${cloneExpr} })()`);
11916
+ return;
11917
+ }
11918
+ lines.push(`${indent}const __el = __existing ?? (() => {`);
11919
+ for (const ln of emitTemplateCloneLines(template, innerIndent)) lines.push(ln);
11920
+ lines.push(`${indent}})()`);
11921
+ }
11922
+ function emitMultiRootTemplateCloneLines(template, indent, varEl, varExtras) {
11923
+ const isSvg = multiRootTemplateNeedsSvgWrap(template);
11924
+ const innerHtmlExpr = isSvg ? `\`<svg>${template}</svg>\`` : `\`${template}\``;
11925
+ const parentExpr = isSvg ? `__tpl.content.firstElementChild` : `__tpl.content`;
11926
+ return [
11927
+ `${indent}const __tpl = document.createElement('template')`,
11928
+ `${indent}__tpl.innerHTML = ${innerHtmlExpr}`,
11929
+ `${indent}${varEl} = ${parentExpr}.firstElementChild.cloneNode(true)`,
11930
+ `${indent}${varExtras} = []`,
11931
+ `${indent}{ let __sib = ${parentExpr}.firstElementChild.nextElementSibling; while (__sib) { ${varExtras}.push(__sib.cloneNode(true)); __sib = __sib.nextElementSibling } }`
11932
+ ];
11933
+ }
11934
+ var SVG_ROOT_TAGS;
11935
+ var init_template_parse = __esm({
11936
+ "../jsx/src/ir-to-client-js/control-flow/stringify/template-parse.ts"() {
11937
+ "use strict";
11938
+ init_js_scanner();
11939
+ SVG_ROOT_TAGS = /* @__PURE__ */ new Set([
11940
+ "svg",
11941
+ "path",
11942
+ "circle",
11943
+ "rect",
11944
+ "line",
11945
+ "polyline",
11946
+ "polygon",
11947
+ "ellipse",
11948
+ "text",
11949
+ "tspan",
11950
+ "textPath",
11951
+ "g",
11952
+ "defs",
11953
+ "use",
11954
+ "symbol",
11955
+ "switch",
11956
+ "clipPath",
11957
+ "mask",
11958
+ "marker",
11959
+ "pattern",
11960
+ "linearGradient",
11961
+ "radialGradient",
11962
+ "stop",
11963
+ "image",
11964
+ "foreignObject",
11965
+ "filter",
11966
+ "feBlend",
11967
+ "feColorMatrix",
11968
+ "feComposite",
11969
+ "feFlood",
11970
+ "feGaussianBlur",
11971
+ "feMerge",
11972
+ "feMergeNode",
11973
+ "feMorphology",
11974
+ "feOffset",
11975
+ "feTurbulence",
11976
+ "animate",
11977
+ "animateTransform",
11978
+ "animateMotion"
11979
+ ]);
11980
+ }
11981
+ });
11982
+
11579
11983
  // ../jsx/src/ir-to-client-js/collect-elements.ts
11580
11984
  function domElementCount(node) {
11581
11985
  switch (node.type) {
@@ -11745,6 +12149,7 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx2, options2
11745
12149
  arrayFreeIdentifiers: n.arrayFreeIdentifiers,
11746
12150
  param: n.param,
11747
12151
  paramBindings: n.paramBindings,
12152
+ index: n.index,
11748
12153
  key: n.key,
11749
12154
  markerId: n.markerId,
11750
12155
  bodyIsMultiRoot: n.bodyIsMultiRoot,
@@ -11926,6 +12331,7 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
11926
12331
  let template = "";
11927
12332
  let staticItemTemplate;
11928
12333
  let skeletonTemplate;
12334
+ let skeletonPaths;
11929
12335
  if (l.childComponent) {
11930
12336
  template = "";
11931
12337
  if (l.isStaticArray && l.children[0]) {
@@ -11945,10 +12351,14 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
11945
12351
  if (l.isStaticArray) {
11946
12352
  staticItemTemplate = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], buildRestSpreadNames(ctx2), 0) : irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx2), 0);
11947
12353
  } else if (!useElementReconciliation && !l.bodyIsMultiRoot && !l.bodyIsItemConditional) {
11948
- skeletonTemplate = buildLoopSkeletonTemplate(l.children[0], {
12354
+ const skeletonSafeSlots = {
11949
12355
  reactiveAttrKeys: new Set(bindings.reactiveAttrs.map((a) => `${a.childSlotId}::${a.attrName}`)),
11950
12356
  reactiveTextSlotIds: new Set(bindings.reactiveTexts.map((t) => t.slotId))
11951
- }) ?? void 0;
12357
+ };
12358
+ skeletonTemplate = buildLoopSkeletonTemplate(l.children[0], skeletonSafeSlots) ?? void 0;
12359
+ if (skeletonTemplate && !templateRootIsSvg(skeletonTemplate)) {
12360
+ skeletonPaths = computeSkeletonSlotPaths(l.children[0], skeletonSafeSlots) ?? void 0;
12361
+ }
11952
12362
  }
11953
12363
  }
11954
12364
  ctx2.loopElements.push({
@@ -11968,6 +12378,7 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
11968
12378
  template,
11969
12379
  staticItemTemplate,
11970
12380
  skeletonTemplate,
12381
+ skeletonPaths,
11971
12382
  childEventHandlers: childHandlers,
11972
12383
  bindings,
11973
12384
  childComponent: l.childComponent,
@@ -12287,6 +12698,7 @@ var init_collect_elements = __esm({
12287
12698
  init_utils();
12288
12699
  init_reactivity();
12289
12700
  init_html_template();
12701
+ init_template_parse();
12290
12702
  init_prop_handling();
12291
12703
  init_walker();
12292
12704
  init_loop_chain();
@@ -12862,6 +13274,7 @@ var init_imports = __esm({
12862
13274
  "__slot",
12863
13275
  "__bfSlot",
12864
13276
  "__bfText",
13277
+ "tAfter",
12865
13278
  // Profile mode (#1690, SR3) — turn-boundary markers around event handlers.
12866
13279
  "beginTurn",
12867
13280
  "endTurn"
@@ -14695,9 +15108,49 @@ var init_event_listener = __esm({
14695
15108
  // ../jsx/src/ir-to-client-js/control-flow/shared.ts
14696
15109
  function loopKeyFn(loop) {
14697
15110
  if (loop.key === null) return "null";
14698
- const params = loop.kind === "nested" ? loop.param : `${loop.param}${loop.index ? `, ${loop.index}` : ""}`;
15111
+ const params = `${loop.param}${loop.index ? `, ${loop.index}` : ""}`;
14699
15112
  return `(${params}) => String(${loop.key})`;
14700
15113
  }
15114
+ function nestedLoopReferencesIndex(inner, comps, events) {
15115
+ const index = inner.index;
15116
+ if (!index) return false;
15117
+ const exprRefs = (text, precomputed) => {
15118
+ if (precomputed) return precomputed.has(index);
15119
+ if (!text) return false;
15120
+ return extractFreeIdentifiersFromText(text).has(index);
15121
+ };
15122
+ if (exprRefs(inner.key)) return true;
15123
+ for (const t of inner.bindings.reactiveTexts) {
15124
+ if (exprRefs(t.expression, t.freeIdentifiers)) return true;
15125
+ }
15126
+ for (const a of inner.bindings.reactiveAttrs) {
15127
+ if (exprRefs(a.expression, a.freeIdentifiers)) return true;
15128
+ }
15129
+ if (inner.mapPreamble && extractFreeIdentifiersFromStatementText(inner.mapPreamble).has(index)) return true;
15130
+ if (inner.template && extractFreeIdentifiersFromTemplateText(inner.template).has(index)) return true;
15131
+ for (const ev of events) {
15132
+ if (exprRefs(ev.handler)) return true;
15133
+ }
15134
+ for (const r2 of inner.bindings.refs) {
15135
+ if (exprRefs(r2.callback)) return true;
15136
+ }
15137
+ for (const c of comps) {
15138
+ for (const p of c.props) {
15139
+ if (exprRefs(attrValueToString(p.value))) return true;
15140
+ }
15141
+ if (c.children?.length) {
15142
+ const childrenExpr = irChildrenToJsExpr(c.children);
15143
+ if (childrenExpr && exprRefs(childrenExpr)) return true;
15144
+ }
15145
+ }
15146
+ return false;
15147
+ }
15148
+ function nestedLoopIndexAlias(inner, syntheticIndexVar, paramHead, comps, events) {
15149
+ const index = inner.index;
15150
+ if (!index || index === paramHead) return null;
15151
+ if (!nestedLoopReferencesIndex(inner, comps, events)) return null;
15152
+ return `const ${index} = ${syntheticIndexVar}`;
15153
+ }
14701
15154
  function buildChildRefBindings(refs, loopParam, loopParamBindings) {
14702
15155
  if (refs.length === 0) return [];
14703
15156
  return refs.map((r2) => ({
@@ -14827,6 +15280,7 @@ var init_shared = __esm({
14827
15280
  init_event_listener();
14828
15281
  init_component_scope();
14829
15282
  init_src();
15283
+ init_csr_substitute();
14830
15284
  }
14831
15285
  });
14832
15286
 
@@ -14890,6 +15344,7 @@ function buildOuterNestedPlan(elem, comp) {
14890
15344
  }
14891
15345
  function buildInnerLoopNestedPlan(elem, innerLoop, innerComps) {
14892
15346
  const outerIndexParam = elem.index || "__idx";
15347
+ const innerIndexParam = innerLoop.index || "__innerIdx";
14893
15348
  const comps = innerComps.map((comp) => ({
14894
15349
  componentName: comp.name,
14895
15350
  selector: buildCompSelector(comp),
@@ -14906,7 +15361,8 @@ function buildInnerLoopNestedPlan(elem, innerLoop, innerComps) {
14906
15361
  innerContainerSlotId: innerLoop.containerSlotId ?? null,
14907
15362
  innerArrayExpr: innerLoop.array,
14908
15363
  innerParam: innerLoop.param,
14909
- innerOffsetExpr: buildLoopChildIndexExpr("__innerIdx", innerLoop.offset),
15364
+ innerIndexParam,
15365
+ innerOffsetExpr: buildLoopChildIndexExpr(innerIndexParam, innerLoop.offset),
14910
15366
  innerPreludeStatements: innerLoop.mapPreamble ? [innerLoop.mapPreamble] : [],
14911
15367
  depth: innerLoop.depth,
14912
15368
  comps
@@ -14923,9 +15379,14 @@ function buildComponentRootedInnerLoopPlan(elem, innerLoop, innerComps) {
14923
15379
  containerVar: `_${varSlotId(elem.slotId)}`,
14924
15380
  outerArrayExpr: elem.array,
14925
15381
  outerParam: elem.param,
15382
+ // Declared index names only (#2231) — the zip shape never indexes by
15383
+ // position, so there's no synthetic fallback and index-less loops keep
15384
+ // byte-identical output.
15385
+ outerIndexParam: elem.index,
14926
15386
  outerPreludeStatements: elem.mapPreamble ? [elem.mapPreamble] : [],
14927
15387
  innerArrayExpr: innerLoop.array,
14928
15388
  innerParam: innerLoop.param,
15389
+ innerIndexParam: innerLoop.index,
14929
15390
  innerPreludeStatements: innerLoop.mapPreamble ? [innerLoop.mapPreamble] : [],
14930
15391
  depth: innerLoop.depth,
14931
15392
  comps
@@ -15026,6 +15487,7 @@ function emitInnerLoopNested(lines, plan) {
15026
15487
  innerContainerSlotId,
15027
15488
  innerArrayExpr,
15028
15489
  innerParam,
15490
+ innerIndexParam,
15029
15491
  innerOffsetExpr,
15030
15492
  innerPreludeStatements,
15031
15493
  depth,
@@ -15044,7 +15506,7 @@ function emitInnerLoopNested(lines, plan) {
15044
15506
  } else {
15045
15507
  lines.push(` const __ic = __outerEl`);
15046
15508
  }
15047
- lines.push(` ${innerArrayExpr}.forEach((${innerParam}, __innerIdx) => {`);
15509
+ lines.push(` ${innerArrayExpr}.forEach((${innerParam}, ${innerIndexParam}) => {`);
15048
15510
  lines.push(` const __innerEl = __ic.children[${innerOffsetExpr}]`);
15049
15511
  lines.push(` if (!__innerEl) return`);
15050
15512
  for (const stmt of innerPreludeStatements) {
@@ -15065,9 +15527,11 @@ function emitComponentRootedInnerLoop(lines, plan) {
15065
15527
  containerVar,
15066
15528
  outerArrayExpr,
15067
15529
  outerParam,
15530
+ outerIndexParam,
15068
15531
  outerPreludeStatements,
15069
15532
  innerArrayExpr,
15070
15533
  innerParam,
15534
+ innerIndexParam,
15071
15535
  innerPreludeStatements,
15072
15536
  depth,
15073
15537
  comps
@@ -15081,11 +15545,11 @@ function emitComponentRootedInnerLoop(lines, plan) {
15081
15545
  lines.push(` const ${scopesVar(i)} = qsaChildScopes(${containerVar}, ${comp.selector})`);
15082
15546
  lines.push(` let ${cursorVar(i)} = 0`);
15083
15547
  });
15084
- lines.push(` ${outerArrayExpr}.forEach((${outerParam}) => {`);
15548
+ lines.push(` ${outerArrayExpr}.forEach((${outerParam}${outerIndexParam ? `, ${outerIndexParam}` : ""}) => {`);
15085
15549
  for (const stmt of outerPreludeStatements) {
15086
15550
  lines.push(` ${stmt}`);
15087
15551
  }
15088
- lines.push(` ${innerArrayExpr}.forEach((${innerParam}) => {`);
15552
+ lines.push(` ${innerArrayExpr}.forEach((${innerParam}${innerIndexParam ? `, ${innerIndexParam}` : ""}) => {`);
15089
15553
  for (const stmt of innerPreludeStatements) {
15090
15554
  lines.push(` ${stmt}`);
15091
15555
  }
@@ -15248,6 +15712,13 @@ function buildBranchInnerLoopsPlan(args2) {
15248
15712
  ...ev,
15249
15713
  handler: wrapInner(ev.handler)
15250
15714
  }));
15715
+ const indexAlias = nestedLoopIndexAlias(
15716
+ inner,
15717
+ `__bidxbr_${i}`,
15718
+ paramHead,
15719
+ inner.childComponents ?? [],
15720
+ inner.bindings.events
15721
+ );
15251
15722
  const reactiveTexts = inner.bindings.reactiveTexts.map((text) => ({
15252
15723
  slotId: text.slotId,
15253
15724
  wrappedExpression: wrapBoth(text.expression),
@@ -15264,6 +15735,7 @@ function buildBranchInnerLoopsPlan(args2) {
15264
15735
  keyFn: loopKeyFn(inner),
15265
15736
  paramHead,
15266
15737
  paramUnwrap,
15738
+ indexAlias,
15267
15739
  wrappedTemplate: inner.template,
15268
15740
  wrappedKey,
15269
15741
  keyDepth: 1,
@@ -15519,7 +15991,7 @@ function buildInnerLoopsPlan(args2) {
15519
15991
  const containerExpr = inner.containerSlotId ? `qsa(${parentElVar}, '[bf="${inner.containerSlotId}"]')` : parentElVar;
15520
15992
  const refsParent = !!outerLoopParam && (inner.arrayFreeIdentifiers?.has(outerLoopParam) ?? false);
15521
15993
  const useReactive = refsParent && !!inner.template;
15522
- const emit = useReactive ? buildReactiveEmit(inner, level, wrapOuter) : buildStaticEmit(inner, level);
15994
+ const emit = useReactive ? buildReactiveEmit(inner, level, wrapOuter, uidSuffix) : buildStaticEmit(inner, level, uidSuffix);
15523
15995
  const arrayExpr = useReactive ? wrapOuter(inner.array) : inner.array;
15524
15996
  const childLevelsPlan = childLevels.length > 0 ? buildInnerLoopsPlan({
15525
15997
  levels: childLevels,
@@ -15546,7 +16018,7 @@ function buildInnerLoopsPlan(args2) {
15546
16018
  }
15547
16019
  return plan;
15548
16020
  }
15549
- function buildReactiveEmit(inner, level, wrapOuter) {
16021
+ function buildReactiveEmit(inner, level, wrapOuter, uidSuffix) {
15550
16022
  const wrapInner = (expr) => wrapLoopParamAsAccessor(expr, inner.param, inner.paramBindings);
15551
16023
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(inner.param, inner.paramBindings);
15552
16024
  const wrappedKey = inner.key ? wrapLoopParamAsAccessor(inner.key, inner.param, inner.paramBindings) : null;
@@ -15593,6 +16065,8 @@ function buildReactiveEmit(inner, level, wrapOuter) {
15593
16065
  };
15594
16066
  });
15595
16067
  const preludeStatements = [];
16068
+ const indexAlias = nestedLoopIndexAlias(inner, `__innerIdx${uidSuffix}`, paramHead, level.comps, level.events);
16069
+ if (indexAlias) preludeStatements.push(indexAlias);
15596
16070
  if (paramUnwrap) preludeStatements.push(paramUnwrap);
15597
16071
  if (inner.mapPreamble) preludeStatements.push(wrapInner(wrapOuter(inner.mapPreamble)));
15598
16072
  const childRefs = buildChildRefBindings(inner.bindings.refs, inner.param, inner.paramBindings);
@@ -15611,8 +16085,11 @@ function buildReactiveEmit(inner, level, wrapOuter) {
15611
16085
  childRefs
15612
16086
  };
15613
16087
  }
15614
- function buildStaticEmit(inner, level) {
15615
- const preludeStatements = inner.mapPreamble ? [inner.mapPreamble] : [];
16088
+ function buildStaticEmit(inner, level, uidSuffix) {
16089
+ const preludeStatements = [];
16090
+ const indexAlias = nestedLoopIndexAlias(inner, `__innerIdx${uidSuffix}`, inner.param, level.comps, level.events);
16091
+ if (indexAlias) preludeStatements.push(indexAlias);
16092
+ if (inner.mapPreamble) preludeStatements.push(inner.mapPreamble);
15616
16093
  return {
15617
16094
  mode: "static",
15618
16095
  rawKey: inner.key ?? null,
@@ -16140,191 +16617,46 @@ function emitReactivePropBindings(lines, ctx2) {
16140
16617
  }
16141
16618
  }
16142
16619
  lines.push(` }`);
16143
- }
16144
- lines.push(` }${bindingIdArg(ctx2, ctx2.reactiveProps[0]?.slotId)})`);
16145
- }
16146
- }
16147
- function emitReactiveChildProps(lines, ctx2) {
16148
- if (ctx2.reactiveChildProps.length > 0) {
16149
- lines.push("");
16150
- lines.push(` // Reactive child component props`);
16151
- lines.push(` createEffect(() => {`);
16152
- const propsByComponent = /* @__PURE__ */ new Map();
16153
- for (const prop of ctx2.reactiveChildProps) {
16154
- const key = `${prop.componentName}_${prop.slotId ?? "__scope"}`;
16155
- if (!propsByComponent.has(key)) {
16156
- propsByComponent.set(key, []);
16157
- }
16158
- propsByComponent.get(key).push(prop);
16159
- }
16160
- for (const [, props] of propsByComponent) {
16161
- const first = props[0];
16162
- const varSuffix = first.slotId ? varSlotId(first.slotId).replace(/-/g, "_") : first.componentName;
16163
- const varName = `__${first.componentName}_${varSuffix}El`;
16164
- const selectorArg = first.slotId ? first.slotId : first.componentName;
16165
- lines.push(` const [${varName}] = $c(__scope, '${selectorArg}')`);
16166
- lines.push(` if (${varName}) {`);
16167
- for (const prop of props) {
16168
- for (const stmt of emitAttrUpdate(varName, prop.attrName, prop.expression, prop)) {
16169
- lines.push(` ${stmt}`);
16170
- }
16171
- }
16172
- lines.push(` }`);
16173
- }
16174
- lines.push(` }${bindingIdArg(ctx2, ctx2.reactiveChildProps[0]?.slotId ?? void 0)})`);
16175
- }
16176
- }
16177
- var init_emit_reactive = __esm({
16178
- "../jsx/src/ir-to-client-js/emit-reactive.ts"() {
16179
- "use strict";
16180
- init_html_constants();
16181
- init_utils();
16182
- init_html_template();
16183
- }
16184
- });
16185
-
16186
- // ../jsx/src/ir-to-client-js/control-flow/stringify/template-parse.ts
16187
- function templateRootIsSvg(template) {
16188
- const stripped = stripLeadingNonContent(template);
16189
- const m = stripped.match(/^<\s*([A-Za-z][A-Za-z0-9-]*)/);
16190
- if (m) {
16191
- const tag = m[1];
16192
- if (SVG_ROOT_TAGS.has(tag)) return true;
16193
- return SVG_ROOT_TAGS.has(tag.toLowerCase());
16194
- }
16195
- const branches = extractConditionalBranchTemplates(stripped);
16196
- if (branches === null || branches.length === 0) return false;
16197
- return branches.every(templateRootIsSvg);
16198
- }
16199
- function stripLeadingNonContent(template) {
16200
- let s = template.trimStart();
16201
- while (s.startsWith("<!--")) {
16202
- const end2 = s.indexOf("-->");
16203
- if (end2 < 0) return s;
16204
- s = s.slice(end2 + 3).trimStart();
16205
- }
16206
- return s;
16207
- }
16208
- function extractConditionalBranchTemplates(template) {
16209
- if (!template.startsWith("${")) return null;
16210
- const exprEnd = findInterpolationEnd(template, 2);
16211
- if (exprEnd < 0) return null;
16212
- const trailing = stripLeadingNonContent(template.slice(exprEnd + 1));
16213
- if (trailing.length > 0) return null;
16214
- const expr = template.slice(2, exprEnd);
16215
- return findTopLevelTemplateLiterals(expr);
16216
- }
16217
- function emitTemplateCloneInline(template) {
16218
- if (templateRootIsSvg(template)) {
16219
- return `const __tpl = document.createElement('template'); __tpl.innerHTML = \`<svg>${template}</svg>\`; return __tpl.content.firstElementChild.firstElementChild.cloneNode(true)`;
16220
- }
16221
- return `const __tpl = document.createElement('template'); __tpl.innerHTML = \`${template}\`; return __tpl.content.firstElementChild.cloneNode(true)`;
16222
- }
16223
- function emitHoistedTemplateDecl(lines, indent, tplVar, skeletonTemplate) {
16224
- const isSvg = templateRootIsSvg(skeletonTemplate);
16225
- const html = isSvg ? `<svg>${skeletonTemplate}</svg>` : skeletonTemplate;
16226
- lines.push(`${indent}const ${tplVar} = document.createElement('template')`);
16227
- lines.push(`${indent}${tplVar}.innerHTML = \`${html}\``);
16228
- }
16229
- function hoistedCloneExpr(tplVar, skeletonTemplate) {
16230
- return templateRootIsSvg(skeletonTemplate) ? `${tplVar}.content.firstElementChild.firstElementChild.cloneNode(true)` : `${tplVar}.content.firstElementChild.cloneNode(true)`;
16231
- }
16232
- function emitTemplateCloneLines(template, indent) {
16233
- if (templateRootIsSvg(template)) {
16234
- return [
16235
- `${indent}const __tpl = document.createElement('template')`,
16236
- `${indent}__tpl.innerHTML = \`<svg>${template}</svg>\``,
16237
- `${indent}return __tpl.content.firstElementChild.firstElementChild.cloneNode(true)`
16238
- ];
16620
+ }
16621
+ lines.push(` }${bindingIdArg(ctx2, ctx2.reactiveProps[0]?.slotId)})`);
16239
16622
  }
16240
- return [
16241
- `${indent}const __tpl = document.createElement('template')`,
16242
- `${indent}__tpl.innerHTML = \`${template}\``,
16243
- `${indent}return __tpl.content.firstElementChild.cloneNode(true)`
16244
- ];
16245
16623
  }
16246
- function emitLoopItemElementSetup(lines, opts) {
16247
- const { template, bodyIsMultiRoot, indent, singleRootLayout } = opts;
16248
- const innerIndent = indent + " ";
16249
- if (bodyIsMultiRoot) {
16250
- lines.push(`${indent}let __el, __extras`);
16251
- lines.push(`${indent}if (__existing) {`);
16252
- lines.push(`${innerIndent}__el = __existing`);
16253
- lines.push(`${indent}} else {`);
16254
- for (const ln of emitMultiRootTemplateCloneLines(template, innerIndent, "__el", "__extras")) {
16255
- lines.push(ln);
16624
+ function emitReactiveChildProps(lines, ctx2) {
16625
+ if (ctx2.reactiveChildProps.length > 0) {
16626
+ lines.push("");
16627
+ lines.push(` // Reactive child component props`);
16628
+ lines.push(` createEffect(() => {`);
16629
+ const propsByComponent = /* @__PURE__ */ new Map();
16630
+ for (const prop of ctx2.reactiveChildProps) {
16631
+ const key = `${prop.componentName}_${prop.slotId ?? "__scope"}`;
16632
+ if (!propsByComponent.has(key)) {
16633
+ propsByComponent.set(key, []);
16634
+ }
16635
+ propsByComponent.get(key).push(prop);
16256
16636
  }
16257
- lines.push(`${innerIndent}__el.__bfExtras = __extras`);
16258
- lines.push(`${indent}}`);
16259
- return;
16260
- }
16261
- if (singleRootLayout === "inline") {
16262
- const cloneExpr = emitTemplateCloneInline(template);
16263
- lines.push(`${indent}const __el = __existing ?? (() => { ${cloneExpr} })()`);
16264
- return;
16637
+ for (const [, props] of propsByComponent) {
16638
+ const first = props[0];
16639
+ const varSuffix = first.slotId ? varSlotId(first.slotId).replace(/-/g, "_") : first.componentName;
16640
+ const varName = `__${first.componentName}_${varSuffix}El`;
16641
+ const selectorArg = first.slotId ? first.slotId : first.componentName;
16642
+ lines.push(` const [${varName}] = $c(__scope, '${selectorArg}')`);
16643
+ lines.push(` if (${varName}) {`);
16644
+ for (const prop of props) {
16645
+ for (const stmt of emitAttrUpdate(varName, prop.attrName, prop.expression, prop)) {
16646
+ lines.push(` ${stmt}`);
16647
+ }
16648
+ }
16649
+ lines.push(` }`);
16650
+ }
16651
+ lines.push(` }${bindingIdArg(ctx2, ctx2.reactiveChildProps[0]?.slotId ?? void 0)})`);
16265
16652
  }
16266
- lines.push(`${indent}const __el = __existing ?? (() => {`);
16267
- for (const ln of emitTemplateCloneLines(template, innerIndent)) lines.push(ln);
16268
- lines.push(`${indent}})()`);
16269
- }
16270
- function emitMultiRootTemplateCloneLines(template, indent, varEl, varExtras) {
16271
- const isSvg = templateRootIsSvg(template);
16272
- const innerHtmlExpr = isSvg ? `\`<svg>${template}</svg>\`` : `\`${template}\``;
16273
- const parentExpr = isSvg ? `__tpl.content.firstElementChild` : `__tpl.content`;
16274
- return [
16275
- `${indent}const __tpl = document.createElement('template')`,
16276
- `${indent}__tpl.innerHTML = ${innerHtmlExpr}`,
16277
- `${indent}${varEl} = ${parentExpr}.firstElementChild.cloneNode(true)`,
16278
- `${indent}${varExtras} = []`,
16279
- `${indent}{ let __sib = ${parentExpr}.firstElementChild.nextElementSibling; while (__sib) { ${varExtras}.push(__sib.cloneNode(true)); __sib = __sib.nextElementSibling } }`
16280
- ];
16281
16653
  }
16282
- var SVG_ROOT_TAGS;
16283
- var init_template_parse = __esm({
16284
- "../jsx/src/ir-to-client-js/control-flow/stringify/template-parse.ts"() {
16654
+ var init_emit_reactive = __esm({
16655
+ "../jsx/src/ir-to-client-js/emit-reactive.ts"() {
16285
16656
  "use strict";
16286
- init_js_scanner();
16287
- SVG_ROOT_TAGS = /* @__PURE__ */ new Set([
16288
- "svg",
16289
- "path",
16290
- "circle",
16291
- "rect",
16292
- "line",
16293
- "polyline",
16294
- "polygon",
16295
- "ellipse",
16296
- "text",
16297
- "tspan",
16298
- "textPath",
16299
- "g",
16300
- "defs",
16301
- "use",
16302
- "symbol",
16303
- "switch",
16304
- "clipPath",
16305
- "mask",
16306
- "marker",
16307
- "pattern",
16308
- "linearGradient",
16309
- "radialGradient",
16310
- "stop",
16311
- "image",
16312
- "foreignObject",
16313
- "filter",
16314
- "feBlend",
16315
- "feColorMatrix",
16316
- "feComposite",
16317
- "feFlood",
16318
- "feGaussianBlur",
16319
- "feMerge",
16320
- "feMergeNode",
16321
- "feMorphology",
16322
- "feOffset",
16323
- "feTurbulence",
16324
- "animate",
16325
- "animateTransform",
16326
- "animateMotion"
16327
- ]);
16657
+ init_html_constants();
16658
+ init_utils();
16659
+ init_html_template();
16328
16660
  }
16329
16661
  });
16330
16662
 
@@ -16349,6 +16681,9 @@ function stringifyBranchInnerLoops(lines, plan, indent, pc) {
16349
16681
  const uid = inner.uidSuffix;
16350
16682
  lines.push(`${indent}{ const __bic${uid} = ${inner.containerExpr}`);
16351
16683
  lines.push(`${indent}if (__bic${uid}) mapArray(() => ${inner.arrayExpr} || [], __bic${uid}, ${inner.keyFn}, (${inner.paramHead}, __bidx${uid}, __existing) => {`);
16684
+ if (inner.indexAlias) {
16685
+ lines.push(`${indent} ${inner.indexAlias}`);
16686
+ }
16352
16687
  if (inner.paramUnwrap) {
16353
16688
  lines.push(`${indent} ${inner.paramUnwrap}`);
16354
16689
  }
@@ -16431,13 +16766,15 @@ var init_loop_child_arm = __esm({
16431
16766
 
16432
16767
  // ../jsx/src/ir-to-client-js/control-flow/stringify/reactive-effects.ts
16433
16768
  function stringifyReactiveEffects(lines, plan, opts) {
16434
- const { indent, elVar, bodyIsMultiRoot } = opts;
16769
+ const { indent, elVar, bodyIsMultiRoot, elementIndexBySlot, textIndexBySlot } = opts;
16435
16770
  const lookup = bodyIsMultiRoot ? "qsaItem" : "qsa";
16436
16771
  const pc = plan.profileComponentName;
16437
16772
  const bindingBfId = (slotId) => profileBindingId(pc, slotId);
16438
16773
  for (const slot of plan.attrSlots) {
16439
16774
  const varName = `__ra_${varSlotId(slot.slotId)}`;
16440
- lines.push(`${indent}{ const ${varName} = ${lookup}(${elVar}, '[bf="${slot.slotId}"]')`);
16775
+ const pIdx = elementIndexBySlot?.get(slot.slotId);
16776
+ const lookupExpr = pIdx !== void 0 ? `__p ? __p[${pIdx}] : ${lookup}(${elVar}, '[bf="${slot.slotId}"]')` : `${lookup}(${elVar}, '[bf="${slot.slotId}"]')`;
16777
+ lines.push(`${indent}{ const ${varName} = ${lookupExpr}`);
16441
16778
  lines.push(`${indent}if (${varName}) {`);
16442
16779
  for (const attr of slot.attrs) {
16443
16780
  lines.push(`${indent} createEffect(() => {`);
@@ -16449,15 +16786,19 @@ function stringifyReactiveEffects(lines, plan, opts) {
16449
16786
  lines.push(`${indent}} }`);
16450
16787
  }
16451
16788
  for (const text of plan.outerTexts) {
16452
- emitOuterText(lines, indent, elVar, text, bindingBfId(text.slotId));
16789
+ emitOuterText(lines, indent, elVar, text, bindingBfId(text.slotId), textIndexBySlot?.get(text.slotId));
16453
16790
  }
16454
16791
  for (const cond of plan.conditionals) {
16455
16792
  emitOuterConditional(lines, indent, elVar, cond, pc);
16456
16793
  }
16457
16794
  }
16458
- function emitOuterText(lines, indent, elVar, text, bfId = "") {
16795
+ function emitOuterText(lines, indent, elVar, text, bfId = "", pIdx) {
16459
16796
  const varName = `__rt_${varSlotId(text.slotId)}`;
16460
- lines.push(`${indent}{ const [${varName}] = $t(${elVar}, '${text.slotId}')`);
16797
+ if (pIdx !== void 0) {
16798
+ lines.push(`${indent}{ const ${varName} = __p ? tAfter(__p[${pIdx}]) : $t(${elVar}, '${text.slotId}')[0]`);
16799
+ } else {
16800
+ lines.push(`${indent}{ const [${varName}] = $t(${elVar}, '${text.slotId}')`);
16801
+ }
16461
16802
  lines.push(`${indent}if (${varName}) createEffect(() => { ${varName}.textContent = String(${text.wrappedExpression}) }${bfId}) }`);
16462
16803
  }
16463
16804
  function emitOuterConditional(lines, indent, elVar, cond, pc) {
@@ -16497,6 +16838,41 @@ var init_reactive_effects = __esm({
16497
16838
  }
16498
16839
  });
16499
16840
 
16841
+ // ../jsx/src/ir-to-client-js/control-flow/stringify/skeleton-paths.ts
16842
+ function pathExpr(base, path25) {
16843
+ let expr = base;
16844
+ for (const idx of path25) {
16845
+ expr += ".firstChild";
16846
+ if (idx > 0) expr += ".nextSibling".repeat(idx);
16847
+ }
16848
+ return expr;
16849
+ }
16850
+ function buildSkeletonPathPlan(skeletonPaths, elVar, opts) {
16851
+ const arrayElems = [];
16852
+ const elementIndexBySlot = /* @__PURE__ */ new Map();
16853
+ const textIndexBySlot = /* @__PURE__ */ new Map();
16854
+ for (const slotId of opts.elementSlotIds) {
16855
+ if (elementIndexBySlot.has(slotId)) continue;
16856
+ const path25 = skeletonPaths.elementPaths.get(slotId);
16857
+ if (!path25) continue;
16858
+ elementIndexBySlot.set(slotId, arrayElems.length);
16859
+ arrayElems.push(pathExpr(elVar, path25));
16860
+ }
16861
+ for (const slotId of opts.textSlotIds) {
16862
+ if (textIndexBySlot.has(slotId)) continue;
16863
+ const path25 = skeletonPaths.textMarkerPaths.get(slotId);
16864
+ if (!path25) continue;
16865
+ textIndexBySlot.set(slotId, arrayElems.length);
16866
+ arrayElems.push(pathExpr(elVar, path25));
16867
+ }
16868
+ return { arrayElems, elementIndexBySlot, textIndexBySlot };
16869
+ }
16870
+ var init_skeleton_paths = __esm({
16871
+ "../jsx/src/ir-to-client-js/control-flow/stringify/skeleton-paths.ts"() {
16872
+ "use strict";
16873
+ }
16874
+ });
16875
+
16500
16876
  // ../jsx/src/ir-to-client-js/control-flow/stringify/component-loop.ts
16501
16877
  function stringifyComponentLoop(lines, plan) {
16502
16878
  const {
@@ -16559,11 +16935,13 @@ var init_component_loop = __esm({
16559
16935
  // ../jsx/src/ir-to-client-js/control-flow/stringify/loop.ts
16560
16936
  function emitLoopChildRefs(lines, refs, opts) {
16561
16937
  if (refs.length === 0) return;
16562
- const { indent, elVar, bodyIsMultiRoot } = opts;
16938
+ const { indent, elVar, bodyIsMultiRoot, elementIndexBySlot } = opts;
16563
16939
  const lookup = bodyIsMultiRoot ? "qsaItem" : "qsa";
16564
16940
  for (const ref of refs) {
16565
16941
  const varName = `__rf_${varSlotId(ref.childSlotId)}`;
16566
- lines.push(`${indent}{ const ${varName} = ${lookup}(${elVar}, '[bf="${ref.childSlotId}"]')`);
16942
+ const pIdx = elementIndexBySlot?.get(ref.childSlotId);
16943
+ const lookupExpr = pIdx !== void 0 ? `__p ? __p[${pIdx}] : ${lookup}(${elVar}, '[bf="${ref.childSlotId}"]')` : `${lookup}(${elVar}, '[bf="${ref.childSlotId}"]')`;
16944
+ lines.push(`${indent}{ const ${varName} = ${lookupExpr}`);
16567
16945
  lines.push(`${indent}if (${varName}) ${emitRefCall(ref.callback, varName)} }`);
16568
16946
  }
16569
16947
  }
@@ -16643,10 +17021,31 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
16643
17021
  singleRootLayout: "inline"
16644
17022
  });
16645
17023
  }
17024
+ let pathPlan = null;
17025
+ if (hoistedTpl && plan.skeletonPaths) {
17026
+ const elementSlotIds = [
17027
+ ...reactiveEffects?.attrSlots.map((s) => s.slotId) ?? [],
17028
+ ...childRefs.map((r2) => r2.childSlotId)
17029
+ ];
17030
+ const textSlotIds = reactiveEffects?.outerTexts.map((t) => t.slotId) ?? [];
17031
+ if (elementSlotIds.length > 0 || textSlotIds.length > 0) {
17032
+ const built = buildSkeletonPathPlan(plan.skeletonPaths, "__el", { elementSlotIds, textSlotIds });
17033
+ if (built.arrayElems.length > 0) {
17034
+ pathPlan = built;
17035
+ lines.push(`${bodyIndent}const __p = __existing ? null : [${built.arrayElems.join(", ")}]`);
17036
+ }
17037
+ }
17038
+ }
16646
17039
  if (reactiveEffects !== null) {
16647
- stringifyReactiveEffects(lines, reactiveEffects, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot });
17040
+ stringifyReactiveEffects(lines, reactiveEffects, {
17041
+ indent: bodyIndent,
17042
+ elVar: "__el",
17043
+ bodyIsMultiRoot,
17044
+ elementIndexBySlot: pathPlan?.elementIndexBySlot,
17045
+ textIndexBySlot: pathPlan?.textIndexBySlot
17046
+ });
16648
17047
  }
16649
- emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot });
17048
+ emitLoopChildRefs(lines, childRefs, { indent: bodyIndent, elVar: "__el", bodyIsMultiRoot, elementIndexBySlot: pathPlan?.elementIndexBySlot });
16650
17049
  lines.push(`${bodyIndent}return __el`);
16651
17050
  lines.push(`${topIndent}}, '${markerId}'${loopBfId})`);
16652
17051
  }
@@ -16705,12 +17104,14 @@ function stringifyStaticLoop(lines, plan) {
16705
17104
  lines.push(` let __iterEl = ${containerVar}.children[${childIndexExpr}]`);
16706
17105
  if (csrMaterialize) {
16707
17106
  lines.push(` if (!__iterEl) {`);
17107
+ const isSvg = csrMaterialize.bodyIsMultiRoot ? multiRootTemplateNeedsSvgWrap(csrMaterialize.itemTemplate) : templateRootIsSvg(csrMaterialize.itemTemplate);
17108
+ const itemHtml = isSvg ? `<svg>${csrMaterialize.itemTemplate}</svg>` : csrMaterialize.itemTemplate;
16708
17109
  if (csrMaterialize.bodyIsMultiRoot) {
16709
17110
  lines.push(` const __mtpl = document.createElement('template')`);
16710
- lines.push(` __mtpl.innerHTML = \`${csrMaterialize.itemTemplate}\``);
17111
+ lines.push(` __mtpl.innerHTML = \`${itemHtml}\``);
16711
17112
  lines.push(` const __anchor = ${containerVar}.children[${childIndexExpr}] ?? null`);
16712
17113
  lines.push(` let __first = null`);
16713
- lines.push(` let __sib = __mtpl.content.firstElementChild`);
17114
+ lines.push(` let __sib = __mtpl.content${isSvg ? ".firstElementChild" : ""}.firstElementChild`);
16714
17115
  lines.push(` while (__sib) {`);
16715
17116
  lines.push(` const __next = __sib.nextElementSibling`);
16716
17117
  lines.push(` const __cloned = __sib.cloneNode(true)`);
@@ -16721,8 +17122,8 @@ function stringifyStaticLoop(lines, plan) {
16721
17122
  lines.push(` __iterEl = __first`);
16722
17123
  } else {
16723
17124
  lines.push(` const __tpl = document.createElement('template')`);
16724
- lines.push(` __tpl.innerHTML = \`${csrMaterialize.itemTemplate}\``);
16725
- lines.push(` const __cloned = __tpl.content.firstElementChild`);
17125
+ lines.push(` __tpl.innerHTML = \`${itemHtml}\``);
17126
+ lines.push(` const __cloned = __tpl.content${isSvg ? ".firstElementChild" : ""}.firstElementChild`);
16726
17127
  lines.push(` if (__cloned) {`);
16727
17128
  lines.push(` const __anchor = ${containerVar}.children[${childIndexExpr}] ?? null`);
16728
17129
  lines.push(` ${containerVar}.insertBefore(__cloned, __anchor)`);
@@ -16763,6 +17164,7 @@ var init_loop = __esm({
16763
17164
  init_emit_reactive();
16764
17165
  init_reactive_effects();
16765
17166
  init_template_parse();
17167
+ init_skeleton_paths();
16766
17168
  init_component_loop();
16767
17169
  init_composite_loop();
16768
17170
  }
@@ -16798,7 +17200,10 @@ function emitReactive(lines, inner, indent, pc) {
16798
17200
  lines.push(`${innerIndent} __innerEl${uid}.__bfExtras = __innerExtras${uid}`);
16799
17201
  lines.push(`${indent} }`);
16800
17202
  } else {
16801
- lines.push(`${indent} let __innerEl${uid} = __existing ?? (() => { const __t = document.createElement('template'); __t.innerHTML = \`${emit.wrappedTemplate}\`; return __t.content.firstElementChild.cloneNode(true) })()`);
17203
+ const isSvg = templateRootIsSvg(emit.wrappedTemplate);
17204
+ const innerHtml = isSvg ? `<svg>${emit.wrappedTemplate}</svg>` : emit.wrappedTemplate;
17205
+ const childPath = isSvg ? ".firstElementChild.firstElementChild" : ".firstElementChild";
17206
+ lines.push(`${indent} let __innerEl${uid} = __existing ?? (() => { const __t = document.createElement('template'); __t.innerHTML = \`${innerHtml}\`; return __t.content${childPath}.cloneNode(true) })()`);
16802
17207
  }
16803
17208
  if (emit.wrappedKey) {
16804
17209
  lines.push(`${indent} __innerEl${uid}.setAttribute('${keyAttrName(inner.keyDepth)}', String(${emit.wrappedKey}))`);
@@ -17407,6 +17812,7 @@ function buildPlainLoopPlan(elem, profileComponentName) {
17407
17812
  mapPreambleWrapped: elem.mapPreamble ? wrap(elem.mapPreamble) : "",
17408
17813
  template: elem.template,
17409
17814
  skeletonTemplate: elem.skeletonTemplate,
17815
+ skeletonPaths: elem.skeletonPaths,
17410
17816
  reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem, profileComponentName) : null,
17411
17817
  childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
17412
17818
  bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
@@ -20448,6 +20854,7 @@ function isStringTypedOperand(expr, isStringName) {
20448
20854
  if (expr.kind === "member" && expr.object.kind === "identifier" && expr.object.name === "props") {
20449
20855
  return isStringName(expr.property);
20450
20856
  }
20857
+ if (expr.kind === "identifier") return isStringName(expr.name);
20451
20858
  if (expr.kind === "binary" && expr.op === "+") {
20452
20859
  return isStringTypedOperand(expr.left, isStringName) || isStringTypedOperand(expr.right, isStringName);
20453
20860
  }
@@ -20519,6 +20926,213 @@ var init_parsed_expr_emitter = __esm({
20519
20926
  }
20520
20927
  });
20521
20928
 
20929
+ // ../jsx/src/adapters/loop-bound-names.ts
20930
+ function collectLoopBoundNames(ir) {
20931
+ const names = /* @__PURE__ */ new Set();
20932
+ const visit3 = (node) => {
20933
+ if (!node) return;
20934
+ switch (node.type) {
20935
+ case "element":
20936
+ case "component":
20937
+ case "fragment":
20938
+ case "provider":
20939
+ for (const child of node.children) visit3(child);
20940
+ break;
20941
+ case "async":
20942
+ visit3(node.fallback);
20943
+ for (const child of node.children) visit3(child);
20944
+ break;
20945
+ case "loop":
20946
+ names.add(node.param);
20947
+ if (node.index) names.add(node.index);
20948
+ for (const binding of node.paramBindings ?? []) names.add(binding.name);
20949
+ if (node.filterPredicate) names.add(node.filterPredicate.param);
20950
+ for (const child of node.children) visit3(child);
20951
+ if (node.childComponent) {
20952
+ for (const child of node.childComponent.children) visit3(child);
20953
+ }
20954
+ for (const nested of node.nestedComponents ?? []) {
20955
+ for (const child of nested.children) visit3(child);
20956
+ }
20957
+ for (const frag of node.flatMapCallback?.fragments ?? []) {
20958
+ visit3(frag.ir);
20959
+ }
20960
+ break;
20961
+ case "conditional":
20962
+ visit3(node.whenTrue);
20963
+ visit3(node.whenFalse);
20964
+ break;
20965
+ case "if-statement":
20966
+ visit3(node.consequent);
20967
+ if (node.alternate) visit3(node.alternate);
20968
+ break;
20969
+ case "text":
20970
+ case "expression":
20971
+ case "slot":
20972
+ break;
20973
+ }
20974
+ };
20975
+ visit3(ir.root);
20976
+ return names;
20977
+ }
20978
+ var init_loop_bound_names = __esm({
20979
+ "../jsx/src/adapters/loop-bound-names.ts"() {
20980
+ "use strict";
20981
+ }
20982
+ });
20983
+
20984
+ // ../jsx/src/signal-init-eval.ts
20985
+ function isTransportable(value2, ancestors = /* @__PURE__ */ new Set()) {
20986
+ if (value2 === null) return true;
20987
+ const t = typeof value2;
20988
+ if (t === "boolean" || t === "number" || t === "string") return true;
20989
+ if (t !== "object") return false;
20990
+ if (ancestors.has(value2)) return false;
20991
+ ancestors.add(value2);
20992
+ try {
20993
+ if (Array.isArray(value2)) {
20994
+ if (Object.keys(value2).length !== value2.length) return false;
20995
+ return value2.every((el) => el !== void 0 && isTransportable(el, ancestors));
20996
+ }
20997
+ const proto = Object.getPrototypeOf(value2);
20998
+ if (proto !== Object.prototype && proto !== null) return false;
20999
+ return Object.values(value2).every((v) => isTransportable(v, ancestors));
21000
+ } finally {
21001
+ ancestors.delete(value2);
21002
+ }
21003
+ }
21004
+ function tryEvaluateSignalInit(expr, props) {
21005
+ const src = expr.trim();
21006
+ if (src === "") return { ok: false };
21007
+ let fn;
21008
+ try {
21009
+ fn = new Function(
21010
+ "props",
21011
+ ...BLOCKED_GLOBALS,
21012
+ `'use strict'; return (
21013
+ ${src}
21014
+ );`
21015
+ );
21016
+ } catch {
21017
+ return { ok: false };
21018
+ }
21019
+ try {
21020
+ const value2 = fn(props ?? {});
21021
+ if (value2 === void 0) return { ok: true, value: void 0 };
21022
+ return isTransportable(value2) ? { ok: true, value: value2 } : { ok: false };
21023
+ } catch {
21024
+ return { ok: false };
21025
+ }
21026
+ }
21027
+ function evaluateSignalInit(expr, props) {
21028
+ const result2 = tryEvaluateSignalInit(expr, props);
21029
+ return result2.ok && result2.value !== void 0 ? result2.value : null;
21030
+ }
21031
+ var BLOCKED_GLOBALS;
21032
+ var init_signal_init_eval = __esm({
21033
+ "../jsx/src/signal-init-eval.ts"() {
21034
+ "use strict";
21035
+ BLOCKED_GLOBALS = [
21036
+ "globalThis",
21037
+ "window",
21038
+ "document",
21039
+ "Date",
21040
+ "Math",
21041
+ "crypto",
21042
+ "performance",
21043
+ "fetch",
21044
+ "setTimeout",
21045
+ "setInterval",
21046
+ "require",
21047
+ "process",
21048
+ "Function"
21049
+ ];
21050
+ }
21051
+ });
21052
+
21053
+ // ../jsx/src/static-literal.ts
21054
+ function evaluateStaticLiteral(expr, bindings) {
21055
+ switch (expr.kind) {
21056
+ case "literal":
21057
+ return { value: expr.value };
21058
+ case "template-literal": {
21059
+ let out = "";
21060
+ for (const part of expr.parts) {
21061
+ if (part.type === "string") {
21062
+ out += part.value;
21063
+ continue;
21064
+ }
21065
+ const resolved = evaluateStaticLiteral(part.expr, bindings);
21066
+ if (!resolved) return null;
21067
+ out += String(resolved.value);
21068
+ }
21069
+ return { value: out };
21070
+ }
21071
+ case "array-literal": {
21072
+ const values2 = [];
21073
+ for (const element of expr.elements) {
21074
+ const resolved = evaluateStaticLiteral(element, bindings);
21075
+ if (!resolved) return null;
21076
+ values2.push(resolved.value);
21077
+ }
21078
+ return { value: values2 };
21079
+ }
21080
+ case "object-literal": {
21081
+ const out = {};
21082
+ for (const prop of expr.properties) {
21083
+ const resolved = evaluateStaticLiteral(prop.value, bindings);
21084
+ if (!resolved) return null;
21085
+ out[prop.key] = resolved.value;
21086
+ }
21087
+ return { value: out };
21088
+ }
21089
+ case "unary": {
21090
+ const resolved = evaluateStaticLiteral(expr.argument, bindings);
21091
+ if (!resolved) return null;
21092
+ if (expr.op === "-") return typeof resolved.value === "number" ? { value: -resolved.value } : null;
21093
+ if (expr.op === "+") return typeof resolved.value === "number" ? { value: +resolved.value } : null;
21094
+ if (expr.op === "!") return { value: !resolved.value };
21095
+ return null;
21096
+ }
21097
+ case "identifier":
21098
+ return bindings?.has(expr.name) ? { value: bindings.get(expr.name) } : null;
21099
+ case "member": {
21100
+ const base = evaluateStaticLiteral(expr.object, bindings);
21101
+ if (!base || base.value === null || typeof base.value !== "object") return null;
21102
+ return { value: base.value[expr.property] };
21103
+ }
21104
+ case "index-access": {
21105
+ const base = evaluateStaticLiteral(expr.object, bindings);
21106
+ const index = evaluateStaticLiteral(expr.index, bindings);
21107
+ if (!base || !index || !Array.isArray(base.value) || typeof index.value !== "number") return null;
21108
+ return { value: base.value[index.value] };
21109
+ }
21110
+ default:
21111
+ return null;
21112
+ }
21113
+ }
21114
+ function isFullyStaticLiteral(expr) {
21115
+ return evaluateStaticLiteral(expr) !== null;
21116
+ }
21117
+ function resolveStaticLoopSource(arrayParsed, localConstants, opts) {
21118
+ if (!arrayParsed) return null;
21119
+ let target2 = arrayParsed;
21120
+ if (arrayParsed.kind === "identifier") {
21121
+ if (opts?.isNameShadowed?.(arrayParsed.name)) return null;
21122
+ const local = localConstants?.find((c) => c.name === arrayParsed.name);
21123
+ if (!local || local.isModule || !local.parsed) return null;
21124
+ target2 = local.parsed;
21125
+ }
21126
+ const resolved = evaluateStaticLiteral(target2);
21127
+ if (!resolved || !Array.isArray(resolved.value)) return null;
21128
+ return resolved.value;
21129
+ }
21130
+ var init_static_literal = __esm({
21131
+ "../jsx/src/static-literal.ts"() {
21132
+ "use strict";
21133
+ }
21134
+ });
21135
+
20522
21136
  // ../jsx/src/query-href-lowering.ts
20523
21137
  function matchQueryHrefCall(callee, args2, localNames) {
20524
21138
  if (callee.kind !== "identifier" || !localNames.has(callee.name)) return null;
@@ -20681,6 +21295,70 @@ var init_attr_value_emitter = __esm({
20681
21295
  }
20682
21296
  });
20683
21297
 
21298
+ // ../jsx/src/adapters/dangerous-inner-html.ts
21299
+ function isDangerousInnerHtmlAttr(attr) {
21300
+ return attr.name === DANGEROUS_INNER_HTML_ATTR;
21301
+ }
21302
+ function resolveDangerousInnerHtml(element) {
21303
+ const attr = element.attrs.find(isDangerousInnerHtmlAttr);
21304
+ if (!attr) return null;
21305
+ if (attr.clientOnly) return null;
21306
+ if (attr.value.kind !== "expression") {
21307
+ return { kind: "dynamic", expr: "", loc: attr.loc };
21308
+ }
21309
+ const parsed = attr.value.parsed ?? parseExpression(attr.value.expr.trim());
21310
+ const html = staticHtmlLiteral(parsed);
21311
+ if (html !== null) return { kind: "static", html };
21312
+ return { kind: "dynamic", expr: attr.value.expr, loc: attr.loc };
21313
+ }
21314
+ function staticHtmlLiteral(parsed) {
21315
+ if (parsed.kind !== "object-literal") return null;
21316
+ if (parsed.properties.length !== 1) return null;
21317
+ const [prop] = parsed.properties;
21318
+ if (prop.shorthand) return null;
21319
+ if (prop.key !== "__html") return null;
21320
+ if (prop.value.kind !== "literal" || prop.value.literalType !== "string") return null;
21321
+ return prop.value.value;
21322
+ }
21323
+ function dangerousInnerHtmlMetacharViolation(html, adapterId) {
21324
+ const pattern = TEMPLATE_METACHAR_PATTERNS[adapterId];
21325
+ if (!pattern) {
21326
+ return `no template-metacharacter guard is defined for adapter '${adapterId}' \u2014 refusing rather than splicing unguarded`;
21327
+ }
21328
+ if (!pattern.test(html)) return null;
21329
+ return `the literal HTML contains a sequence ${adapterId}'s own template compiler would interpret (not inert text once spliced into the template)`;
21330
+ }
21331
+ function dangerousInnerHtmlDiagnostic(expr, loc, reason2) {
21332
+ const detail2 = reason2 ? ` \u2014 ${reason2}.` : "";
21333
+ return {
21334
+ code: "BF101",
21335
+ severity: "error",
21336
+ message: `dangerouslySetInnerHTML requires a compile-time string literal __html value on template adapters (e.g. { __html: '...' })${expr ? `: ${expr.trim()}` : ""}${detail2}`,
21337
+ loc,
21338
+ suggestion: {
21339
+ message: "Dynamic or signal-derived HTML for dangerouslySetInnerHTML is only supported on Hono/CSR today (tracked separately: https://github.com/piconic-ai/barefootjs/issues/2215). Use an inline string literal, or defer it to the client with /* @client */ (e.g. dangerouslySetInnerHTML={/* @client */ { __html: expr }}) so hydration sets it instead of SSR."
21340
+ }
21341
+ };
21342
+ }
21343
+ var DANGEROUS_INNER_HTML_ATTR, TEMPLATE_METACHAR_PATTERNS;
21344
+ var init_dangerous_inner_html = __esm({
21345
+ "../jsx/src/adapters/dangerous-inner-html.ts"() {
21346
+ "use strict";
21347
+ init_expression_parser();
21348
+ DANGEROUS_INNER_HTML_ATTR = "dangerouslySetInnerHTML";
21349
+ TEMPLATE_METACHAR_PATTERNS = {
21350
+ blade: /\{\{|\{!!|<\?|@\w|<\/?\s*x[-:]/,
21351
+ erb: /<%/,
21352
+ "go-template": /\{\{/,
21353
+ jinja: /\{\{|\{%|\{#/,
21354
+ minijinja: /\{\{|\{%|\{#/,
21355
+ mojolicious: /<%|^\s*%/m,
21356
+ twig: /\{\{|\{%|\{#/,
21357
+ xslate: /<:|^\s*:/m
21358
+ };
21359
+ }
21360
+ });
21361
+
20684
21362
  // ../jsx/src/combine-client-js.ts
20685
21363
  import ts19 from "typescript";
20686
21364
  function combineParentChildClientJs(files2) {
@@ -23497,6 +24175,7 @@ __export(src_exports, {
23497
24175
  buildStaticBudget: () => buildStaticBudget,
23498
24176
  buildWhyUpdate: () => buildWhyUpdate,
23499
24177
  collectContextConsumers: () => collectContextConsumers,
24178
+ collectLoopBoundNames: () => collectLoopBoundNames,
23500
24179
  collectModuleStringConsts: () => collectModuleStringConsts,
23501
24180
  combineParentChildClientJs: () => combineParentChildClientJs,
23502
24181
  compileJSX: () => compileJSX,
@@ -23505,6 +24184,8 @@ __export(src_exports, {
23505
24184
  createError: () => createError,
23506
24185
  createProgramForCorpus: () => createProgramForCorpus,
23507
24186
  createProgramForFile: () => createProgramForFile,
24187
+ dangerousInnerHtmlDiagnostic: () => dangerousInnerHtmlDiagnostic,
24188
+ dangerousInnerHtmlMetacharViolation: () => dangerousInnerHtmlMetacharViolation,
23508
24189
  describeFallback: () => describeFallback,
23509
24190
  diffProfiles: () => diffProfiles,
23510
24191
  diffStaticBudget: () => diffStaticBudget,
@@ -23517,6 +24198,8 @@ __export(src_exports, {
23517
24198
  envSignalReaderFor: () => envSignalReaderFor,
23518
24199
  evalStringArrayJoin: () => evalStringArrayJoin,
23519
24200
  evaluateProfileGates: () => evaluateProfileGates,
24201
+ evaluateSignalInit: () => evaluateSignalInit,
24202
+ evaluateStaticLiteral: () => evaluateStaticLiteral,
23520
24203
  exprToString: () => exprToString,
23521
24204
  extractArrowBodyExpression: () => extractArrowBodyExpression,
23522
24205
  extractFunctionParams: () => extractFunctionParams,
@@ -23557,6 +24240,8 @@ __export(src_exports, {
23557
24240
  identifierPath: () => identifierPath,
23558
24241
  importsSearchParams: () => importsSearchParams,
23559
24242
  isBooleanAttr: () => isBooleanAttr,
24243
+ isDangerousInnerHtmlAttr: () => isDangerousInnerHtmlAttr,
24244
+ isFullyStaticLiteral: () => isFullyStaticLiteral,
23560
24245
  isLowerableLoopDestructure: () => isLowerableLoopDestructure,
23561
24246
  isLowerableObjectRestDestructure: () => isLowerableObjectRestDestructure,
23562
24247
  isStringConcatBinary: () => isStringConcatBinary,
@@ -23591,7 +24276,9 @@ __export(src_exports, {
23591
24276
  registerLoweringPlugin: () => registerLoweringPlugin,
23592
24277
  renderImportMapHtml: () => renderImportMapHtml,
23593
24278
  resetCompilerCounters: () => resetCompilerCounters,
24279
+ resolveDangerousInnerHtml: () => resolveDangerousInnerHtml,
23594
24280
  resolveSetters: () => resolveSetters,
24281
+ resolveStaticLoopSource: () => resolveStaticLoopSource,
23595
24282
  rewriteImportsForTemplate: () => rewriteImportsForTemplate,
23596
24283
  searchParamsLocalNames: () => searchParamsLocalNames,
23597
24284
  serializeParsedExpr: () => serializeParsedExpr,
@@ -23599,6 +24286,7 @@ __export(src_exports, {
23599
24286
  stringifyParsedExpr: () => stringifyParsedExpr,
23600
24287
  testAdapter: () => testAdapter,
23601
24288
  traceUpdatePath: () => traceUpdatePath,
24289
+ tryEvaluateSignalInit: () => tryEvaluateSignalInit,
23602
24290
  tsNodeToParsedExpr: () => tsNodeToParsedExpr
23603
24291
  });
23604
24292
  var init_src2 = __esm({
@@ -23616,6 +24304,9 @@ var init_src2 = __esm({
23616
24304
  init_jsx_adapter();
23617
24305
  init_template_imports();
23618
24306
  init_parsed_expr_emitter();
24307
+ init_loop_bound_names();
24308
+ init_signal_init_eval();
24309
+ init_static_literal();
23619
24310
  init_env_signal();
23620
24311
  init_query_href_lowering();
23621
24312
  init_lowering_registry();
@@ -23623,6 +24314,7 @@ var init_src2 = __esm({
23623
24314
  init_builtin_lowering_plugins();
23624
24315
  init_ir_node_emitter();
23625
24316
  init_attr_value_emitter();
24317
+ init_dangerous_inner_html();
23626
24318
  init_ir_to_client_js();
23627
24319
  init_source_map();
23628
24320
  init_combine_client_js();
@@ -25096,7 +25788,7 @@ async function build(config, options2 = {}) {
25096
25788
  }
25097
25789
  }
25098
25790
  let runtimeKeepHash = cache2.runtimeKeepHash;
25099
- if (runtimeMode === "treeshake") {
25791
+ if (runtimeMode !== "full") {
25100
25792
  if (!domDistFile) {
25101
25793
  console.warn("Warning: @barefootjs/client dist not found. Skipping barefoot.js generation.");
25102
25794
  runtimeKeepHash = void 0;
@@ -25133,7 +25825,7 @@ async function build(config, options2 = {}) {
25133
25825
  runtimeKeepHash = void 0;
25134
25826
  } else {
25135
25827
  const keepNames = /* @__PURE__ */ new Set([
25136
- ...ALWAYS_KEEP_RUNTIME_EXPORTS,
25828
+ ...runtimeMode === "treeshake-exact" ? [] : ALWAYS_KEEP_RUNTIME_EXPORTS,
25137
25829
  ...config.runtimeKeep ?? [],
25138
25830
  ...merged.names
25139
25831
  ]);
@@ -25144,7 +25836,17 @@ async function build(config, options2 = {}) {
25144
25836
  distHash: hashBytes(distBytes),
25145
25837
  keep: [...keepNames].sort()
25146
25838
  }));
25147
- if (nextKeepHash === cache2.runtimeKeepHash && await fileExists(runtimeOutPath)) {
25839
+ if (keepNames.size === 0) {
25840
+ if (nextKeepHash !== cache2.runtimeKeepHash) {
25841
+ try {
25842
+ await unlink(runtimeOutPath);
25843
+ anyOutputChanged = true;
25844
+ console.log(`Skipped: ${runtimeSubdir}/barefoot.js (no runtime exports used)`);
25845
+ } catch {
25846
+ }
25847
+ }
25848
+ runtimeKeepHash = nextKeepHash;
25849
+ } else if (nextKeepHash === cache2.runtimeKeepHash && await fileExists(runtimeOutPath)) {
25148
25850
  runtimeKeepHash = nextKeepHash;
25149
25851
  } else {
25150
25852
  try {