@barefootjs/cli 0.5.0 → 0.5.1

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 +179 -31
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -359,13 +359,16 @@ var init_js_scanner = __esm({
359
359
  });
360
360
 
361
361
  // ../shared/src/markers.ts
362
+ function loopItemMarker(key) {
363
+ return `${BF_LOOP_ITEM}:${key}`;
364
+ }
362
365
  function loopStartMarker(markerId) {
363
366
  return `${BF_LOOP_START}:${markerId}`;
364
367
  }
365
368
  function loopEndMarker(markerId) {
366
369
  return `${BF_LOOP_END}:${markerId}`;
367
370
  }
368
- var BF_SCOPE, BF_SLOT, BF_HOST, BF_AT, BF_COND, BF_LOOP_START, BF_LOOP_END, BF_KEY, BF_KEY_PREFIX, BF_PLACEHOLDER, BF_PARENT_SCOPE_PLACEHOLDER;
371
+ var BF_SCOPE, BF_SLOT, BF_HOST, BF_AT, BF_COND, BF_LOOP_START, BF_LOOP_END, BF_LOOP_ITEM, BF_KEY, BF_KEY_PREFIX, BF_PLACEHOLDER, BF_PARENT_SCOPE_PLACEHOLDER;
369
372
  var init_markers = __esm({
370
373
  "../shared/src/markers.ts"() {
371
374
  "use strict";
@@ -376,6 +379,7 @@ var init_markers = __esm({
376
379
  BF_COND = "bf-c";
377
380
  BF_LOOP_START = "bf-loop";
378
381
  BF_LOOP_END = "bf-/loop";
382
+ BF_LOOP_ITEM = "bf-loop-i";
379
383
  BF_KEY = "data-key";
380
384
  BF_KEY_PREFIX = "data-key-";
381
385
  BF_PLACEHOLDER = "data-bf-ph";
@@ -1376,6 +1380,9 @@ function buildSpreadAttrsMergeCall(args2) {
1376
1380
  }
1377
1381
  return `\${spreadAttrs({${objMembers.join(", ")}})}`;
1378
1382
  }
1383
+ function itemAnchorTemplate(keyExpr) {
1384
+ return `<!--${loopItemMarker("${" + keyExpr + "}")}-->`;
1385
+ }
1379
1386
  function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, branchSlotsVar, insideLoop = false, inHoistedChildren = false) {
1380
1387
  const recurse = (n) => irToHtmlTemplate(n, restSpreadNames, loopDepth, loopParams, branchSlotsVar, insideLoop, inHoistedChildren);
1381
1388
  const wrapExpr = (expr) => wrapExprWithLoopParams(expr, loopParams);
@@ -1467,7 +1474,10 @@ function irToHtmlTemplate(node, restSpreadNames, loopDepth = 0, loopParams, bran
1467
1474
  }
1468
1475
  case "loop": {
1469
1476
  const innerRecurse = (n) => irToHtmlTemplate(n, restSpreadNames, loopDepth + 1, loopParams, branchSlotsVar, insideLoop);
1470
- const childTemplate = node.children.map(innerRecurse).join("");
1477
+ let childTemplate = node.children.map(innerRecurse).join("");
1478
+ if (node.bodyIsItemConditional && node.key) {
1479
+ childTemplate = `${itemAnchorTemplate(node.key)}${childTemplate}`;
1480
+ }
1471
1481
  const indexParam = node.index ? `, ${node.index}` : "";
1472
1482
  const rawChainedArray = applyLoopChain(node);
1473
1483
  const { array: iterArray, callbackParam } = applyIterationShape(node, rawChainedArray, indexParam);
@@ -2030,7 +2040,10 @@ function generateCsrTemplateWithOpts(node, opts) {
2030
2040
  return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${keyArg || (slotArg ? ", undefined" : "")}${slotArg})}`;
2031
2041
  }
2032
2042
  case "loop": {
2033
- const childTemplate = node.children.map(recurseInLoop).join("");
2043
+ let childTemplate = node.children.map(recurseInLoop).join("");
2044
+ if (node.bodyIsItemConditional && node.key) {
2045
+ childTemplate = `${itemAnchorTemplate(node.key)}${childTemplate}`;
2046
+ }
2034
2047
  const indexParam = node.index ? `, ${node.index}` : "";
2035
2048
  const chainedTemplateArray = node.sortComparator || node.filterPredicate ? applyLoopChain(node, node.templateArray) : node.templateArray;
2036
2049
  const rawArrayExpr = transformExpr(node.array, chainedTemplateArray);
@@ -7385,6 +7398,22 @@ function containsJsxInExpression(node) {
7385
7398
  }
7386
7399
  return ts11.forEachChild(node, containsJsxInExpression) ?? false;
7387
7400
  }
7401
+ function callsJsxHelper(node, ctx2) {
7402
+ let found = false;
7403
+ const visit3 = (n) => {
7404
+ if (found) return;
7405
+ if (ts11.isCallExpression(n) && ts11.isIdentifier(n.expression)) {
7406
+ const name = n.expression.text;
7407
+ if (ctx2.analyzer.jsxFunctions.has(name) || ctx2.analyzer.jsxMultiReturnFunctions.has(name)) {
7408
+ found = true;
7409
+ return;
7410
+ }
7411
+ }
7412
+ ts11.forEachChild(n, visit3);
7413
+ };
7414
+ visit3(node);
7415
+ return found;
7416
+ }
7388
7417
  function containsAwaitExpression(node) {
7389
7418
  if (ts11.isAwaitExpression(node)) return true;
7390
7419
  if (ts11.isFunctionDeclaration(node) || ts11.isFunctionExpression(node) || ts11.isArrowFunction(node)) return false;
@@ -7466,7 +7495,7 @@ function transformJsxExpression(expr, ctx2, isClientOnly = false) {
7466
7495
  if (node.operatorToken.kind === ts11.SyntaxKind.AmpersandAmpersandToken) {
7467
7496
  return transformLogicalAnd(node, ctx2);
7468
7497
  }
7469
- if ((node.operatorToken.kind === ts11.SyntaxKind.QuestionQuestionToken || node.operatorToken.kind === ts11.SyntaxKind.BarBarToken) && containsJsxInExpression(node.right)) {
7498
+ if ((node.operatorToken.kind === ts11.SyntaxKind.QuestionQuestionToken || node.operatorToken.kind === ts11.SyntaxKind.BarBarToken) && (containsJsxInExpression(node.right) || callsJsxHelper(node.right, ctx2))) {
7470
7499
  return transformNullishCoalescing(node, ctx2);
7471
7500
  }
7472
7501
  return null;
@@ -7906,17 +7935,20 @@ function checkLoopKey(callback, ctx2, isNested) {
7906
7935
  body = ret.expression;
7907
7936
  }
7908
7937
  while (ts11.isParenthesizedExpression(body)) body = body.expression;
7938
+ function checkJsxOperand(node) {
7939
+ let n = node;
7940
+ while (ts11.isParenthesizedExpression(n)) n = n.expression;
7941
+ if (ts11.isJsxElement(n)) checkOpening(n.openingElement);
7942
+ else if (ts11.isJsxSelfClosingElement(n)) checkOpening(n);
7943
+ }
7909
7944
  if (ts11.isConditionalExpression(body)) {
7910
- const whenTrue = body.whenTrue;
7911
- const whenFalse = body.whenFalse;
7912
- let wt = whenTrue;
7913
- let wf = whenFalse;
7914
- while (ts11.isParenthesizedExpression(wt)) wt = wt.expression;
7915
- while (ts11.isParenthesizedExpression(wf)) wf = wf.expression;
7916
- if (ts11.isJsxElement(wt)) checkOpening(wt.openingElement);
7917
- else if (ts11.isJsxSelfClosingElement(wt)) checkOpening(wt);
7918
- if (ts11.isJsxElement(wf)) checkOpening(wf.openingElement);
7919
- else if (ts11.isJsxSelfClosingElement(wf)) checkOpening(wf);
7945
+ checkJsxOperand(body.whenTrue);
7946
+ checkJsxOperand(body.whenFalse);
7947
+ return;
7948
+ }
7949
+ if (ts11.isBinaryExpression(body) && (body.operatorToken.kind === ts11.SyntaxKind.AmpersandAmpersandToken || body.operatorToken.kind === ts11.SyntaxKind.BarBarToken || body.operatorToken.kind === ts11.SyntaxKind.QuestionQuestionToken)) {
7950
+ checkJsxOperand(body.left);
7951
+ checkJsxOperand(body.right);
7920
7952
  return;
7921
7953
  }
7922
7954
  if (ts11.isJsxElement(body)) {
@@ -7938,6 +7970,39 @@ function loopBodyIsMultiRoot(children) {
7938
7970
  if (only.type !== "fragment") return false;
7939
7971
  return loopBodyIsMultiRoot(only.children);
7940
7972
  }
7973
+ function branchHasNoElement(node) {
7974
+ if (node.type === "element" || node.type === "component") return false;
7975
+ if (node.type === "conditional") {
7976
+ return branchHasNoElement(node.whenTrue) || branchHasNoElement(node.whenFalse);
7977
+ }
7978
+ if (node.type === "fragment") {
7979
+ const real = node.children.filter(
7980
+ (c) => !(c.type === "text" && typeof c.value === "string" && !c.value.trim())
7981
+ );
7982
+ return real.length !== 1 || branchHasNoElement(real[0]);
7983
+ }
7984
+ return true;
7985
+ }
7986
+ function loopBodyItemConditional(children) {
7987
+ const real = children.filter(
7988
+ (c) => !(c.type === "text" && typeof c.value === "string" && !c.value.trim())
7989
+ );
7990
+ if (real.length !== 1) return null;
7991
+ const only = real[0];
7992
+ if (only.type !== "conditional") return null;
7993
+ if (branchHasNoElement(only.whenTrue) || branchHasNoElement(only.whenFalse)) {
7994
+ return only;
7995
+ }
7996
+ return null;
7997
+ }
7998
+ function extractItemConditionalKey(cond) {
7999
+ const a = branchHasNoElement(cond.whenTrue) ? null : extractLoopKey(cond.whenTrue);
8000
+ const b = branchHasNoElement(cond.whenFalse) ? null : extractLoopKey(cond.whenFalse);
8001
+ if (a !== null && b !== null) {
8002
+ return normalizeKeyExpr(a) === normalizeKeyExpr(b) ? a : null;
8003
+ }
8004
+ return a ?? b;
8005
+ }
7941
8006
  function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
7942
8007
  const isNested = ctx2.loopParams.size > 0;
7943
8008
  const propAccess = node.expression;
@@ -8134,6 +8199,16 @@ function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
8134
8199
  ctx2.loopParams.add(param);
8135
8200
  }
8136
8201
  if (index) ctx2.loopParams.add(index);
8202
+ const tryTransformRenderableBody = (expr) => {
8203
+ if (!ts11.isBinaryExpression(expr)) return;
8204
+ const op = expr.operatorToken.kind;
8205
+ if (op !== ts11.SyntaxKind.AmpersandAmpersandToken && op !== ts11.SyntaxKind.BarBarToken && op !== ts11.SyntaxKind.QuestionQuestionToken) {
8206
+ return;
8207
+ }
8208
+ if (!containsJsxInExpression(expr) && !callsJsxHelper(expr, ctx2)) return;
8209
+ const transformed = transformJsxExpression(expr, ctx2, isClientOnly);
8210
+ if (transformed) children = [transformed];
8211
+ };
8137
8212
  const body = callback.body;
8138
8213
  if (ts11.isJsxElement(body) || ts11.isJsxSelfClosingElement(body) || ts11.isJsxFragment(body)) {
8139
8214
  const transformed = transformNode(body, ctx2);
@@ -8156,6 +8231,8 @@ function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
8156
8231
  children = [transformConditional(inner, ctx2)];
8157
8232
  } else if (method === "flatMap" && ts11.isArrayLiteralExpression(inner)) {
8158
8233
  children = transformArrayLiteralChildren(inner, ctx2);
8234
+ } else {
8235
+ tryTransformRenderableBody(inner);
8159
8236
  }
8160
8237
  } else if (method === "flatMap" && ts11.isArrayLiteralExpression(body)) {
8161
8238
  children = transformArrayLiteralChildren(body, ctx2);
@@ -8203,6 +8280,8 @@ function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
8203
8280
  if (method === "flatMap" && children.length === 0) {
8204
8281
  flatMapCallback = buildFlatMapCallback(callback, body, ctx2);
8205
8282
  }
8283
+ } else {
8284
+ tryTransformRenderableBody(body);
8206
8285
  }
8207
8286
  if (paramBindings) {
8208
8287
  for (const b of paramBindings) ctx2.loopParams.delete(b.name);
@@ -8217,7 +8296,9 @@ function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
8217
8296
  if (ts11.isArrowFunction(node.arguments[0]) && children.length > 0) {
8218
8297
  checkLoopKey(node.arguments[0], ctx2, isNested);
8219
8298
  }
8220
- const key = children.length > 0 ? extractLoopKey(children[0]) : null;
8299
+ const itemConditional = children.length > 0 ? loopBodyItemConditional(children) : null;
8300
+ const bodyIsItemConditional = itemConditional !== null;
8301
+ const key = bodyIsItemConditional ? extractItemConditionalKey(itemConditional) : children.length > 0 ? extractLoopKey(children[0]) : null;
8221
8302
  let childComponent;
8222
8303
  if (children.length === 1 && children[0].type === "component") {
8223
8304
  const comp = children[0];
@@ -8263,6 +8344,7 @@ function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
8263
8344
  callsReactiveGetters: callsReactive || void 0,
8264
8345
  hasFunctionCalls: hasCalls || void 0,
8265
8346
  bodyIsMultiRoot: bodyIsMultiRoot || void 0,
8347
+ bodyIsItemConditional: bodyIsItemConditional || void 0,
8266
8348
  childComponent,
8267
8349
  nestedComponents,
8268
8350
  filterPredicate,
@@ -9433,7 +9515,8 @@ function collectLoopChildReactiveAttrs(node, ctx2, loopParam, loopParamBindings)
9433
9515
  const valueStr = attrValueToString(attr.value);
9434
9516
  if (!valueStr) continue;
9435
9517
  const expanded = expandConstantForReactivity(valueStr, ctx2, attr.freeIdentifiers);
9436
- if (!attr.clientOnly && classifyReactivity(expanded.expr, ctx2, loopParam, loopParamBindings, expanded.freeIds).kind === "none") continue;
9518
+ const reactive = classifyReactivity(expanded.expr, ctx2, loopParam, loopParamBindings, expanded.freeIds).kind !== "none" || attr.callsReactiveGetters || attr.hasFunctionCalls;
9519
+ if (!attr.clientOnly && !reactive) continue;
9437
9520
  attrs.push({
9438
9521
  childSlotId: el.slotId,
9439
9522
  attrName: attr.name,
@@ -9557,6 +9640,7 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx2, options)
9557
9640
  key: n.key,
9558
9641
  markerId: n.markerId,
9559
9642
  bodyIsMultiRoot: n.bodyIsMultiRoot,
9643
+ bodyIsItemConditional: n.bodyIsItemConditional,
9560
9644
  iterationShape: n.iterationShape,
9561
9645
  containerSlotId: scope.parentSlotId,
9562
9646
  template,
@@ -9763,6 +9847,7 @@ function collectElements(node, ctx2, siblingOffsets, insideConditional = false)
9763
9847
  key: l.key,
9764
9848
  markerId: l.markerId,
9765
9849
  bodyIsMultiRoot: l.bodyIsMultiRoot,
9850
+ bodyIsItemConditional: l.bodyIsItemConditional,
9766
9851
  iterationShape: l.iterationShape,
9767
9852
  template,
9768
9853
  staticItemTemplate,
@@ -9956,6 +10041,7 @@ function collectBranchLoops(node, ctx2, siblingOffsets) {
9956
10041
  key: n.key,
9957
10042
  markerId: n.markerId,
9958
10043
  bodyIsMultiRoot: n.bodyIsMultiRoot,
10044
+ bodyIsItemConditional: n.bodyIsItemConditional,
9959
10045
  iterationShape: n.iterationShape,
9960
10046
  template: childTemplate,
9961
10047
  containerSlotId: containerSlot,
@@ -10624,6 +10710,7 @@ var init_imports = __esm({
10624
10710
  "getLoopChildren",
10625
10711
  "getLoopNodes",
10626
10712
  "mapArray",
10713
+ "mapArrayAnchored",
10627
10714
  "createDisposableEffect",
10628
10715
  "createComponent",
10629
10716
  "renderChild",
@@ -10647,7 +10734,8 @@ var init_imports = __esm({
10647
10734
  "qsaChildScopes",
10648
10735
  "upsertChildItem",
10649
10736
  "__slot",
10650
- "__bfSlot"
10737
+ "__bfSlot",
10738
+ "__bfText"
10651
10739
  ];
10652
10740
  RUNTIME_MODULE = "@barefootjs/client/runtime";
10653
10741
  IMPORT_PLACEHOLDER = "/* __BAREFOOTJS_DOM_IMPORTS__ */";
@@ -13528,17 +13616,21 @@ function emitDynamicTextUpdates(lines, ctx2) {
13528
13616
  const conditionalElems = elems.filter((e) => e.insideConditional);
13529
13617
  const normalElems = elems.filter((e) => !e.insideConditional);
13530
13618
  if (normalElems.length > 0 || conditionalElems.length > 0) {
13619
+ for (const elem of normalElems) {
13620
+ const v = varSlotId(elem.slotId);
13621
+ lines.push(` let __anchor_${v} = _${v}`);
13622
+ }
13531
13623
  lines.push(` createEffect(() => {`);
13532
13624
  if (normalElems.length > 0) {
13533
13625
  lines.push(` const __val = ${expr}`);
13534
13626
  for (const elem of normalElems) {
13535
13627
  const v = varSlotId(elem.slotId);
13536
- lines.push(` if (_${v} && !__val?.__isSlot) _${v}.nodeValue = String(__val ?? '')`);
13628
+ lines.push(` __anchor_${v} = __bfText(__anchor_${v}, __val)`);
13537
13629
  }
13538
13630
  for (const elem of conditionalElems) {
13539
13631
  const v = varSlotId(elem.slotId);
13540
13632
  lines.push(` const [__el_${v}] = $t(__scope, '${elem.slotId}')`);
13541
- lines.push(` if (__el_${v} && !__val?.__isSlot) __el_${v}.nodeValue = String(__val ?? '')`);
13633
+ lines.push(` __bfText(__el_${v}, __val)`);
13542
13634
  }
13543
13635
  } else {
13544
13636
  lines.push(` let __val`);
@@ -13546,7 +13638,7 @@ function emitDynamicTextUpdates(lines, ctx2) {
13546
13638
  for (const elem of conditionalElems) {
13547
13639
  const v = varSlotId(elem.slotId);
13548
13640
  lines.push(` const [__el_${v}] = $t(__scope, '${elem.slotId}')`);
13549
- lines.push(` if (__el_${v} && !__val?.__isSlot) __el_${v}.nodeValue = String(__val ?? '')`);
13641
+ lines.push(` __bfText(__el_${v}, __val)`);
13550
13642
  }
13551
13643
  }
13552
13644
  lines.push(` })`);
@@ -14079,8 +14171,14 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
14079
14171
  template,
14080
14172
  reactiveEffects,
14081
14173
  childRefs,
14082
- bodyIsMultiRoot
14174
+ bodyIsMultiRoot,
14175
+ anchored,
14176
+ anchorKeyExpr
14083
14177
  } = plan;
14178
+ if (anchored) {
14179
+ stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr);
14180
+ return;
14181
+ }
14084
14182
  if (reactiveEffects === null && !bodyIsMultiRoot && childRefs.length === 0) {
14085
14183
  const unwrapInline = paramUnwrap ? `${paramUnwrap} ` : "";
14086
14184
  const preamble = mapPreambleWrapped ? `${mapPreambleWrapped}; ` : "";
@@ -14107,6 +14205,39 @@ function stringifyPlainLoop(lines, plan, topIndent = " ") {
14107
14205
  lines.push(`${bodyIndent}return __el`);
14108
14206
  lines.push(`${topIndent}}, '${markerId}')`);
14109
14207
  }
14208
+ function stringifyAnchoredLoop(lines, plan, topIndent, anchorKeyExpr) {
14209
+ const {
14210
+ containerVar,
14211
+ markerId,
14212
+ arrayExpr,
14213
+ keyFn,
14214
+ paramHead,
14215
+ paramUnwrap,
14216
+ indexParam,
14217
+ mapPreambleWrapped,
14218
+ reactiveEffects
14219
+ } = plan;
14220
+ const condSlot = reactiveEffects?.conditionals[0]?.slotId ?? null;
14221
+ lines.push(`${topIndent}mapArrayAnchored(() => ${arrayExpr}, ${containerVar}, ${keyFn}, (${paramHead}, ${indexParam}, __existing) => {`);
14222
+ const bodyIndent = topIndent + " ";
14223
+ if (paramUnwrap) lines.push(`${bodyIndent}${paramUnwrap}`);
14224
+ if (mapPreambleWrapped) lines.push(`${bodyIndent}${mapPreambleWrapped}`);
14225
+ lines.push(`${bodyIndent}const __anchor = __existing ?? document.createComment(\`bf-loop-i:\${${anchorKeyExpr}}\`)`);
14226
+ lines.push(`${bodyIndent}let __frag = null`);
14227
+ lines.push(`${bodyIndent}if (!__existing) {`);
14228
+ lines.push(`${bodyIndent} __frag = document.createDocumentFragment()`);
14229
+ lines.push(`${bodyIndent} __frag.appendChild(__anchor)`);
14230
+ if (condSlot) {
14231
+ lines.push(`${bodyIndent} __frag.appendChild(document.createComment('bf-cond-start:${condSlot}'))`);
14232
+ lines.push(`${bodyIndent} __frag.appendChild(document.createComment('bf-cond-end:${condSlot}'))`);
14233
+ }
14234
+ lines.push(`${bodyIndent}}`);
14235
+ if (reactiveEffects !== null) {
14236
+ stringifyReactiveEffects(lines, reactiveEffects, { indent: bodyIndent, elVar: "__anchor", bodyIsMultiRoot: false });
14237
+ }
14238
+ lines.push(`${bodyIndent}return __frag ?? __anchor`);
14239
+ lines.push(`${topIndent}}, '${markerId}')`);
14240
+ }
14110
14241
  function stringifyStaticLoop(lines, plan) {
14111
14242
  const { containerVar, arrayExpr, param, indexParam, childIndexExpr, attrsBySlot, texts, childRefs, csrMaterialize } = plan;
14112
14243
  const hasAttrs = attrsBySlot.length > 0;
@@ -14667,10 +14798,10 @@ function emitArmBody2(lines, body, mode, indent) {
14667
14798
  }
14668
14799
  for (const te of body.textEffects) {
14669
14800
  const v = varSlotId(te.slotId);
14670
- lines.push(`${indent}const [__el_${v}] = $t(__branchScope, '${te.slotId}')`);
14801
+ lines.push(`${indent}let __anchor_${v} = $t(__branchScope, '${te.slotId}')[0]`);
14671
14802
  lines.push(`${indent}__disposers.push(createDisposableEffect(() => {`);
14672
14803
  lines.push(`${indent} const __val = ${te.expression}`);
14673
- lines.push(`${indent} if (__el_${v} && !__val?.__isSlot) __el_${v}.nodeValue = String(__val ?? '')`);
14804
+ lines.push(`${indent} __anchor_${v} = __bfText(__anchor_${v}, __val)`);
14674
14805
  lines.push(`${indent}}))`);
14675
14806
  }
14676
14807
  if (body.loops.length > 0) {
@@ -14767,6 +14898,9 @@ var init_build_component_loop = __esm({
14767
14898
 
14768
14899
  // ../jsx/src/ir-to-client-js/control-flow/plan/build-loop.ts
14769
14900
  function buildLoopPlan(elem, opts) {
14901
+ if (elem.bodyIsItemConditional) {
14902
+ return buildPlainLoopPlan(elem);
14903
+ }
14770
14904
  if (elem.isStaticArray) {
14771
14905
  return buildStaticLoopPlan(elem, opts.unsafeLocalNames);
14772
14906
  }
@@ -14796,7 +14930,15 @@ function buildPlainLoopPlan(elem) {
14796
14930
  template: elem.template,
14797
14931
  reactiveEffects: hasReactive2 ? buildLoopReactiveEffectsPlan(elem) : null,
14798
14932
  childRefs: buildChildRefBindings(elem.bindings.refs, elem.param, elem.paramBindings),
14799
- bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false
14933
+ bodyIsMultiRoot: elem.bodyIsMultiRoot ?? false,
14934
+ anchored: elem.bodyIsItemConditional ?? false,
14935
+ // Fall back to the iteration index when the loop has no key. A whole-item
14936
+ // conditional without a key is a BF023 error, but the emitted client JS
14937
+ // must still parse — an empty `anchorKeyExpr` would produce
14938
+ // `createComment(`bf-loop-i:${}`)` (a SyntaxError that breaks the whole
14939
+ // bundle). `elem.index || '__idx'` matches `indexParam` above, so the
14940
+ // anchor value stays consistent with the renderItem's own index param.
14941
+ anchorKeyExpr: elem.key ? wrap(elem.key) : elem.index || "__idx"
14800
14942
  };
14801
14943
  }
14802
14944
  function buildStaticLoopPlan(elem, unsafeLocalNames) {
@@ -15888,17 +16030,23 @@ function runSinglePass(source, filePath, startingCounter) {
15888
16030
  }
15889
16031
  function visit3(node) {
15890
16032
  if (ts15.isJsxAttribute(node) && node.initializer && ts15.isJsxExpression(node.initializer) && node.initializer.expression) {
15891
- let expr = node.initializer.expression;
15892
- while (ts15.isParenthesizedExpression(expr)) expr = expr.expression;
15893
- if (ts15.isArrowFunction(expr) && arrowBodyContainsJsx(expr)) {
15894
- const handled = handleInlineArrow(expr);
15895
- if (handled) {
15896
- return;
15897
- }
16033
+ if (tryHandleArrowValue(node.initializer.expression)) {
16034
+ return;
15898
16035
  }
15899
16036
  }
16037
+ if (ts15.isPropertyAssignment(node) && node.initializer) {
16038
+ if (tryHandleArrowValue(node.initializer)) return;
16039
+ }
15900
16040
  ts15.forEachChild(node, visit3);
15901
16041
  }
16042
+ function tryHandleArrowValue(initializer) {
16043
+ let expr = initializer;
16044
+ while (ts15.isParenthesizedExpression(expr)) expr = expr.expression;
16045
+ if (ts15.isArrowFunction(expr) && arrowBodyContainsJsx(expr)) {
16046
+ return handleInlineArrow(expr);
16047
+ }
16048
+ return false;
16049
+ }
15902
16050
  function handleInlineArrow(arrow) {
15903
16051
  const paramNames = collectArrowParamNames(arrow);
15904
16052
  const free = collectFreeIdentifiers(arrow);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "CLI for agent-driven UI component discovery and scaffolding",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -31,7 +31,7 @@
31
31
  "typescript": "^5.0.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@barefootjs/jsx": "0.5.0",
34
+ "@barefootjs/jsx": "0.5.1",
35
35
  "@types/node": "^22.0.0"
36
36
  }
37
37
  }