@barefootjs/go-template 0.31.4 → 0.31.6

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/vite.js CHANGED
@@ -5,7 +5,7 @@ import { barefoot as coreBarefoot } from "@barefootjs/vite";
5
5
  import { devModuleUrl, loadManifest, resolveDevOrigin, resolveScriptAssets, toPosixRelative } from "@barefootjs/vite";
6
6
 
7
7
  // ../jsx/src/compiler.ts
8
- import ts23 from "typescript";
8
+ import ts24 from "typescript";
9
9
 
10
10
  // ../jsx/src/analyzer.ts
11
11
  import ts9 from "typescript";
@@ -2597,7 +2597,7 @@ var REACTIVE_PRIMITIVES = new Set([
2597
2597
  ]);
2598
2598
 
2599
2599
  // ../jsx/src/jsx-to-ir.ts
2600
- import ts12 from "typescript";
2600
+ import ts13 from "typescript";
2601
2601
 
2602
2602
  // ../jsx/src/types.ts
2603
2603
  var SCOPE_FORBIDDEN = {
@@ -2647,6 +2647,7 @@ function preambleAnalysisTemplateText(p) {
2647
2647
  }
2648
2648
 
2649
2649
  // ../jsx/src/module-exports.ts
2650
+ import ts10 from "typescript";
2650
2651
  function formatParamWithType(p) {
2651
2652
  const rest = p.isRest ? "..." : "";
2652
2653
  const optional = p.optional ? "?" : "";
@@ -2677,12 +2678,55 @@ function findReachableNames(primaryRefs, declarations) {
2677
2678
  }
2678
2679
  return reachable;
2679
2680
  }
2681
+ function findAssignedNames(bodyText, candidates) {
2682
+ const assigned = new Set;
2683
+ if (candidates.size === 0)
2684
+ return assigned;
2685
+ const sf = ts10.createSourceFile("bf-assignment-scan.tsx", bodyText, ts10.ScriptTarget.Latest, false, ts10.ScriptKind.TSX);
2686
+ const record = (target) => {
2687
+ if (ts10.isIdentifier(target) && candidates.has(target.text)) {
2688
+ assigned.add(target.text);
2689
+ }
2690
+ };
2691
+ const visit = (node) => {
2692
+ if (ts10.isBinaryExpression(node) && isAssignmentOperator(node.operatorToken.kind)) {
2693
+ record(node.left);
2694
+ } else if ((ts10.isPrefixUnaryExpression(node) || ts10.isPostfixUnaryExpression(node)) && (node.operator === ts10.SyntaxKind.PlusPlusToken || node.operator === ts10.SyntaxKind.MinusMinusToken)) {
2695
+ record(node.operand);
2696
+ }
2697
+ ts10.forEachChild(node, visit);
2698
+ };
2699
+ ts10.forEachChild(sf, visit);
2700
+ return assigned;
2701
+ }
2702
+ function closeOverWritersOfMutableBindings(primaryRefs, declarations, mutableNames) {
2703
+ let reachable = findReachableNames(primaryRefs, declarations);
2704
+ if (mutableNames.size === 0)
2705
+ return reachable;
2706
+ let seedText = primaryRefs;
2707
+ for (let round = 0;round <= declarations.length; round++) {
2708
+ const survivingMutables = new Set([...reachable].filter((name) => mutableNames.has(name)));
2709
+ if (survivingMutables.size === 0)
2710
+ return reachable;
2711
+ const added = declarations.filter((d) => !reachable.has(d.name)).filter((d) => findAssignedNames(d.body, survivingMutables).size > 0).map((d) => d.name);
2712
+ if (added.length === 0)
2713
+ return reachable;
2714
+ seedText += `
2715
+ ` + added.join(`
2716
+ `);
2717
+ reachable = findReachableNames(seedText, declarations);
2718
+ }
2719
+ return reachable;
2720
+ }
2721
+ function isAssignmentOperator(kind) {
2722
+ return kind >= ts10.SyntaxKind.FirstAssignment && kind <= ts10.SyntaxKind.LastAssignment;
2723
+ }
2680
2724
 
2681
2725
  // ../jsx/src/reactivity-checker.ts
2682
- import ts10 from "typescript";
2726
+ import ts11 from "typescript";
2683
2727
 
2684
2728
  // ../jsx/src/free-refs.ts
2685
- import ts11 from "typescript";
2729
+ import ts12 from "typescript";
2686
2730
  var _bindingMapCache = new WeakMap;
2687
2731
 
2688
2732
  // ../jsx/src/to-locale-date-lowering.ts
@@ -3096,13 +3140,13 @@ var KEYWORDS_AND_GLOBALS = new Set([
3096
3140
  ]);
3097
3141
 
3098
3142
  // ../jsx/src/ir-to-client-js/walk-prop-accesses.ts
3099
- import ts13 from "typescript";
3143
+ import ts14 from "typescript";
3100
3144
 
3101
3145
  // ../jsx/src/value-references.ts
3102
- import ts14 from "typescript";
3146
+ import ts15 from "typescript";
3103
3147
 
3104
3148
  // ../jsx/src/relocate.ts
3105
- import ts15 from "typescript";
3149
+ import ts16 from "typescript";
3106
3150
 
3107
3151
  // ../jsx/src/lowering-registry.ts
3108
3152
  var plugins = [];
@@ -3298,10 +3342,10 @@ function formatDateLocalNames(metadata) {
3298
3342
  }
3299
3343
 
3300
3344
  // ../jsx/src/ir-to-client-js/prune-unused-prop-extractions.ts
3301
- import ts16 from "typescript";
3345
+ import ts17 from "typescript";
3302
3346
 
3303
3347
  // ../jsx/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts
3304
- import ts17 from "typescript";
3348
+ import ts18 from "typescript";
3305
3349
  var NO_PREAMBLE = {
3306
3350
  lazySafe: true,
3307
3351
  facts: { declaredNames: new Set, freeNames: new Set }
@@ -3351,7 +3395,7 @@ var INERT_BINDING_GLOBALS = new Set([
3351
3395
  ]);
3352
3396
 
3353
3397
  // ../jsx/src/ir-to-client-js/emit-reactive.ts
3354
- import ts18 from "typescript";
3398
+ import ts19 from "typescript";
3355
3399
 
3356
3400
  // ../jsx/src/ir-to-client-js/control-flow/stringify/event-delegation.ts
3357
3401
  var NON_BUBBLING_EVENTS = new Set([
@@ -3366,7 +3410,7 @@ var NON_BUBBLING_EVENTS = new Set([
3366
3410
  ]);
3367
3411
 
3368
3412
  // ../jsx/src/ir-to-client-js/rewrite-props-object.ts
3369
- import ts19 from "typescript";
3413
+ import ts20 from "typescript";
3370
3414
 
3371
3415
  // ../jsx/src/ir-to-client-js/source-map.ts
3372
3416
  var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -3457,15 +3501,15 @@ class SourceMapGenerator {
3457
3501
  }
3458
3502
 
3459
3503
  // ../jsx/src/preprocess-inline-jsx-callbacks.ts
3460
- import ts20 from "typescript";
3504
+ import ts21 from "typescript";
3461
3505
 
3462
3506
  // ../jsx/src/ssr-defaults.ts
3463
- import ts21 from "typescript";
3507
+ import ts22 from "typescript";
3464
3508
  var UNRESOLVED = Symbol("unresolved");
3465
3509
  var NO_RETURN = Symbol("no-return");
3466
3510
 
3467
3511
  // ../jsx/src/augment-inherited-props.ts
3468
- import ts22 from "typescript";
3512
+ import ts23 from "typescript";
3469
3513
  function collectContextConsumers(metadata) {
3470
3514
  const constants = metadata.localConstants ?? [];
3471
3515
  const contextDefaults = new Map;
@@ -3497,47 +3541,47 @@ function collectContextConsumers(metadata) {
3497
3541
  }
3498
3542
  function parseUseContextArg(source) {
3499
3543
  const expr = parseSingleExpression(source);
3500
- if (!expr || !ts22.isCallExpression(expr))
3544
+ if (!expr || !ts23.isCallExpression(expr))
3501
3545
  return null;
3502
- if (!ts22.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
3546
+ if (!ts23.isIdentifier(expr.expression) || expr.expression.text !== "useContext")
3503
3547
  return null;
3504
3548
  if (expr.arguments.length !== 1)
3505
3549
  return null;
3506
3550
  const arg = expr.arguments[0];
3507
- return ts22.isIdentifier(arg) ? arg.text : null;
3551
+ return ts23.isIdentifier(arg) ? arg.text : null;
3508
3552
  }
3509
3553
  function parseCreateContextDefault(source) {
3510
3554
  const expr = parseSingleExpression(source);
3511
- if (!expr || !ts22.isCallExpression(expr))
3555
+ if (!expr || !ts23.isCallExpression(expr))
3512
3556
  return null;
3513
3557
  if (expr.arguments.length === 0)
3514
3558
  return null;
3515
3559
  const arg = expr.arguments[0];
3516
- if (ts22.isStringLiteral(arg) || ts22.isNoSubstitutionTemplateLiteral(arg))
3560
+ if (ts23.isStringLiteral(arg) || ts23.isNoSubstitutionTemplateLiteral(arg))
3517
3561
  return arg.text;
3518
- if (ts22.isNumericLiteral(arg))
3562
+ if (ts23.isNumericLiteral(arg))
3519
3563
  return Number(arg.text);
3520
- if (arg.kind === ts22.SyntaxKind.TrueKeyword)
3564
+ if (arg.kind === ts23.SyntaxKind.TrueKeyword)
3521
3565
  return true;
3522
- if (arg.kind === ts22.SyntaxKind.FalseKeyword)
3566
+ if (arg.kind === ts23.SyntaxKind.FalseKeyword)
3523
3567
  return false;
3524
3568
  return null;
3525
3569
  }
3526
3570
  function isObjectLiteralCreateContextDefault(source) {
3527
3571
  const expr = parseSingleExpression(source);
3528
- if (!expr || !ts22.isCallExpression(expr))
3572
+ if (!expr || !ts23.isCallExpression(expr))
3529
3573
  return false;
3530
3574
  if (expr.arguments.length === 0)
3531
3575
  return false;
3532
- return ts22.isObjectLiteralExpression(expr.arguments[0]);
3576
+ return ts23.isObjectLiteralExpression(expr.arguments[0]);
3533
3577
  }
3534
3578
  function parseSingleExpression(source) {
3535
- const sf = ts22.createSourceFile("__ctx.ts", `(${source})`, ts22.ScriptTarget.Latest, false);
3579
+ const sf = ts23.createSourceFile("__ctx.ts", `(${source})`, ts23.ScriptTarget.Latest, false);
3536
3580
  const stmt = sf.statements[0];
3537
- if (!stmt || !ts22.isExpressionStatement(stmt))
3581
+ if (!stmt || !ts23.isExpressionStatement(stmt))
3538
3582
  return null;
3539
3583
  let e = stmt.expression;
3540
- while (ts22.isParenthesizedExpression(e))
3584
+ while (ts23.isParenthesizedExpression(e))
3541
3585
  e = e.expression;
3542
3586
  return e;
3543
3587
  }
@@ -3562,25 +3606,25 @@ function augmentInheritedPropAccesses(ir) {
3562
3606
  const pinCoalesceLiterals = (s) => {
3563
3607
  if (!s || !s.includes(propsObj))
3564
3608
  return;
3565
- const sf = ts22.createSourceFile("__aug.ts", `(${s})`, ts22.ScriptTarget.Latest, false);
3609
+ const sf = ts23.createSourceFile("__aug.ts", `(${s})`, ts23.ScriptTarget.Latest, false);
3566
3610
  const visit = (n) => {
3567
- if (ts22.isBinaryExpression(n) && (n.operatorToken.kind === ts22.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts22.SyntaxKind.BarBarToken)) {
3611
+ if (ts23.isBinaryExpression(n) && (n.operatorToken.kind === ts23.SyntaxKind.QuestionQuestionToken || n.operatorToken.kind === ts23.SyntaxKind.BarBarToken)) {
3568
3612
  let left = n.left;
3569
- while (ts22.isParenthesizedExpression(left))
3613
+ while (ts23.isParenthesizedExpression(left))
3570
3614
  left = left.expression;
3571
- if (ts22.isPropertyAccessExpression(left) && ts22.isIdentifier(left.expression) && left.expression.text === propsObj) {
3615
+ if (ts23.isPropertyAccessExpression(left) && ts23.isIdentifier(left.expression) && left.expression.text === propsObj) {
3572
3616
  const name = left.name.text;
3573
3617
  let right = n.right;
3574
- while (ts22.isParenthesizedExpression(right))
3618
+ while (ts23.isParenthesizedExpression(right))
3575
3619
  right = right.expression;
3576
- if (ts22.isPrefixUnaryExpression(right))
3620
+ if (ts23.isPrefixUnaryExpression(right))
3577
3621
  right = right.operand;
3578
- const kind = ts22.isNumericLiteral(right) ? "number" : right.kind === ts22.SyntaxKind.TrueKeyword || right.kind === ts22.SyntaxKind.FalseKeyword ? "boolean" : ts22.isStringLiteralLike(right) ? "string" : null;
3622
+ const kind = ts23.isNumericLiteral(right) ? "number" : right.kind === ts23.SyntaxKind.TrueKeyword || right.kind === ts23.SyntaxKind.FalseKeyword ? "boolean" : ts23.isStringLiteralLike(right) ? "string" : null;
3579
3623
  if (kind && !coalesceLiteralTypes.has(name))
3580
3624
  coalesceLiteralTypes.set(name, kind);
3581
3625
  }
3582
3626
  }
3583
- ts22.forEachChild(n, visit);
3627
+ ts23.forEachChild(n, visit);
3584
3628
  };
3585
3629
  visit(sf);
3586
3630
  };
@@ -3691,33 +3735,33 @@ function augmentInheritedPropAccesses(ir) {
3691
3735
  }
3692
3736
  }
3693
3737
  function parseStaticStringConst(source) {
3694
- const sf = ts22.createSourceFile("__const.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
3738
+ const sf = ts23.createSourceFile("__const.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
3695
3739
  const stmt = sf.statements[0];
3696
- if (!stmt || !ts22.isVariableStatement(stmt))
3740
+ if (!stmt || !ts23.isVariableStatement(stmt))
3697
3741
  return null;
3698
3742
  let init = stmt.declarationList.declarations[0]?.initializer;
3699
- while (init && ts22.isParenthesizedExpression(init))
3743
+ while (init && ts23.isParenthesizedExpression(init))
3700
3744
  init = init.expression;
3701
3745
  if (!init)
3702
3746
  return null;
3703
- if (ts22.isStringLiteral(init) || ts22.isNoSubstitutionTemplateLiteral(init)) {
3747
+ if (ts23.isStringLiteral(init) || ts23.isNoSubstitutionTemplateLiteral(init)) {
3704
3748
  return init.text;
3705
3749
  }
3706
3750
  return evalStringArrayJoin(source);
3707
3751
  }
3708
3752
  function evalTemplateOfStringConsts(source, resolved) {
3709
- const sf = ts22.createSourceFile("__const.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
3753
+ const sf = ts23.createSourceFile("__const.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
3710
3754
  const stmt = sf.statements[0];
3711
- if (!stmt || !ts22.isVariableStatement(stmt))
3755
+ if (!stmt || !ts23.isVariableStatement(stmt))
3712
3756
  return null;
3713
3757
  let init = stmt.declarationList.declarations[0]?.initializer;
3714
- while (init && ts22.isParenthesizedExpression(init))
3758
+ while (init && ts23.isParenthesizedExpression(init))
3715
3759
  init = init.expression;
3716
- if (!init || !ts22.isTemplateExpression(init))
3760
+ if (!init || !ts23.isTemplateExpression(init))
3717
3761
  return null;
3718
3762
  let out = init.head.text;
3719
3763
  for (const span of init.templateSpans) {
3720
- if (!ts22.isIdentifier(span.expression))
3764
+ if (!ts23.isIdentifier(span.expression))
3721
3765
  return null;
3722
3766
  const value = resolved.get(span.expression.text);
3723
3767
  if (value === undefined)
@@ -3745,28 +3789,28 @@ function collectModuleStringConsts(constants) {
3745
3789
  return map;
3746
3790
  }
3747
3791
  function evalStringArrayJoin(source) {
3748
- const sf = ts22.createSourceFile("__join.ts", `const __x = (${source});`, ts22.ScriptTarget.Latest, false);
3792
+ const sf = ts23.createSourceFile("__join.ts", `const __x = (${source});`, ts23.ScriptTarget.Latest, false);
3749
3793
  const stmt = sf.statements[0];
3750
- if (!stmt || !ts22.isVariableStatement(stmt))
3794
+ if (!stmt || !ts23.isVariableStatement(stmt))
3751
3795
  return null;
3752
3796
  let node = stmt.declarationList.declarations[0]?.initializer;
3753
- while (node && ts22.isParenthesizedExpression(node))
3797
+ while (node && ts23.isParenthesizedExpression(node))
3754
3798
  node = node.expression;
3755
- if (!node || !ts22.isCallExpression(node))
3799
+ if (!node || !ts23.isCallExpression(node))
3756
3800
  return null;
3757
3801
  const callee = node.expression;
3758
- if (!ts22.isPropertyAccessExpression(callee))
3802
+ if (!ts23.isPropertyAccessExpression(callee))
3759
3803
  return null;
3760
3804
  if (callee.name.text !== "join")
3761
3805
  return null;
3762
3806
  let recv = callee.expression;
3763
- while (ts22.isParenthesizedExpression(recv))
3807
+ while (ts23.isParenthesizedExpression(recv))
3764
3808
  recv = recv.expression;
3765
- if (!ts22.isArrayLiteralExpression(recv))
3809
+ if (!ts23.isArrayLiteralExpression(recv))
3766
3810
  return null;
3767
3811
  const parts = [];
3768
3812
  for (const el of recv.elements) {
3769
- if (ts22.isStringLiteral(el) || ts22.isNoSubstitutionTemplateLiteral(el)) {
3813
+ if (ts23.isStringLiteral(el) || ts23.isNoSubstitutionTemplateLiteral(el)) {
3770
3814
  parts.push(el.text);
3771
3815
  } else {
3772
3816
  return null;
@@ -3775,7 +3819,7 @@ function evalStringArrayJoin(source) {
3775
3819
  let sep = ",";
3776
3820
  if (node.arguments.length >= 1) {
3777
3821
  const arg = node.arguments[0];
3778
- if (ts22.isStringLiteral(arg) || ts22.isNoSubstitutionTemplateLiteral(arg))
3822
+ if (ts23.isStringLiteral(arg) || ts23.isNoSubstitutionTemplateLiteral(arg))
3779
3823
  sep = arg.text;
3780
3824
  else
3781
3825
  return null;
@@ -3783,11 +3827,11 @@ function evalStringArrayJoin(source) {
3783
3827
  return parts.join(sep);
3784
3828
  }
3785
3829
  function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
3786
- if (!ts22.isElementAccessExpression(val))
3830
+ if (!ts23.isElementAccessExpression(val))
3787
3831
  return null;
3788
3832
  const obj = val.expression;
3789
3833
  const arg = val.argumentExpression;
3790
- if (!ts22.isIdentifier(obj) || !ts22.isIdentifier(arg))
3834
+ if (!ts23.isIdentifier(obj) || !ts23.isIdentifier(arg))
3791
3835
  return null;
3792
3836
  let indexPropName;
3793
3837
  let defaultKey;
@@ -3803,35 +3847,35 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
3803
3847
  const constInfo = localConstants.find((c) => c.name === obj.text && c.isModule);
3804
3848
  if (constInfo?.value === undefined)
3805
3849
  return null;
3806
- const sf = ts22.createSourceFile("__rec.ts", `(${constInfo.value})`, ts22.ScriptTarget.Latest, true);
3850
+ const sf = ts23.createSourceFile("__rec.ts", `(${constInfo.value})`, ts23.ScriptTarget.Latest, true);
3807
3851
  if (sf.statements.length !== 1)
3808
3852
  return null;
3809
3853
  const stmt = sf.statements[0];
3810
- if (!ts22.isExpressionStatement(stmt))
3854
+ if (!ts23.isExpressionStatement(stmt))
3811
3855
  return null;
3812
3856
  let parsed = stmt.expression;
3813
- while (ts22.isParenthesizedExpression(parsed))
3857
+ while (ts23.isParenthesizedExpression(parsed))
3814
3858
  parsed = parsed.expression;
3815
- if (!ts22.isObjectLiteralExpression(parsed))
3859
+ if (!ts23.isObjectLiteralExpression(parsed))
3816
3860
  return null;
3817
3861
  const entries = [];
3818
3862
  for (const prop of parsed.properties) {
3819
- if (!ts22.isPropertyAssignment(prop))
3863
+ if (!ts23.isPropertyAssignment(prop))
3820
3864
  return null;
3821
3865
  let key;
3822
- if (ts22.isIdentifier(prop.name)) {
3866
+ if (ts23.isIdentifier(prop.name)) {
3823
3867
  key = prop.name.text;
3824
- } else if (ts22.isStringLiteral(prop.name) || ts22.isNoSubstitutionTemplateLiteral(prop.name)) {
3868
+ } else if (ts23.isStringLiteral(prop.name) || ts23.isNoSubstitutionTemplateLiteral(prop.name)) {
3825
3869
  key = prop.name.text;
3826
3870
  } else {
3827
3871
  return null;
3828
3872
  }
3829
3873
  let v = prop.initializer;
3830
- while (ts22.isParenthesizedExpression(v))
3874
+ while (ts23.isParenthesizedExpression(v))
3831
3875
  v = v.expression;
3832
- if (ts22.isNumericLiteral(v)) {
3876
+ if (ts23.isNumericLiteral(v)) {
3833
3877
  entries.push({ key, value: { kind: "number", text: v.text } });
3834
- } else if (ts22.isStringLiteral(v) || ts22.isNoSubstitutionTemplateLiteral(v)) {
3878
+ } else if (ts23.isStringLiteral(v) || ts23.isNoSubstitutionTemplateLiteral(v)) {
3835
3879
  entries.push({ key, value: { kind: "string", text: v.text } });
3836
3880
  } else {
3837
3881
  return null;
@@ -3887,7 +3931,7 @@ function computeSsrSeedPlan(metadata) {
3887
3931
  // ../jsx/src/rich-type-refusal.ts
3888
3932
  var EMPTY_BINDINGS2 = new Map;
3889
3933
  // ../jsx/src/shared-program.ts
3890
- import ts24 from "typescript";
3934
+ import ts25 from "typescript";
3891
3935
  // ../jsx/src/adapters/interface.ts
3892
3936
  class BaseAdapter {
3893
3937
  renderChildren(children) {
@@ -3940,7 +3984,7 @@ class JsxAdapter extends BaseAdapter {
3940
3984
  ...localFunctions.map((f) => ({ name: f.name, body: f.body })),
3941
3985
  ...localConstants.map((c) => ({ name: c.name, body: c.value }))
3942
3986
  ];
3943
- const reachable = findReachableNames(primaryRefText, declarations);
3987
+ const reachable = closeOverWritersOfMutableBindings(primaryRefText, declarations, new Set(ir.metadata.localConstants.filter((c) => (c.declarationKind ?? "const") !== "const").map((c) => c.name)));
3944
3988
  const reachableBodies = [...reachable].map((name) => {
3945
3989
  const func = localFunctions.find((f) => f.name === name);
3946
3990
  if (func)
@@ -3972,7 +4016,8 @@ class JsxAdapter extends BaseAdapter {
3972
4016
  if (signal.setter) {
3973
4017
  const setterUsed = identifierPattern(signal.setter).test(setterRefText);
3974
4018
  if (setterUsed) {
3975
- lines.push(` const ${signal.setter} = (..._args: any[]) => {}`);
4019
+ const setterType = preserveTypes && signal.type.kind !== "unknown" ? `(valueOrFn: ${signal.type.raw} | ((prev: ${signal.type.raw}) => ${signal.type.raw})) => void` : null;
4020
+ lines.push(setterType ? ` const ${signal.setter}: ${setterType} = () => {}` : ` const ${signal.setter} = (..._args: any[]) => {}`);
3976
4021
  }
3977
4022
  }
3978
4023
  }
@@ -4168,7 +4213,7 @@ class JsxAdapter extends BaseAdapter {
4168
4213
  }
4169
4214
 
4170
4215
  // ../jsx/src/adapters/template-imports.ts
4171
- import ts25 from "typescript";
4216
+ import ts26 from "typescript";
4172
4217
  var CLIENT_PACKAGE_SOURCES = new Set([
4173
4218
  "@barefootjs/client",
4174
4219
  "@barefootjs/client/runtime"
@@ -4307,7 +4352,8 @@ export default ${this.componentName}` : "";
4307
4352
  const noArgDefault = hasRequiredProps ? "" : ` = {} as ${propsTypeExpr}`;
4308
4353
  const lines = [];
4309
4354
  const exportPrefix = ir.metadata.isExported === false ? "" : "export ";
4310
- lines.push(`${exportPrefix}function ${name}(${fullPropsDestructure}${typeAnnotation}${noArgDefault}) {`);
4355
+ const typeParameters = ir.metadata.typeParameters ?? "";
4356
+ lines.push(`${exportPrefix}function ${name}${typeParameters}(${fullPropsDestructure}${typeAnnotation}${noArgDefault}) {`);
4311
4357
  if (hasClientInteractivity) {
4312
4358
  lines.push(` const __scopeId = (/_s\\d/.test(__bfScope || '') ? __bfScope : null) || __instanceId || \`${name}_\${Math.random().toString(36).slice(2, 8)}\``);
4313
4359
  } else {
@@ -4905,7 +4951,7 @@ function dangerousInnerHtmlDiagnostic(expr, loc, reason) {
4905
4951
  };
4906
4952
  }
4907
4953
  // ../jsx/src/combine-client-js.ts
4908
- import ts26 from "typescript";
4954
+ import ts27 from "typescript";
4909
4955
  // ../jsx/src/loop-destructure.ts
4910
4956
  function isLowerableLoopDestructure(loop) {
4911
4957
  const bindings = loop.paramBindings;
@@ -5045,9 +5091,9 @@ function escapeRe(s) {
5045
5091
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5046
5092
  }
5047
5093
  // ../jsx/src/debug.ts
5048
- import ts27 from "typescript";
5049
- // ../jsx/src/profiler.ts
5050
5094
  import ts28 from "typescript";
5095
+ // ../jsx/src/profiler.ts
5096
+ import ts29 from "typescript";
5051
5097
 
5052
5098
  // ../jsx/src/index.ts
5053
5099
  registerBuiltinLoweringPlugins();
@@ -5483,6 +5529,7 @@ function collectNestedComponents(node, result) {
5483
5529
  ...loop.childComponent,
5484
5530
  isDynamic: !loop.isStaticArray,
5485
5531
  isPropDerived: !!loop.isPropDerivedArray,
5532
+ clientOnly: loop.clientOnly,
5486
5533
  loopKey: loop.key ?? undefined,
5487
5534
  loopParam: loop.param ?? undefined,
5488
5535
  bodyChildren: hasBodyChildren ? loop.childComponent.children : undefined,
@@ -6678,7 +6725,7 @@ function computeObjectMemoInitialValue(ctx, memo) {
6678
6725
  }
6679
6726
 
6680
6727
  // src/adapter/memo/template-interp.ts
6681
- import ts29 from "typescript";
6728
+ import ts30 from "typescript";
6682
6729
  function computeTemplateLiteralMemoInitialValue(ctx, memo, propsParams) {
6683
6730
  const localKeyBindings = new Map;
6684
6731
  let templateValue;
@@ -7309,7 +7356,7 @@ function collectPropsReadByCtorInit(body, propsObjectName, propNames) {
7309
7356
  }
7310
7357
 
7311
7358
  // src/adapter/spread/spread-codegen.ts
7312
- import ts30 from "typescript";
7359
+ import ts31 from "typescript";
7313
7360
  function collectSpreadSlots(ctx, node) {
7314
7361
  const result = [];
7315
7362
  collectSpreadSlotsRecursive(ctx, node, result);
@@ -7544,7 +7591,7 @@ function recordIndexAccessToGoMap(ctx, val, ir) {
7544
7591
  if (val.kind !== "index-access" || val.object.kind !== "identifier" || val.index.kind !== "identifier") {
7545
7592
  return null;
7546
7593
  }
7547
- const tsVal = ts30.factory.createElementAccessExpression(ts30.factory.createIdentifier(val.object.name), ts30.factory.createIdentifier(val.index.name));
7594
+ const tsVal = ts31.factory.createElementAccessExpression(ts31.factory.createIdentifier(val.object.name), ts31.factory.createIdentifier(val.index.name));
7548
7595
  const parsed = parseRecordIndexAccess(tsVal, ir.metadata.localConstants ?? [], ir.metadata.propsParams);
7549
7596
  if (!parsed)
7550
7597
  return null;
@@ -7845,7 +7892,7 @@ class GoTemplateAdapter extends BaseAdapter {
7845
7892
  }
7846
7893
  inLoop = false;
7847
7894
  bakedStaticChildLoopCache = new Map;
7848
- loopParamStack = [];
7895
+ scope = BindingScope.EMPTY;
7849
7896
  loopKeyDepthStack = [];
7850
7897
  loopScalarItemStack = [];
7851
7898
  loopWrapperStack = [];
@@ -7903,6 +7950,7 @@ class GoTemplateAdapter extends BaseAdapter {
7903
7950
  this.state.referencedDerivedConsts = new Set;
7904
7951
  this.state.templateVarCounter = 0;
7905
7952
  this.state.pendingChildrenDefines = [];
7953
+ this.scope = BindingScope.EMPTY;
7906
7954
  this.primeCompileState(ir);
7907
7955
  this.state.stringValueNames = collectStringValueNames(ir);
7908
7956
  this.recordDerivedFieldDeps(ir, new Set((ir.metadata.propsParams ?? []).map((p) => p.name)));
@@ -8143,6 +8191,12 @@ ${scriptRegistrations}${templateBody}
8143
8191
  isNestedArrayShadowed(param, nestedArrayFields) {
8144
8192
  return nestedArrayFields.has(capitalizeFieldName(param.name)) || nestedArrayFields.has(capitalizeFieldName(param.sourceName ?? param.name));
8145
8193
  }
8194
+ isOrphanedClientOnlyNested(nested) {
8195
+ return !!nested.clientOnly && !nested.isDynamic && !nested.isPropDerived;
8196
+ }
8197
+ propDerivedNestedArrayFields(nestedComponents) {
8198
+ return new Set(nestedComponents.filter((n) => n.isPropDerived).map((n) => `${n.name}s`));
8199
+ }
8146
8200
  propParamFieldNamesUnion(params) {
8147
8201
  return params.flatMap((p) => [capitalizeFieldName(p.name), capitalizeFieldName(p.sourceName ?? p.name)]);
8148
8202
  }
@@ -8181,7 +8235,7 @@ ${scriptRegistrations}${templateBody}
8181
8235
  recordRepropsSpec(ir, componentName, nestedComponents, spreadSlots) {
8182
8236
  if (!this.childDerivedFieldDeps.has(componentName))
8183
8237
  return;
8184
- const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
8238
+ const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
8185
8239
  const params = (ir.metadata.propsParams ?? []).filter((p) => !this.isNestedArrayShadowed(p, nestedArrayFields));
8186
8240
  const takenInput = new Set(this.propParamFieldNamesUnion(ir.metadata.propsParams ?? []));
8187
8241
  const eligible = nestedComponents.every((n) => n.isDynamic && !n.isPropDerived) && spreadSlots.length === 0 && !ir.metadata.restPropsName && this.nonCollidingContextConsumers(takenInput).length === 0;
@@ -8395,8 +8449,8 @@ ${goFields.join(`
8395
8449
  if (this.usesSearchParams(ir)) {
8396
8450
  lines.push("\tSearchParams bf.SearchParams // Optional: request query for searchParams()");
8397
8451
  }
8398
- const inputNested = nestedComponents.filter((n) => !n.isDynamic || n.isPropDerived);
8399
- const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
8452
+ const inputNested = nestedComponents.filter((n) => (!n.isDynamic || n.isPropDerived) && !this.isOrphanedClientOnlyNested(n));
8453
+ const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
8400
8454
  for (const param of ir.metadata.propsParams) {
8401
8455
  const fieldName = capitalizeFieldName(param.sourceName ?? param.name);
8402
8456
  if (this.isNestedArrayShadowed(param, nestedArrayFields))
@@ -8533,7 +8587,7 @@ ${goFields.join(`
8533
8587
  lines.push(` scopeID = "${componentName}_" + randomID(6)`);
8534
8588
  lines.push("\t}");
8535
8589
  lines.push("");
8536
- const staticNested = nestedComponents.filter((n) => !n.isDynamic || n.isPropDerived);
8590
+ const staticNested = nestedComponents.filter((n) => (!n.isDynamic || n.isPropDerived) && !this.isOrphanedClientOnlyNested(n));
8537
8591
  const emittedWrapperVars = new Set;
8538
8592
  const staticWithBody = staticNested.filter((n) => n.bodyChildren && n.bodyChildren.length > 0);
8539
8593
  const staticWithoutBody = staticNested.filter((n) => !n.bodyChildren || n.bodyChildren.length === 0);
@@ -8615,7 +8669,7 @@ ${goFields.join(`
8615
8669
  if (this.usesSearchParams(ir)) {
8616
8670
  lines.push("\t\tSearchParams: in.SearchParams,");
8617
8671
  }
8618
- const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
8672
+ const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
8619
8673
  const memoFallbacks = new Map;
8620
8674
  for (const memo of ir.metadata.memos) {
8621
8675
  const stripped = memo.computation.replace(/^\(\)\s*=>\s*/, "");
@@ -9113,7 +9167,7 @@ ${goFields.join(`
9113
9167
  }
9114
9168
  }
9115
9169
  emitPropsDataFields(lines, ir, nestedComponents, propTypeOverrides, takenJsonTags) {
9116
- const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
9170
+ const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
9117
9171
  const propFieldNames = new Set;
9118
9172
  for (const param of ir.metadata.propsParams) {
9119
9173
  const fieldName = capitalizeFieldName(param.name);
@@ -9187,6 +9241,8 @@ ${goFields.join(`
9187
9241
  lines.push(` ${this.contextFieldName(c)} ${this.contextConsumerGoType(c)} \`json:"${jsonTag}"\``);
9188
9242
  }
9189
9243
  for (const nested of nestedComponents) {
9244
+ if (this.isOrphanedClientOnlyNested(nested))
9245
+ continue;
9190
9246
  const elemType = nested.bodyChildren?.length ? this.loopBodyWrapperName(componentName, nested) : `${nested.name}Props`;
9191
9247
  if (nested.isDynamic && !nested.isPropDerived) {
9192
9248
  lines.push(` ${nested.name}s []${elemType} \`json:"-"\``);
@@ -9783,8 +9839,7 @@ ${goFields.join(`
9783
9839
  const inlinedNum = this.resolveModuleNumericConst(name);
9784
9840
  if (inlinedNum !== null)
9785
9841
  return inlinedNum;
9786
- const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
9787
- if (currentLoopParam && name === currentLoopParam)
9842
+ if (this.isCurrentLoopItem(name))
9788
9843
  return ".";
9789
9844
  if (this.isOuterLoopParam(name))
9790
9845
  return `$${name}`;
@@ -9851,16 +9906,16 @@ ${goFields.join(`
9851
9906
  }
9852
9907
  return false;
9853
9908
  }
9909
+ isCurrentLoopItem(name) {
9910
+ const hit = this.scope.lookup(name);
9911
+ return hit !== null && hit.depth === 0 && hit.binding.source === "item";
9912
+ }
9854
9913
  isOuterLoopParam(name) {
9855
- const top = this.loopParamStack.length - 1;
9856
- for (let i = 0;i < top; i++) {
9857
- if (this.loopParamStack[i] === name)
9858
- return true;
9859
- }
9860
- return false;
9914
+ const hit = this.scope.lookup(name);
9915
+ return hit !== null && hit.depth > 0 && hit.binding.source === "item";
9861
9916
  }
9862
9917
  rootFieldRef(name) {
9863
- const prefix = this.loopParamStack.length > 0 ? "$." : ".";
9918
+ const prefix = this.inLoop ? "$." : ".";
9864
9919
  return `${prefix}${capitalizeFieldName(name)}`;
9865
9920
  }
9866
9921
  searchParamsFieldRef(name) {
@@ -9870,9 +9925,8 @@ ${goFields.join(`
9870
9925
  return collectModuleStringConsts(constants);
9871
9926
  }
9872
9927
  resolveModuleStringConst(name) {
9873
- if (this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name) {
9928
+ if (this.isCurrentLoopItem(name))
9874
9929
  return null;
9875
- }
9876
9930
  if (this.loopVarRefCount.has(name))
9877
9931
  return null;
9878
9932
  if (this.isOuterLoopParam(name))
@@ -9883,9 +9937,8 @@ ${goFields.join(`
9883
9937
  return `"${escapeGoString(value)}"`;
9884
9938
  }
9885
9939
  resolveModuleNumericConst(name) {
9886
- if (this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name) {
9940
+ if (this.isCurrentLoopItem(name))
9887
9941
  return null;
9888
- }
9889
9942
  if (this.loopVarRefCount.has(name))
9890
9943
  return null;
9891
9944
  if (this.isOuterLoopParam(name))
@@ -9940,7 +9993,7 @@ ${goFields.join(`
9940
9993
  if (property === "length" && object.kind === "call" && object.callee.kind === "identifier" && object.args.length === 0) {
9941
9994
  const slice = this.state.memoBackedLoopSlice.get(object.callee.name);
9942
9995
  if (slice) {
9943
- const prefix = this.loopParamStack.length > 0 ? "$." : ".";
9996
+ const prefix = this.inLoop ? "$." : ".";
9944
9997
  return `len ${prefix}${slice}`;
9945
9998
  }
9946
9999
  }
@@ -9965,8 +10018,7 @@ ${goFields.join(`
9965
10018
  if (staticValue !== null)
9966
10019
  return staticValue;
9967
10020
  }
9968
- const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
9969
- if (object.kind === "identifier" && currentLoopParam && object.name === currentLoopParam) {
10021
+ if (object.kind === "identifier" && this.isCurrentLoopItem(object.name)) {
9970
10022
  return `.${goFieldNameForKey(property)}`;
9971
10023
  }
9972
10024
  const obj = emit(object);
@@ -10728,7 +10780,7 @@ ${goFields.join(`
10728
10780
  return this.renderParsedExpr(parsed);
10729
10781
  }
10730
10782
  isLoopShadowedName(name) {
10731
- return this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name || this.loopVarRefCount.has(name) || this.isOuterLoopParam(name) || this.loopBindingStack.some((bindings) => bindings.has(name));
10783
+ return this.scope.isBound(name) || this.loopVarRefCount.has(name);
10732
10784
  }
10733
10785
  loopRowChildPropOverrides(comp) {
10734
10786
  const childShape = this.childComponentShapes.get(comp.name);
@@ -10943,8 +10995,7 @@ ${goFields.join(`
10943
10995
  const inlined = this.resolveModuleStringConst(expr.name);
10944
10996
  if (inlined !== null)
10945
10997
  return plain(inlined);
10946
- const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
10947
- if (currentLoopParam && expr.name === currentLoopParam) {
10998
+ if (this.isCurrentLoopItem(expr.name)) {
10948
10999
  return plain(".");
10949
11000
  }
10950
11001
  if (this.isOuterLoopParam(expr.name)) {
@@ -11001,11 +11052,8 @@ ${goFields.join(`
11001
11052
  if (expr.object.kind === "identifier" && this.state.propsObjectName && expr.object.name === this.state.propsObjectName) {
11002
11053
  return plain(this.rootFieldRef(expr.property));
11003
11054
  }
11004
- {
11005
- const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
11006
- if (expr.object.kind === "identifier" && currentLoopParam && expr.object.name === currentLoopParam) {
11007
- return plain(`.${capitalizeFieldName(expr.property)}`);
11008
- }
11055
+ if (expr.object.kind === "identifier" && this.isCurrentLoopItem(expr.object.name)) {
11056
+ return plain(`.${capitalizeFieldName(expr.property)}`);
11009
11057
  }
11010
11058
  const obj = this.renderConditionExpr(expr.object);
11011
11059
  if (expr.property === "length") {
@@ -11183,12 +11231,12 @@ ${goFields.join(`
11183
11231
  });
11184
11232
  }
11185
11233
  const bakedChildLoop = loop.childComponent ? this.getBakedStaticChildLoop(loop.markerId, loop.childComponent, loop.arrayParsed, loop.param, loop.key ?? undefined) : null;
11186
- const bakedElementLoop = loop.childComponent ? null : analyzeBakeableStaticElementLoop(loop, this.state.localConstants, { isNameShadowed: (name) => this.state.staticLoopSourceBoundNames.has(name) });
11234
+ const bakedElementLoop = loop.childComponent ? null : analyzeBakeableStaticElementLoop(loop, this.state.localConstants, { isNameShadowed: this.scope.asShadowPredicate() });
11187
11235
  if (bakedElementLoop) {
11188
11236
  return this.renderUnrolledStaticElementLoop(loop, bakedElementLoop.items);
11189
11237
  }
11190
11238
  const arrayName = loop.array.trim();
11191
- if (bakedChildLoop === null && /^[A-Za-z_$][\w$]*$/.test(arrayName)) {
11239
+ if (bakedChildLoop === null && /^[A-Za-z_$][\w$]*$/.test(arrayName) && !this.scope.isBound(arrayName)) {
11192
11240
  const arrayConst = this.state.localConstants.find((c) => c.name === arrayName);
11193
11241
  if (arrayConst && !arrayConst.isModule && arrayConst.parsed && !this.isStringExpr(arrayConst.parsed, new Set)) {
11194
11242
  this.state.errors.push({
@@ -11197,7 +11245,8 @@ ${goFields.join(`
11197
11245
  message: `Loop array \`${arrayName}\` is a local computed value (\`${arrayConst.value}\`) that the Go template adapter cannot bind as a template variable — only a string-derived local resolves to a generated struct field.`,
11198
11246
  loc: loop.loc ?? this.makeLoc(),
11199
11247
  suggestion: {
11200
- message: "Pre-compute the array server-side and pass it as a prop, or mark the loop position as @client-only so it runs in JS on the client."
11248
+ message: "Pre-compute the array server-side and pass it as a prop, or mark the loop position as @client-only so it runs in JS on the client.",
11249
+ escape: [{ kind: "prop-precompute" }, { kind: "client-directive" }]
11201
11250
  }
11202
11251
  });
11203
11252
  }
@@ -11216,6 +11265,9 @@ ${goFields.join(`
11216
11265
  }
11217
11266
  const wasInLoopOuter = this.inLoop;
11218
11267
  this.inLoop = true;
11268
+ const scopeLoop = loop.iterationShape === "keys" ? { ...loop, param: "" } : loop;
11269
+ const prevScope = this.scope;
11270
+ this.scope = prevScope.enterLoopRow(scopeLoop);
11219
11271
  const addedLoopVars = [];
11220
11272
  for (const d of loop.preamble?.declarations ?? []) {
11221
11273
  this.loopVarRefCount.set(d.name, (this.loopVarRefCount.get(d.name) ?? 0) + 1);
@@ -11227,17 +11279,14 @@ ${goFields.join(`
11227
11279
  this.loopBindingStack.push(built.bindings);
11228
11280
  this.loopRestExcludeStack.push(built.restExcludes);
11229
11281
  pushedBindingMap = true;
11230
- this.loopParamStack.push("");
11231
11282
  if (rangeIndex !== "_") {
11232
11283
  this.loopVarRefCount.set(rangeIndex, (this.loopVarRefCount.get(rangeIndex) ?? 0) + 1);
11233
11284
  addedLoopVars.push(rangeIndex);
11234
11285
  }
11235
11286
  } else if (loop.iterationShape === "keys") {
11236
- this.loopParamStack.push("");
11237
11287
  this.loopVarRefCount.set(param, (this.loopVarRefCount.get(param) ?? 0) + 1);
11238
11288
  addedLoopVars.push(param);
11239
11289
  } else {
11240
- this.loopParamStack.push(param);
11241
11290
  if (rangeIndex !== "_") {
11242
11291
  this.loopVarRefCount.set(rangeIndex, (this.loopVarRefCount.get(rangeIndex) ?? 0) + 1);
11243
11292
  addedLoopVars.push(rangeIndex);
@@ -11259,7 +11308,7 @@ ${goFields.join(`
11259
11308
  else
11260
11309
  this.loopVarRefCount.set(v, rc);
11261
11310
  }
11262
- this.loopParamStack.pop();
11311
+ this.scope = prevScope;
11263
11312
  if (pushedBindingMap) {
11264
11313
  this.loopBindingStack.pop();
11265
11314
  this.loopRestExcludeStack.pop();
@@ -11291,7 +11340,8 @@ ${goFields.join(`
11291
11340
  this.loopWrapperStack.push(false);
11292
11341
  this.loopKeyDepthStack.push(loop.depth);
11293
11342
  this.loopScalarItemStack.push(this.scalarLiteralLoopGoType(loop.arrayParsed, loop.itemType) !== null);
11294
- this.loopParamStack.push(loop.param);
11343
+ const prevScope = this.scope;
11344
+ this.scope = prevScope.enterLoopRow(loop);
11295
11345
  let body = "";
11296
11346
  for (const item of items) {
11297
11347
  this.staticLoopItemStack.push({ param: loop.param, item });
@@ -11311,7 +11361,7 @@ ${goFields.join(`
11311
11361
  break;
11312
11362
  }
11313
11363
  }
11314
- this.loopParamStack.pop();
11364
+ this.scope = prevScope;
11315
11365
  this.loopScalarItemStack.pop();
11316
11366
  this.loopKeyDepthStack.pop();
11317
11367
  this.loopWrapperStack.pop();
@@ -11480,8 +11530,7 @@ ${children}`;
11480
11530
  }
11481
11531
  if (this.inLoop) {
11482
11532
  const trimmed = value.expr.trim();
11483
- const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
11484
- if (currentLoopParam && trimmed === currentLoopParam) {
11533
+ if (this.isCurrentLoopItem(trimmed)) {
11485
11534
  return `{{bf_spread_attrs (bf_js_keys .)}}`;
11486
11535
  }
11487
11536
  const restInfo = this.lookupRestExclude(trimmed);