@barefootjs/vite 0.31.2 → 0.31.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1655,6 +1655,24 @@ function templatePartsToJsExpr(parts, opts) {
1655
1655
  return result;
1656
1656
  }
1657
1657
 
1658
+ // ../jsx/src/identifier-pattern.ts
1659
+ function withUnicodeFlag(flags) {
1660
+ return flags.includes("u") ? flags : `${flags}u`;
1661
+ }
1662
+ function escapeIdentifierForRegex(name) {
1663
+ return name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1664
+ }
1665
+ var ID_BOUNDARY_BEFORE = "(?<![\\p{ID_Continue}$])";
1666
+ var ID_BOUNDARY_AFTER = "(?![\\p{ID_Continue}$])";
1667
+ function identifierPattern(name, flags = "") {
1668
+ const esc = escapeIdentifierForRegex(name);
1669
+ return new RegExp(`${ID_BOUNDARY_BEFORE}${esc}${ID_BOUNDARY_AFTER}`, withUnicodeFlag(flags));
1670
+ }
1671
+ function identifierCallPattern(name, flags = "") {
1672
+ const esc = escapeIdentifierForRegex(name);
1673
+ return new RegExp(`${ID_BOUNDARY_BEFORE}${esc}\\s*\\(`, withUnicodeFlag(flags));
1674
+ }
1675
+
1658
1676
  // ../jsx/src/scanner/js-scanner.ts
1659
1677
  import ts2 from "typescript";
1660
1678
  function* iterateJsTokens(text, start = 0, end = text.length) {
@@ -2227,9 +2245,6 @@ function inferDefaultValue(type) {
2227
2245
  return "{}";
2228
2246
  return "undefined";
2229
2247
  }
2230
- function escapeRegExp(s) {
2231
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2232
- }
2233
2248
  function freeIdsFromRefs(refs) {
2234
2249
  const out = new Set;
2235
2250
  if (!refs)
@@ -2357,16 +2372,16 @@ function wrapLoopParamAsAccessor(expr, paramName, bindings) {
2357
2372
  if (bindings && bindings.length > 0) {
2358
2373
  return rewriteLoopBindingRefs(expr, bindings, "__bfItem()");
2359
2374
  }
2360
- const re = new RegExp(`\\b${escapeRegExp(paramName)}\\b(?!\\s*\\()(?!-)`, "g");
2361
- return replaceInExprContexts(expr, re, `${paramName}()`);
2375
+ const re = new RegExp(`${ID_BOUNDARY_BEFORE}${escapeIdentifierForRegex(paramName)}(?!\\s*\\()(?!-)${ID_BOUNDARY_AFTER}`, "gu");
2376
+ return replaceInExprContexts(expr, re, () => `${paramName}()`);
2362
2377
  }
2363
2378
  function rewriteLoopBindingRefs(expr, bindings, accessor) {
2364
2379
  const byName = new Map;
2365
2380
  for (const b of bindings)
2366
2381
  byName.set(b.name, b);
2367
2382
  const preprocessed = expandShorthandBindings(expr, new Set(byName.keys()));
2368
- const alt = bindings.map((b) => escapeRegExp(b.name)).join("|");
2369
- const re = new RegExp(`\\b(${alt})\\b`, "g");
2383
+ const alt = bindings.map((b) => escapeIdentifierForRegex(b.name)).join("|");
2384
+ const re = new RegExp(`${ID_BOUNDARY_BEFORE}(${alt})${ID_BOUNDARY_AFTER}`, "gu");
2370
2385
  return replaceInExprContexts(preprocessed, re, (_m, name) => renderLoopBindingAccess(byName.get(name), accessor));
2371
2386
  }
2372
2387
  function expandShorthandBindings(expr, bindingNames) {
@@ -2633,7 +2648,7 @@ function stopAt(...kinds) {
2633
2648
 
2634
2649
  // ../jsx/src/ir-to-client-js/csr-substitute.ts
2635
2650
  import ts4 from "typescript";
2636
- function csrSubstitute(value, env) {
2651
+ function csrSubstitute(value, env, enclosingScope) {
2637
2652
  if (!value || value.trim().length === 0) {
2638
2653
  return { rewritten: value, freeIdentifiers: new Set };
2639
2654
  }
@@ -2641,7 +2656,7 @@ function csrSubstitute(value, env) {
2641
2656
  let current = value;
2642
2657
  let lastFreeIdentifiers = new Set;
2643
2658
  for (let i = 0;i < maxIter; i++) {
2644
- const step = csrSubstituteOnce(current, env);
2659
+ const step = csrSubstituteOnce(current, env, enclosingScope);
2645
2660
  lastFreeIdentifiers = step.freeIdentifiers;
2646
2661
  if (step.rewritten === current)
2647
2662
  break;
@@ -2649,7 +2664,7 @@ function csrSubstitute(value, env) {
2649
2664
  }
2650
2665
  return { rewritten: current, freeIdentifiers: lastFreeIdentifiers };
2651
2666
  }
2652
- function csrSubstituteOnce(value, env) {
2667
+ function csrSubstituteOnce(value, env, enclosingScope) {
2653
2668
  if (!value || value.trim().length === 0) {
2654
2669
  return { rewritten: value, freeIdentifiers: new Set };
2655
2670
  }
@@ -2667,7 +2682,7 @@ function csrSubstituteOnce(value, env) {
2667
2682
  if (boundStack[i].has(name))
2668
2683
  return true;
2669
2684
  }
2670
- return false;
2685
+ return enclosingScope?.isBound(name) ?? false;
2671
2686
  };
2672
2687
  const recordSubstitution = (start, end, sub) => {
2673
2688
  splices.push({ start: start - OFFSET, end: end - OFFSET, text: `(${sub.replacement})` });
@@ -2852,6 +2867,83 @@ function derivesScopeFromSlot(comp) {
2852
2867
  return comp.slotId != null && comp.loopItemRoot !== true;
2853
2868
  }
2854
2869
 
2870
+ // ../jsx/src/scope/binding-scope.ts
2871
+ class BindingScope {
2872
+ frames;
2873
+ static EMPTY = new BindingScope([]);
2874
+ constructor(frames) {
2875
+ this.frames = frames;
2876
+ }
2877
+ enterLoopRow(loop) {
2878
+ const bindings = new Map;
2879
+ if (loop.paramBindings && loop.paramBindings.length > 0) {
2880
+ for (const b of loop.paramBindings)
2881
+ bindings.set(b.name, { source: "destructure" });
2882
+ } else {
2883
+ bindings.set(loop.param, { source: "item" });
2884
+ }
2885
+ if (loop.index != null)
2886
+ bindings.set(loop.index, { source: "index" });
2887
+ for (const name of loop.preamble?.declaredNames ?? [])
2888
+ bindings.set(name, { source: "preamble" });
2889
+ const frame = { kind: "loop-row", bindings };
2890
+ return new BindingScope([frame, ...this.frames]);
2891
+ }
2892
+ enterCallback(params) {
2893
+ const bindings = new Map;
2894
+ for (const name of params)
2895
+ bindings.set(name, { source: "param" });
2896
+ const frame = { kind: "callback", bindings };
2897
+ return new BindingScope([frame, ...this.frames]);
2898
+ }
2899
+ isBound(name) {
2900
+ for (const frame of this.frames) {
2901
+ if (frame.bindings.has(name))
2902
+ return true;
2903
+ }
2904
+ return false;
2905
+ }
2906
+ lookup(name) {
2907
+ for (let depth = 0;depth < this.frames.length; depth++) {
2908
+ const frame = this.frames[depth];
2909
+ const binding = frame.bindings.get(name);
2910
+ if (binding)
2911
+ return { depth, frame, binding };
2912
+ }
2913
+ return null;
2914
+ }
2915
+ boundNames() {
2916
+ if (this.boundNamesCache)
2917
+ return this.boundNamesCache;
2918
+ const names = new Set;
2919
+ for (const frame of this.frames) {
2920
+ for (const name of frame.bindings.keys())
2921
+ names.add(name);
2922
+ }
2923
+ this.boundNamesCache = names;
2924
+ return names;
2925
+ }
2926
+ boundNamesCache;
2927
+ valueBoundNamesCache;
2928
+ valueBoundNames() {
2929
+ if (this.valueBoundNamesCache)
2930
+ return this.valueBoundNamesCache;
2931
+ const names = new Set;
2932
+ for (const frame of this.frames) {
2933
+ for (const [name, binding] of frame.bindings) {
2934
+ if (binding.source === "item" || binding.source === "index" || binding.source === "destructure") {
2935
+ names.add(name);
2936
+ }
2937
+ }
2938
+ }
2939
+ this.valueBoundNamesCache = names;
2940
+ return names;
2941
+ }
2942
+ asShadowPredicate() {
2943
+ return (name) => this.isBound(name);
2944
+ }
2945
+ }
2946
+
2855
2947
  // ../jsx/src/ir-to-client-js/html-template.ts
2856
2948
  function createStringProtector() {
2857
2949
  const strings = [];
@@ -4077,7 +4169,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4077
4169
  const source = templateExpr ?? expr;
4078
4170
  if (!source)
4079
4171
  return source;
4080
- const { rewritten, freeIdentifiers: freeIdentifiers2 } = csrSubstitute(source, env);
4172
+ const { rewritten, freeIdentifiers: freeIdentifiers2 } = csrSubstitute(source, env, opts.scope);
4081
4173
  if (unsafeLocalNames && unsafeLocalNames.size > 0 && setIntersects(freeIdentifiers2, unsafeLocalNames)) {
4082
4174
  return UNSAFE_TEMPLATE_EXPR;
4083
4175
  }
@@ -4219,15 +4311,8 @@ function generateCsrTemplateWithOpts(node, opts) {
4219
4311
  return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${keyArg || (slotArg ? ", undefined" : "")}${slotArg})}`;
4220
4312
  }
4221
4313
  case "loop": {
4222
- const boundHere = new Set(opts.loopBoundNames ?? []);
4223
- if (node.paramBindings && node.paramBindings.length > 0) {
4224
- for (const b of node.paramBindings)
4225
- boundHere.add(b.name);
4226
- } else if (!node.param.startsWith("[") && !node.param.startsWith("{")) {
4227
- boundHere.add(node.param);
4228
- }
4229
- if (node.index)
4230
- boundHere.add(node.index);
4314
+ const childScope = (opts.scope ?? BindingScope.EMPTY).enterLoopRow(node);
4315
+ const boundHere = childScope.boundNames();
4231
4316
  const childEnv = {
4232
4317
  ...env,
4233
4318
  substitutions: new Map([...env.substitutions].filter(([name]) => !boundHere.has(name)))
@@ -4236,7 +4321,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4236
4321
  ...opts,
4237
4322
  loopDepth: loopDepth + 1,
4238
4323
  inHoistedChildren: false,
4239
- loopBoundNames: boundHere,
4324
+ scope: childScope,
4240
4325
  csrEnv: childEnv
4241
4326
  });
4242
4327
  let childTemplate = node.children.map(recurseInLoopBody).join("");
@@ -5439,7 +5524,8 @@ function visit(node, ctx, targetComponentName, namedExports) {
5439
5524
  if (!ctx.componentNode) {
5440
5525
  collectAmbientGlobals(node, ctx);
5441
5526
  }
5442
- if (ts9.isVariableStatement(node) && !ctx.componentNode) {
5527
+ const isDeclareStatement = ts9.isVariableStatement(node) && (node.modifiers?.some((m) => m.kind === ts9.SyntaxKind.DeclareKeyword) ?? false);
5528
+ if (ts9.isVariableStatement(node) && !ctx.componentNode && !isDeclareStatement) {
5443
5529
  const isExported = node.modifiers?.some((m) => m.kind === ts9.SyntaxKind.ExportKeyword) ?? false;
5444
5530
  const isLet = (node.declarationList.flags & ts9.NodeFlags.Let) !== 0;
5445
5531
  const isModuleClientDirective = hasLeadingClientDirectiveOnStatement(node, ctx.sourceFile);
@@ -5452,7 +5538,7 @@ function visit(node, ctx, targetComponentName, namedExports) {
5452
5538
  }
5453
5539
  continue;
5454
5540
  }
5455
- if (ts9.isIdentifier(decl.name) && decl.initializer && !isArrowComponentFunction(decl)) {
5541
+ if (ts9.isIdentifier(decl.name) && (decl.initializer || isLet) && !isArrowComponentFunction(decl)) {
5456
5542
  collectConstant(decl, ctx, true, isLet ? "let" : "const", isExported);
5457
5543
  }
5458
5544
  }
@@ -7023,6 +7109,7 @@ function collectConstant(node, ctx, _isModule, declarationKind = "const", isExpo
7023
7109
  value,
7024
7110
  parsed,
7025
7111
  typedValue: typedValue !== value ? typedValue : undefined,
7112
+ typeAnnotation: node.type ? node.type.getText(ctx.sourceFile) : undefined,
7026
7113
  valueBranches,
7027
7114
  declarationKind,
7028
7115
  isExported,
@@ -8752,7 +8839,7 @@ function findReachableNames(primaryRefs, declarations) {
8752
8839
  const reachable = new Set;
8753
8840
  const queue = [];
8754
8841
  for (const name of allNames) {
8755
- if (new RegExp(`\\b${name}\\b`).test(primaryRefs)) {
8842
+ if (identifierPattern(name).test(primaryRefs)) {
8756
8843
  reachable.add(name);
8757
8844
  queue.push(name);
8758
8845
  }
@@ -8761,7 +8848,7 @@ function findReachableNames(primaryRefs, declarations) {
8761
8848
  const current = queue.shift();
8762
8849
  const body = bodyMap.get(current) || "";
8763
8850
  for (const name of allNames) {
8764
- if (!reachable.has(name) && new RegExp(`\\b${name}\\b`).test(body)) {
8851
+ if (!reachable.has(name) && identifierPattern(name).test(body)) {
8765
8852
  reachable.add(name);
8766
8853
  queue.push(name);
8767
8854
  }
@@ -9607,8 +9694,9 @@ function rewriteBarePropRefs2(text, expr, ctx) {
9607
9694
  let propNames = getDestructuredPropNames(ctx);
9608
9695
  if (!propNames)
9609
9696
  return dateLowered === text ? undefined : dateLowered;
9610
- if (ctx.loopParams.size > 0) {
9611
- const filtered = new Set([...propNames].filter((n) => !ctx.loopParams.has(n)));
9697
+ const shadowingNames = ctx.scope.boundNames();
9698
+ if (shadowingNames.size > 0) {
9699
+ const filtered = new Set([...propNames].filter((n) => !shadowingNames.has(n)));
9612
9700
  if (filtered.size === 0)
9613
9701
  return dateLowered === text ? undefined : dateLowered;
9614
9702
  propNames = filtered;
@@ -9684,22 +9772,22 @@ function createTransformContext(analyzer) {
9684
9772
  spreadIdCounter: 0,
9685
9773
  isRoot: true,
9686
9774
  insideComponentChildren: false,
9687
- loopParams: new Set,
9775
+ scope: BindingScope.EMPTY,
9688
9776
  loopDepth: 0,
9689
9777
  patterns: {
9690
9778
  signals: analyzer.signals.map((s) => ({
9691
9779
  getter: s.getter,
9692
- pattern: new RegExp(`\\b${s.getter}\\s*\\(`)
9780
+ pattern: identifierCallPattern(s.getter)
9693
9781
  })),
9694
9782
  memos: analyzer.memos.map((m) => ({
9695
9783
  name: m.name,
9696
- pattern: new RegExp(`\\b${m.name}\\s*\\(`)
9784
+ pattern: identifierCallPattern(m.name)
9697
9785
  })),
9698
- props: analyzer.propsParams.filter((p) => p.name !== "children").map((p) => ({ name: p.name, pattern: new RegExp(`\\b${p.name}\\b`) })),
9786
+ props: analyzer.propsParams.filter((p) => p.name !== "children").map((p) => ({ name: p.name, pattern: identifierPattern(p.name) })),
9699
9787
  constants: analyzer.localConstants.map((c) => ({
9700
9788
  name: c.name,
9701
9789
  value: c.value,
9702
- pattern: new RegExp(`\\b${c.name}\\b`)
9790
+ pattern: identifierPattern(c.name)
9703
9791
  }))
9704
9792
  },
9705
9793
  getJS(node) {
@@ -9761,7 +9849,8 @@ function generateSpreadSlotId(ctx) {
9761
9849
  return `Spread_${ctx.spreadIdCounter++}`;
9762
9850
  }
9763
9851
  function makeBindingEnv(ctx) {
9764
- const loopKey = ctx.loopParams.size === 0 ? "" : Array.from(ctx.loopParams).sort().join("\x00");
9852
+ const boundNames = ctx.scope.valueBoundNames();
9853
+ const loopKey = boundNames.size === 0 ? "" : Array.from(boundNames).sort().join("\x00");
9765
9854
  if (ctx._bindingEnv && ctx._bindingEnvLoopKey === loopKey) {
9766
9855
  return ctx._bindingEnv;
9767
9856
  }
@@ -9776,7 +9865,7 @@ function makeBindingEnv(ctx) {
9776
9865
  localFunctions: a.localFunctions,
9777
9866
  imports: a.imports,
9778
9867
  ambientGlobals: a.ambientGlobals,
9779
- loopParams: new Set(ctx.loopParams),
9868
+ loopParams: boundNames,
9780
9869
  checker: a.checker
9781
9870
  };
9782
9871
  ctx._bindingEnv = env;
@@ -10469,7 +10558,8 @@ function transformExpressionInner(expr, ctx, node, isClientOnly) {
10469
10558
  freeRefs
10470
10559
  };
10471
10560
  const reactive = isReactiveExpression(exprText, ctx, expr) || isReactiveOrigin(origin);
10472
- const refsLoopParam = ctx.loopParams.size > 0 && Array.from(ctx.loopParams).some((p) => new RegExp(`\\b${p}\\b`).test(exprText));
10561
+ const scopeValueNames = ctx.scope.valueBoundNames();
10562
+ const refsLoopParam = scopeValueNames.size > 0 && Array.from(scopeValueNames).some((p) => identifierPattern(p).test(exprText));
10473
10563
  const callsReactive = exprCallsReactiveGetters(expr, ctx);
10474
10564
  const hasCalls = exprHasFunctionCalls(expr);
10475
10565
  const needsSlot = reactive || isClientOnly || refsLoopParam || callsReactive || hasCalls;
@@ -10504,7 +10594,7 @@ function transformJsxFunctionCall(callExpr, jsxFunc, ctx, _isClientOnly) {
10504
10594
  const substitutedGetJS = (node) => {
10505
10595
  let text = baseGetJS(node);
10506
10596
  for (const [paramName, argExpr] of substitutions) {
10507
- text = text.replace(new RegExp(`\\b${paramName}\\b`, "g"), argExpr);
10597
+ text = text.replace(identifierPattern(paramName, "g"), () => argExpr);
10508
10598
  }
10509
10599
  return text;
10510
10600
  };
@@ -10546,7 +10636,7 @@ function transformMultiReturnJsxFunctionCall(callExpr, info, ctx) {
10546
10636
  const substitutedGetJS = (node) => {
10547
10637
  let text = baseGetJS(node);
10548
10638
  for (const [paramName, argExpr] of substitutions) {
10549
- text = text.replace(new RegExp(`\\b${paramName}\\b`, "g"), argExpr);
10639
+ text = text.replace(identifierPattern(paramName, "g"), () => argExpr);
10550
10640
  }
10551
10641
  return text;
10552
10642
  };
@@ -11557,7 +11647,7 @@ function extractItemConditionalKey(cond) {
11557
11647
  return a ?? b;
11558
11648
  }
11559
11649
  function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
11560
- const isNested = ctx.loopParams.size > 0;
11650
+ const isNested = ctx.scope.valueBoundNames().size > 0;
11561
11651
  const diagCountAtEntry = ctx.analyzer.errors.length;
11562
11652
  const depth = ctx.loopDepth;
11563
11653
  const propAccess = node.expression;
@@ -11716,14 +11806,8 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
11716
11806
  indexType = secondParam.type.getText(ctx.sourceFile);
11717
11807
  }
11718
11808
  }
11719
- if (paramBindings) {
11720
- for (const b of paramBindings)
11721
- ctx.loopParams.add(b.name);
11722
- } else {
11723
- ctx.loopParams.add(param);
11724
- }
11725
- if (index)
11726
- ctx.loopParams.add(index);
11809
+ const savedScope = ctx.scope;
11810
+ ctx.scope = ctx.scope.enterLoopRow({ param, index, paramBindings });
11727
11811
  ctx.loopDepth++;
11728
11812
  const tryTransformRenderableBody = (expr) => {
11729
11813
  if (!ts12.isBinaryExpression(expr))
@@ -11784,6 +11868,24 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
11784
11868
  }
11785
11869
  }
11786
11870
  const returnStmt = children.length === 0 ? body.statements.find((s) => ts12.isReturnStatement(s) && s.expression != null) : undefined;
11871
+ let rowScopeBeforePreamble = null;
11872
+ if (returnStmt) {
11873
+ const preambleNames = new Set;
11874
+ for (const stmt of body.statements) {
11875
+ if (stmt === returnStmt)
11876
+ break;
11877
+ collectPreambleDeclaredNames(stmt, preambleNames);
11878
+ }
11879
+ if (preambleNames.size > 0) {
11880
+ rowScopeBeforePreamble = ctx.scope;
11881
+ ctx.scope = savedScope.enterLoopRow({
11882
+ param,
11883
+ index,
11884
+ paramBindings,
11885
+ preamble: { declaredNames: [...preambleNames] }
11886
+ });
11887
+ }
11888
+ }
11787
11889
  if (returnStmt && returnStmt.expression) {
11788
11890
  let returnExpr = returnStmt.expression;
11789
11891
  while (ts12.isParenthesizedExpression(returnExpr)) {
@@ -11853,6 +11955,9 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
11853
11955
  }
11854
11956
  }
11855
11957
  }
11958
+ if (rowScopeBeforePreamble) {
11959
+ ctx.scope = rowScopeBeforePreamble;
11960
+ }
11856
11961
  if (method === "flatMap" && children.length === 0 && !flatMapProjectionCall(body)) {
11857
11962
  flatMapCallback = buildFlatMapCallback(callback, body, ctx);
11858
11963
  }
@@ -11881,14 +11986,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
11881
11986
  }
11882
11987
  }));
11883
11988
  }
11884
- if (paramBindings) {
11885
- for (const b of paramBindings)
11886
- ctx.loopParams.delete(b.name);
11887
- } else {
11888
- ctx.loopParams.delete(param);
11889
- }
11890
- if (index)
11891
- ctx.loopParams.delete(index);
11989
+ ctx.scope = savedScope;
11892
11990
  ctx.loopDepth--;
11893
11991
  }
11894
11992
  if (children.length === 0 && !flatMapCallback) {
@@ -12635,7 +12733,7 @@ function parseTemplateLiteral(expr, ctx) {
12635
12733
  }
12636
12734
  function tryResolveTemplateSpanFromConst(expr, ctx) {
12637
12735
  if (ts12.isIdentifier(expr)) {
12638
- if (ctx.loopParams.has(expr.text))
12736
+ if (ctx.scope.isBound(expr.text))
12639
12737
  return null;
12640
12738
  const constInfo = findLocalConst(expr.text, ctx.analyzer);
12641
12739
  if (!constInfo)
@@ -12651,7 +12749,7 @@ function tryResolveTemplateSpanFromConst(expr, ctx) {
12651
12749
  if (ts12.isElementAccessExpression(expr)) {
12652
12750
  if (!ts12.isIdentifier(expr.expression))
12653
12751
  return null;
12654
- if (ctx.loopParams.has(expr.expression.text))
12752
+ if (ctx.scope.isBound(expr.expression.text))
12655
12753
  return null;
12656
12754
  const constInfo = findLocalConst(expr.expression.text, ctx.analyzer);
12657
12755
  if (!constInfo)
@@ -12730,7 +12828,7 @@ function hasDynamicTagBinding(name, sourceFile) {
12730
12828
  return found;
12731
12829
  }
12732
12830
  function tryResolveIdentifierAsTemplateLiteral(ident, ctx) {
12733
- if (ctx.loopParams.has(ident.text))
12831
+ if (ctx.scope.isBound(ident.text))
12734
12832
  return null;
12735
12833
  const constInfo = findLocalConst(ident.text, ctx.analyzer);
12736
12834
  if (!constInfo)
@@ -13079,10 +13177,11 @@ function isSignalOrMemoArray(array, ctx) {
13079
13177
  return false;
13080
13178
  }
13081
13179
  function referencesLoopParam(expr, ctx) {
13082
- if (ctx.loopParams.size === 0)
13180
+ const boundNames = ctx.scope.valueBoundNames();
13181
+ if (boundNames.size === 0)
13083
13182
  return false;
13084
- for (const p of ctx.loopParams) {
13085
- if (new RegExp(`\\b${p}\\b`).test(expr))
13183
+ for (const p of boundNames) {
13184
+ if (identifierPattern(p).test(expr))
13086
13185
  return true;
13087
13186
  }
13088
13187
  return false;
@@ -13161,9 +13260,10 @@ function hasReactiveAttributes(attrs, ctx) {
13161
13260
  if (isSignalOrMemoReference(valueToCheck, ctx) || isPropsReference(valueToCheck, ctx)) {
13162
13261
  return true;
13163
13262
  }
13164
- if (ctx.loopParams.size > 0) {
13165
- for (const p of ctx.loopParams) {
13166
- if (new RegExp(`\\b${p}\\b`).test(valueToCheck))
13263
+ const scopeValueNames = ctx.scope.valueBoundNames();
13264
+ if (scopeValueNames.size > 0) {
13265
+ for (const p of scopeValueNames) {
13266
+ if (identifierPattern(p).test(valueToCheck))
13167
13267
  return true;
13168
13268
  }
13169
13269
  }
@@ -13369,18 +13469,22 @@ function buildIfStatementChain(analyzer, ctx, opts) {
13369
13469
  }
13370
13470
 
13371
13471
  // ../jsx/src/ir-to-client-js/prop-handling.ts
13372
- function expandDynamicPropValue(value, ctx) {
13472
+ function expandDynamicPropValue(value, ctx, scope) {
13373
13473
  const trimmedValue = value.trim();
13474
+ if (scope?.isBound(trimmedValue))
13475
+ return value;
13374
13476
  const constant = ctx.localConstants.find((c) => c.name === trimmedValue);
13375
13477
  if (constant && constant.value) {
13376
13478
  return constant.value;
13377
13479
  }
13378
13480
  return value;
13379
13481
  }
13380
- function expandConstantForReactivity(expr, ctx, originalFreeIds) {
13482
+ function expandConstantForReactivity(expr, ctx, originalFreeIds, scope) {
13381
13483
  if (ctx.propsObjectName)
13382
13484
  return { expr, freeIds: originalFreeIds };
13383
13485
  const trimmedValue = expr.trim();
13486
+ if (scope?.isBound(trimmedValue))
13487
+ return { expr, freeIds: originalFreeIds };
13384
13488
  const constant = ctx.localConstants.find((c) => c.name === trimmedValue);
13385
13489
  if (constant && constant.value) {
13386
13490
  return { expr: constant.value, freeIds: constant.freeIdentifiers };
@@ -13416,6 +13520,16 @@ function getControlledPropName(signal, propsParams, propsObjectName = null) {
13416
13520
  }
13417
13521
 
13418
13522
  // ../jsx/src/ir-to-client-js/reactivity.ts
13523
+ function buildLoopRowScope(loopParam, loopParamBindings, preambleNames, loopIndex) {
13524
+ if (!loopParam)
13525
+ return;
13526
+ return BindingScope.EMPTY.enterLoopRow({
13527
+ param: loopParam,
13528
+ paramBindings: loopParamBindings,
13529
+ index: loopIndex,
13530
+ preamble: preambleNames && preambleNames.size > 0 ? { declaredNames: [...preambleNames] } : undefined
13531
+ });
13532
+ }
13419
13533
  function decideWrapFromAstFlags(node) {
13420
13534
  if (node.origin && isReactiveOrigin(node.origin)) {
13421
13535
  return { wrap: true, reason: "proven-reactive" };
@@ -13444,12 +13558,12 @@ function decideWrapForChildProp(expandedValue, ctx, prop) {
13444
13558
  }
13445
13559
  function needsEffectWrapper(expr, ctx, freeIdentifiers2) {
13446
13560
  for (const signal of ctx.signals) {
13447
- if (new RegExp(`\\b${signal.getter}\\s*\\(`).test(expr)) {
13561
+ if (identifierCallPattern(signal.getter).test(expr)) {
13448
13562
  return true;
13449
13563
  }
13450
13564
  }
13451
13565
  for (const memo of ctx.memos) {
13452
- if (new RegExp(`\\b${memo.name}\\s*\\(`).test(expr)) {
13566
+ if (identifierCallPattern(memo.name).test(expr)) {
13453
13567
  return true;
13454
13568
  }
13455
13569
  }
@@ -13639,8 +13753,9 @@ function traverseForComponents(node, components, skipConditionals = false) {
13639
13753
  }
13640
13754
  });
13641
13755
  }
13642
- function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false) {
13756
+ function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false, preambleNames, loopIndex) {
13643
13757
  const texts = [];
13758
+ const scope = buildLoopRowScope(loopParam, loopParamBindings, preambleNames, loopIndex);
13644
13759
  walkIR(node, false, {
13645
13760
  ...stopAt("loop", "async", "ifStatement"),
13646
13761
  expression: ({ node: n, scope: insideConditional }) => {
@@ -13649,7 +13764,7 @@ function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings,
13649
13764
  if (n.preambleRegion)
13650
13765
  return;
13651
13766
  const originFreeIds = freeIdsFromRefs(n.origin?.freeRefs);
13652
- const expanded = expandConstantForReactivity(n.expr, ctx, originFreeIds);
13767
+ const expanded = expandConstantForReactivity(n.expr, ctx, originFreeIds, scope);
13653
13768
  const reactive = classifyReactivity(expanded.expr, ctx, loopParam, loopParamBindings, expanded.freeIds).kind !== "none" || decideWrapFromAstFlags(n).wrap;
13654
13769
  if (!reactive)
13655
13770
  return;
@@ -13673,8 +13788,9 @@ function anyNameIn(names, set) {
13673
13788
  return true;
13674
13789
  return false;
13675
13790
  }
13676
- function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false, preambleNames) {
13791
+ function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false, preambleNames, loopIndex) {
13677
13792
  const attrs = [];
13793
+ const scope = buildLoopRowScope(loopParam, loopParamBindings, preambleNames, loopIndex);
13678
13794
  traverseElements(node, (el) => {
13679
13795
  if (el.slotId) {
13680
13796
  for (const attr of el.attrs) {
@@ -13687,7 +13803,7 @@ function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings,
13687
13803
  const valueStr = attrValueToString(attr.value);
13688
13804
  if (!valueStr)
13689
13805
  continue;
13690
- const expanded = expandConstantForReactivity(valueStr, ctx, attr.freeIdentifiers);
13806
+ const expanded = expandConstantForReactivity(valueStr, ctx, attr.freeIdentifiers, scope);
13691
13807
  const readsPreamble = preambleNames !== undefined && preambleNames.size > 0 && anyNameIn(expanded.freeIds ?? extractFreeIdentifiersFromText(expanded.expr), preambleNames);
13692
13808
  const reactive = classifyReactivity(expanded.expr, ctx, loopParam, loopParamBindings, expanded.freeIds).kind !== "none" || readsPreamble || attr.callsReactiveGetters || attr.hasFunctionCalls;
13693
13809
  if (!attr.clientOnly && !reactive)
@@ -13992,13 +14108,13 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx, options)
13992
14108
  const emitDepth = fixedDepth ?? scope.depth + 1;
13993
14109
  const loopParamsForTemplate = outerLoopParam ? [outerLoopParam, { param: n.param, bindings: n.paramBindings }] : undefined;
13994
14110
  const template = n.children.map((c) => irToPlaceholderTemplate(c, undefined, emitDepth, loopParamsForTemplate)).join("");
13995
- const refsOuter = outerLoopParam ? new RegExp(`\\b${outerLoopParam}\\b`).test(n.array) : false;
14111
+ const refsOuter = outerLoopParam ? identifierPattern(outerLoopParam).test(n.array) : false;
13996
14112
  const bindings = emptyLoopChildBindings();
13997
14113
  const innerPreambleNames = preambleNamesOf(n);
13998
14114
  if (ctx) {
13999
14115
  for (const child of n.children) {
14000
- bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, n.param, n.paramBindings));
14001
- bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, n.param, n.paramBindings, false, innerPreambleNames));
14116
+ bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, n.param, n.paramBindings, false, innerPreambleNames, n.index));
14117
+ bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, n.param, n.paramBindings, false, innerPreambleNames, n.index));
14002
14118
  bindings.refs.push(...collectLoopChildRefs(child));
14003
14119
  }
14004
14120
  }
@@ -14025,7 +14141,7 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx, options)
14025
14141
  bindings.events.push(...collectLoopChildEventsWithNesting(child));
14026
14142
  }
14027
14143
  if (ctx) {
14028
- bindings.conditionals.push(...collectLoopChildConditionals({ type: "fragment", children: n.children, loc: n.loc }, ctx, siblingOffsets, n.param, n.paramBindings));
14144
+ bindings.conditionals.push(...collectLoopChildConditionals({ type: "fragment", children: n.children, loc: n.loc }, ctx, siblingOffsets, n.param, n.paramBindings, innerPreambleNames, n.index));
14029
14145
  }
14030
14146
  }
14031
14147
  result.push({
@@ -14221,7 +14337,7 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
14221
14337
  return;
14222
14338
  const projectionInner = l.method === "flatMap" && l.children.length === 1 && l.children[0].type === "loop" ? l.children[0] : undefined;
14223
14339
  const childHandlers = [];
14224
- const bindings = projectionInner ? emptyLoopChildBindings() : collectLoopChildBindings(l.children, ctx, siblingOffsets, l.param, l.paramBindings, preambleNamesOf(l));
14340
+ const bindings = projectionInner ? emptyLoopChildBindings() : collectLoopChildBindings(l.children, ctx, siblingOffsets, l.param, l.paramBindings, preambleNamesOf(l), l.index);
14225
14341
  if (!projectionInner) {
14226
14342
  for (const child of l.children) {
14227
14343
  childHandlers.push(...collectEventHandlersFromIR(child));
@@ -14476,7 +14592,7 @@ function collectBranchLoops(node, ctx, siblingOffsets) {
14476
14592
  } else {
14477
14593
  childTemplate = n.children.map((c) => irToHtmlTemplate(c, undefined, 0, branchLoopParamSpec)).join("");
14478
14594
  }
14479
- const branchBindings = ctx && !projectionInner ? collectLoopChildBindings(n.children, ctx, siblingOffsets, n.param, n.paramBindings, preambleNamesOf(n)) : emptyLoopChildBindings();
14595
+ const branchBindings = ctx && !projectionInner ? collectLoopChildBindings(n.children, ctx, siblingOffsets, n.param, n.paramBindings, preambleNamesOf(n), n.index) : emptyLoopChildBindings();
14480
14596
  loops.push({
14481
14597
  kind: "branch",
14482
14598
  array: n.array,
@@ -14562,19 +14678,20 @@ function preambleNamesOf(loop) {
14562
14678
  const declared = loop.preamble?.declaredNames;
14563
14679
  return declared && declared.length > 0 ? new Set(declared) : undefined;
14564
14680
  }
14565
- function collectLoopChildBindings(children, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames) {
14681
+ function collectLoopChildBindings(children, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex) {
14566
14682
  const bindings = emptyLoopChildBindings();
14567
14683
  for (const child of children) {
14568
14684
  bindings.events.push(...collectLoopChildEventsWithNesting(child));
14569
- bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, loopParam, loopParamBindings, true, preambleNames));
14570
- bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, loopParam, loopParamBindings, true));
14685
+ bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, loopParam, loopParamBindings, true, preambleNames, loopIndex));
14686
+ bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, loopParam, loopParamBindings, true, preambleNames, loopIndex));
14571
14687
  bindings.refs.push(...collectLoopChildRefs(child));
14572
- bindings.conditionals.push(...collectLoopChildConditionals(child, ctx, siblingOffsets, loopParam, loopParamBindings));
14688
+ bindings.conditionals.push(...collectLoopChildConditionals(child, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex));
14573
14689
  }
14574
14690
  return bindings;
14575
14691
  }
14576
- function collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loopParamBindings) {
14692
+ function collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex) {
14577
14693
  const conditionals = [];
14694
+ const scope = buildLoopRowScope(loopParam, loopParamBindings, preambleNames, loopIndex);
14578
14695
  const refsAnyBindingViaFreeIds = (freeIds) => {
14579
14696
  if (loopParamBindings && loopParamBindings.length > 0) {
14580
14697
  for (const b of loopParamBindings) {
@@ -14594,7 +14711,7 @@ function collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loop
14594
14711
  const refsLoopParamInSource = refsAnyBindingViaFreeIds(sourceFreeIds);
14595
14712
  if (!n.reactive && !refsLoopParamInSource)
14596
14713
  return;
14597
- const expanded = expandConstantForReactivity(n.condition, ctx, sourceFreeIds);
14714
+ const expanded = expandConstantForReactivity(n.condition, ctx, sourceFreeIds, scope);
14598
14715
  if (classifyReactivity(expanded.expr, ctx, loopParam, loopParamBindings, expanded.freeIds).kind === "none")
14599
14716
  return;
14600
14717
  const loopParamsForCond = loopParam ? [{ param: loopParam, bindings: loopParamBindings }] : undefined;
@@ -14605,23 +14722,23 @@ function collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loop
14605
14722
  condition: expanded.expr,
14606
14723
  whenTrueHtml,
14607
14724
  whenFalseHtml,
14608
- whenTrue: summarizeLoopChildBranch(n.whenTrue, ctx, siblingOffsets, loopParam, loopParamBindings),
14609
- whenFalse: summarizeLoopChildBranch(n.whenFalse, ctx, siblingOffsets, loopParam, loopParamBindings),
14725
+ whenTrue: summarizeLoopChildBranch(n.whenTrue, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex),
14726
+ whenFalse: summarizeLoopChildBranch(n.whenFalse, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex),
14610
14727
  ...expanded.freeIds !== undefined && { conditionFreeIdentifiers: expanded.freeIds }
14611
14728
  });
14612
14729
  }
14613
14730
  });
14614
14731
  return conditionals;
14615
14732
  }
14616
- function summarizeLoopChildBranch(node, ctx, siblingOffsets, loopParam, loopParamBindings) {
14733
+ function summarizeLoopChildBranch(node, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex) {
14617
14734
  const inner = collectInnerLoops([node], siblingOffsets, loopParam, ctx, branchInnerLoopOptions);
14618
14735
  return {
14619
14736
  childComponents: collectConditionalBranchChildComponents(node),
14620
14737
  innerLoops: inner.length > 0 ? inner : undefined,
14621
- conditionals: collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loopParamBindings),
14738
+ conditionals: collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex),
14622
14739
  events: collectConditionalBranchEvents(node),
14623
- reactiveAttrs: collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, true),
14624
- reactiveTexts: node.type === "expression" && node.hasFunctionCalls ? [] : collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, true)
14740
+ reactiveAttrs: collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, true, preambleNames, loopIndex),
14741
+ reactiveTexts: node.type === "expression" && node.hasFunctionCalls ? [] : collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, true, preambleNames, loopIndex)
14625
14742
  };
14626
14743
  }
14627
14744
 
@@ -15193,7 +15310,7 @@ var MODULE_CONSTANTS_PLACEHOLDER = "/* __MODULE_LEVEL_CONSTANTS__ */";
15193
15310
  function detectUsedImports(code) {
15194
15311
  const used = new Set;
15195
15312
  for (const name of RUNTIME_IMPORT_CANDIDATES) {
15196
- if (new RegExp(`\\b${name}\\s*\\(`).test(code)) {
15313
+ if (identifierCallPattern(name).test(code)) {
15197
15314
  used.add(name);
15198
15315
  }
15199
15316
  }
@@ -15594,7 +15711,7 @@ function containsAnyIdentifier(node, names) {
15594
15711
  function scanRefsByName(text, bindings) {
15595
15712
  const result = new Map;
15596
15713
  for (const name of bindings.keys()) {
15597
- const re = new RegExp(`\\b${name}\\b`);
15714
+ const re = identifierPattern(name);
15598
15715
  if (re.test(text))
15599
15716
  result.set(name, []);
15600
15717
  }
@@ -18262,7 +18379,7 @@ function buildStaticArrayDelegationPlan(elem, profileComponentName) {
18262
18379
  function buildKeyedOrIndexLookup(args) {
18263
18380
  const hasBindings = (args.paramBindings?.length ?? 0) > 0;
18264
18381
  if (args.key !== null) {
18265
- const keyWithItem = hasBindings ? substituteLoopBindings(args.key, args.paramBindings, "item") : args.key.replace(new RegExp(`\\b${args.param}\\b`, "g"), "item");
18382
+ const keyWithItem = hasBindings ? substituteLoopBindings(args.key, args.paramBindings, "item") : args.key.replace(identifierPattern(args.param, "g"), "item");
18266
18383
  return {
18267
18384
  kind: "keyed",
18268
18385
  arrayExpr: args.array,
@@ -20468,7 +20585,7 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
20468
20585
  }
20469
20586
  for (const nested of ev.nestedLoops) {
20470
20587
  const rawKey = nested.key ?? "";
20471
- const innerKeyExpr = nested.paramBindings && nested.paramBindings.length > 0 ? substituteLoopBindings(rawKey, nested.paramBindings, "item") : rawKey.replace(new RegExp(`\\b${nested.param}\\b`, "g"), "item");
20588
+ const innerKeyExpr = nested.paramBindings && nested.paramBindings.length > 0 ? substituteLoopBindings(rawKey, nested.paramBindings, "item") : rawKey.replace(identifierPattern(nested.param, "g"), "item");
20472
20589
  const outerRef = hasBindings ? "__bfLoopItem" : param;
20473
20590
  ls.push(` const ${nested.param} = ${outerRef} && ${nested.array}.find(item => String(${innerKeyExpr}) === innerKey${nested.depth})`);
20474
20591
  }
@@ -21162,7 +21279,7 @@ function rewritePropsObjectRef(code, propsObjectName) {
21162
21279
  const srcPropsName = propsObjectName ?? "props";
21163
21280
  if (srcPropsName === PROPS_PARAM)
21164
21281
  return code;
21165
- if (!new RegExp(`\\b${srcPropsName}\\b`).test(code))
21282
+ if (!identifierPattern(srcPropsName).test(code))
21166
21283
  return code;
21167
21284
  const sourceFile = ts19.createSourceFile("init-body.ts", code, ts19.ScriptTarget.Latest, true, ts19.ScriptKind.TS);
21168
21285
  const spans = [];
@@ -23572,7 +23689,7 @@ class JsxAdapter extends BaseAdapter {
23572
23689
  lines.push(` const ${signal.getter} = () => ${initialValue}`);
23573
23690
  }
23574
23691
  if (signal.setter) {
23575
- const setterUsed = new RegExp(`\\b${signal.setter}\\b`).test(setterRefText);
23692
+ const setterUsed = identifierPattern(signal.setter).test(setterRefText);
23576
23693
  if (setterUsed) {
23577
23694
  lines.push(` const ${signal.setter} = (..._args: any[]) => {}`);
23578
23695
  }
@@ -23592,7 +23709,7 @@ class JsxAdapter extends BaseAdapter {
23592
23709
  continue;
23593
23710
  const keyword = constant.declarationKind ?? "const";
23594
23711
  if (!constant.value) {
23595
- const typeAnnotation = preserveTypes && constant.type ? `: ${constant.type.raw}` : "";
23712
+ const typeAnnotation = preserveTypes && (constant.typeAnnotation ?? constant.type) ? `: ${constant.typeAnnotation ?? constant.type?.raw}` : "";
23596
23713
  lines.push(` ${keyword} ${constant.name}${typeAnnotation}`);
23597
23714
  continue;
23598
23715
  }
@@ -23602,7 +23719,8 @@ class JsxAdapter extends BaseAdapter {
23602
23719
  if (!reachable.has(constant.name))
23603
23720
  continue;
23604
23721
  const constValue = preserveTypes ? constant.typedValue ?? constant.value : constant.value;
23605
- lines.push(` ${keyword} ${constant.name} = ${constValue}`);
23722
+ const letTypeAnnotation = preserveTypes && keyword === "let" && constant.typeAnnotation ? `: ${constant.typeAnnotation}` : "";
23723
+ lines.push(` ${keyword} ${constant.name}${letTypeAnnotation} = ${constValue}`);
23606
23724
  }
23607
23725
  for (const func of localFunctions) {
23608
23726
  if (moduleScopeNames.has(func.name))
@@ -23709,7 +23827,8 @@ class JsxAdapter extends BaseAdapter {
23709
23827
  const keyword = c.declarationKind ?? "const";
23710
23828
  const exportKw = c.isExported ? "export " : "";
23711
23829
  if (!c.value) {
23712
- entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}` });
23830
+ const typeAnnotation = preserveTypes && (c.typeAnnotation ?? c.type) ? `: ${c.typeAnnotation ?? c.type?.raw}` : "";
23831
+ entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}${typeAnnotation}` });
23713
23832
  continue;
23714
23833
  }
23715
23834
  const trimmed = c.value.trim();
@@ -23718,7 +23837,8 @@ class JsxAdapter extends BaseAdapter {
23718
23837
  if (c.isExported && /^createContext\b/.test(trimmed))
23719
23838
  continue;
23720
23839
  const value = preserveTypes ? c.typedValue ?? c.value : c.value;
23721
- entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name} = ${value}` });
23840
+ const letTypeAnnotation = preserveTypes && keyword === "let" && c.typeAnnotation ? `: ${c.typeAnnotation}` : "";
23841
+ entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}${letTypeAnnotation} = ${value}` });
23722
23842
  }
23723
23843
  for (const f of ir.metadata.localFunctions) {
23724
23844
  if (!f.isModule || !moduleNames.has(f.name))
@@ -23767,6 +23887,7 @@ class JsxAdapter extends BaseAdapter {
23767
23887
  }
23768
23888
 
23769
23889
  // ../jsx/src/adapters/template-imports.ts
23890
+ import ts25 from "typescript";
23770
23891
  var CLIENT_PACKAGE_SOURCES = new Set([
23771
23892
  "@barefootjs/client",
23772
23893
  "@barefootjs/client/runtime"
@@ -24156,11 +24277,11 @@ function registerBuiltinLoweringPlugins() {
24156
24277
  registerLoweringPlugin(plugin);
24157
24278
  }
24158
24279
  // ../jsx/src/combine-client-js.ts
24159
- import ts25 from "typescript";
24160
- // ../jsx/src/debug.ts
24161
24280
  import ts26 from "typescript";
24162
- // ../jsx/src/profiler.ts
24281
+ // ../jsx/src/debug.ts
24163
24282
  import ts27 from "typescript";
24283
+ // ../jsx/src/profiler.ts
24284
+ import ts28 from "typescript";
24164
24285
 
24165
24286
  // ../jsx/src/index.ts
24166
24287
  registerBuiltinLoweringPlugins();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/vite",
3
- "version": "0.31.2",
3
+ "version": "0.31.4",
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.31.2"
41
+ "@barefootjs/shared": "0.31.4"
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.31.2",
49
- "@barefootjs/go-template": "0.31.2",
50
- "@barefootjs/hono": "0.31.2",
51
- "@barefootjs/jsx": "0.31.2",
48
+ "@barefootjs/client": "0.31.4",
49
+ "@barefootjs/go-template": "0.31.4",
50
+ "@barefootjs/hono": "0.31.4",
51
+ "@barefootjs/jsx": "0.31.4",
52
52
  "typescript": "^5.0.0",
53
53
  "vite": "^6.0.0"
54
54
  }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Regression (#2588): a relative specifier inside a DYNAMIC `import()` must
3
+ * be re-anchored to the emitted template's directory, exactly like the
4
+ * static `import` statements `rewriteImportsForTemplate` already handles.
5
+ *
6
+ * Dynamic imports never reach `ir.metadata.templateImports` — they ride
7
+ * along inside declaration source text that the adapter re-emits verbatim
8
+ * (`generateModuleScopeDeclarations`' consts/functions, and a component
9
+ * body's local handlers). Before the fix they were emitted untouched, so a
10
+ * specifier written relative to `components/` still said `../lib/heavy`
11
+ * once the template landed in `app/dist/components/` — a path that does
12
+ * not exist. The backend bundler then hard-fails (`Could not resolve
13
+ * "../lib/heavy"`), so this is a build break, not a type-only defect.
14
+ *
15
+ * The fixture mirrors the layout that hits this in the wild (piconic-ai/koma):
16
+ * a `components` dir and a plain `lib` dir side by side, with templates
17
+ * emitted to a DEEPER directory — root-relative and template-relative only
18
+ * diverge when the two depths differ, so a flat layout would not reproduce.
19
+ * Lives under `packages/vite/` (not a system tmpdir) so `@barefootjs/client`
20
+ * resolves through the monorepo's real workspace symlinks — same reason
21
+ * `e2e-fixture`/`e2e-fixture-dev`/`e2e-fixture-relimport` do.
22
+ */
23
+ import { describe, test, expect, afterAll } from 'bun:test'
24
+ import { build } from 'vite'
25
+ import { mkdtemp, rm, readFile } from 'node:fs/promises'
26
+ import { tmpdir } from 'node:os'
27
+ import { join, resolve } from 'node:path'
28
+ import { HonoAdapter } from '@barefootjs/hono/adapter'
29
+ import { barefoot } from '../plugin.ts'
30
+
31
+ const FIXTURE_ROOT = resolve(import.meta.dirname, '../../e2e-fixture-dynimport')
32
+ const APP_ROOT = join(FIXTURE_ROOT, 'app')
33
+ const COMPONENTS_DIR = join(FIXTURE_ROOT, 'components')
34
+
35
+ // `app/dist/components/Lazy.tsx` → up three → the fixture root, where `lib/`
36
+ // sits. The source says `../lib/heavy` from `components/`; the emitted
37
+ // template has to say this instead.
38
+ const REANCHORED = '../../../lib/heavy'
39
+
40
+ describe('dynamic import() re-anchoring in emitted templates', () => {
41
+ let outDir: string
42
+ let templatesDir: string
43
+
44
+ afterAll(async () => {
45
+ await rm(outDir, { recursive: true, force: true })
46
+ await rm(join(APP_ROOT, 'dist'), { recursive: true, force: true })
47
+ })
48
+
49
+ test('re-anchors dynamic imports at module scope, in type position, and inside a component body', async () => {
50
+ outDir = await mkdtemp(join(tmpdir(), 'barefoot-vite-dynimport-dist-'))
51
+ templatesDir = join(APP_ROOT, 'dist/components')
52
+
53
+ await build({
54
+ configFile: false,
55
+ root: APP_ROOT,
56
+ base: '/static/',
57
+ logLevel: 'warn',
58
+ build: { outDir, emptyOutDir: true },
59
+ plugins: [
60
+ barefoot({
61
+ adapter: new HonoAdapter(),
62
+ components: [COMPONENTS_DIR],
63
+ templates: templatesDir,
64
+ }),
65
+ ],
66
+ })
67
+
68
+ const template = await readFile(join(templatesDir, 'Lazy.tsx'), 'utf8')
69
+
70
+ // Nothing anywhere may still carry the source-relative form. Asserted
71
+ // first and globally: a per-site check would pass while some fourth
72
+ // emission path silently leaked the old specifier.
73
+ expect(template).not.toContain("'../lib/heavy'")
74
+
75
+ // Module scope, TYPE position — `typeof import('…')` is an
76
+ // ImportTypeNode, a different AST node from the call expression below,
77
+ // and was missed independently.
78
+ expect(template).toContain(`typeof import('${REANCHORED}')`)
79
+
80
+ // Module scope, VALUE position — inside a re-emitted const's body.
81
+ expect(template).toContain(`modPromise = import('${REANCHORED}')`)
82
+
83
+ // Component scope — inside a local handler in the component body,
84
+ // which is emitted by a different code path than module declarations.
85
+ expect(template).toContain(`await import('${REANCHORED}')`)
86
+
87
+ // The re-anchored path must actually resolve on disk from the emitted
88
+ // template's own directory — the assertions above only pin the string.
89
+ const target = resolve(templatesDir, REANCHORED + '.ts')
90
+ expect(await readFile(target, 'utf8')).toContain('export function heavy')
91
+ }, 60_000)
92
+ })