@barefootjs/jsx 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.
Files changed (65) hide show
  1. package/dist/adapters/jsx-adapter.d.ts.map +1 -1
  2. package/dist/adapters/template-imports.d.ts +27 -15
  3. package/dist/adapters/template-imports.d.ts.map +1 -1
  4. package/dist/analyzer.d.ts.map +1 -1
  5. package/dist/debug.d.ts.map +1 -1
  6. package/dist/identifier-pattern.d.ts +62 -0
  7. package/dist/identifier-pattern.d.ts.map +1 -0
  8. package/dist/index.d.ts +3 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +280 -119
  11. package/dist/ir-to-client-js/collect-elements.d.ts +23 -2
  12. package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
  13. package/dist/ir-to-client-js/control-flow/plan/build-event-delegation.d.ts.map +1 -1
  14. package/dist/ir-to-client-js/control-flow/stringify/event-delegation.d.ts.map +1 -1
  15. package/dist/ir-to-client-js/csr-substitute.d.ts +12 -1
  16. package/dist/ir-to-client-js/csr-substitute.d.ts.map +1 -1
  17. package/dist/ir-to-client-js/html-template.d.ts +20 -12
  18. package/dist/ir-to-client-js/html-template.d.ts.map +1 -1
  19. package/dist/ir-to-client-js/imports.d.ts.map +1 -1
  20. package/dist/ir-to-client-js/prop-handling.d.ts +25 -2
  21. package/dist/ir-to-client-js/prop-handling.d.ts.map +1 -1
  22. package/dist/ir-to-client-js/reactivity.d.ts +30 -2
  23. package/dist/ir-to-client-js/reactivity.d.ts.map +1 -1
  24. package/dist/ir-to-client-js/rewrite-props-object.d.ts.map +1 -1
  25. package/dist/ir-to-client-js/utils.d.ts.map +1 -1
  26. package/dist/jsx-to-ir.d.ts.map +1 -1
  27. package/dist/module-exports.d.ts.map +1 -1
  28. package/dist/prop-rewrite.d.ts +1 -1
  29. package/dist/relocate.d.ts.map +1 -1
  30. package/dist/scope/binding-scope.d.ts +179 -0
  31. package/dist/scope/binding-scope.d.ts.map +1 -0
  32. package/dist/types.d.ts +8 -0
  33. package/dist/types.d.ts.map +1 -1
  34. package/package.json +2 -2
  35. package/src/__tests__/binding-scope-preamble-shadowing.test.ts +115 -0
  36. package/src/__tests__/binding-scope-ratchet.test.ts +194 -0
  37. package/src/__tests__/binding-scope.test.ts +200 -0
  38. package/src/__tests__/csr-materialize-loop-preamble-shadow.test.ts +76 -0
  39. package/src/__tests__/csr-substitute-enclosing-scope.test.ts +77 -0
  40. package/src/__tests__/identifier-pattern.test.ts +170 -0
  41. package/src/__tests__/let-type-annotation.test.ts +208 -0
  42. package/src/__tests__/loop-child-reactive-attr-const-shadow.test.ts +107 -0
  43. package/src/__tests__/rewrite-dynamic-imports.test.ts +98 -0
  44. package/src/adapters/jsx-adapter.ts +34 -5
  45. package/src/adapters/template-imports.ts +93 -0
  46. package/src/analyzer.ts +20 -3
  47. package/src/debug.ts +4 -3
  48. package/src/identifier-pattern.ts +79 -0
  49. package/src/index.ts +5 -1
  50. package/src/ir-to-client-js/collect-elements.ts +44 -15
  51. package/src/ir-to-client-js/control-flow/plan/build-event-delegation.ts +2 -1
  52. package/src/ir-to-client-js/control-flow/stringify/event-delegation.ts +2 -1
  53. package/src/ir-to-client-js/csr-substitute.ts +19 -2
  54. package/src/ir-to-client-js/html-template.ts +37 -31
  55. package/src/ir-to-client-js/imports.ts +2 -1
  56. package/src/ir-to-client-js/prop-handling.ts +28 -1
  57. package/src/ir-to-client-js/reactivity.ts +54 -4
  58. package/src/ir-to-client-js/rewrite-props-object.ts +2 -1
  59. package/src/ir-to-client-js/utils.ts +9 -8
  60. package/src/jsx-to-ir.ts +187 -73
  61. package/src/module-exports.ts +3 -2
  62. package/src/prop-rewrite.ts +1 -1
  63. package/src/relocate.ts +2 -1
  64. package/src/scope/binding-scope.ts +238 -0
  65. package/src/types.ts +8 -0
package/dist/index.js CHANGED
@@ -2332,6 +2332,24 @@ function templatePartsToJsExpr(parts, opts) {
2332
2332
  return result;
2333
2333
  }
2334
2334
 
2335
+ // src/identifier-pattern.ts
2336
+ function withUnicodeFlag(flags) {
2337
+ return flags.includes("u") ? flags : `${flags}u`;
2338
+ }
2339
+ function escapeIdentifierForRegex(name) {
2340
+ return name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2341
+ }
2342
+ var ID_BOUNDARY_BEFORE = "(?<![\\p{ID_Continue}$])";
2343
+ var ID_BOUNDARY_AFTER = "(?![\\p{ID_Continue}$])";
2344
+ function identifierPattern(name, flags = "") {
2345
+ const esc = escapeIdentifierForRegex(name);
2346
+ return new RegExp(`${ID_BOUNDARY_BEFORE}${esc}${ID_BOUNDARY_AFTER}`, withUnicodeFlag(flags));
2347
+ }
2348
+ function identifierCallPattern(name, flags = "") {
2349
+ const esc = escapeIdentifierForRegex(name);
2350
+ return new RegExp(`${ID_BOUNDARY_BEFORE}${esc}\\s*\\(`, withUnicodeFlag(flags));
2351
+ }
2352
+
2335
2353
  // src/ir-to-client-js/utils.ts
2336
2354
  import {
2337
2355
  BF_KEY as DATA_KEY,
@@ -2458,9 +2476,6 @@ function inferDefaultValue(type) {
2458
2476
  return "{}";
2459
2477
  return "undefined";
2460
2478
  }
2461
- function escapeRegExp(s) {
2462
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2463
- }
2464
2479
  function freeIdsFromRefs(refs) {
2465
2480
  const out = new Set;
2466
2481
  if (!refs)
@@ -2588,16 +2603,16 @@ function wrapLoopParamAsAccessor(expr, paramName, bindings) {
2588
2603
  if (bindings && bindings.length > 0) {
2589
2604
  return rewriteLoopBindingRefs(expr, bindings, "__bfItem()");
2590
2605
  }
2591
- const re = new RegExp(`\\b${escapeRegExp(paramName)}\\b(?!\\s*\\()(?!-)`, "g");
2592
- return replaceInExprContexts(expr, re, `${paramName}()`);
2606
+ const re = new RegExp(`${ID_BOUNDARY_BEFORE}${escapeIdentifierForRegex(paramName)}(?!\\s*\\()(?!-)${ID_BOUNDARY_AFTER}`, "gu");
2607
+ return replaceInExprContexts(expr, re, () => `${paramName}()`);
2593
2608
  }
2594
2609
  function rewriteLoopBindingRefs(expr, bindings, accessor) {
2595
2610
  const byName = new Map;
2596
2611
  for (const b of bindings)
2597
2612
  byName.set(b.name, b);
2598
2613
  const preprocessed = expandShorthandBindings(expr, new Set(byName.keys()));
2599
- const alt = bindings.map((b) => escapeRegExp(b.name)).join("|");
2600
- const re = new RegExp(`\\b(${alt})\\b`, "g");
2614
+ const alt = bindings.map((b) => escapeIdentifierForRegex(b.name)).join("|");
2615
+ const re = new RegExp(`${ID_BOUNDARY_BEFORE}(${alt})${ID_BOUNDARY_AFTER}`, "gu");
2601
2616
  return replaceInExprContexts(preprocessed, re, (_m, name) => renderLoopBindingAccess(byName.get(name), accessor));
2602
2617
  }
2603
2618
  function expandShorthandBindings(expr, bindingNames) {
@@ -2868,7 +2883,7 @@ function stopAt(...kinds) {
2868
2883
 
2869
2884
  // src/ir-to-client-js/csr-substitute.ts
2870
2885
  import ts4 from "typescript";
2871
- function csrSubstitute(value, env) {
2886
+ function csrSubstitute(value, env, enclosingScope) {
2872
2887
  if (!value || value.trim().length === 0) {
2873
2888
  return { rewritten: value, freeIdentifiers: new Set };
2874
2889
  }
@@ -2876,7 +2891,7 @@ function csrSubstitute(value, env) {
2876
2891
  let current = value;
2877
2892
  let lastFreeIdentifiers = new Set;
2878
2893
  for (let i = 0;i < maxIter; i++) {
2879
- const step = csrSubstituteOnce(current, env);
2894
+ const step = csrSubstituteOnce(current, env, enclosingScope);
2880
2895
  lastFreeIdentifiers = step.freeIdentifiers;
2881
2896
  if (step.rewritten === current)
2882
2897
  break;
@@ -2884,7 +2899,7 @@ function csrSubstitute(value, env) {
2884
2899
  }
2885
2900
  return { rewritten: current, freeIdentifiers: lastFreeIdentifiers };
2886
2901
  }
2887
- function csrSubstituteOnce(value, env) {
2902
+ function csrSubstituteOnce(value, env, enclosingScope) {
2888
2903
  if (!value || value.trim().length === 0) {
2889
2904
  return { rewritten: value, freeIdentifiers: new Set };
2890
2905
  }
@@ -2902,7 +2917,7 @@ function csrSubstituteOnce(value, env) {
2902
2917
  if (boundStack[i].has(name))
2903
2918
  return true;
2904
2919
  }
2905
- return false;
2920
+ return enclosingScope?.isBound(name) ?? false;
2906
2921
  };
2907
2922
  const recordSubstitution = (start, end, sub) => {
2908
2923
  splices.push({ start: start - OFFSET, end: end - OFFSET, text: `(${sub.replacement})` });
@@ -3090,6 +3105,83 @@ function derivesScopeFromSlot(comp) {
3090
3105
  return comp.slotId != null && comp.loopItemRoot !== true;
3091
3106
  }
3092
3107
 
3108
+ // src/scope/binding-scope.ts
3109
+ class BindingScope {
3110
+ frames;
3111
+ static EMPTY = new BindingScope([]);
3112
+ constructor(frames) {
3113
+ this.frames = frames;
3114
+ }
3115
+ enterLoopRow(loop) {
3116
+ const bindings = new Map;
3117
+ if (loop.paramBindings && loop.paramBindings.length > 0) {
3118
+ for (const b of loop.paramBindings)
3119
+ bindings.set(b.name, { source: "destructure" });
3120
+ } else {
3121
+ bindings.set(loop.param, { source: "item" });
3122
+ }
3123
+ if (loop.index != null)
3124
+ bindings.set(loop.index, { source: "index" });
3125
+ for (const name of loop.preamble?.declaredNames ?? [])
3126
+ bindings.set(name, { source: "preamble" });
3127
+ const frame = { kind: "loop-row", bindings };
3128
+ return new BindingScope([frame, ...this.frames]);
3129
+ }
3130
+ enterCallback(params) {
3131
+ const bindings = new Map;
3132
+ for (const name of params)
3133
+ bindings.set(name, { source: "param" });
3134
+ const frame = { kind: "callback", bindings };
3135
+ return new BindingScope([frame, ...this.frames]);
3136
+ }
3137
+ isBound(name) {
3138
+ for (const frame of this.frames) {
3139
+ if (frame.bindings.has(name))
3140
+ return true;
3141
+ }
3142
+ return false;
3143
+ }
3144
+ lookup(name) {
3145
+ for (let depth = 0;depth < this.frames.length; depth++) {
3146
+ const frame = this.frames[depth];
3147
+ const binding = frame.bindings.get(name);
3148
+ if (binding)
3149
+ return { depth, frame, binding };
3150
+ }
3151
+ return null;
3152
+ }
3153
+ boundNames() {
3154
+ if (this.boundNamesCache)
3155
+ return this.boundNamesCache;
3156
+ const names = new Set;
3157
+ for (const frame of this.frames) {
3158
+ for (const name of frame.bindings.keys())
3159
+ names.add(name);
3160
+ }
3161
+ this.boundNamesCache = names;
3162
+ return names;
3163
+ }
3164
+ boundNamesCache;
3165
+ valueBoundNamesCache;
3166
+ valueBoundNames() {
3167
+ if (this.valueBoundNamesCache)
3168
+ return this.valueBoundNamesCache;
3169
+ const names = new Set;
3170
+ for (const frame of this.frames) {
3171
+ for (const [name, binding] of frame.bindings) {
3172
+ if (binding.source === "item" || binding.source === "index" || binding.source === "destructure") {
3173
+ names.add(name);
3174
+ }
3175
+ }
3176
+ }
3177
+ this.valueBoundNamesCache = names;
3178
+ return names;
3179
+ }
3180
+ asShadowPredicate() {
3181
+ return (name) => this.isBound(name);
3182
+ }
3183
+ }
3184
+
3093
3185
  // src/ir-to-client-js/html-template.ts
3094
3186
  function createStringProtector() {
3095
3187
  const strings = [];
@@ -4315,7 +4407,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4315
4407
  const source = templateExpr ?? expr;
4316
4408
  if (!source)
4317
4409
  return source;
4318
- const { rewritten, freeIdentifiers: freeIdentifiers2 } = csrSubstitute(source, env);
4410
+ const { rewritten, freeIdentifiers: freeIdentifiers2 } = csrSubstitute(source, env, opts.scope);
4319
4411
  if (unsafeLocalNames && unsafeLocalNames.size > 0 && setIntersects(freeIdentifiers2, unsafeLocalNames)) {
4320
4412
  return UNSAFE_TEMPLATE_EXPR;
4321
4413
  }
@@ -4457,15 +4549,8 @@ function generateCsrTemplateWithOpts(node, opts) {
4457
4549
  return `\${renderChild('${nameForRegistryRef(node.name)}', ${propsExpr}${keyArg || (slotArg ? ", undefined" : "")}${slotArg})}`;
4458
4550
  }
4459
4551
  case "loop": {
4460
- const boundHere = new Set(opts.loopBoundNames ?? []);
4461
- if (node.paramBindings && node.paramBindings.length > 0) {
4462
- for (const b of node.paramBindings)
4463
- boundHere.add(b.name);
4464
- } else if (!node.param.startsWith("[") && !node.param.startsWith("{")) {
4465
- boundHere.add(node.param);
4466
- }
4467
- if (node.index)
4468
- boundHere.add(node.index);
4552
+ const childScope = (opts.scope ?? BindingScope.EMPTY).enterLoopRow(node);
4553
+ const boundHere = childScope.boundNames();
4469
4554
  const childEnv = {
4470
4555
  ...env,
4471
4556
  substitutions: new Map([...env.substitutions].filter(([name]) => !boundHere.has(name)))
@@ -4474,7 +4559,7 @@ function generateCsrTemplateWithOpts(node, opts) {
4474
4559
  ...opts,
4475
4560
  loopDepth: loopDepth + 1,
4476
4561
  inHoistedChildren: false,
4477
- loopBoundNames: boundHere,
4562
+ scope: childScope,
4478
4563
  csrEnv: childEnv
4479
4564
  });
4480
4565
  let childTemplate = node.children.map(recurseInLoopBody).join("");
@@ -6008,7 +6093,8 @@ function visit(node, ctx, targetComponentName, namedExports) {
6008
6093
  if (!ctx.componentNode) {
6009
6094
  collectAmbientGlobals(node, ctx);
6010
6095
  }
6011
- if (ts9.isVariableStatement(node) && !ctx.componentNode) {
6096
+ const isDeclareStatement = ts9.isVariableStatement(node) && (node.modifiers?.some((m) => m.kind === ts9.SyntaxKind.DeclareKeyword) ?? false);
6097
+ if (ts9.isVariableStatement(node) && !ctx.componentNode && !isDeclareStatement) {
6012
6098
  const isExported = node.modifiers?.some((m) => m.kind === ts9.SyntaxKind.ExportKeyword) ?? false;
6013
6099
  const isLet = (node.declarationList.flags & ts9.NodeFlags.Let) !== 0;
6014
6100
  const isModuleClientDirective = hasLeadingClientDirectiveOnStatement(node, ctx.sourceFile);
@@ -6021,7 +6107,7 @@ function visit(node, ctx, targetComponentName, namedExports) {
6021
6107
  }
6022
6108
  continue;
6023
6109
  }
6024
- if (ts9.isIdentifier(decl.name) && decl.initializer && !isArrowComponentFunction(decl)) {
6110
+ if (ts9.isIdentifier(decl.name) && (decl.initializer || isLet) && !isArrowComponentFunction(decl)) {
6025
6111
  collectConstant(decl, ctx, true, isLet ? "let" : "const", isExported);
6026
6112
  }
6027
6113
  }
@@ -7592,6 +7678,7 @@ function collectConstant(node, ctx, _isModule, declarationKind = "const", isExpo
7592
7678
  value,
7593
7679
  parsed,
7594
7680
  typedValue: typedValue !== value ? typedValue : undefined,
7681
+ typeAnnotation: node.type ? node.type.getText(ctx.sourceFile) : undefined,
7595
7682
  valueBranches,
7596
7683
  declarationKind,
7597
7684
  isExported,
@@ -9328,7 +9415,7 @@ function findReachableNames(primaryRefs, declarations) {
9328
9415
  const reachable = new Set;
9329
9416
  const queue = [];
9330
9417
  for (const name of allNames) {
9331
- if (new RegExp(`\\b${name}\\b`).test(primaryRefs)) {
9418
+ if (identifierPattern(name).test(primaryRefs)) {
9332
9419
  reachable.add(name);
9333
9420
  queue.push(name);
9334
9421
  }
@@ -9337,7 +9424,7 @@ function findReachableNames(primaryRefs, declarations) {
9337
9424
  const current = queue.shift();
9338
9425
  const body = bodyMap.get(current) || "";
9339
9426
  for (const name of allNames) {
9340
- if (!reachable.has(name) && new RegExp(`\\b${name}\\b`).test(body)) {
9427
+ if (!reachable.has(name) && identifierPattern(name).test(body)) {
9341
9428
  reachable.add(name);
9342
9429
  queue.push(name);
9343
9430
  }
@@ -10199,8 +10286,9 @@ function rewriteBarePropRefs2(text, expr, ctx) {
10199
10286
  let propNames = getDestructuredPropNames(ctx);
10200
10287
  if (!propNames)
10201
10288
  return dateLowered === text ? undefined : dateLowered;
10202
- if (ctx.loopParams.size > 0) {
10203
- const filtered = new Set([...propNames].filter((n) => !ctx.loopParams.has(n)));
10289
+ const shadowingNames = ctx.scope.boundNames();
10290
+ if (shadowingNames.size > 0) {
10291
+ const filtered = new Set([...propNames].filter((n) => !shadowingNames.has(n)));
10204
10292
  if (filtered.size === 0)
10205
10293
  return dateLowered === text ? undefined : dateLowered;
10206
10294
  propNames = filtered;
@@ -10276,22 +10364,22 @@ function createTransformContext(analyzer) {
10276
10364
  spreadIdCounter: 0,
10277
10365
  isRoot: true,
10278
10366
  insideComponentChildren: false,
10279
- loopParams: new Set,
10367
+ scope: BindingScope.EMPTY,
10280
10368
  loopDepth: 0,
10281
10369
  patterns: {
10282
10370
  signals: analyzer.signals.map((s) => ({
10283
10371
  getter: s.getter,
10284
- pattern: new RegExp(`\\b${s.getter}\\s*\\(`)
10372
+ pattern: identifierCallPattern(s.getter)
10285
10373
  })),
10286
10374
  memos: analyzer.memos.map((m) => ({
10287
10375
  name: m.name,
10288
- pattern: new RegExp(`\\b${m.name}\\s*\\(`)
10376
+ pattern: identifierCallPattern(m.name)
10289
10377
  })),
10290
- props: analyzer.propsParams.filter((p) => p.name !== "children").map((p) => ({ name: p.name, pattern: new RegExp(`\\b${p.name}\\b`) })),
10378
+ props: analyzer.propsParams.filter((p) => p.name !== "children").map((p) => ({ name: p.name, pattern: identifierPattern(p.name) })),
10291
10379
  constants: analyzer.localConstants.map((c) => ({
10292
10380
  name: c.name,
10293
10381
  value: c.value,
10294
- pattern: new RegExp(`\\b${c.name}\\b`)
10382
+ pattern: identifierPattern(c.name)
10295
10383
  }))
10296
10384
  },
10297
10385
  getJS(node) {
@@ -10353,7 +10441,8 @@ function generateSpreadSlotId(ctx) {
10353
10441
  return `Spread_${ctx.spreadIdCounter++}`;
10354
10442
  }
10355
10443
  function makeBindingEnv(ctx) {
10356
- const loopKey = ctx.loopParams.size === 0 ? "" : Array.from(ctx.loopParams).sort().join("\x00");
10444
+ const boundNames = ctx.scope.valueBoundNames();
10445
+ const loopKey = boundNames.size === 0 ? "" : Array.from(boundNames).sort().join("\x00");
10357
10446
  if (ctx._bindingEnv && ctx._bindingEnvLoopKey === loopKey) {
10358
10447
  return ctx._bindingEnv;
10359
10448
  }
@@ -10368,7 +10457,7 @@ function makeBindingEnv(ctx) {
10368
10457
  localFunctions: a.localFunctions,
10369
10458
  imports: a.imports,
10370
10459
  ambientGlobals: a.ambientGlobals,
10371
- loopParams: new Set(ctx.loopParams),
10460
+ loopParams: boundNames,
10372
10461
  checker: a.checker
10373
10462
  };
10374
10463
  ctx._bindingEnv = env;
@@ -11061,7 +11150,8 @@ function transformExpressionInner(expr, ctx, node, isClientOnly) {
11061
11150
  freeRefs
11062
11151
  };
11063
11152
  const reactive = isReactiveExpression(exprText, ctx, expr) || isReactiveOrigin(origin);
11064
- const refsLoopParam = ctx.loopParams.size > 0 && Array.from(ctx.loopParams).some((p) => new RegExp(`\\b${p}\\b`).test(exprText));
11153
+ const scopeValueNames = ctx.scope.valueBoundNames();
11154
+ const refsLoopParam = scopeValueNames.size > 0 && Array.from(scopeValueNames).some((p) => identifierPattern(p).test(exprText));
11065
11155
  const callsReactive = exprCallsReactiveGetters(expr, ctx);
11066
11156
  const hasCalls = exprHasFunctionCalls(expr);
11067
11157
  const needsSlot = reactive || isClientOnly || refsLoopParam || callsReactive || hasCalls;
@@ -11096,7 +11186,7 @@ function transformJsxFunctionCall(callExpr, jsxFunc, ctx, _isClientOnly) {
11096
11186
  const substitutedGetJS = (node) => {
11097
11187
  let text = baseGetJS(node);
11098
11188
  for (const [paramName, argExpr] of substitutions) {
11099
- text = text.replace(new RegExp(`\\b${paramName}\\b`, "g"), argExpr);
11189
+ text = text.replace(identifierPattern(paramName, "g"), () => argExpr);
11100
11190
  }
11101
11191
  return text;
11102
11192
  };
@@ -11138,7 +11228,7 @@ function transformMultiReturnJsxFunctionCall(callExpr, info, ctx) {
11138
11228
  const substitutedGetJS = (node) => {
11139
11229
  let text = baseGetJS(node);
11140
11230
  for (const [paramName, argExpr] of substitutions) {
11141
- text = text.replace(new RegExp(`\\b${paramName}\\b`, "g"), argExpr);
11231
+ text = text.replace(identifierPattern(paramName, "g"), () => argExpr);
11142
11232
  }
11143
11233
  return text;
11144
11234
  };
@@ -12149,7 +12239,7 @@ function extractItemConditionalKey(cond) {
12149
12239
  return a ?? b;
12150
12240
  }
12151
12241
  function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
12152
- const isNested = ctx.loopParams.size > 0;
12242
+ const isNested = ctx.scope.valueBoundNames().size > 0;
12153
12243
  const diagCountAtEntry = ctx.analyzer.errors.length;
12154
12244
  const depth = ctx.loopDepth;
12155
12245
  const propAccess = node.expression;
@@ -12308,14 +12398,8 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
12308
12398
  indexType = secondParam.type.getText(ctx.sourceFile);
12309
12399
  }
12310
12400
  }
12311
- if (paramBindings) {
12312
- for (const b of paramBindings)
12313
- ctx.loopParams.add(b.name);
12314
- } else {
12315
- ctx.loopParams.add(param);
12316
- }
12317
- if (index)
12318
- ctx.loopParams.add(index);
12401
+ const savedScope = ctx.scope;
12402
+ ctx.scope = ctx.scope.enterLoopRow({ param, index, paramBindings });
12319
12403
  ctx.loopDepth++;
12320
12404
  const tryTransformRenderableBody = (expr) => {
12321
12405
  if (!ts12.isBinaryExpression(expr))
@@ -12376,6 +12460,24 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
12376
12460
  }
12377
12461
  }
12378
12462
  const returnStmt = children.length === 0 ? body.statements.find((s) => ts12.isReturnStatement(s) && s.expression != null) : undefined;
12463
+ let rowScopeBeforePreamble = null;
12464
+ if (returnStmt) {
12465
+ const preambleNames = new Set;
12466
+ for (const stmt of body.statements) {
12467
+ if (stmt === returnStmt)
12468
+ break;
12469
+ collectPreambleDeclaredNames(stmt, preambleNames);
12470
+ }
12471
+ if (preambleNames.size > 0) {
12472
+ rowScopeBeforePreamble = ctx.scope;
12473
+ ctx.scope = savedScope.enterLoopRow({
12474
+ param,
12475
+ index,
12476
+ paramBindings,
12477
+ preamble: { declaredNames: [...preambleNames] }
12478
+ });
12479
+ }
12480
+ }
12379
12481
  if (returnStmt && returnStmt.expression) {
12380
12482
  let returnExpr = returnStmt.expression;
12381
12483
  while (ts12.isParenthesizedExpression(returnExpr)) {
@@ -12445,6 +12547,9 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
12445
12547
  }
12446
12548
  }
12447
12549
  }
12550
+ if (rowScopeBeforePreamble) {
12551
+ ctx.scope = rowScopeBeforePreamble;
12552
+ }
12448
12553
  if (method === "flatMap" && children.length === 0 && !flatMapProjectionCall(body)) {
12449
12554
  flatMapCallback = buildFlatMapCallback(callback, body, ctx);
12450
12555
  }
@@ -12473,14 +12578,7 @@ function transformMapCall(node, ctx, isClientOnly = false, method = "map") {
12473
12578
  }
12474
12579
  }));
12475
12580
  }
12476
- if (paramBindings) {
12477
- for (const b of paramBindings)
12478
- ctx.loopParams.delete(b.name);
12479
- } else {
12480
- ctx.loopParams.delete(param);
12481
- }
12482
- if (index)
12483
- ctx.loopParams.delete(index);
12581
+ ctx.scope = savedScope;
12484
12582
  ctx.loopDepth--;
12485
12583
  }
12486
12584
  if (children.length === 0 && !flatMapCallback) {
@@ -13227,7 +13325,7 @@ function parseTemplateLiteral(expr, ctx) {
13227
13325
  }
13228
13326
  function tryResolveTemplateSpanFromConst(expr, ctx) {
13229
13327
  if (ts12.isIdentifier(expr)) {
13230
- if (ctx.loopParams.has(expr.text))
13328
+ if (ctx.scope.isBound(expr.text))
13231
13329
  return null;
13232
13330
  const constInfo = findLocalConst(expr.text, ctx.analyzer);
13233
13331
  if (!constInfo)
@@ -13243,7 +13341,7 @@ function tryResolveTemplateSpanFromConst(expr, ctx) {
13243
13341
  if (ts12.isElementAccessExpression(expr)) {
13244
13342
  if (!ts12.isIdentifier(expr.expression))
13245
13343
  return null;
13246
- if (ctx.loopParams.has(expr.expression.text))
13344
+ if (ctx.scope.isBound(expr.expression.text))
13247
13345
  return null;
13248
13346
  const constInfo = findLocalConst(expr.expression.text, ctx.analyzer);
13249
13347
  if (!constInfo)
@@ -13322,7 +13420,7 @@ function hasDynamicTagBinding(name, sourceFile) {
13322
13420
  return found;
13323
13421
  }
13324
13422
  function tryResolveIdentifierAsTemplateLiteral(ident, ctx) {
13325
- if (ctx.loopParams.has(ident.text))
13423
+ if (ctx.scope.isBound(ident.text))
13326
13424
  return null;
13327
13425
  const constInfo = findLocalConst(ident.text, ctx.analyzer);
13328
13426
  if (!constInfo)
@@ -13671,10 +13769,11 @@ function isSignalOrMemoArray(array, ctx) {
13671
13769
  return false;
13672
13770
  }
13673
13771
  function referencesLoopParam(expr, ctx) {
13674
- if (ctx.loopParams.size === 0)
13772
+ const boundNames = ctx.scope.valueBoundNames();
13773
+ if (boundNames.size === 0)
13675
13774
  return false;
13676
- for (const p of ctx.loopParams) {
13677
- if (new RegExp(`\\b${p}\\b`).test(expr))
13775
+ for (const p of boundNames) {
13776
+ if (identifierPattern(p).test(expr))
13678
13777
  return true;
13679
13778
  }
13680
13779
  return false;
@@ -13753,9 +13852,10 @@ function hasReactiveAttributes(attrs, ctx) {
13753
13852
  if (isSignalOrMemoReference(valueToCheck, ctx) || isPropsReference(valueToCheck, ctx)) {
13754
13853
  return true;
13755
13854
  }
13756
- if (ctx.loopParams.size > 0) {
13757
- for (const p of ctx.loopParams) {
13758
- if (new RegExp(`\\b${p}\\b`).test(valueToCheck))
13855
+ const scopeValueNames = ctx.scope.valueBoundNames();
13856
+ if (scopeValueNames.size > 0) {
13857
+ for (const p of scopeValueNames) {
13858
+ if (identifierPattern(p).test(valueToCheck))
13759
13859
  return true;
13760
13860
  }
13761
13861
  }
@@ -13961,18 +14061,22 @@ function buildIfStatementChain(analyzer, ctx, opts) {
13961
14061
  }
13962
14062
 
13963
14063
  // src/ir-to-client-js/prop-handling.ts
13964
- function expandDynamicPropValue(value, ctx) {
14064
+ function expandDynamicPropValue(value, ctx, scope) {
13965
14065
  const trimmedValue = value.trim();
14066
+ if (scope?.isBound(trimmedValue))
14067
+ return value;
13966
14068
  const constant = ctx.localConstants.find((c) => c.name === trimmedValue);
13967
14069
  if (constant && constant.value) {
13968
14070
  return constant.value;
13969
14071
  }
13970
14072
  return value;
13971
14073
  }
13972
- function expandConstantForReactivity(expr, ctx, originalFreeIds) {
14074
+ function expandConstantForReactivity(expr, ctx, originalFreeIds, scope) {
13973
14075
  if (ctx.propsObjectName)
13974
14076
  return { expr, freeIds: originalFreeIds };
13975
14077
  const trimmedValue = expr.trim();
14078
+ if (scope?.isBound(trimmedValue))
14079
+ return { expr, freeIds: originalFreeIds };
13976
14080
  const constant = ctx.localConstants.find((c) => c.name === trimmedValue);
13977
14081
  if (constant && constant.value) {
13978
14082
  return { expr: constant.value, freeIds: constant.freeIdentifiers };
@@ -14008,6 +14112,16 @@ function getControlledPropName(signal, propsParams, propsObjectName = null) {
14008
14112
  }
14009
14113
 
14010
14114
  // src/ir-to-client-js/reactivity.ts
14115
+ function buildLoopRowScope(loopParam, loopParamBindings, preambleNames, loopIndex) {
14116
+ if (!loopParam)
14117
+ return;
14118
+ return BindingScope.EMPTY.enterLoopRow({
14119
+ param: loopParam,
14120
+ paramBindings: loopParamBindings,
14121
+ index: loopIndex,
14122
+ preamble: preambleNames && preambleNames.size > 0 ? { declaredNames: [...preambleNames] } : undefined
14123
+ });
14124
+ }
14011
14125
  function decideWrapFromAstFlags(node) {
14012
14126
  if (node.origin && isReactiveOrigin(node.origin)) {
14013
14127
  return { wrap: true, reason: "proven-reactive" };
@@ -14036,12 +14150,12 @@ function decideWrapForChildProp(expandedValue, ctx, prop) {
14036
14150
  }
14037
14151
  function needsEffectWrapper(expr, ctx, freeIdentifiers2) {
14038
14152
  for (const signal of ctx.signals) {
14039
- if (new RegExp(`\\b${signal.getter}\\s*\\(`).test(expr)) {
14153
+ if (identifierCallPattern(signal.getter).test(expr)) {
14040
14154
  return true;
14041
14155
  }
14042
14156
  }
14043
14157
  for (const memo of ctx.memos) {
14044
- if (new RegExp(`\\b${memo.name}\\s*\\(`).test(expr)) {
14158
+ if (identifierCallPattern(memo.name).test(expr)) {
14045
14159
  return true;
14046
14160
  }
14047
14161
  }
@@ -14231,8 +14345,9 @@ function traverseForComponents(node, components, skipConditionals = false) {
14231
14345
  }
14232
14346
  });
14233
14347
  }
14234
- function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false) {
14348
+ function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false, preambleNames, loopIndex) {
14235
14349
  const texts = [];
14350
+ const scope = buildLoopRowScope(loopParam, loopParamBindings, preambleNames, loopIndex);
14236
14351
  walkIR(node, false, {
14237
14352
  ...stopAt("loop", "async", "ifStatement"),
14238
14353
  expression: ({ node: n, scope: insideConditional }) => {
@@ -14241,7 +14356,7 @@ function collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings,
14241
14356
  if (n.preambleRegion)
14242
14357
  return;
14243
14358
  const originFreeIds = freeIdsFromRefs(n.origin?.freeRefs);
14244
- const expanded = expandConstantForReactivity(n.expr, ctx, originFreeIds);
14359
+ const expanded = expandConstantForReactivity(n.expr, ctx, originFreeIds, scope);
14245
14360
  const reactive = classifyReactivity(expanded.expr, ctx, loopParam, loopParamBindings, expanded.freeIds).kind !== "none" || decideWrapFromAstFlags(n).wrap;
14246
14361
  if (!reactive)
14247
14362
  return;
@@ -14265,8 +14380,9 @@ function anyNameIn(names, set) {
14265
14380
  return true;
14266
14381
  return false;
14267
14382
  }
14268
- function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false, preambleNames) {
14383
+ function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, stopAtReactiveConditionals = false, preambleNames, loopIndex) {
14269
14384
  const attrs = [];
14385
+ const scope = buildLoopRowScope(loopParam, loopParamBindings, preambleNames, loopIndex);
14270
14386
  traverseElements(node, (el) => {
14271
14387
  if (el.slotId) {
14272
14388
  for (const attr of el.attrs) {
@@ -14279,7 +14395,7 @@ function collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings,
14279
14395
  const valueStr = attrValueToString(attr.value);
14280
14396
  if (!valueStr)
14281
14397
  continue;
14282
- const expanded = expandConstantForReactivity(valueStr, ctx, attr.freeIdentifiers);
14398
+ const expanded = expandConstantForReactivity(valueStr, ctx, attr.freeIdentifiers, scope);
14283
14399
  const readsPreamble = preambleNames !== undefined && preambleNames.size > 0 && anyNameIn(expanded.freeIds ?? extractFreeIdentifiersFromText(expanded.expr), preambleNames);
14284
14400
  const reactive = classifyReactivity(expanded.expr, ctx, loopParam, loopParamBindings, expanded.freeIds).kind !== "none" || readsPreamble || attr.callsReactiveGetters || attr.hasFunctionCalls;
14285
14401
  if (!attr.clientOnly && !reactive)
@@ -14584,13 +14700,13 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx, options)
14584
14700
  const emitDepth = fixedDepth ?? scope.depth + 1;
14585
14701
  const loopParamsForTemplate = outerLoopParam ? [outerLoopParam, { param: n.param, bindings: n.paramBindings }] : undefined;
14586
14702
  const template = n.children.map((c) => irToPlaceholderTemplate(c, undefined, emitDepth, loopParamsForTemplate)).join("");
14587
- const refsOuter = outerLoopParam ? new RegExp(`\\b${outerLoopParam}\\b`).test(n.array) : false;
14703
+ const refsOuter = outerLoopParam ? identifierPattern(outerLoopParam).test(n.array) : false;
14588
14704
  const bindings = emptyLoopChildBindings();
14589
14705
  const innerPreambleNames = preambleNamesOf(n);
14590
14706
  if (ctx) {
14591
14707
  for (const child of n.children) {
14592
- bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, n.param, n.paramBindings));
14593
- bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, n.param, n.paramBindings, false, innerPreambleNames));
14708
+ bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, n.param, n.paramBindings, false, innerPreambleNames, n.index));
14709
+ bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, n.param, n.paramBindings, false, innerPreambleNames, n.index));
14594
14710
  bindings.refs.push(...collectLoopChildRefs(child));
14595
14711
  }
14596
14712
  }
@@ -14617,7 +14733,7 @@ function collectInnerLoops(nodes, siblingOffsets, outerLoopParam, ctx, options)
14617
14733
  bindings.events.push(...collectLoopChildEventsWithNesting(child));
14618
14734
  }
14619
14735
  if (ctx) {
14620
- bindings.conditionals.push(...collectLoopChildConditionals({ type: "fragment", children: n.children, loc: n.loc }, ctx, siblingOffsets, n.param, n.paramBindings));
14736
+ bindings.conditionals.push(...collectLoopChildConditionals({ type: "fragment", children: n.children, loc: n.loc }, ctx, siblingOffsets, n.param, n.paramBindings, innerPreambleNames, n.index));
14621
14737
  }
14622
14738
  }
14623
14739
  result.push({
@@ -14813,7 +14929,7 @@ function collectElements(node, ctx, siblingOffsets, insideConditional = false) {
14813
14929
  return;
14814
14930
  const projectionInner = l.method === "flatMap" && l.children.length === 1 && l.children[0].type === "loop" ? l.children[0] : undefined;
14815
14931
  const childHandlers = [];
14816
- const bindings = projectionInner ? emptyLoopChildBindings() : collectLoopChildBindings(l.children, ctx, siblingOffsets, l.param, l.paramBindings, preambleNamesOf(l));
14932
+ const bindings = projectionInner ? emptyLoopChildBindings() : collectLoopChildBindings(l.children, ctx, siblingOffsets, l.param, l.paramBindings, preambleNamesOf(l), l.index);
14817
14933
  if (!projectionInner) {
14818
14934
  for (const child of l.children) {
14819
14935
  childHandlers.push(...collectEventHandlersFromIR(child));
@@ -15068,7 +15184,7 @@ function collectBranchLoops(node, ctx, siblingOffsets) {
15068
15184
  } else {
15069
15185
  childTemplate = n.children.map((c) => irToHtmlTemplate(c, undefined, 0, branchLoopParamSpec)).join("");
15070
15186
  }
15071
- const branchBindings = ctx && !projectionInner ? collectLoopChildBindings(n.children, ctx, siblingOffsets, n.param, n.paramBindings, preambleNamesOf(n)) : emptyLoopChildBindings();
15187
+ const branchBindings = ctx && !projectionInner ? collectLoopChildBindings(n.children, ctx, siblingOffsets, n.param, n.paramBindings, preambleNamesOf(n), n.index) : emptyLoopChildBindings();
15072
15188
  loops.push({
15073
15189
  kind: "branch",
15074
15190
  array: n.array,
@@ -15154,19 +15270,20 @@ function preambleNamesOf(loop) {
15154
15270
  const declared = loop.preamble?.declaredNames;
15155
15271
  return declared && declared.length > 0 ? new Set(declared) : undefined;
15156
15272
  }
15157
- function collectLoopChildBindings(children, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames) {
15273
+ function collectLoopChildBindings(children, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex) {
15158
15274
  const bindings = emptyLoopChildBindings();
15159
15275
  for (const child of children) {
15160
15276
  bindings.events.push(...collectLoopChildEventsWithNesting(child));
15161
- bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, loopParam, loopParamBindings, true, preambleNames));
15162
- bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, loopParam, loopParamBindings, true));
15277
+ bindings.reactiveAttrs.push(...collectLoopChildReactiveAttrs(child, ctx, loopParam, loopParamBindings, true, preambleNames, loopIndex));
15278
+ bindings.reactiveTexts.push(...collectLoopChildReactiveTexts(child, ctx, loopParam, loopParamBindings, true, preambleNames, loopIndex));
15163
15279
  bindings.refs.push(...collectLoopChildRefs(child));
15164
- bindings.conditionals.push(...collectLoopChildConditionals(child, ctx, siblingOffsets, loopParam, loopParamBindings));
15280
+ bindings.conditionals.push(...collectLoopChildConditionals(child, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex));
15165
15281
  }
15166
15282
  return bindings;
15167
15283
  }
15168
- function collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loopParamBindings) {
15284
+ function collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex) {
15169
15285
  const conditionals = [];
15286
+ const scope = buildLoopRowScope(loopParam, loopParamBindings, preambleNames, loopIndex);
15170
15287
  const refsAnyBindingViaFreeIds = (freeIds) => {
15171
15288
  if (loopParamBindings && loopParamBindings.length > 0) {
15172
15289
  for (const b of loopParamBindings) {
@@ -15186,7 +15303,7 @@ function collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loop
15186
15303
  const refsLoopParamInSource = refsAnyBindingViaFreeIds(sourceFreeIds);
15187
15304
  if (!n.reactive && !refsLoopParamInSource)
15188
15305
  return;
15189
- const expanded = expandConstantForReactivity(n.condition, ctx, sourceFreeIds);
15306
+ const expanded = expandConstantForReactivity(n.condition, ctx, sourceFreeIds, scope);
15190
15307
  if (classifyReactivity(expanded.expr, ctx, loopParam, loopParamBindings, expanded.freeIds).kind === "none")
15191
15308
  return;
15192
15309
  const loopParamsForCond = loopParam ? [{ param: loopParam, bindings: loopParamBindings }] : undefined;
@@ -15197,23 +15314,23 @@ function collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loop
15197
15314
  condition: expanded.expr,
15198
15315
  whenTrueHtml,
15199
15316
  whenFalseHtml,
15200
- whenTrue: summarizeLoopChildBranch(n.whenTrue, ctx, siblingOffsets, loopParam, loopParamBindings),
15201
- whenFalse: summarizeLoopChildBranch(n.whenFalse, ctx, siblingOffsets, loopParam, loopParamBindings),
15317
+ whenTrue: summarizeLoopChildBranch(n.whenTrue, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex),
15318
+ whenFalse: summarizeLoopChildBranch(n.whenFalse, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex),
15202
15319
  ...expanded.freeIds !== undefined && { conditionFreeIdentifiers: expanded.freeIds }
15203
15320
  });
15204
15321
  }
15205
15322
  });
15206
15323
  return conditionals;
15207
15324
  }
15208
- function summarizeLoopChildBranch(node, ctx, siblingOffsets, loopParam, loopParamBindings) {
15325
+ function summarizeLoopChildBranch(node, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex) {
15209
15326
  const inner = collectInnerLoops([node], siblingOffsets, loopParam, ctx, branchInnerLoopOptions);
15210
15327
  return {
15211
15328
  childComponents: collectConditionalBranchChildComponents(node),
15212
15329
  innerLoops: inner.length > 0 ? inner : undefined,
15213
- conditionals: collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loopParamBindings),
15330
+ conditionals: collectLoopChildConditionals(node, ctx, siblingOffsets, loopParam, loopParamBindings, preambleNames, loopIndex),
15214
15331
  events: collectConditionalBranchEvents(node),
15215
- reactiveAttrs: collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, true),
15216
- reactiveTexts: node.type === "expression" && node.hasFunctionCalls ? [] : collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, true)
15332
+ reactiveAttrs: collectLoopChildReactiveAttrs(node, ctx, loopParam, loopParamBindings, true, preambleNames, loopIndex),
15333
+ reactiveTexts: node.type === "expression" && node.hasFunctionCalls ? [] : collectLoopChildReactiveTexts(node, ctx, loopParam, loopParamBindings, true, preambleNames, loopIndex)
15217
15334
  };
15218
15335
  }
15219
15336
 
@@ -15785,7 +15902,7 @@ var MODULE_CONSTANTS_PLACEHOLDER = "/* __MODULE_LEVEL_CONSTANTS__ */";
15785
15902
  function detectUsedImports(code) {
15786
15903
  const used = new Set;
15787
15904
  for (const name of RUNTIME_IMPORT_CANDIDATES) {
15788
- if (new RegExp(`\\b${name}\\s*\\(`).test(code)) {
15905
+ if (identifierCallPattern(name).test(code)) {
15789
15906
  used.add(name);
15790
15907
  }
15791
15908
  }
@@ -16204,7 +16321,7 @@ function containsAnyIdentifier(node, names) {
16204
16321
  function scanRefsByName(text, bindings) {
16205
16322
  const result = new Map;
16206
16323
  for (const name of bindings.keys()) {
16207
- const re = new RegExp(`\\b${name}\\b`);
16324
+ const re = identifierPattern(name);
16208
16325
  if (re.test(text))
16209
16326
  result.set(name, []);
16210
16327
  }
@@ -18898,7 +19015,7 @@ function buildStaticArrayDelegationPlan(elem, profileComponentName) {
18898
19015
  function buildKeyedOrIndexLookup(args) {
18899
19016
  const hasBindings = (args.paramBindings?.length ?? 0) > 0;
18900
19017
  if (args.key !== null) {
18901
- const keyWithItem = hasBindings ? substituteLoopBindings(args.key, args.paramBindings, "item") : args.key.replace(new RegExp(`\\b${args.param}\\b`, "g"), "item");
19018
+ const keyWithItem = hasBindings ? substituteLoopBindings(args.key, args.paramBindings, "item") : args.key.replace(identifierPattern(args.param, "g"), "item");
18902
19019
  return {
18903
19020
  kind: "keyed",
18904
19021
  arrayExpr: args.array,
@@ -21104,7 +21221,7 @@ function emitKeyedLookup(ls, ev, handlerCall, lookup) {
21104
21221
  }
21105
21222
  for (const nested of ev.nestedLoops) {
21106
21223
  const rawKey = nested.key ?? "";
21107
- const innerKeyExpr = nested.paramBindings && nested.paramBindings.length > 0 ? substituteLoopBindings(rawKey, nested.paramBindings, "item") : rawKey.replace(new RegExp(`\\b${nested.param}\\b`, "g"), "item");
21224
+ const innerKeyExpr = nested.paramBindings && nested.paramBindings.length > 0 ? substituteLoopBindings(rawKey, nested.paramBindings, "item") : rawKey.replace(identifierPattern(nested.param, "g"), "item");
21108
21225
  const outerRef = hasBindings ? "__bfLoopItem" : param;
21109
21226
  ls.push(` const ${nested.param} = ${outerRef} && ${nested.array}.find(item => String(${innerKeyExpr}) === innerKey${nested.depth})`);
21110
21227
  }
@@ -21798,7 +21915,7 @@ function rewritePropsObjectRef(code, propsObjectName) {
21798
21915
  const srcPropsName = propsObjectName ?? "props";
21799
21916
  if (srcPropsName === PROPS_PARAM)
21800
21917
  return code;
21801
- if (!new RegExp(`\\b${srcPropsName}\\b`).test(code))
21918
+ if (!identifierPattern(srcPropsName).test(code))
21802
21919
  return code;
21803
21920
  const sourceFile = ts19.createSourceFile("init-body.ts", code, ts19.ScriptTarget.Latest, true, ts19.ScriptKind.TS);
21804
21921
  const spans = [];
@@ -24543,7 +24660,7 @@ class JsxAdapter extends BaseAdapter {
24543
24660
  lines.push(` const ${signal.getter} = () => ${initialValue}`);
24544
24661
  }
24545
24662
  if (signal.setter) {
24546
- const setterUsed = new RegExp(`\\b${signal.setter}\\b`).test(setterRefText);
24663
+ const setterUsed = identifierPattern(signal.setter).test(setterRefText);
24547
24664
  if (setterUsed) {
24548
24665
  lines.push(` const ${signal.setter} = (..._args: any[]) => {}`);
24549
24666
  }
@@ -24563,7 +24680,7 @@ class JsxAdapter extends BaseAdapter {
24563
24680
  continue;
24564
24681
  const keyword = constant.declarationKind ?? "const";
24565
24682
  if (!constant.value) {
24566
- const typeAnnotation = preserveTypes && constant.type ? `: ${constant.type.raw}` : "";
24683
+ const typeAnnotation = preserveTypes && (constant.typeAnnotation ?? constant.type) ? `: ${constant.typeAnnotation ?? constant.type?.raw}` : "";
24567
24684
  lines.push(` ${keyword} ${constant.name}${typeAnnotation}`);
24568
24685
  continue;
24569
24686
  }
@@ -24573,7 +24690,8 @@ class JsxAdapter extends BaseAdapter {
24573
24690
  if (!reachable.has(constant.name))
24574
24691
  continue;
24575
24692
  const constValue = preserveTypes ? constant.typedValue ?? constant.value : constant.value;
24576
- lines.push(` ${keyword} ${constant.name} = ${constValue}`);
24693
+ const letTypeAnnotation = preserveTypes && keyword === "let" && constant.typeAnnotation ? `: ${constant.typeAnnotation}` : "";
24694
+ lines.push(` ${keyword} ${constant.name}${letTypeAnnotation} = ${constValue}`);
24577
24695
  }
24578
24696
  for (const func of localFunctions) {
24579
24697
  if (moduleScopeNames.has(func.name))
@@ -24680,7 +24798,8 @@ class JsxAdapter extends BaseAdapter {
24680
24798
  const keyword = c.declarationKind ?? "const";
24681
24799
  const exportKw = c.isExported ? "export " : "";
24682
24800
  if (!c.value) {
24683
- entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}` });
24801
+ const typeAnnotation = preserveTypes && (c.typeAnnotation ?? c.type) ? `: ${c.typeAnnotation ?? c.type?.raw}` : "";
24802
+ entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}${typeAnnotation}` });
24684
24803
  continue;
24685
24804
  }
24686
24805
  const trimmed = c.value.trim();
@@ -24689,7 +24808,8 @@ class JsxAdapter extends BaseAdapter {
24689
24808
  if (c.isExported && /^createContext\b/.test(trimmed))
24690
24809
  continue;
24691
24810
  const value = preserveTypes ? c.typedValue ?? c.value : c.value;
24692
- entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name} = ${value}` });
24811
+ const letTypeAnnotation = preserveTypes && keyword === "let" && c.typeAnnotation ? `: ${c.typeAnnotation}` : "";
24812
+ entries.push({ line: c.loc.start.line, text: `${exportKw}${keyword} ${c.name}${letTypeAnnotation} = ${value}` });
24693
24813
  }
24694
24814
  for (const f of ir.metadata.localFunctions) {
24695
24815
  if (!f.isModule || !moduleNames.has(f.name))
@@ -24738,6 +24858,7 @@ class JsxAdapter extends BaseAdapter {
24738
24858
  }
24739
24859
 
24740
24860
  // src/adapters/template-imports.ts
24861
+ import ts25 from "typescript";
24741
24862
  var CLIENT_PACKAGE_SOURCES = new Set([
24742
24863
  "@barefootjs/client",
24743
24864
  "@barefootjs/client/runtime"
@@ -24784,6 +24905,44 @@ function rewriteImportsForTemplate(imports, shimSource, rewriteRelative) {
24784
24905
  function specKey(s) {
24785
24906
  return `${s.isDefault ? "d" : ""}${s.isNamespace ? "n" : ""}:${s.name}:${s.alias ?? ""}`;
24786
24907
  }
24908
+ function rewriteDynamicImportsInSource(sourceText, rewriteRelative) {
24909
+ if (!sourceText.includes("import"))
24910
+ return sourceText;
24911
+ const sf = ts25.createSourceFile("bf-template-fragment.tsx", sourceText, ts25.ScriptTarget.Latest, false, ts25.ScriptKind.TSX);
24912
+ const edits = [];
24913
+ const visit3 = (node) => {
24914
+ if (ts25.isCallExpression(node) && node.expression.kind === ts25.SyntaxKind.ImportKeyword && node.arguments.length > 0 && ts25.isStringLiteralLike(node.arguments[0])) {
24915
+ collect(node.arguments[0]);
24916
+ }
24917
+ if (ts25.isImportTypeNode(node) && ts25.isLiteralTypeNode(node.argument)) {
24918
+ const literal = node.argument.literal;
24919
+ if (ts25.isStringLiteralLike(literal))
24920
+ collect(literal);
24921
+ }
24922
+ ts25.forEachChild(node, visit3);
24923
+ };
24924
+ const collect = (literal) => {
24925
+ const specifier = literal.text;
24926
+ if (!specifier.startsWith("."))
24927
+ return;
24928
+ const next = rewriteRelative(specifier);
24929
+ if (next === specifier)
24930
+ return;
24931
+ edits.push({
24932
+ start: literal.getStart(sf),
24933
+ end: literal.getEnd(),
24934
+ text: `'${next}'`
24935
+ });
24936
+ };
24937
+ ts25.forEachChild(sf, visit3);
24938
+ if (edits.length === 0)
24939
+ return sourceText;
24940
+ let out = sourceText;
24941
+ for (const edit of edits.sort((a, b) => b.start - a.start)) {
24942
+ out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
24943
+ }
24944
+ return out;
24945
+ }
24787
24946
 
24788
24947
  // src/adapters/test-adapter.ts
24789
24948
  class TestAdapter extends JsxAdapter {
@@ -25570,7 +25729,7 @@ function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
25570
25729
  };
25571
25730
  }
25572
25731
  // src/combine-client-js.ts
25573
- import ts25 from "typescript";
25732
+ import ts26 from "typescript";
25574
25733
  var CHILD_PLACEHOLDER_RE = /import '\/\* @bf-child:(\w+) \*\/'/g;
25575
25734
  function combineParentChildClientJs(files) {
25576
25735
  const result = new Map;
@@ -25627,10 +25786,10 @@ function combineParentChildClientJs(files) {
25627
25786
  return result;
25628
25787
  }
25629
25788
  function parseAndMerge(content, importsBySource, otherImports, codeSections) {
25630
- const sourceFile = ts25.createSourceFile("combine.js", content, ts25.ScriptTarget.Latest, false, ts25.ScriptKind.JS);
25789
+ const sourceFile = ts26.createSourceFile("combine.js", content, ts26.ScriptTarget.Latest, false, ts26.ScriptKind.JS);
25631
25790
  const importSpans = [];
25632
25791
  for (const stmt of sourceFile.statements) {
25633
- if (!ts25.isImportDeclaration(stmt))
25792
+ if (!ts26.isImportDeclaration(stmt))
25634
25793
  continue;
25635
25794
  const start = stmt.getStart(sourceFile);
25636
25795
  const end = stmt.getEnd();
@@ -25640,8 +25799,8 @@ function parseAndMerge(content, importsBySource, otherImports, codeSections) {
25640
25799
  continue;
25641
25800
  const clause = stmt.importClause;
25642
25801
  const bindings = clause?.namedBindings;
25643
- const specifier = ts25.isStringLiteral(stmt.moduleSpecifier) ? stmt.moduleSpecifier.text : "";
25644
- if (clause && !clause.name && bindings && ts25.isNamedImports(bindings)) {
25802
+ const specifier = ts26.isStringLiteral(stmt.moduleSpecifier) ? stmt.moduleSpecifier.text : "";
25803
+ if (clause && !clause.name && bindings && ts26.isNamedImports(bindings)) {
25645
25804
  if (!importsBySource.has(specifier)) {
25646
25805
  importsBySource.set(specifier, new Set);
25647
25806
  }
@@ -25808,7 +25967,7 @@ function escapeRe(s) {
25808
25967
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25809
25968
  }
25810
25969
  // src/debug.ts
25811
- import ts26 from "typescript";
25970
+ import ts27 from "typescript";
25812
25971
  function buildComponentGraph(source, filePath, componentName) {
25813
25972
  const ctx = analyzeComponent(source, filePath, componentName);
25814
25973
  if (!ctx.jsxReturn) {
@@ -27093,7 +27252,7 @@ function truncateExpr(expr, max = 40) {
27093
27252
  function exprReadsPropMember(expr, propsObjectName) {
27094
27253
  let sf;
27095
27254
  try {
27096
- sf = ts26.createSourceFile("__attr.tsx", `(${expr})`, ts26.ScriptTarget.Latest, true, ts26.ScriptKind.TSX);
27255
+ sf = ts27.createSourceFile("__attr.tsx", `(${expr})`, ts27.ScriptTarget.Latest, true, ts27.ScriptKind.TSX);
27097
27256
  } catch {
27098
27257
  return false;
27099
27258
  }
@@ -27101,11 +27260,11 @@ function exprReadsPropMember(expr, propsObjectName) {
27101
27260
  const visit3 = (n) => {
27102
27261
  if (found)
27103
27262
  return;
27104
- if (ts26.isPropertyAccessExpression(n) && ts26.isIdentifier(n.expression) && n.expression.text === propsObjectName && n.name.text !== "children") {
27263
+ if (ts27.isPropertyAccessExpression(n) && ts27.isIdentifier(n.expression) && n.expression.text === propsObjectName && n.name.text !== "children") {
27105
27264
  found = true;
27106
27265
  return;
27107
27266
  }
27108
- ts26.forEachChild(n, visit3);
27267
+ ts27.forEachChild(n, visit3);
27109
27268
  };
27110
27269
  visit3(sf);
27111
27270
  return found;
@@ -27135,12 +27294,12 @@ function attrValueToString2(value) {
27135
27294
  function extractReactiveDeps(expr, signalGetters, memoNames) {
27136
27295
  const deps = [];
27137
27296
  for (const getter of signalGetters) {
27138
- if (new RegExp(`\\b${getter}\\s*\\(`).test(expr)) {
27297
+ if (identifierCallPattern(getter).test(expr)) {
27139
27298
  deps.push(getter);
27140
27299
  }
27141
27300
  }
27142
27301
  for (const memo of memoNames) {
27143
- if (new RegExp(`\\b${memo}\\s*\\(`).test(expr)) {
27302
+ if (identifierCallPattern(memo).test(expr)) {
27144
27303
  deps.push(memo);
27145
27304
  }
27146
27305
  }
@@ -27153,7 +27312,7 @@ function extractSetterRefs(expr, signalGetters) {
27153
27312
  refs.push(match[1]);
27154
27313
  }
27155
27314
  for (const getter of signalGetters) {
27156
- if (new RegExp(`\\b${getter}\\s*\\(`).test(expr)) {
27315
+ if (identifierCallPattern(getter).test(expr)) {
27157
27316
  refs.push(getter);
27158
27317
  }
27159
27318
  }
@@ -27175,7 +27334,7 @@ function findSourceFile2(meta) {
27175
27334
  return null;
27176
27335
  }
27177
27336
  // src/profiler.ts
27178
- import ts27 from "typescript";
27337
+ import ts28 from "typescript";
27179
27338
  var PROFILE_SCHEMA_VERSION = 1;
27180
27339
  var DEFAULT_FANOUT_THRESHOLD = 8;
27181
27340
  function buildStaticBudget(source, filePath, componentName, options = {}) {
@@ -27445,15 +27604,15 @@ function joinProfilerEvents(events, index) {
27445
27604
  return { joined, unattributed, diagnostics };
27446
27605
  }
27447
27606
  function findUninstrumentedEffects(source, filePath, instrumentedLines) {
27448
- const sf = ts27.createSourceFile(filePath, source, ts27.ScriptTarget.Latest, true, ts27.ScriptKind.TSX);
27607
+ const sf = ts28.createSourceFile(filePath, source, ts28.ScriptTarget.Latest, true, ts28.ScriptKind.TSX);
27449
27608
  const out = [];
27450
27609
  const visit3 = (node) => {
27451
- if (ts27.isCallExpression(node) && ts27.isIdentifier(node.expression) && node.expression.text === "createEffect") {
27610
+ if (ts28.isCallExpression(node) && ts28.isIdentifier(node.expression) && node.expression.text === "createEffect") {
27452
27611
  const line = sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;
27453
27612
  if (!instrumentedLines.has(line))
27454
27613
  out.push({ file: filePath, line });
27455
27614
  }
27456
- ts27.forEachChild(node, visit3);
27615
+ ts28.forEachChild(node, visit3);
27457
27616
  };
27458
27617
  visit3(sf);
27459
27618
  out.sort((a, b) => a.line - b.line);
@@ -27761,13 +27920,13 @@ function assessBatchSafety(args) {
27761
27920
  const signalGetters = new Set(args.graph.signals.map((s) => s.name));
27762
27921
  let sf;
27763
27922
  try {
27764
- sf = ts27.createSourceFile("__h.ts", `const __h = ${args.handler}`, ts27.ScriptTarget.Latest, true);
27923
+ sf = ts28.createSourceFile("__h.ts", `const __h = ${args.handler}`, ts28.ScriptTarget.Latest, true);
27765
27924
  } catch {
27766
27925
  return "unverified";
27767
27926
  }
27768
27927
  const calls = [];
27769
27928
  const visit3 = (node) => {
27770
- if (ts27.isCallExpression(node) && ts27.isIdentifier(node.expression)) {
27929
+ if (ts28.isCallExpression(node) && ts28.isIdentifier(node.expression)) {
27771
27930
  const name = node.expression.text;
27772
27931
  if (setters.has(name))
27773
27932
  calls.push({ pos: node.getStart(sf), kind: "write" });
@@ -27776,7 +27935,7 @@ function assessBatchSafety(args) {
27776
27935
  else if (!signalGetters.has(name) && !memoNames.has(name))
27777
27936
  calls.push({ pos: node.getStart(sf), kind: "risky" });
27778
27937
  }
27779
- ts27.forEachChild(node, visit3);
27938
+ ts28.forEachChild(node, visit3);
27780
27939
  };
27781
27940
  visit3(sf);
27782
27941
  calls.sort((a, b) => a.pos - b.pos);
@@ -28422,6 +28581,7 @@ export {
28422
28581
  serializeParsedExpr,
28423
28582
  searchParamsLocalNames,
28424
28583
  rewriteImportsForTemplate,
28584
+ rewriteDynamicImportsInSource,
28425
28585
  resolveStaticLoopSource,
28426
28586
  resolveSetters,
28427
28587
  resolveDangerousInnerHtml,
@@ -28570,6 +28730,7 @@ export {
28570
28730
  ErrorCodes,
28571
28731
  ENV_SIGNAL_READERS,
28572
28732
  CALLBACK_METHODS,
28733
+ BindingScope,
28573
28734
  BaseAdapter,
28574
28735
  BUILTIN_LOWERING_PLUGINS,
28575
28736
  BROWSER_ONLY_CLIENT_APIS,