@barefootjs/go-template 0.31.9 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -361,6 +361,10 @@ class CompileState {
361
361
  synthObjectStructNames = new Map;
362
362
  needsStringsImport = false;
363
363
  }
364
+ function resolveSignalParsedThroughSeedPlan(state, signal) {
365
+ const step = state.ssrSeedPlan.steps.find((s) => s.kind === "derived" && s.origin === "signal" && s.name === signal.getter);
366
+ return step?.kind === "derived" ? step.parsed : signal.parsed;
367
+ }
364
368
 
365
369
  // src/adapter/analysis/component-tree.ts
366
370
  function hasClientInteractivity(ir) {
@@ -2673,7 +2677,18 @@ function collectNullishConsumedPropNames(ctx, ir) {
2673
2677
  };
2674
2678
  walk(ir.root);
2675
2679
  for (const signal of ir.metadata.signals) {
2676
- const match = ctx.extractPropFallback(signal.initialValue, signal.parsed);
2680
+ const resolvedParsed = resolveSignalParsedThroughSeedPlan(ctx.state, signal);
2681
+ let match = ctx.extractPropFallback(signal.initialValue, resolvedParsed);
2682
+ if (!match) {
2683
+ const collision = ctx.extractCollisionDerivation(resolvedParsed);
2684
+ if (collision) {
2685
+ const collisionParam = ir.metadata.propsParams.find((p) => p.name === collision.propName);
2686
+ const collisionField = collisionParam ? capitalizeFieldName(collisionParam.sourceName ?? collision.propName) : null;
2687
+ if (collisionField && capitalizeFieldName(signal.getter) === collisionField) {
2688
+ match = { propName: collision.propName, goFallback: collision.goFallback };
2689
+ }
2690
+ }
2691
+ }
2677
2692
  if (!match || !optionalParams.has(match.propName))
2678
2693
  continue;
2679
2694
  const f = match.goFallback;
@@ -2859,6 +2874,7 @@ class GoTemplateAdapter extends BaseAdapter {
2859
2874
  convertConditionToGo: (jsCondition, preParsed) => this.convertConditionToGo(jsCondition, preParsed),
2860
2875
  extractPropNameFromInitialValue: (initialValue, preParsed) => this.extractPropNameFromInitialValue(initialValue, preParsed),
2861
2876
  extractPropFallback: (initialValue, preParsed) => this.extractPropFallback(initialValue, preParsed),
2877
+ extractCollisionDerivation: (parsed) => this.extractCollisionDerivation(parsed),
2862
2878
  resolveModuleStringConst: (name) => this.resolveModuleStringConst(name)
2863
2879
  };
2864
2880
  get errors() {
@@ -3637,6 +3653,7 @@ ${goFields.join(`
3637
3653
  }
3638
3654
  lines.push("");
3639
3655
  }
3656
+ this.emitCallerPropsInit(lines, ir, nestedComponents, staticWithoutBody, staticWithBody, dynamicWithBody, emittedWrapperVars, propTypeOverrides);
3640
3657
  lines.push(` return ${propsTypeName}{`);
3641
3658
  lines.push("\t\tScopeID: scopeID,");
3642
3659
  lines.push("\t\tBfParent: in.BfParent,");
@@ -3644,6 +3661,7 @@ ${goFields.join(`
3644
3661
  if (this.usesSearchParams(ir)) {
3645
3662
  lines.push("\t\tSearchParams: in.SearchParams,");
3646
3663
  }
3664
+ lines.push("\t\tBfCallerProps: bfCallerProps,");
3647
3665
  const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
3648
3666
  const memoFallbacks = new Map;
3649
3667
  for (const memo of ir.metadata.memos) {
@@ -3669,7 +3687,8 @@ ${goFields.join(`
3669
3687
  continue;
3670
3688
  const hoisted = propFallbackVars.get(param.name);
3671
3689
  if (hoisted) {
3672
- lines.push(` ${fieldName}: ${hoisted.varName},`);
3690
+ const value = hoisted.collisionWrap ? `${hoisted.varName} ${hoisted.collisionWrap.operator} ${hoisted.collisionWrap.operand}` : hoisted.varName;
3691
+ lines.push(` ${fieldName}: ${value},`);
3673
3692
  } else {
3674
3693
  const paramDefault = goPropDefault(param.defaultValue);
3675
3694
  const memoFold = memoFallbacks.get(fieldName);
@@ -3691,7 +3710,7 @@ ${goFields.join(`
3691
3710
  const fieldName = capitalizeFieldName(signal.getter);
3692
3711
  if (propFieldNames.has(fieldName))
3693
3712
  continue;
3694
- const fallbackMatch = this.extractPropFallback(signal.initialValue, signal.parsed);
3713
+ const fallbackMatch = this.extractPropFallback(signal.initialValue, this.resolvedSignalParsed(signal));
3695
3714
  const hoisted = fallbackMatch ? propFallbackVars.get(fallbackMatch.propName) : undefined;
3696
3715
  if (hoisted) {
3697
3716
  lines.push(` ${fieldName}: ${hoisted.varName},`);
@@ -3748,6 +3767,53 @@ ${goFields.join(`
3748
3767
  lines.push("\t}");
3749
3768
  lines.push("}");
3750
3769
  }
3770
+ emitCallerPropsInit(lines, ir, nestedComponents, staticWithoutBody, staticWithBody, dynamicWithBody, emittedWrapperVars, propTypeOverrides) {
3771
+ lines.push("\tbfCallerProps := map[string]interface{}{}");
3772
+ const bfCallerPropsTakenTags = new Set;
3773
+ const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
3774
+ for (const param of ir.metadata.propsParams) {
3775
+ if (param.name === "children")
3776
+ continue;
3777
+ if (this.isNestedArrayShadowed(param, nestedArrayFields))
3778
+ continue;
3779
+ const callerKey = this.claimJsonTag(this.toJsonTag(param.sourceName ?? param.name), bfCallerPropsTakenTags);
3780
+ if (callerKey === "-")
3781
+ continue;
3782
+ const inputField = `in.${capitalizeFieldName(param.sourceName ?? param.name)}`;
3783
+ const goType = resolvePropGoType(this.emitCtx, param, propTypeOverrides);
3784
+ const isNillable = goType === "interface{}" || goType === "map[string]interface{}" || goType.startsWith("[]");
3785
+ if (param.optional && isNillable) {
3786
+ lines.push(` if ${inputField} != nil {`);
3787
+ lines.push(` bfCallerProps["${callerKey}"] = ${inputField}`);
3788
+ lines.push(` }`);
3789
+ } else {
3790
+ lines.push(` bfCallerProps["${callerKey}"] = ${inputField}`);
3791
+ }
3792
+ }
3793
+ for (const nested of [...staticWithoutBody, ...staticWithBody, ...dynamicWithBody]) {
3794
+ if (!nested.isPropDerived)
3795
+ continue;
3796
+ const varName = `${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`;
3797
+ const isBuilt = staticWithoutBody.includes(nested) || emittedWrapperVars.has(varName);
3798
+ if (!isBuilt)
3799
+ continue;
3800
+ const arrayFieldName = capitalizeFieldName(`${nested.name}s`);
3801
+ const param = ir.metadata.propsParams.find((p) => capitalizeFieldName(p.name) === arrayFieldName || capitalizeFieldName(p.sourceName ?? p.name) === arrayFieldName);
3802
+ if (!param || !this.isNestedArrayShadowed(param, nestedArrayFields))
3803
+ continue;
3804
+ const callerKey = this.claimJsonTag(this.toJsonTag(param.sourceName ?? param.name), bfCallerPropsTakenTags);
3805
+ if (callerKey === "-")
3806
+ continue;
3807
+ if (param.optional) {
3808
+ lines.push(` if in.${nested.name}s != nil {`);
3809
+ lines.push(` bfCallerProps["${callerKey}"] = ${varName}`);
3810
+ lines.push(` }`);
3811
+ } else {
3812
+ lines.push(` bfCallerProps["${callerKey}"] = ${varName}`);
3813
+ }
3814
+ }
3815
+ lines.push("");
3816
+ }
3751
3817
  emitStaticChildInstances(lines, ir) {
3752
3818
  const staticChildren = this.collectStaticChildInstances(ir.root, ir.metadata.propsParams);
3753
3819
  for (const child of staticChildren) {
@@ -4199,6 +4265,7 @@ ${goFields.join(`
4199
4265
  if (this.usesSearchParams(ir)) {
4200
4266
  lines.push('\tSearchParams bf.SearchParams `json:"-"`');
4201
4267
  }
4268
+ lines.push('\tBfCallerProps map[string]interface{} `json:"-"`');
4202
4269
  }
4203
4270
  emitPropsDataFields(lines, ir, nestedComponents, propTypeOverrides, takenJsonTags) {
4204
4271
  const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
@@ -4219,7 +4286,7 @@ ${goFields.join(`
4219
4286
  const fieldName = capitalizeFieldName(signal.getter);
4220
4287
  if (propFieldNames.has(fieldName))
4221
4288
  continue;
4222
- const jsonTag = this.claimJsonTag(this.toJsonTag(signal.getter), takenJsonTags);
4289
+ const jsonTag = "-";
4223
4290
  const synthType = this.state.synthStructTypes.get(signal.getter);
4224
4291
  if (synthType) {
4225
4292
  lines.push(` ${fieldName} ${typeInfoToGo(this.emitCtx, synthType)} \`json:"${jsonTag}"\``);
@@ -4251,7 +4318,7 @@ ${goFields.join(`
4251
4318
  const fieldName = capitalizeFieldName(memo.name);
4252
4319
  if (propFieldNames.has(fieldName))
4253
4320
  continue;
4254
- const jsonTag = this.claimJsonTag(this.toJsonTag(memo.name), takenJsonTags);
4321
+ const jsonTag = "-";
4255
4322
  const goType = this.inferMemoType(memo, ir.metadata.signals, propsParamMap);
4256
4323
  lines.push(` ${fieldName} ${goType} \`json:"${jsonTag}"\``);
4257
4324
  }
@@ -4271,15 +4338,22 @@ ${goFields.join(`
4271
4338
  ...ir.metadata.memos.map((m) => capitalizeFieldName(m.name))
4272
4339
  ]);
4273
4340
  for (const c of this.nonCollidingContextConsumers(takenProps)) {
4274
- const jsonTag = this.claimJsonTag(this.toJsonTag(c.localName), takenJsonTags);
4341
+ const jsonTag = "-";
4275
4342
  lines.push(` ${this.contextFieldName(c)} ${this.contextConsumerGoType(c)} \`json:"${jsonTag}"\``);
4276
4343
  }
4344
+ const propDrivingFieldNames = new Set;
4345
+ for (const p of ir.metadata.propsParams) {
4346
+ propDrivingFieldNames.add(capitalizeFieldName(p.name));
4347
+ propDrivingFieldNames.add(capitalizeFieldName(p.sourceName ?? p.name));
4348
+ }
4277
4349
  for (const nested of nestedComponents) {
4278
4350
  if (this.isOrphanedClientOnlyNested(nested))
4279
4351
  continue;
4280
4352
  const elemType = nested.bodyChildren?.length ? this.loopBodyWrapperName(componentName, nested) : `${nested.name}Props`;
4281
4353
  if (nested.isDynamic && !nested.isPropDerived) {
4282
4354
  lines.push(` ${nested.name}s []${elemType} \`json:"-"\``);
4355
+ } else if (nested.isDynamic && nested.isPropDerived && !propDrivingFieldNames.has(`${nested.name}s`)) {
4356
+ lines.push(` ${nested.name}s []${elemType} \`json:"-"\``);
4283
4357
  } else {
4284
4358
  const jsonTag = this.claimJsonTag(this.toJsonTag(`${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`), takenJsonTags);
4285
4359
  lines.push(` ${nested.name}s []${elemType} \`json:"${jsonTag}"\``);
@@ -4290,7 +4364,7 @@ ${goFields.join(`
4290
4364
  lines.push(` ${child.fieldName} ${child.name}Props \`json:"-"\``);
4291
4365
  }
4292
4366
  for (const slot of spreadSlots) {
4293
- const jsonTag = this.claimJsonTag(this.toJsonTag(slot.slotId), takenJsonTags);
4367
+ const jsonTag = "-";
4294
4368
  lines.push(` ${slot.slotId} map[string]any \`json:"${jsonTag}"\``);
4295
4369
  }
4296
4370
  }
@@ -4603,7 +4677,19 @@ ${goFields.join(`
4603
4677
  }
4604
4678
  const propTypeOverrides = buildPropTypeOverrides(this.emitCtx, ir);
4605
4679
  for (const signal of ir.metadata.signals) {
4606
- const match = this.extractPropFallback(signal.initialValue, signal.parsed);
4680
+ let match = this.extractPropFallback(signal.initialValue, this.resolvedSignalParsed(signal));
4681
+ let collisionWrap;
4682
+ if (!match) {
4683
+ const collision = this.extractCollisionDerivation(this.resolvedSignalParsed(signal));
4684
+ if (collision) {
4685
+ const collisionParam = ir.metadata.propsParams.find((p) => p.name === collision.propName);
4686
+ const collisionField = collisionParam ? capitalizeFieldName(collisionParam.sourceName ?? collision.propName) : null;
4687
+ if (collisionField && capitalizeFieldName(signal.getter) === collisionField) {
4688
+ match = { propName: collision.propName, goFallback: collision.goFallback };
4689
+ collisionWrap = { operator: collision.operator, operand: collision.operand };
4690
+ }
4691
+ }
4692
+ }
4607
4693
  if (!match)
4608
4694
  continue;
4609
4695
  if (result.has(match.propName))
@@ -4642,11 +4728,15 @@ ${goFields.join(`
4642
4728
  fieldName,
4643
4729
  goFallback: match.goFallback,
4644
4730
  zeroLiteral,
4645
- ...nullishLowered ? { assertType: concreteType } : {}
4731
+ ...nullishLowered ? { assertType: concreteType } : {},
4732
+ ...collisionWrap ? { collisionWrap } : {}
4646
4733
  });
4647
4734
  }
4648
4735
  return result;
4649
4736
  }
4737
+ resolvedSignalParsed(signal) {
4738
+ return resolveSignalParsedThroughSeedPlan(this.state, signal);
4739
+ }
4650
4740
  extractPropFallback(initialValue, preParsed) {
4651
4741
  const structural = preParsed ? this.extractPropFallbackFromParsed(preParsed) : null;
4652
4742
  if (structural)
@@ -4689,6 +4779,26 @@ ${goFields.join(`
4689
4779
  return null;
4690
4780
  return { propName, goFallback };
4691
4781
  }
4782
+ extractCollisionDerivation(parsed) {
4783
+ if (!parsed || parsed.kind !== "binary")
4784
+ return null;
4785
+ if (!["*", "+", "-", "/"].includes(parsed.op))
4786
+ return null;
4787
+ const { right } = parsed;
4788
+ if (right.kind !== "literal" || right.literalType !== "number" || typeof right.value !== "number" || !Number.isInteger(right.value) || right.value < 0) {
4789
+ return null;
4790
+ }
4791
+ if (parsed.op === "/" && right.value === 0)
4792
+ return null;
4793
+ const coalesce = parsed.left;
4794
+ if (coalesce.kind !== "logical" || coalesce.op !== "??" || coalesce.right.kind !== "literal" || coalesce.right.literalType !== "number") {
4795
+ return null;
4796
+ }
4797
+ const inner = this.extractPropFallbackFromParsed(parsed.left);
4798
+ if (!inner)
4799
+ return null;
4800
+ return { ...inner, operator: parsed.op, operand: String(right.value) };
4801
+ }
4692
4802
  extractPropNameFromInitialValue(initialValue, preParsed) {
4693
4803
  if (!this.state.propsObjectName) {
4694
4804
  if (preParsed?.kind === "logical" && (preParsed.op === "??" || preParsed.op === "||") && preParsed.left.kind === "identifier") {
@@ -6,13 +6,13 @@
6
6
  * file even when the set is empty — the next divergence lands here, not in
7
7
  * a re-created file.
8
8
  *
9
- * Empty — #2630's `static-array-from-props-with-component-precomputed`
10
- * divergence graduated once the harness (`test-render.ts`'s
9
+ * (#2630's `static-array-from-props-with-component-precomputed` divergence
10
+ * graduated once the harness (`test-render.ts`'s
11
11
  * `buildDynamicChildLoopSeeding`, despite the name — see its doc comment)
12
12
  * learned to seed a prop-backed static child-component loop's Props slice
13
13
  * the same way it already seeded a signal-backed dynamic one: the adapter's
14
14
  * own `emission` was never the bug, only this harness's route-handler
15
- * stand-in was missing the prop-derived case.
15
+ * stand-in was missing the prop-derived case.)
16
16
  */
17
17
  import type { RenderDivergences } from '@barefootjs/jsx';
18
18
  export declare const renderDivergences: RenderDivergences;
package/dist/vite.js CHANGED
@@ -3929,6 +3929,38 @@ function classify(name, origin, expr, parsed, available) {
3929
3929
  }
3930
3930
  return { kind: "derived", name, origin, expr, parsed, frees: [...frees] };
3931
3931
  }
3932
+ function localConstExprsByName(metadata) {
3933
+ const out = new Map;
3934
+ for (const c of metadata.localConstants ?? []) {
3935
+ if (c.isModule || c.declarationKind !== "const" || c.value === undefined)
3936
+ continue;
3937
+ out.set(c.name, c.parsed ?? parseExpression(c.value.trim()));
3938
+ }
3939
+ return out;
3940
+ }
3941
+ function resolveThroughLocalConsts(parsed, localConsts) {
3942
+ let current = parsed;
3943
+ const maxIter = localConsts.size + 1;
3944
+ for (let i = 0;i < maxIter; i++) {
3945
+ const frees = freeIdentifiers(current);
3946
+ if (frees === null)
3947
+ break;
3948
+ let changed = false;
3949
+ for (const name of frees) {
3950
+ const value = localConsts.get(name);
3951
+ if (!value)
3952
+ continue;
3953
+ const inlined = inlineBinding(current, name, value);
3954
+ if (inlined === null)
3955
+ continue;
3956
+ current = inlined;
3957
+ changed = true;
3958
+ }
3959
+ if (!changed)
3960
+ break;
3961
+ }
3962
+ return current;
3963
+ }
3932
3964
  function computeSsrSeedPlan(metadata) {
3933
3965
  const baseScope = metadata.propsParams.map((p) => p.name);
3934
3966
  if (metadata.propsObjectName)
@@ -3937,6 +3969,7 @@ function computeSsrSeedPlan(metadata) {
3937
3969
  baseScope.push(name);
3938
3970
  }
3939
3971
  const available = new Set(baseScope);
3972
+ const localConsts = localConstExprsByName(metadata);
3940
3973
  const steps = [];
3941
3974
  for (const signal of metadata.signals) {
3942
3975
  if (signal.envReader) {
@@ -3948,13 +3981,13 @@ function computeSsrSeedPlan(metadata) {
3948
3981
  }
3949
3982
  }
3950
3983
  const expr = signal.initialValue.trim();
3951
- steps.push(expr === "" ? { kind: "opaque", name: signal.getter, origin: "signal" } : classify(signal.getter, "signal", expr, parseExpression(expr), available));
3984
+ steps.push(expr === "" ? { kind: "opaque", name: signal.getter, origin: "signal" } : classify(signal.getter, "signal", expr, resolveThroughLocalConsts(parseExpression(expr), localConsts), available));
3952
3985
  available.add(signal.getter);
3953
3986
  }
3954
3987
  for (const memo of metadata.memos) {
3955
3988
  const body = extractArrowBodyExpression(memo.computation);
3956
3989
  const expr = body?.trim() ?? "";
3957
- steps.push(expr === "" ? { kind: "opaque", name: memo.name, origin: "memo" } : classify(memo.name, "memo", expr, memo.parsed ?? parseExpression(expr), available));
3990
+ steps.push(expr === "" ? { kind: "opaque", name: memo.name, origin: "memo" } : classify(memo.name, "memo", expr, resolveThroughLocalConsts(memo.parsed ?? parseExpression(expr), localConsts), available));
3958
3991
  available.add(memo.name);
3959
3992
  }
3960
3993
  return { baseScope, steps };
@@ -5458,6 +5491,10 @@ class CompileState {
5458
5491
  synthObjectStructNames = new Map;
5459
5492
  needsStringsImport = false;
5460
5493
  }
5494
+ function resolveSignalParsedThroughSeedPlan(state, signal) {
5495
+ const step = state.ssrSeedPlan.steps.find((s) => s.kind === "derived" && s.origin === "signal" && s.name === signal.getter);
5496
+ return step?.kind === "derived" ? step.parsed : signal.parsed;
5497
+ }
5461
5498
 
5462
5499
  // src/adapter/analysis/component-tree.ts
5463
5500
  function hasClientInteractivity(ir) {
@@ -7740,7 +7777,18 @@ function collectNullishConsumedPropNames(ctx, ir) {
7740
7777
  };
7741
7778
  walk(ir.root);
7742
7779
  for (const signal of ir.metadata.signals) {
7743
- const match = ctx.extractPropFallback(signal.initialValue, signal.parsed);
7780
+ const resolvedParsed = resolveSignalParsedThroughSeedPlan(ctx.state, signal);
7781
+ let match = ctx.extractPropFallback(signal.initialValue, resolvedParsed);
7782
+ if (!match) {
7783
+ const collision = ctx.extractCollisionDerivation(resolvedParsed);
7784
+ if (collision) {
7785
+ const collisionParam = ir.metadata.propsParams.find((p) => p.name === collision.propName);
7786
+ const collisionField = collisionParam ? capitalizeFieldName(collisionParam.sourceName ?? collision.propName) : null;
7787
+ if (collisionField && capitalizeFieldName(signal.getter) === collisionField) {
7788
+ match = { propName: collision.propName, goFallback: collision.goFallback };
7789
+ }
7790
+ }
7791
+ }
7744
7792
  if (!match || !optionalParams.has(match.propName))
7745
7793
  continue;
7746
7794
  const f = match.goFallback;
@@ -7925,6 +7973,7 @@ class GoTemplateAdapter extends BaseAdapter {
7925
7973
  convertConditionToGo: (jsCondition, preParsed) => this.convertConditionToGo(jsCondition, preParsed),
7926
7974
  extractPropNameFromInitialValue: (initialValue, preParsed) => this.extractPropNameFromInitialValue(initialValue, preParsed),
7927
7975
  extractPropFallback: (initialValue, preParsed) => this.extractPropFallback(initialValue, preParsed),
7976
+ extractCollisionDerivation: (parsed) => this.extractCollisionDerivation(parsed),
7928
7977
  resolveModuleStringConst: (name) => this.resolveModuleStringConst(name)
7929
7978
  };
7930
7979
  get errors() {
@@ -8703,6 +8752,7 @@ ${goFields.join(`
8703
8752
  }
8704
8753
  lines.push("");
8705
8754
  }
8755
+ this.emitCallerPropsInit(lines, ir, nestedComponents, staticWithoutBody, staticWithBody, dynamicWithBody, emittedWrapperVars, propTypeOverrides);
8706
8756
  lines.push(` return ${propsTypeName}{`);
8707
8757
  lines.push("\t\tScopeID: scopeID,");
8708
8758
  lines.push("\t\tBfParent: in.BfParent,");
@@ -8710,6 +8760,7 @@ ${goFields.join(`
8710
8760
  if (this.usesSearchParams(ir)) {
8711
8761
  lines.push("\t\tSearchParams: in.SearchParams,");
8712
8762
  }
8763
+ lines.push("\t\tBfCallerProps: bfCallerProps,");
8713
8764
  const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
8714
8765
  const memoFallbacks = new Map;
8715
8766
  for (const memo of ir.metadata.memos) {
@@ -8735,7 +8786,8 @@ ${goFields.join(`
8735
8786
  continue;
8736
8787
  const hoisted = propFallbackVars.get(param.name);
8737
8788
  if (hoisted) {
8738
- lines.push(` ${fieldName}: ${hoisted.varName},`);
8789
+ const value = hoisted.collisionWrap ? `${hoisted.varName} ${hoisted.collisionWrap.operator} ${hoisted.collisionWrap.operand}` : hoisted.varName;
8790
+ lines.push(` ${fieldName}: ${value},`);
8739
8791
  } else {
8740
8792
  const paramDefault = goPropDefault(param.defaultValue);
8741
8793
  const memoFold = memoFallbacks.get(fieldName);
@@ -8757,7 +8809,7 @@ ${goFields.join(`
8757
8809
  const fieldName = capitalizeFieldName(signal.getter);
8758
8810
  if (propFieldNames.has(fieldName))
8759
8811
  continue;
8760
- const fallbackMatch = this.extractPropFallback(signal.initialValue, signal.parsed);
8812
+ const fallbackMatch = this.extractPropFallback(signal.initialValue, this.resolvedSignalParsed(signal));
8761
8813
  const hoisted = fallbackMatch ? propFallbackVars.get(fallbackMatch.propName) : undefined;
8762
8814
  if (hoisted) {
8763
8815
  lines.push(` ${fieldName}: ${hoisted.varName},`);
@@ -8814,6 +8866,53 @@ ${goFields.join(`
8814
8866
  lines.push("\t}");
8815
8867
  lines.push("}");
8816
8868
  }
8869
+ emitCallerPropsInit(lines, ir, nestedComponents, staticWithoutBody, staticWithBody, dynamicWithBody, emittedWrapperVars, propTypeOverrides) {
8870
+ lines.push("\tbfCallerProps := map[string]interface{}{}");
8871
+ const bfCallerPropsTakenTags = new Set;
8872
+ const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
8873
+ for (const param of ir.metadata.propsParams) {
8874
+ if (param.name === "children")
8875
+ continue;
8876
+ if (this.isNestedArrayShadowed(param, nestedArrayFields))
8877
+ continue;
8878
+ const callerKey = this.claimJsonTag(this.toJsonTag(param.sourceName ?? param.name), bfCallerPropsTakenTags);
8879
+ if (callerKey === "-")
8880
+ continue;
8881
+ const inputField = `in.${capitalizeFieldName(param.sourceName ?? param.name)}`;
8882
+ const goType = resolvePropGoType(this.emitCtx, param, propTypeOverrides);
8883
+ const isNillable = goType === "interface{}" || goType === "map[string]interface{}" || goType.startsWith("[]");
8884
+ if (param.optional && isNillable) {
8885
+ lines.push(` if ${inputField} != nil {`);
8886
+ lines.push(` bfCallerProps["${callerKey}"] = ${inputField}`);
8887
+ lines.push(` }`);
8888
+ } else {
8889
+ lines.push(` bfCallerProps["${callerKey}"] = ${inputField}`);
8890
+ }
8891
+ }
8892
+ for (const nested of [...staticWithoutBody, ...staticWithBody, ...dynamicWithBody]) {
8893
+ if (!nested.isPropDerived)
8894
+ continue;
8895
+ const varName = `${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`;
8896
+ const isBuilt = staticWithoutBody.includes(nested) || emittedWrapperVars.has(varName);
8897
+ if (!isBuilt)
8898
+ continue;
8899
+ const arrayFieldName = capitalizeFieldName(`${nested.name}s`);
8900
+ const param = ir.metadata.propsParams.find((p) => capitalizeFieldName(p.name) === arrayFieldName || capitalizeFieldName(p.sourceName ?? p.name) === arrayFieldName);
8901
+ if (!param || !this.isNestedArrayShadowed(param, nestedArrayFields))
8902
+ continue;
8903
+ const callerKey = this.claimJsonTag(this.toJsonTag(param.sourceName ?? param.name), bfCallerPropsTakenTags);
8904
+ if (callerKey === "-")
8905
+ continue;
8906
+ if (param.optional) {
8907
+ lines.push(` if in.${nested.name}s != nil {`);
8908
+ lines.push(` bfCallerProps["${callerKey}"] = ${varName}`);
8909
+ lines.push(` }`);
8910
+ } else {
8911
+ lines.push(` bfCallerProps["${callerKey}"] = ${varName}`);
8912
+ }
8913
+ }
8914
+ lines.push("");
8915
+ }
8817
8916
  emitStaticChildInstances(lines, ir) {
8818
8917
  const staticChildren = this.collectStaticChildInstances(ir.root, ir.metadata.propsParams);
8819
8918
  for (const child of staticChildren) {
@@ -9265,6 +9364,7 @@ ${goFields.join(`
9265
9364
  if (this.usesSearchParams(ir)) {
9266
9365
  lines.push('\tSearchParams bf.SearchParams `json:"-"`');
9267
9366
  }
9367
+ lines.push('\tBfCallerProps map[string]interface{} `json:"-"`');
9268
9368
  }
9269
9369
  emitPropsDataFields(lines, ir, nestedComponents, propTypeOverrides, takenJsonTags) {
9270
9370
  const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
@@ -9285,7 +9385,7 @@ ${goFields.join(`
9285
9385
  const fieldName = capitalizeFieldName(signal.getter);
9286
9386
  if (propFieldNames.has(fieldName))
9287
9387
  continue;
9288
- const jsonTag = this.claimJsonTag(this.toJsonTag(signal.getter), takenJsonTags);
9388
+ const jsonTag = "-";
9289
9389
  const synthType = this.state.synthStructTypes.get(signal.getter);
9290
9390
  if (synthType) {
9291
9391
  lines.push(` ${fieldName} ${typeInfoToGo(this.emitCtx, synthType)} \`json:"${jsonTag}"\``);
@@ -9317,7 +9417,7 @@ ${goFields.join(`
9317
9417
  const fieldName = capitalizeFieldName(memo.name);
9318
9418
  if (propFieldNames.has(fieldName))
9319
9419
  continue;
9320
- const jsonTag = this.claimJsonTag(this.toJsonTag(memo.name), takenJsonTags);
9420
+ const jsonTag = "-";
9321
9421
  const goType = this.inferMemoType(memo, ir.metadata.signals, propsParamMap);
9322
9422
  lines.push(` ${fieldName} ${goType} \`json:"${jsonTag}"\``);
9323
9423
  }
@@ -9337,15 +9437,22 @@ ${goFields.join(`
9337
9437
  ...ir.metadata.memos.map((m) => capitalizeFieldName(m.name))
9338
9438
  ]);
9339
9439
  for (const c of this.nonCollidingContextConsumers(takenProps)) {
9340
- const jsonTag = this.claimJsonTag(this.toJsonTag(c.localName), takenJsonTags);
9440
+ const jsonTag = "-";
9341
9441
  lines.push(` ${this.contextFieldName(c)} ${this.contextConsumerGoType(c)} \`json:"${jsonTag}"\``);
9342
9442
  }
9443
+ const propDrivingFieldNames = new Set;
9444
+ for (const p of ir.metadata.propsParams) {
9445
+ propDrivingFieldNames.add(capitalizeFieldName(p.name));
9446
+ propDrivingFieldNames.add(capitalizeFieldName(p.sourceName ?? p.name));
9447
+ }
9343
9448
  for (const nested of nestedComponents) {
9344
9449
  if (this.isOrphanedClientOnlyNested(nested))
9345
9450
  continue;
9346
9451
  const elemType = nested.bodyChildren?.length ? this.loopBodyWrapperName(componentName, nested) : `${nested.name}Props`;
9347
9452
  if (nested.isDynamic && !nested.isPropDerived) {
9348
9453
  lines.push(` ${nested.name}s []${elemType} \`json:"-"\``);
9454
+ } else if (nested.isDynamic && nested.isPropDerived && !propDrivingFieldNames.has(`${nested.name}s`)) {
9455
+ lines.push(` ${nested.name}s []${elemType} \`json:"-"\``);
9349
9456
  } else {
9350
9457
  const jsonTag = this.claimJsonTag(this.toJsonTag(`${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`), takenJsonTags);
9351
9458
  lines.push(` ${nested.name}s []${elemType} \`json:"${jsonTag}"\``);
@@ -9356,7 +9463,7 @@ ${goFields.join(`
9356
9463
  lines.push(` ${child.fieldName} ${child.name}Props \`json:"-"\``);
9357
9464
  }
9358
9465
  for (const slot of spreadSlots) {
9359
- const jsonTag = this.claimJsonTag(this.toJsonTag(slot.slotId), takenJsonTags);
9466
+ const jsonTag = "-";
9360
9467
  lines.push(` ${slot.slotId} map[string]any \`json:"${jsonTag}"\``);
9361
9468
  }
9362
9469
  }
@@ -9669,7 +9776,19 @@ ${goFields.join(`
9669
9776
  }
9670
9777
  const propTypeOverrides = buildPropTypeOverrides(this.emitCtx, ir);
9671
9778
  for (const signal of ir.metadata.signals) {
9672
- const match = this.extractPropFallback(signal.initialValue, signal.parsed);
9779
+ let match = this.extractPropFallback(signal.initialValue, this.resolvedSignalParsed(signal));
9780
+ let collisionWrap;
9781
+ if (!match) {
9782
+ const collision = this.extractCollisionDerivation(this.resolvedSignalParsed(signal));
9783
+ if (collision) {
9784
+ const collisionParam = ir.metadata.propsParams.find((p) => p.name === collision.propName);
9785
+ const collisionField = collisionParam ? capitalizeFieldName(collisionParam.sourceName ?? collision.propName) : null;
9786
+ if (collisionField && capitalizeFieldName(signal.getter) === collisionField) {
9787
+ match = { propName: collision.propName, goFallback: collision.goFallback };
9788
+ collisionWrap = { operator: collision.operator, operand: collision.operand };
9789
+ }
9790
+ }
9791
+ }
9673
9792
  if (!match)
9674
9793
  continue;
9675
9794
  if (result.has(match.propName))
@@ -9708,11 +9827,15 @@ ${goFields.join(`
9708
9827
  fieldName,
9709
9828
  goFallback: match.goFallback,
9710
9829
  zeroLiteral,
9711
- ...nullishLowered ? { assertType: concreteType } : {}
9830
+ ...nullishLowered ? { assertType: concreteType } : {},
9831
+ ...collisionWrap ? { collisionWrap } : {}
9712
9832
  });
9713
9833
  }
9714
9834
  return result;
9715
9835
  }
9836
+ resolvedSignalParsed(signal) {
9837
+ return resolveSignalParsedThroughSeedPlan(this.state, signal);
9838
+ }
9716
9839
  extractPropFallback(initialValue, preParsed) {
9717
9840
  const structural = preParsed ? this.extractPropFallbackFromParsed(preParsed) : null;
9718
9841
  if (structural)
@@ -9755,6 +9878,26 @@ ${goFields.join(`
9755
9878
  return null;
9756
9879
  return { propName, goFallback };
9757
9880
  }
9881
+ extractCollisionDerivation(parsed) {
9882
+ if (!parsed || parsed.kind !== "binary")
9883
+ return null;
9884
+ if (!["*", "+", "-", "/"].includes(parsed.op))
9885
+ return null;
9886
+ const { right } = parsed;
9887
+ if (right.kind !== "literal" || right.literalType !== "number" || typeof right.value !== "number" || !Number.isInteger(right.value) || right.value < 0) {
9888
+ return null;
9889
+ }
9890
+ if (parsed.op === "/" && right.value === 0)
9891
+ return null;
9892
+ const coalesce = parsed.left;
9893
+ if (coalesce.kind !== "logical" || coalesce.op !== "??" || coalesce.right.kind !== "literal" || coalesce.right.literalType !== "number") {
9894
+ return null;
9895
+ }
9896
+ const inner = this.extractPropFallbackFromParsed(parsed.left);
9897
+ if (!inner)
9898
+ return null;
9899
+ return { ...inner, operator: parsed.op, operand: String(right.value) };
9900
+ }
9758
9901
  extractPropNameFromInitialValue(initialValue, preParsed) {
9759
9902
  if (!this.state.propsObjectName) {
9760
9903
  if (preParsed?.kind === "logical" && (preParsed.op === "??" || preParsed.op === "||") && preParsed.left.kind === "identifier") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/go-template",
3
- "version": "0.31.9",
3
+ "version": "0.32.0",
4
4
  "description": "Go html/template adapter for BarefootJS - generates Go template files from IR",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,7 +49,7 @@
49
49
  "directory": "packages/adapter-go-template"
50
50
  },
51
51
  "dependencies": {
52
- "@barefootjs/shared": "0.31.9"
52
+ "@barefootjs/shared": "0.32.0"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@barefootjs/jsx": ">=0.2.0",
@@ -67,9 +67,9 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "@barefootjs/adapter-tests": "0.1.0",
70
- "@barefootjs/client": "0.31.9",
71
- "@barefootjs/jsx": "0.31.9",
72
- "@barefootjs/vite": "0.31.9",
70
+ "@barefootjs/client": "0.32.0",
71
+ "@barefootjs/jsx": "0.32.0",
72
+ "@barefootjs/vite": "0.32.0",
73
73
  "vite": "^6.0.0"
74
74
  }
75
75
  }