@barefootjs/vite 0.33.0 → 0.33.2

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 +262 -79
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -5057,6 +5057,7 @@ var ErrorCodes = {
5057
5057
  MISSING_KEY_IN_LIST: "BF023",
5058
5058
  MISSING_KEY_IN_NESTED_LIST: "BF024",
5059
5059
  UNSUPPORTED_DESTRUCTURE_REST: "BF025",
5060
+ RETURN_VALUE_NOT_JSX: "BF027",
5060
5061
  PROPS_DESTRUCTURING: "BF043",
5061
5062
  SIGNAL_GETTER_NOT_CALLED: "BF044",
5062
5063
  JSX_IN_LOCAL_FUNCTION: "BF045",
@@ -5088,6 +5089,7 @@ var errorMessages = {
5088
5089
  [ErrorCodes.MISSING_KEY_IN_LIST]: "Missing key attribute in list rendering. Add a key prop for efficient updates",
5089
5090
  [ErrorCodes.MISSING_KEY_IN_NESTED_LIST]: "Nested .map() loop requires key attribute for event delegation. Add a key prop to elements in the inner loop",
5090
5091
  [ErrorCodes.UNSUPPORTED_DESTRUCTURE_REST]: "Computed property key in .map() callback destructure is not supported. Rewrite the callback to destructure explicit bindings (e.g., `({ a, b }) => ...`) so the compiler can rewrite references to per-item signal accessors.",
5092
+ [ErrorCodes.RETURN_VALUE_NOT_JSX]: "Component's return value is not recognized as JSX — return the JSX expression directly instead of binding it to a local variable first.",
5091
5093
  [ErrorCodes.PROPS_DESTRUCTURING]: "Props destructuring in function parameters breaks reactivity. Use props object directly.",
5092
5094
  [ErrorCodes.SIGNAL_GETTER_NOT_CALLED]: "Signal/memo getter passed without calling it. Use getter() to read the value.",
5093
5095
  [ErrorCodes.JSX_IN_LOCAL_FUNCTION]: "Local function returns JSX but cannot be inlined. Extract it as a top-level PascalCase component or use a single return statement.",
@@ -5730,6 +5732,14 @@ function visitComponentBody(node, ctx) {
5730
5732
  }
5731
5733
  }
5732
5734
  if (isTopLevel && (ts9.isTryStatement(node) || ts9.isSwitchStatement(node) || ts9.isForStatement(node) || ts9.isForInStatement(node) || ts9.isForOfStatement(node) || ts9.isWhileStatement(node) || ts9.isDoStatement(node) || ts9.isThrowStatement(node) || ts9.isBlock(node) && node.parent === ctx.componentBodyBlock)) {
5735
+ if (ts9.isBlock(node)) {
5736
+ const returnedLocal = findBlockBodyReturnedJsxLocalName(node);
5737
+ if (returnedLocal) {
5738
+ ctx.errors.push(createError(ErrorCodes.RETURN_VALUE_NOT_JSX, getSourceLocation(node, ctx.sourceFile, ctx.filePath), {
5739
+ message: `Component '${ctx.componentName ?? "(unknown)"}' return value is not recognized ` + `as JSX — return the JSX expression directly instead of binding it to a local ` + `variable first (\`return ${returnedLocal}\` after \`const ${returnedLocal} = ` + `<jsx/>\` is not resolved at return position).`
5740
+ }));
5741
+ }
5742
+ }
5733
5743
  collectInitStatement(node, ctx);
5734
5744
  return;
5735
5745
  }
@@ -5765,6 +5775,31 @@ function unwrapJsxTransparent(expr) {
5765
5775
  }
5766
5776
  return current;
5767
5777
  }
5778
+ function findBlockBodyReturnedJsxLocalName(block) {
5779
+ const stmts = block.statements;
5780
+ const last = stmts[stmts.length - 1];
5781
+ if (!last || !ts9.isReturnStatement(last) || !last.expression)
5782
+ return null;
5783
+ const returned = unwrapJsxTransparent(last.expression);
5784
+ if (!ts9.isIdentifier(returned))
5785
+ return null;
5786
+ const name = returned.text;
5787
+ for (const stmt of stmts) {
5788
+ if (!ts9.isVariableStatement(stmt))
5789
+ continue;
5790
+ for (const decl of stmt.declarationList.declarations) {
5791
+ if (!ts9.isIdentifier(decl.name) || decl.name.text !== name || !decl.initializer)
5792
+ continue;
5793
+ let init = decl.initializer;
5794
+ while (ts9.isParenthesizedExpression(init))
5795
+ init = init.expression;
5796
+ if (ts9.isJsxElement(init) || ts9.isJsxSelfClosingElement(init) || ts9.isJsxFragment(init) || initializerShapeContainsJsx(init) || isMapLikeCallWithJsx(init)) {
5797
+ return name;
5798
+ }
5799
+ }
5800
+ }
5801
+ return null;
5802
+ }
5768
5803
  function extractJsxFromExpression(expr) {
5769
5804
  const inner = unwrapJsxTransparent(expr);
5770
5805
  if (ts9.isJsxElement(inner) || ts9.isJsxFragment(inner) || ts9.isJsxSelfClosingElement(inner)) {
@@ -10106,8 +10141,14 @@ function buildIRRoot(analyzer) {
10106
10141
  }
10107
10142
  ctx.isRoot = false;
10108
10143
  const ir = transformJsxExpression(jsxReturn, ctx);
10109
- if (ir === null)
10144
+ if (ir === null) {
10145
+ if (ts13.isIdentifier(jsxReturn) && (analyzer.jsxConstants.has(jsxReturn.text) || analyzer.inlineableJsxConsts.has(jsxReturn.text))) {
10146
+ analyzer.errors.push(createError(ErrorCodes.RETURN_VALUE_NOT_JSX, getSourceLocation(jsxReturn, analyzer.sourceFile, analyzer.filePath), {
10147
+ message: `Component '${analyzer.componentName ?? "(unknown)"}' return value is not recognized ` + `as JSX — return the JSX expression directly instead of binding it to a local variable ` + `first (\`return ${jsxReturn.text}\` after \`const ${jsxReturn.text} = <jsx/>\` is not ` + `resolved at return position).`
10148
+ }));
10149
+ }
10110
10150
  return null;
10151
+ }
10111
10152
  return wrapInScopeElement(ir);
10112
10153
  }
10113
10154
  function needsScopeWrapper(ir) {
@@ -10557,6 +10598,31 @@ function unwrapHoistedFragment(node) {
10557
10598
  return node;
10558
10599
  return { ...only, needsScope: true };
10559
10600
  }
10601
+ function markDataKeyCarrier(children) {
10602
+ for (let i = 0;i < children.length; i++) {
10603
+ const marked = markCarrierIn(children[i]);
10604
+ if (!marked)
10605
+ continue;
10606
+ const out = children.slice();
10607
+ out[i] = marked;
10608
+ return out;
10609
+ }
10610
+ return children;
10611
+ }
10612
+ function markCarrierIn(node) {
10613
+ if (node.type === "element") {
10614
+ return { ...node, carriesDataKey: true };
10615
+ }
10616
+ if (node.type === "conditional") {
10617
+ const cond = node;
10618
+ const whenTrue = markCarrierIn(cond.whenTrue);
10619
+ const whenFalse = markCarrierIn(cond.whenFalse);
10620
+ if (!whenTrue && !whenFalse)
10621
+ return null;
10622
+ return { ...cond, whenTrue: whenTrue ?? cond.whenTrue, whenFalse: whenFalse ?? cond.whenFalse };
10623
+ }
10624
+ return null;
10625
+ }
10560
10626
  function transformFragment(node, ctx) {
10561
10627
  const isFragmentRoot = ctx.isRoot;
10562
10628
  const isTransparent = isFragmentRoot && isTransparentFragment(node, ctx);
@@ -10567,7 +10633,7 @@ function transformFragment(node, ctx) {
10567
10633
  const needsScopeComment = isFragmentRoot && !isTransparent || undefined;
10568
10634
  return {
10569
10635
  type: "fragment",
10570
- children,
10636
+ children: needsScopeComment ? markDataKeyCarrier(children) : children,
10571
10637
  transparent: isTransparent || undefined,
10572
10638
  needsScopeComment,
10573
10639
  loc: getSourceLocation(node, ctx.sourceFile, ctx.filePath)
@@ -13222,6 +13288,35 @@ function getStringValue(node) {
13222
13288
  }
13223
13289
  return null;
13224
13290
  }
13291
+ function unwrapTransparentTsWrappers(node) {
13292
+ let n = node;
13293
+ while (ts13.isParenthesizedExpression(n) || ts13.isAsExpression(n) || ts13.isSatisfiesExpression(n) || ts13.isNonNullExpression(n)) {
13294
+ n = n.expression;
13295
+ }
13296
+ return n;
13297
+ }
13298
+ function expressionWrapsJsx(node) {
13299
+ const n = unwrapTransparentTsWrappers(node);
13300
+ if (ts13.isJsxElement(n) || ts13.isJsxSelfClosingElement(n) || ts13.isJsxFragment(n))
13301
+ return true;
13302
+ if (ts13.isConditionalExpression(n)) {
13303
+ return expressionWrapsJsx(n.whenTrue) || expressionWrapsJsx(n.whenFalse);
13304
+ }
13305
+ if (ts13.isArrayLiteralExpression(n)) {
13306
+ return n.elements.some((el) => expressionWrapsJsx(ts13.isSpreadElement(el) ? el.expression : el));
13307
+ }
13308
+ return false;
13309
+ }
13310
+ function reportNakedJsxWrapperProp(ctx, attr, propName, jsxExpr) {
13311
+ const shape = ts13.isConditionalExpression(jsxExpr) ? "a ternary" : "an array literal";
13312
+ ctx.analyzer.errors.push(createError(ErrorCodes.UNSUPPORTED_JSX_PATTERN, getSourceLocation(attr, ctx.sourceFile, ctx.filePath), {
13313
+ message: `Prop '${propName}' is ${shape} wrapping JSX (${jsxExpr.getText(ctx.sourceFile)}). ` + `This shape is not compiled — only a JSX element/fragment given DIRECTLY as the prop value is.`,
13314
+ suggestion: {
13315
+ message: `Move the conditional/array out of the prop position: compute it in a local ` + `const and pass it as the component's children instead of a named prop ` + `(e.g. const ${propName} = ${jsxExpr.getText(ctx.sourceFile)}; <Comp>{${propName}}</Comp>). ` + `Wrapping the ternary/array in a fragment at the prop position ` + `(${propName}={<>{${jsxExpr.getText(ctx.sourceFile)}}</>}) is NOT a safe escape here: it compiles, ` + `but the child's own reactive prop getter receives the branch's HTML unbranded and re-escapes it as ` + `text on the child's very next reactive run, corrupting the DOM (a narrower gap #2651's door ` + `inventory left open — tracked separately).`,
13316
+ escape: [{ kind: "rewrite" }]
13317
+ }
13318
+ }));
13319
+ }
13225
13320
  function processComponentProps(attributes, ctx) {
13226
13321
  const props = [];
13227
13322
  for (const attr of attributes.properties) {
@@ -13233,10 +13328,7 @@ function processComponentProps(attributes, ctx) {
13233
13328
  continue;
13234
13329
  const name = attr.name.getText(ctx.sourceFile);
13235
13330
  if (attr.initializer && ts13.isJsxExpression(attr.initializer) && attr.initializer.expression) {
13236
- let jsxExpr = attr.initializer.expression;
13237
- while (ts13.isParenthesizedExpression(jsxExpr)) {
13238
- jsxExpr = jsxExpr.expression;
13239
- }
13331
+ const jsxExpr = unwrapTransparentTsWrappers(attr.initializer.expression);
13240
13332
  if (ts13.isJsxElement(jsxExpr) || ts13.isJsxSelfClosingElement(jsxExpr) || ts13.isJsxFragment(jsxExpr)) {
13241
13333
  const prevInsideComponentChildren = ctx.insideComponentChildren;
13242
13334
  ctx.insideComponentChildren = true;
@@ -13251,6 +13343,10 @@ function processComponentProps(attributes, ctx) {
13251
13343
  continue;
13252
13344
  }
13253
13345
  }
13346
+ if ((ts13.isConditionalExpression(jsxExpr) || ts13.isArrayLiteralExpression(jsxExpr)) && expressionWrapsJsx(jsxExpr)) {
13347
+ reportNakedJsxWrapperProp(ctx, attr, name, jsxExpr);
13348
+ continue;
13349
+ }
13254
13350
  }
13255
13351
  let value = getAttributeValue(attr, ctx);
13256
13352
  if (value.kind === "template") {
@@ -13634,6 +13730,50 @@ function buildIfStatementChain(analyzer, ctx, opts) {
13634
13730
  }
13635
13731
 
13636
13732
  // ../jsx/src/ir-to-client-js/prop-handling.ts
13733
+ function resolveRestSpreadOrigin(ctx, name) {
13734
+ const byName = localConstantValues(ctx);
13735
+ const visited = new Set;
13736
+ let current = name.trim();
13737
+ while (current !== undefined && !visited.has(current)) {
13738
+ if (ctx.restPropsName && current === ctx.restPropsName)
13739
+ return "rest";
13740
+ if (ctx.propsObjectName && current === ctx.propsObjectName)
13741
+ return "props";
13742
+ visited.add(current);
13743
+ current = byName.get(current)?.trim();
13744
+ }
13745
+ return null;
13746
+ }
13747
+ var _localConstantValuesCache = new WeakMap;
13748
+ function localConstantValues(ctx) {
13749
+ const cached = _localConstantValuesCache.get(ctx);
13750
+ if (cached)
13751
+ return cached;
13752
+ const byName = new Map;
13753
+ for (const constant of ctx.localConstants) {
13754
+ if (!byName.has(constant.name))
13755
+ byName.set(constant.name, constant.value);
13756
+ }
13757
+ _localConstantValuesCache.set(ctx, byName);
13758
+ return byName;
13759
+ }
13760
+ var _restSpreadNamesCache = new WeakMap;
13761
+ function resolveRestSpreadNames(ctx) {
13762
+ const cached = _restSpreadNamesCache.get(ctx);
13763
+ if (cached)
13764
+ return cached;
13765
+ const names = new Set;
13766
+ if (ctx.restPropsName)
13767
+ names.add(ctx.restPropsName);
13768
+ if (ctx.propsObjectName)
13769
+ names.add(ctx.propsObjectName);
13770
+ for (const constant of ctx.localConstants) {
13771
+ if (resolveRestSpreadOrigin(ctx, constant.name) !== null)
13772
+ names.add(constant.name);
13773
+ }
13774
+ _restSpreadNamesCache.set(ctx, names);
13775
+ return names;
13776
+ }
13637
13777
  function expandDynamicPropValue(value, ctx, scope) {
13638
13778
  const trimmedValue = value.trim();
13639
13779
  if (scope?.isBound(trimmedValue))
@@ -13722,6 +13862,9 @@ function decideWrapForChildProp(expandedValue, ctx, prop) {
13722
13862
  return decideWrapForAttr(expandedValue, ctx, prop);
13723
13863
  }
13724
13864
  function needsEffectWrapper(expr, ctx, freeIdentifiers2) {
13865
+ return needsEffectWrapperCore(expr, ctx, freeIdentifiers2, new Set);
13866
+ }
13867
+ function needsEffectWrapperCore(expr, ctx, freeIdentifiers2, visitedConstants) {
13725
13868
  for (const signal of ctx.signals) {
13726
13869
  if (identifierCallPattern(signal.getter).test(expr)) {
13727
13870
  return true;
@@ -13744,6 +13887,19 @@ function needsEffectWrapper(expr, ctx, freeIdentifiers2) {
13744
13887
  if (propsAccess.test(expr))
13745
13888
  return true;
13746
13889
  }
13890
+ for (const constant of ctx.localConstants) {
13891
+ if (visitedConstants.has(constant.name))
13892
+ continue;
13893
+ if (constant.value === undefined || constant.containsArrow)
13894
+ continue;
13895
+ const referenced = freeIdentifiers2 ? freeIdentifiers2.has(constant.name) : tokenContainsIdent(expr, constant.name);
13896
+ if (!referenced)
13897
+ continue;
13898
+ visitedConstants.add(constant.name);
13899
+ if (needsEffectWrapperCore(constant.value, ctx, constant.freeIdentifiers, visitedConstants)) {
13900
+ return true;
13901
+ }
13902
+ }
13747
13903
  return false;
13748
13904
  }
13749
13905
  function classifyReactivity(expr, ctx, loopParam, loopParamBindings, freeIdentifiers2) {
@@ -14332,10 +14488,11 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx, options)
14332
14488
  const innerPreambleNames = preambleNamesOf(n);
14333
14489
  if (ctx) {
14334
14490
  for (const child of n.children) {
14335
- bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, n.param, n.paramBindings, false, innerPreambleNames, n.index));
14336
- bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, n.param, n.paramBindings, false, innerPreambleNames, n.index));
14491
+ bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, n.param, n.paramBindings, true, innerPreambleNames, n.index));
14492
+ bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, n.param, n.paramBindings, true, innerPreambleNames, n.index));
14337
14493
  bindings.refs.push(...collectLoopChildRefs(child));
14338
14494
  }
14495
+ bindings.conditionals.push(...collectLoopChildConditionals({ type: "fragment", children: n.children, loc: n.loc }, ctx, siblingOffsets, n.param, n.paramBindings, innerPreambleNames, n.index));
14339
14496
  }
14340
14497
  let childComponents;
14341
14498
  if (collectBindings) {
@@ -14359,9 +14516,6 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx, options)
14359
14516
  for (const child of n.children) {
14360
14517
  bindings.events.push(...collectLoopChildEventsWithNesting(child));
14361
14518
  }
14362
- if (ctx) {
14363
- bindings.conditionals.push(...collectLoopChildConditionals({ type: "fragment", children: n.children, loc: n.loc }, ctx, siblingOffsets, n.param, n.paramBindings, innerPreambleNames, n.index));
14364
- }
14365
14519
  }
14366
14520
  result.push({
14367
14521
  kind: "nested",
@@ -14419,24 +14573,14 @@ function jsxChildrenContainComponent(nodes) {
14419
14573
  function isSingleElementJsxChildren2(nodes) {
14420
14574
  return nodes.length === 1 && nodes[0].type === "element";
14421
14575
  }
14422
- function buildRestSpreadNames(ctx) {
14423
- const names = new Set;
14424
- if (ctx.restPropsName)
14425
- names.add(ctx.restPropsName);
14426
- if (ctx.propsObjectName)
14427
- names.add(ctx.propsObjectName);
14428
- return names;
14429
- }
14430
14576
  function buildComponentPropsExpr(props, ctx) {
14431
- const restName = ctx.restPropsName;
14432
- const propsObjName = ctx.propsObjectName;
14433
14577
  const knownSpreadProp = props.find((p) => {
14434
14578
  if (p.name !== "..." && !p.name.startsWith("..."))
14435
14579
  return false;
14436
14580
  if (p.value.kind !== "spread" && p.value.kind !== "expression")
14437
14581
  return false;
14438
14582
  const expr = p.value.kind === "spread" ? p.value.expr : p.value.expr;
14439
- return expr === restName || expr === propsObjName;
14583
+ return resolveRestSpreadOrigin(ctx, expr) !== null;
14440
14584
  });
14441
14585
  const spreadSource = knownSpreadProp ? PROPS_PARAM : null;
14442
14586
  const propsForInit = [];
@@ -14584,13 +14728,13 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
14584
14728
  if (l.childComponent) {
14585
14729
  template = "";
14586
14730
  if (l.isStaticArray && l.children[0]) {
14587
- staticItemTemplate = irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx), 0, undefined, undefined);
14731
+ staticItemTemplate = irToHtmlTemplate(l.children[0], resolveRestSpreadNames(ctx), 0, undefined, undefined);
14588
14732
  }
14589
14733
  } else if (l.children[0] && !projectionInner) {
14590
14734
  const loopParamSpec = [{ param: l.param, bindings: l.paramBindings }];
14591
- template = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], buildRestSpreadNames(ctx), 0, loopParamSpec) : irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx), 0, loopParamSpec);
14735
+ template = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], resolveRestSpreadNames(ctx), 0, loopParamSpec) : irToHtmlTemplate(l.children[0], resolveRestSpreadNames(ctx), 0, loopParamSpec);
14592
14736
  if (l.isStaticArray) {
14593
- staticItemTemplate = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], buildRestSpreadNames(ctx), 0) : irToHtmlTemplate(l.children[0], buildRestSpreadNames(ctx), 0);
14737
+ staticItemTemplate = useElementReconciliation ? irToPlaceholderTemplate(l.children[0], resolveRestSpreadNames(ctx), 0) : irToHtmlTemplate(l.children[0], resolveRestSpreadNames(ctx), 0);
14594
14738
  } else if (!useElementReconciliation && !l.bodyIsMultiRoot && !l.bodyIsItemConditional) {
14595
14739
  const skeletonSafeSlots = {
14596
14740
  reactiveAttrKeys: new Set(bindings.reactiveAttrs.map((a) => `${a.childSlotId}::${a.attrName}`)),
@@ -14642,11 +14786,11 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
14642
14786
  preambleRegions: l.preambleRegions,
14643
14787
  flatMapClient: projectionInner ? {
14644
14788
  params: l.index ? `(${l.param}, ${l.index})` : `(${l.param})`,
14645
- body: renderFlatMapProjectionClientBody(projectionInner, buildRestSpreadNames(ctx)),
14789
+ body: renderFlatMapProjectionClientBody(projectionInner, resolveRestSpreadNames(ctx)),
14646
14790
  keyed: projectionInner.key !== null
14647
14791
  } : l.flatMapCallback ? {
14648
14792
  params: l.flatMapCallback.params,
14649
- body: renderFlatMapClientBody(l.flatMapCallback, buildRestSpreadNames(ctx)),
14793
+ body: renderFlatMapClientBody(l.flatMapCallback, resolveRestSpreadNames(ctx)),
14650
14794
  keyed: flatMapCallbackHasKeyedLeaf(l.flatMapCallback)
14651
14795
  } : undefined
14652
14796
  });
@@ -14713,10 +14857,9 @@ function collectFromElement(element, ctx, insideConditional = false) {
14713
14857
  for (const attr of element.attrs) {
14714
14858
  if (attr.name === "..." && attr.value) {
14715
14859
  const spreadVal = attrValueToString(attr.value) ?? "";
14716
- const elemRestName = ctx.restPropsName;
14717
- const elemPropsObjName = ctx.propsObjectName;
14718
- if (spreadVal && (spreadVal === elemRestName || spreadVal === elemPropsObjName)) {
14719
- const consumedKeys = spreadVal === elemRestName ? ctx.propsParams.map((p) => p.sourceName ?? p.name) : [];
14860
+ const spreadOrigin = spreadVal ? resolveRestSpreadOrigin(ctx, spreadVal) : null;
14861
+ if (spreadOrigin !== null) {
14862
+ const consumedKeys = spreadOrigin === "rest" ? ctx.propsParams.map((p) => p.sourceName ?? p.name) : [];
14720
14863
  const staticAttrKeys = element.attrs.filter((a) => a.name !== "...").map((a) => a.name);
14721
14864
  const excludeKeys = [...new Set([...consumedKeys, ...staticAttrKeys])];
14722
14865
  ctx.restAttrElements.push({
@@ -14795,7 +14938,7 @@ function collectBranchTextEffects(node) {
14795
14938
  }
14796
14939
  function collectBranchLoops(node, ctx, siblingOffsets) {
14797
14940
  const loops = [];
14798
- const restNames = ctx ? buildRestSpreadNames(ctx) : undefined;
14941
+ const restNames = ctx ? resolveRestSpreadNames(ctx) : undefined;
14799
14942
  walkIR(node, null, {
14800
14943
  ...stopAt("conditional", "ifStatement"),
14801
14944
  element: ({ node: el, scope: parentSlotId, descend }) => {
@@ -14863,7 +15006,7 @@ function collectBranchLoops(node, ctx, siblingOffsets) {
14863
15006
  return loops;
14864
15007
  }
14865
15008
  function buildConditionalMetadata(node, ctx, siblingOffsets) {
14866
- const restNames = buildRestSpreadNames(ctx);
15009
+ const restNames = resolveRestSpreadNames(ctx);
14867
15010
  return {
14868
15011
  slotId: node.slotId,
14869
15012
  condition: node.condition,
@@ -15497,6 +15640,7 @@ var RUNTIME_IMPORT_CANDIDATES = [
15497
15640
  "mapArrayLazy",
15498
15641
  "patchLeaf",
15499
15642
  "createDisposableEffect",
15643
+ "findCondContainer",
15500
15644
  "createComponent",
15501
15645
  "renderChild",
15502
15646
  "registerComponent",
@@ -16506,12 +16650,9 @@ function emitRegistrationAndHydration(lines, ctx, _ir, graph, inlinability) {
16506
16650
  lines.push("");
16507
16651
  const propNamesForStaticCheck = new Set(ctx.propsParams.map((p) => p.name));
16508
16652
  const { inlinableConstants, unsafeLocalNames } = inlinability ?? buildInlinableConstants(ctx, graph, _ir.root);
16509
- const restSpreadNames = new Set;
16510
- if (ctx.restPropsName)
16511
- restSpreadNames.add(ctx.restPropsName);
16512
- if (ctx.propsObjectName)
16513
- restSpreadNames.add(ctx.propsObjectName);
16514
- const isCommentScope = _ir.root.type === "fragment" && _ir.root.needsScopeComment || _ir.root.type === "component";
16653
+ const restSpreadNames = resolveRestSpreadNames(ctx);
16654
+ const isFragmentRoot = _ir.root.type === "fragment" && !!_ir.root.needsScopeComment;
16655
+ const isCommentScope = isFragmentRoot || _ir.root.type === "component";
16515
16656
  const defParts = [`init: init${name}`];
16516
16657
  if (canGenerateStaticTemplate(_ir.root, propNamesForStaticCheck, inlinableConstants, unsafeLocalNames)) {
16517
16658
  const markupSlotIds = new Set(ctx.dynamicElements.map((e) => e.slotId));
@@ -16529,6 +16670,9 @@ function emitRegistrationAndHydration(lines, ctx, _ir, graph, inlinability) {
16529
16670
  if (isCommentScope) {
16530
16671
  defParts.push("comment: true");
16531
16672
  }
16673
+ if (isFragmentRoot) {
16674
+ defParts.push("fragmentRoot: true");
16675
+ }
16532
16676
  const registryKey = nameForRegistryRef(name);
16533
16677
  if (registryKey !== name) {
16534
16678
  defParts.push(`name: '${name}'`);
@@ -17354,7 +17498,8 @@ function emitProviderAndChildInits(lines, ctx) {
17354
17498
  lines.push(` upsertChild(__scope, '${registryName}', '${child.slotId}', ${child.propsExpr})`);
17355
17499
  continue;
17356
17500
  }
17357
- const scopeRef = child.slotId ? `_${varSlotId(child.slotId)}` : "__scope";
17501
+ const isCommentRoot = child.slotId !== null && child.slotId === ctx.commentScopeRootSlotId;
17502
+ const scopeRef = !child.slotId || isCommentRoot ? "__scope" : `_${varSlotId(child.slotId)}`;
17358
17503
  lines.push(` initChild('${registryName}', ${scopeRef}, ${child.propsExpr})`);
17359
17504
  }
17360
17505
  }
@@ -18004,6 +18149,7 @@ function buildBranchInnerLoopsPlan(args) {
18004
18149
  const {
18005
18150
  innerLoops,
18006
18151
  scopeVar,
18152
+ condSlotId,
18007
18153
  outerLoopParam,
18008
18154
  outerLoopParamBindings,
18009
18155
  wrapOuter
@@ -18018,7 +18164,7 @@ function buildBranchInnerLoopsPlan(args) {
18018
18164
  const wrapInner = (expr) => wrapLoopParamAsAccessor(expr, inner.param, inner.paramBindings);
18019
18165
  const wrapBoth = (expr) => wrapLoopParamAsAccessor(wrapOuter(expr), inner.param, inner.paramBindings);
18020
18166
  const csl = inner.containerSlotId;
18021
- const containerExpr = csl ? `(${scopeVar}.querySelector('[bf="${csl}"]') ?? ${scopeVar}.querySelector(\`[${BF_HOST}="\${__scopeId}"][${BF_AT}="${csl}"]\`) ?? ${scopeVar})` : scopeVar;
18167
+ const containerExpr = csl ? `(${scopeVar}.querySelector('[bf="${csl}"]') ?? ${scopeVar}.querySelector(\`[${BF_HOST}="\${__scopeId}"][${BF_AT}="${csl}"]\`) ?? ${scopeVar})` : `findCondContainer(${scopeVar}, '${condSlotId}')`;
18022
18168
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(inner.param, inner.paramBindings);
18023
18169
  const wrappedKey = inner.key ? wrapLoopParamAsAccessor(inner.key, inner.param, inner.paramBindings) : null;
18024
18170
  const wrapIRNode = (node) => {
@@ -18102,13 +18248,15 @@ function buildLoopChildConditionalsPlan(args) {
18102
18248
  branch: cond.whenTrue,
18103
18249
  wrap,
18104
18250
  loopParam,
18105
- loopParamBindings
18251
+ loopParamBindings,
18252
+ condId: cond.slotId
18106
18253
  }),
18107
18254
  whenFalseArm: buildLoopChildArmPlan({
18108
18255
  branch: cond.whenFalse,
18109
18256
  wrap,
18110
18257
  loopParam,
18111
- loopParamBindings
18258
+ loopParamBindings,
18259
+ condId: cond.slotId
18112
18260
  })
18113
18261
  });
18114
18262
  }
@@ -18148,7 +18296,7 @@ function buildArmTextsPlan(texts, wrap) {
18148
18296
  }));
18149
18297
  }
18150
18298
  function buildLoopChildArmPlan(args) {
18151
- const { branch, wrap, loopParam, loopParamBindings } = args;
18299
+ const { branch, wrap, loopParam, loopParamBindings, condId } = args;
18152
18300
  return {
18153
18301
  events: buildBranchEventBindingsPlan({
18154
18302
  events: branch.events,
@@ -18161,6 +18309,7 @@ function buildLoopChildArmPlan(args) {
18161
18309
  innerLoops: buildBranchInnerLoopsPlan({
18162
18310
  innerLoops: branch.innerLoops,
18163
18311
  scopeVar: "__branchScope",
18312
+ condSlotId: condId,
18164
18313
  outerLoopParam: loopParam,
18165
18314
  outerLoopParamBindings: loopParamBindings,
18166
18315
  wrapOuter: wrap
@@ -18214,8 +18363,8 @@ function buildReactiveEffectsPlan(args) {
18214
18363
  wrappedCondition: wrap(cond.condition),
18215
18364
  whenTrueTemplateHtml: addCondAttrToTemplate(wrap(cond.whenTrueHtml), cond.slotId),
18216
18365
  whenFalseTemplateHtml: addCondAttrToTemplate(wrap(cond.whenFalseHtml), cond.slotId),
18217
- whenTrueArm: buildOuterArm(cond.whenTrue, wrap, loopParam, loopParamBindings, profileComponentName),
18218
- whenFalseArm: buildOuterArm(cond.whenFalse, wrap, loopParam, loopParamBindings, profileComponentName),
18366
+ whenTrueArm: buildOuterArm(cond.whenTrue, wrap, loopParam, loopParamBindings, cond.slotId, profileComponentName),
18367
+ whenFalseArm: buildOuterArm(cond.whenFalse, wrap, loopParam, loopParamBindings, cond.slotId, profileComponentName),
18219
18368
  ...cond.readsPreamble && { readsPreamble: true }
18220
18369
  });
18221
18370
  }
@@ -18227,7 +18376,7 @@ function buildReactiveEffectsPlan(args) {
18227
18376
  profileComponentName
18228
18377
  };
18229
18378
  }
18230
- function buildOuterArm(branch, wrap, loopParam, loopParamBindings, profileComponentName) {
18379
+ function buildOuterArm(branch, wrap, loopParam, loopParamBindings, condSlotId, profileComponentName) {
18231
18380
  return {
18232
18381
  events: buildBranchEventBindingsPlan({
18233
18382
  events: branch.events,
@@ -18241,6 +18390,7 @@ function buildOuterArm(branch, wrap, loopParam, loopParamBindings, profileCompon
18241
18390
  innerLoops: buildBranchInnerLoopsPlan({
18242
18391
  innerLoops: branch.innerLoops,
18243
18392
  scopeVar: "__branchScope",
18393
+ condSlotId,
18244
18394
  outerLoopParam: loopParam,
18245
18395
  outerLoopParamBindings: loopParamBindings,
18246
18396
  wrapOuter: wrap
@@ -18342,6 +18492,7 @@ function buildInnerLoopsPlan(args) {
18342
18492
  }
18343
18493
  function buildReactiveEmit(inner, level, wrapOuter, uidSuffix, outerLoopParam, outerLoopParamBindings) {
18344
18494
  const wrapInner = (expr) => wrapLoopParamAsAccessor(expr, inner.param, inner.paramBindings);
18495
+ const wrapBoth = (expr) => wrapLoopParamAsAccessor(wrapOuter(expr), inner.param, inner.paramBindings);
18345
18496
  const { head: paramHead, unwrap: paramUnwrap } = destructureLoopParam(inner.param, inner.paramBindings);
18346
18497
  const wrappedKey = inner.key ? wrapLoopParamAsAccessor(inner.key, inner.param, inner.paramBindings) : null;
18347
18498
  const wrapIRNode = (node) => {
@@ -18403,6 +18554,13 @@ function buildReactiveEmit(inner, level, wrapOuter, uidSuffix, outerLoopParam, o
18403
18554
  }));
18404
18555
  }
18405
18556
  const childRefs = buildChildRefBindings(inner.bindings.refs, inner.param, inner.paramBindings);
18557
+ const conditionals = buildLoopChildConditionalsPlan({
18558
+ conditionals: inner.bindings.conditionals,
18559
+ scopeVar: `__innerEl${uidSuffix}`,
18560
+ wrap: wrapBoth,
18561
+ loopParam: inner.param,
18562
+ loopParamBindings: inner.paramBindings
18563
+ });
18406
18564
  return {
18407
18565
  mode: "reactive",
18408
18566
  keyFn: loopKeyFn(inner),
@@ -18415,6 +18573,7 @@ function buildReactiveEmit(inner, level, wrapOuter, uidSuffix, outerLoopParam, o
18415
18573
  events,
18416
18574
  reactiveTexts,
18417
18575
  reactiveAttrs,
18576
+ conditionals,
18418
18577
  childRefs
18419
18578
  };
18420
18579
  }
@@ -19367,6 +19526,17 @@ function bindingIdArg(ctx, slotId) {
19367
19526
  return "";
19368
19527
  return `, ${JSON.stringify(`${ctx.componentName}#binding:${slotId}`)}`;
19369
19528
  }
19529
+ function emitValueUpdateStatements(target, expression) {
19530
+ return [
19531
+ `const __val = String(${expression})`,
19532
+ `if ('value' in ${target}) { if (${target}.value !== __val) ${target}.value = __val } else { ${target}.setAttribute('value', __val) }`
19533
+ ];
19534
+ }
19535
+ function emitChildValueMirrorStatements(target, expression) {
19536
+ return [
19537
+ `if ('value' in ${target}) { const __val = String(${expression}); if (${target}.value !== __val) ${target}.value = __val }`
19538
+ ];
19539
+ }
19370
19540
  function emitAttrUpdate(target, attrName, expression, meta) {
19371
19541
  const htmlName = toHTMLAttrName(attrName);
19372
19542
  if (attrName === "dangerouslySetInnerHTML" || htmlName === "dangerouslySetInnerHTML") {
@@ -19385,10 +19555,7 @@ function emitAttrUpdate(target, attrName, expression, meta) {
19385
19555
  ];
19386
19556
  }
19387
19557
  if (htmlName === "value") {
19388
- return [
19389
- `const __val = String(${expression})`,
19390
- `if (${target}.value !== __val) ${target}.value = __val`
19391
- ];
19558
+ return emitValueUpdateStatements(target, expression);
19392
19559
  }
19393
19560
  if (isBooleanAttr(htmlName)) {
19394
19561
  return [`${target}.${htmlName} = !!(${expression})`];
@@ -19636,30 +19803,30 @@ function emitReactivePropBindings(lines, ctx) {
19636
19803
  propsBySlot.get(prop.slotId).push(prop);
19637
19804
  }
19638
19805
  for (const [slotId, props] of propsBySlot) {
19639
- const v = varSlotId(slotId);
19640
- lines.push(` if (_${v}) {`);
19806
+ const ref = slotId === ctx.commentScopeRootSlotId ? "__scope" : `_${varSlotId(slotId)}`;
19807
+ lines.push(` if (${ref}) {`);
19641
19808
  for (const prop of props) {
19642
19809
  const value = `${prop.expression}()`;
19643
19810
  if (prop.propName === "selected") {
19644
19811
  if (prop.componentName === "TabsContent") {
19645
- lines.push(` _${v}.setAttribute('data-state', ${value} ? 'active' : 'inactive')`);
19812
+ lines.push(` ${ref}.setAttribute('data-state', ${value} ? 'active' : 'inactive')`);
19646
19813
  lines.push(` if (${value}) {`);
19647
- lines.push(` _${v}.classList.remove('hidden')`);
19814
+ lines.push(` ${ref}.classList.remove('hidden')`);
19648
19815
  lines.push(` } else {`);
19649
- lines.push(` _${v}.classList.add('hidden')`);
19816
+ lines.push(` ${ref}.classList.add('hidden')`);
19650
19817
  lines.push(` }`);
19651
19818
  } else {
19652
- lines.push(` _${v}.setAttribute('aria-selected', String(${value}))`);
19653
- lines.push(` _${v}.setAttribute('data-state', ${value} ? 'active' : 'inactive')`);
19654
- lines.push(` _${v}.setAttribute('tabindex', ${value} ? '0' : '-1')`);
19819
+ lines.push(` ${ref}.setAttribute('aria-selected', String(${value}))`);
19820
+ lines.push(` ${ref}.setAttribute('data-state', ${value} ? 'active' : 'inactive')`);
19821
+ lines.push(` ${ref}.setAttribute('tabindex', ${value} ? '0' : '-1')`);
19655
19822
  }
19656
19823
  } else if (prop.propName === "value") {
19657
- lines.push(` const __val = String(${value})`);
19658
- lines.push(` if (_${v}.value !== __val) _${v}.value = __val`);
19824
+ for (const stmt of emitChildValueMirrorStatements(ref, value))
19825
+ lines.push(` ${stmt}`);
19659
19826
  } else if (isBooleanAttr(prop.propName)) {
19660
- lines.push(` _${v}.${prop.propName} = !!(${value})`);
19827
+ lines.push(` ${ref}.${prop.propName} = !!(${value})`);
19661
19828
  } else {
19662
- lines.push(` _${v}.setAttribute('${prop.propName}', String(${value}))`);
19829
+ lines.push(` ${ref}.setAttribute('${prop.propName}', String(${value}))`);
19663
19830
  }
19664
19831
  }
19665
19832
  lines.push(` }`);
@@ -19682,13 +19849,17 @@ function emitReactiveChildProps(lines, ctx) {
19682
19849
  }
19683
19850
  for (const [, props] of propsByComponent) {
19684
19851
  const first = props[0];
19852
+ const isCommentRoot = first.slotId !== null && first.slotId === ctx.commentScopeRootSlotId;
19685
19853
  const varSuffix = first.slotId ? varSlotId(first.slotId).replace(/-/g, "_") : first.componentName;
19686
- const varName = `__${first.componentName}_${varSuffix}El`;
19687
- const selectorArg = first.slotId ? first.slotId : first.componentName;
19688
- lines.push(` const [${varName}] = $c(__scope, '${selectorArg}')`);
19854
+ const varName = isCommentRoot ? "__scope" : `__${first.componentName}_${varSuffix}El`;
19855
+ if (!isCommentRoot) {
19856
+ const selectorArg = first.slotId ? first.slotId : first.componentName;
19857
+ lines.push(` const [${varName}] = $c(__scope, '${selectorArg}')`);
19858
+ }
19689
19859
  lines.push(` if (${varName}) {`);
19690
19860
  for (const prop of props) {
19691
- for (const stmt of emitAttrUpdate(varName, prop.attrName, prop.expression, prop)) {
19861
+ const stmts = toHTMLAttrName(prop.attrName) === "value" ? emitChildValueMirrorStatements(varName, prop.expression) : emitAttrUpdate(varName, prop.attrName, prop.expression, prop);
19862
+ for (const stmt of stmts) {
19692
19863
  lines.push(` ${stmt}`);
19693
19864
  }
19694
19865
  }
@@ -20249,8 +20420,9 @@ function seedDiffersExpr(target, a) {
20249
20420
  return `${target}.getAttribute('style') !== styleToCss(__x)`;
20250
20421
  if (html === "class")
20251
20422
  return `${target}.getAttribute('class') !== (__x != null ? String(__x) : null)`;
20252
- if (html === "value")
20253
- return `${target}.value !== String(__x)`;
20423
+ if (html === "value") {
20424
+ return `('value' in ${target} ? ${target}.value !== String(__x) : ${target}.getAttribute('value') !== String(__x))`;
20425
+ }
20254
20426
  if (isBooleanAttr(html))
20255
20427
  return `${target}.${html} !== !!(__x)`;
20256
20428
  if (a.meta.presenceOrUndefined) {
@@ -20608,6 +20780,9 @@ function emitReactive(lines, inner, indent, pc) {
20608
20780
  }
20609
20781
  lines.push(`${indent} }${profileBindingId(pc, attr.slotId)}) }`);
20610
20782
  }
20783
+ if (emit.conditionals.length > 0) {
20784
+ stringifyLoopChildConditionals(lines, emit.conditionals, `${indent} `, pc);
20785
+ }
20611
20786
  emitLoopChildRefs(lines, emit.childRefs, {
20612
20787
  indent: `${indent} `,
20613
20788
  elVar: `__innerEl${uid}`,
@@ -21371,6 +21546,9 @@ function generateElementRefs(ctx) {
21371
21546
  for (const slotId of componentSlots) {
21372
21547
  regularSlots.delete(slotId);
21373
21548
  }
21549
+ if (ctx.commentScopeRootSlotId) {
21550
+ componentSlots.delete(ctx.commentScopeRootSlotId);
21551
+ }
21374
21552
  if (regularSlots.size === 0 && componentSlots.size === 0)
21375
21553
  return "";
21376
21554
  const refLines = [];
@@ -21522,10 +21700,18 @@ var PHASES = [
21522
21700
 
21523
21701
  // ../jsx/src/ir-to-client-js/rewrite-props-object.ts
21524
21702
  import ts20 from "typescript";
21525
- function rewritePropsObjectRef(code, propsObjectName) {
21526
- const srcPropsName = propsObjectName ?? "props";
21527
- if (srcPropsName === PROPS_PARAM)
21528
- return code;
21703
+ function rewritePropsObjectRef(code, propsObjectName, restPropsName = null) {
21704
+ let result = code;
21705
+ const seen = new Set;
21706
+ for (const srcPropsName of [propsObjectName ?? "props", restPropsName]) {
21707
+ if (srcPropsName === null || srcPropsName === PROPS_PARAM || seen.has(srcPropsName))
21708
+ continue;
21709
+ seen.add(srcPropsName);
21710
+ result = rewriteOneName(result, srcPropsName);
21711
+ }
21712
+ return result;
21713
+ }
21714
+ function rewriteOneName(code, srcPropsName) {
21529
21715
  if (!identifierPattern(srcPropsName).test(code))
21530
21716
  return code;
21531
21717
  const sourceFile = ts20.createSourceFile("init-body.ts", code, ts20.ScriptTarget.Latest, true, ts20.ScriptKind.TS);
@@ -21593,7 +21779,7 @@ function generateInitFunction(ir, ctx, siblingComponents, localImportPrefixes) {
21593
21779
  runPhases(lines, phaseCtx, PHASES);
21594
21780
  const hydrateLine = emitRegistrationAndHydration(lines, ctx, ir, graph, inlinability);
21595
21781
  let generatedCode = rewritePropsObjectRef(lines.join(`
21596
- `), ctx.propsObjectName);
21782
+ `), ctx.propsObjectName, ctx.restPropsName);
21597
21783
  generatedCode += `
21598
21784
  ` + hydrateLine;
21599
21785
  const moduleConstantsCode = emitModuleLevelDeclarations(classification.moduleLevelConstants, classification.moduleLevelFunctions, classification.moduleLevelSignals, classification.moduleLevelMemos);
@@ -21856,6 +22042,7 @@ function createContext(ir, scope, adapterCapabilities, profile) {
21856
22042
  refElements: [],
21857
22043
  childInits: [],
21858
22044
  deferredChildSlots: new Set,
22045
+ commentScopeRootSlotId: ir.root.type === "component" ? ir.root.slotId : null,
21859
22046
  reactiveProps: [],
21860
22047
  reactiveChildProps: [],
21861
22048
  reactiveAttrs: [],
@@ -21892,11 +22079,7 @@ function generateTemplateOnlyMount(ir, ctx) {
21892
22079
  const propNamesForStaticCheck = new Set(ctx.propsParams.map((p) => p.name));
21893
22080
  const graph = buildReferencesGraph(ctx, ir.root);
21894
22081
  const { inlinableConstants, unsafeLocalNames } = buildInlinableConstants(ctx, graph, ir.root);
21895
- const restSpreadNames = new Set;
21896
- if (ctx.restPropsName)
21897
- restSpreadNames.add(ctx.restPropsName);
21898
- if (ctx.propsObjectName)
21899
- restSpreadNames.add(ctx.propsObjectName);
22082
+ const restSpreadNames = resolveRestSpreadNames(ctx);
21900
22083
  let templateHtml;
21901
22084
  if (canGenerateStaticTemplate(ir.root, propNamesForStaticCheck, inlinableConstants, unsafeLocalNames)) {
21902
22085
  const markupSlotIds = new Set(ctx.dynamicElements.map((e) => e.slotId));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/vite",
3
- "version": "0.33.0",
3
+ "version": "0.33.2",
4
4
  "description": "Vite plugin for BarefootJS: Vite/Rollup owns bundling, hashing, chunking, tree-shaking and minification of client assets, BarefootJS keeps only the JSX to (template, client JS) compile",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -38,17 +38,17 @@
38
38
  "directory": "packages/vite"
39
39
  },
40
40
  "dependencies": {
41
- "@barefootjs/shared": "0.33.0"
41
+ "@barefootjs/shared": "0.33.2"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "@barefootjs/jsx": ">=0.2.0",
45
45
  "vite": "^6.0.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@barefootjs/client": "0.33.0",
49
- "@barefootjs/go-template": "0.33.0",
50
- "@barefootjs/hono": "0.33.0",
51
- "@barefootjs/jsx": "0.33.0",
48
+ "@barefootjs/client": "0.33.2",
49
+ "@barefootjs/go-template": "0.33.2",
50
+ "@barefootjs/hono": "0.33.2",
51
+ "@barefootjs/jsx": "0.33.2",
52
52
  "typescript": "^5.0.0",
53
53
  "vite": "^6.0.0"
54
54
  }