@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/index.js CHANGED
@@ -26,7 +26,8 @@ import {
26
26
  dangerousInnerHtmlMetacharViolation,
27
27
  dangerousInnerHtmlDiagnostic,
28
28
  collectLoopBoundNames as collectLoopBoundNames2,
29
- evaluateStaticLiteral as evaluateStaticLiteral3
29
+ evaluateStaticLiteral as evaluateStaticLiteral3,
30
+ BindingScope
30
31
  } from "@barefootjs/jsx";
31
32
  import { findInterpolationEnd } from "@barefootjs/jsx/scanner";
32
33
  import { BF_REGION, escapeHtml } from "@barefootjs/shared";
@@ -463,6 +464,7 @@ function collectNestedComponents(node, result) {
463
464
  ...loop.childComponent,
464
465
  isDynamic: !loop.isStaticArray,
465
466
  isPropDerived: !!loop.isPropDerivedArray,
467
+ clientOnly: loop.clientOnly,
466
468
  loopKey: loop.key ?? undefined,
467
469
  loopParam: loop.param ?? undefined,
468
470
  bodyChildren: hasBodyChildren ? loop.childComponent.children : undefined,
@@ -2856,7 +2858,7 @@ class GoTemplateAdapter extends BaseAdapter {
2856
2858
  }
2857
2859
  inLoop = false;
2858
2860
  bakedStaticChildLoopCache = new Map;
2859
- loopParamStack = [];
2861
+ scope = BindingScope.EMPTY;
2860
2862
  loopKeyDepthStack = [];
2861
2863
  loopScalarItemStack = [];
2862
2864
  loopWrapperStack = [];
@@ -2914,6 +2916,7 @@ class GoTemplateAdapter extends BaseAdapter {
2914
2916
  this.state.referencedDerivedConsts = new Set;
2915
2917
  this.state.templateVarCounter = 0;
2916
2918
  this.state.pendingChildrenDefines = [];
2919
+ this.scope = BindingScope.EMPTY;
2917
2920
  this.primeCompileState(ir);
2918
2921
  this.state.stringValueNames = collectStringValueNames(ir);
2919
2922
  this.recordDerivedFieldDeps(ir, new Set((ir.metadata.propsParams ?? []).map((p) => p.name)));
@@ -3154,6 +3157,12 @@ ${scriptRegistrations}${templateBody}
3154
3157
  isNestedArrayShadowed(param, nestedArrayFields) {
3155
3158
  return nestedArrayFields.has(capitalizeFieldName(param.name)) || nestedArrayFields.has(capitalizeFieldName(param.sourceName ?? param.name));
3156
3159
  }
3160
+ isOrphanedClientOnlyNested(nested) {
3161
+ return !!nested.clientOnly && !nested.isDynamic && !nested.isPropDerived;
3162
+ }
3163
+ propDerivedNestedArrayFields(nestedComponents) {
3164
+ return new Set(nestedComponents.filter((n) => n.isPropDerived).map((n) => `${n.name}s`));
3165
+ }
3157
3166
  propParamFieldNamesUnion(params) {
3158
3167
  return params.flatMap((p) => [capitalizeFieldName(p.name), capitalizeFieldName(p.sourceName ?? p.name)]);
3159
3168
  }
@@ -3192,7 +3201,7 @@ ${scriptRegistrations}${templateBody}
3192
3201
  recordRepropsSpec(ir, componentName, nestedComponents, spreadSlots) {
3193
3202
  if (!this.childDerivedFieldDeps.has(componentName))
3194
3203
  return;
3195
- const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
3204
+ const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
3196
3205
  const params = (ir.metadata.propsParams ?? []).filter((p) => !this.isNestedArrayShadowed(p, nestedArrayFields));
3197
3206
  const takenInput = new Set(this.propParamFieldNamesUnion(ir.metadata.propsParams ?? []));
3198
3207
  const eligible = nestedComponents.every((n) => n.isDynamic && !n.isPropDerived) && spreadSlots.length === 0 && !ir.metadata.restPropsName && this.nonCollidingContextConsumers(takenInput).length === 0;
@@ -3406,8 +3415,8 @@ ${goFields.join(`
3406
3415
  if (this.usesSearchParams(ir)) {
3407
3416
  lines.push("\tSearchParams bf.SearchParams // Optional: request query for searchParams()");
3408
3417
  }
3409
- const inputNested = nestedComponents.filter((n) => !n.isDynamic || n.isPropDerived);
3410
- const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
3418
+ const inputNested = nestedComponents.filter((n) => (!n.isDynamic || n.isPropDerived) && !this.isOrphanedClientOnlyNested(n));
3419
+ const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
3411
3420
  for (const param of ir.metadata.propsParams) {
3412
3421
  const fieldName = capitalizeFieldName(param.sourceName ?? param.name);
3413
3422
  if (this.isNestedArrayShadowed(param, nestedArrayFields))
@@ -3544,7 +3553,7 @@ ${goFields.join(`
3544
3553
  lines.push(` scopeID = "${componentName}_" + randomID(6)`);
3545
3554
  lines.push("\t}");
3546
3555
  lines.push("");
3547
- const staticNested = nestedComponents.filter((n) => !n.isDynamic || n.isPropDerived);
3556
+ const staticNested = nestedComponents.filter((n) => (!n.isDynamic || n.isPropDerived) && !this.isOrphanedClientOnlyNested(n));
3548
3557
  const emittedWrapperVars = new Set;
3549
3558
  const staticWithBody = staticNested.filter((n) => n.bodyChildren && n.bodyChildren.length > 0);
3550
3559
  const staticWithoutBody = staticNested.filter((n) => !n.bodyChildren || n.bodyChildren.length === 0);
@@ -3626,7 +3635,7 @@ ${goFields.join(`
3626
3635
  if (this.usesSearchParams(ir)) {
3627
3636
  lines.push("\t\tSearchParams: in.SearchParams,");
3628
3637
  }
3629
- const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
3638
+ const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
3630
3639
  const memoFallbacks = new Map;
3631
3640
  for (const memo of ir.metadata.memos) {
3632
3641
  const stripped = memo.computation.replace(/^\(\)\s*=>\s*/, "");
@@ -4124,7 +4133,7 @@ ${goFields.join(`
4124
4133
  }
4125
4134
  }
4126
4135
  emitPropsDataFields(lines, ir, nestedComponents, propTypeOverrides, takenJsonTags) {
4127
- const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
4136
+ const nestedArrayFields = this.propDerivedNestedArrayFields(nestedComponents);
4128
4137
  const propFieldNames = new Set;
4129
4138
  for (const param of ir.metadata.propsParams) {
4130
4139
  const fieldName = capitalizeFieldName(param.name);
@@ -4198,6 +4207,8 @@ ${goFields.join(`
4198
4207
  lines.push(` ${this.contextFieldName(c)} ${this.contextConsumerGoType(c)} \`json:"${jsonTag}"\``);
4199
4208
  }
4200
4209
  for (const nested of nestedComponents) {
4210
+ if (this.isOrphanedClientOnlyNested(nested))
4211
+ continue;
4201
4212
  const elemType = nested.bodyChildren?.length ? this.loopBodyWrapperName(componentName, nested) : `${nested.name}Props`;
4202
4213
  if (nested.isDynamic && !nested.isPropDerived) {
4203
4214
  lines.push(` ${nested.name}s []${elemType} \`json:"-"\``);
@@ -4794,8 +4805,7 @@ ${goFields.join(`
4794
4805
  const inlinedNum = this.resolveModuleNumericConst(name);
4795
4806
  if (inlinedNum !== null)
4796
4807
  return inlinedNum;
4797
- const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
4798
- if (currentLoopParam && name === currentLoopParam)
4808
+ if (this.isCurrentLoopItem(name))
4799
4809
  return ".";
4800
4810
  if (this.isOuterLoopParam(name))
4801
4811
  return `$${name}`;
@@ -4862,16 +4872,16 @@ ${goFields.join(`
4862
4872
  }
4863
4873
  return false;
4864
4874
  }
4875
+ isCurrentLoopItem(name) {
4876
+ const hit = this.scope.lookup(name);
4877
+ return hit !== null && hit.depth === 0 && hit.binding.source === "item";
4878
+ }
4865
4879
  isOuterLoopParam(name) {
4866
- const top = this.loopParamStack.length - 1;
4867
- for (let i = 0;i < top; i++) {
4868
- if (this.loopParamStack[i] === name)
4869
- return true;
4870
- }
4871
- return false;
4880
+ const hit = this.scope.lookup(name);
4881
+ return hit !== null && hit.depth > 0 && hit.binding.source === "item";
4872
4882
  }
4873
4883
  rootFieldRef(name) {
4874
- const prefix = this.loopParamStack.length > 0 ? "$." : ".";
4884
+ const prefix = this.inLoop ? "$." : ".";
4875
4885
  return `${prefix}${capitalizeFieldName(name)}`;
4876
4886
  }
4877
4887
  searchParamsFieldRef(name) {
@@ -4881,9 +4891,8 @@ ${goFields.join(`
4881
4891
  return collectModuleStringConstsShared(constants);
4882
4892
  }
4883
4893
  resolveModuleStringConst(name) {
4884
- if (this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name) {
4894
+ if (this.isCurrentLoopItem(name))
4885
4895
  return null;
4886
- }
4887
4896
  if (this.loopVarRefCount.has(name))
4888
4897
  return null;
4889
4898
  if (this.isOuterLoopParam(name))
@@ -4894,9 +4903,8 @@ ${goFields.join(`
4894
4903
  return `"${escapeGoString(value)}"`;
4895
4904
  }
4896
4905
  resolveModuleNumericConst(name) {
4897
- if (this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name) {
4906
+ if (this.isCurrentLoopItem(name))
4898
4907
  return null;
4899
- }
4900
4908
  if (this.loopVarRefCount.has(name))
4901
4909
  return null;
4902
4910
  if (this.isOuterLoopParam(name))
@@ -4951,7 +4959,7 @@ ${goFields.join(`
4951
4959
  if (property === "length" && object.kind === "call" && object.callee.kind === "identifier" && object.args.length === 0) {
4952
4960
  const slice = this.state.memoBackedLoopSlice.get(object.callee.name);
4953
4961
  if (slice) {
4954
- const prefix = this.loopParamStack.length > 0 ? "$." : ".";
4962
+ const prefix = this.inLoop ? "$." : ".";
4955
4963
  return `len ${prefix}${slice}`;
4956
4964
  }
4957
4965
  }
@@ -4976,8 +4984,7 @@ ${goFields.join(`
4976
4984
  if (staticValue !== null)
4977
4985
  return staticValue;
4978
4986
  }
4979
- const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
4980
- if (object.kind === "identifier" && currentLoopParam && object.name === currentLoopParam) {
4987
+ if (object.kind === "identifier" && this.isCurrentLoopItem(object.name)) {
4981
4988
  return `.${goFieldNameForKey(property)}`;
4982
4989
  }
4983
4990
  const obj = emit(object);
@@ -5739,7 +5746,7 @@ ${goFields.join(`
5739
5746
  return this.renderParsedExpr(parsed);
5740
5747
  }
5741
5748
  isLoopShadowedName(name) {
5742
- 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));
5749
+ return this.scope.isBound(name) || this.loopVarRefCount.has(name);
5743
5750
  }
5744
5751
  loopRowChildPropOverrides(comp) {
5745
5752
  const childShape = this.childComponentShapes.get(comp.name);
@@ -5954,8 +5961,7 @@ ${goFields.join(`
5954
5961
  const inlined = this.resolveModuleStringConst(expr.name);
5955
5962
  if (inlined !== null)
5956
5963
  return plain(inlined);
5957
- const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
5958
- if (currentLoopParam && expr.name === currentLoopParam) {
5964
+ if (this.isCurrentLoopItem(expr.name)) {
5959
5965
  return plain(".");
5960
5966
  }
5961
5967
  if (this.isOuterLoopParam(expr.name)) {
@@ -6012,11 +6018,8 @@ ${goFields.join(`
6012
6018
  if (expr.object.kind === "identifier" && this.state.propsObjectName && expr.object.name === this.state.propsObjectName) {
6013
6019
  return plain(this.rootFieldRef(expr.property));
6014
6020
  }
6015
- {
6016
- const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
6017
- if (expr.object.kind === "identifier" && currentLoopParam && expr.object.name === currentLoopParam) {
6018
- return plain(`.${capitalizeFieldName(expr.property)}`);
6019
- }
6021
+ if (expr.object.kind === "identifier" && this.isCurrentLoopItem(expr.object.name)) {
6022
+ return plain(`.${capitalizeFieldName(expr.property)}`);
6020
6023
  }
6021
6024
  const obj = this.renderConditionExpr(expr.object);
6022
6025
  if (expr.property === "length") {
@@ -6194,12 +6197,12 @@ ${goFields.join(`
6194
6197
  });
6195
6198
  }
6196
6199
  const bakedChildLoop = loop.childComponent ? this.getBakedStaticChildLoop(loop.markerId, loop.childComponent, loop.arrayParsed, loop.param, loop.key ?? undefined) : null;
6197
- const bakedElementLoop = loop.childComponent ? null : analyzeBakeableStaticElementLoop(loop, this.state.localConstants, { isNameShadowed: (name) => this.state.staticLoopSourceBoundNames.has(name) });
6200
+ const bakedElementLoop = loop.childComponent ? null : analyzeBakeableStaticElementLoop(loop, this.state.localConstants, { isNameShadowed: this.scope.asShadowPredicate() });
6198
6201
  if (bakedElementLoop) {
6199
6202
  return this.renderUnrolledStaticElementLoop(loop, bakedElementLoop.items);
6200
6203
  }
6201
6204
  const arrayName = loop.array.trim();
6202
- if (bakedChildLoop === null && /^[A-Za-z_$][\w$]*$/.test(arrayName)) {
6205
+ if (bakedChildLoop === null && /^[A-Za-z_$][\w$]*$/.test(arrayName) && !this.scope.isBound(arrayName)) {
6203
6206
  const arrayConst = this.state.localConstants.find((c) => c.name === arrayName);
6204
6207
  if (arrayConst && !arrayConst.isModule && arrayConst.parsed && !this.isStringExpr(arrayConst.parsed, new Set)) {
6205
6208
  this.state.errors.push({
@@ -6208,7 +6211,8 @@ ${goFields.join(`
6208
6211
  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.`,
6209
6212
  loc: loop.loc ?? this.makeLoc(),
6210
6213
  suggestion: {
6211
- 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."
6214
+ 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.",
6215
+ escape: [{ kind: "prop-precompute" }, { kind: "client-directive" }]
6212
6216
  }
6213
6217
  });
6214
6218
  }
@@ -6227,6 +6231,9 @@ ${goFields.join(`
6227
6231
  }
6228
6232
  const wasInLoopOuter = this.inLoop;
6229
6233
  this.inLoop = true;
6234
+ const scopeLoop = loop.iterationShape === "keys" ? { ...loop, param: "" } : loop;
6235
+ const prevScope = this.scope;
6236
+ this.scope = prevScope.enterLoopRow(scopeLoop);
6230
6237
  const addedLoopVars = [];
6231
6238
  for (const d of loop.preamble?.declarations ?? []) {
6232
6239
  this.loopVarRefCount.set(d.name, (this.loopVarRefCount.get(d.name) ?? 0) + 1);
@@ -6238,17 +6245,14 @@ ${goFields.join(`
6238
6245
  this.loopBindingStack.push(built.bindings);
6239
6246
  this.loopRestExcludeStack.push(built.restExcludes);
6240
6247
  pushedBindingMap = true;
6241
- this.loopParamStack.push("");
6242
6248
  if (rangeIndex !== "_") {
6243
6249
  this.loopVarRefCount.set(rangeIndex, (this.loopVarRefCount.get(rangeIndex) ?? 0) + 1);
6244
6250
  addedLoopVars.push(rangeIndex);
6245
6251
  }
6246
6252
  } else if (loop.iterationShape === "keys") {
6247
- this.loopParamStack.push("");
6248
6253
  this.loopVarRefCount.set(param, (this.loopVarRefCount.get(param) ?? 0) + 1);
6249
6254
  addedLoopVars.push(param);
6250
6255
  } else {
6251
- this.loopParamStack.push(param);
6252
6256
  if (rangeIndex !== "_") {
6253
6257
  this.loopVarRefCount.set(rangeIndex, (this.loopVarRefCount.get(rangeIndex) ?? 0) + 1);
6254
6258
  addedLoopVars.push(rangeIndex);
@@ -6270,7 +6274,7 @@ ${goFields.join(`
6270
6274
  else
6271
6275
  this.loopVarRefCount.set(v, rc);
6272
6276
  }
6273
- this.loopParamStack.pop();
6277
+ this.scope = prevScope;
6274
6278
  if (pushedBindingMap) {
6275
6279
  this.loopBindingStack.pop();
6276
6280
  this.loopRestExcludeStack.pop();
@@ -6302,7 +6306,8 @@ ${goFields.join(`
6302
6306
  this.loopWrapperStack.push(false);
6303
6307
  this.loopKeyDepthStack.push(loop.depth);
6304
6308
  this.loopScalarItemStack.push(this.scalarLiteralLoopGoType(loop.arrayParsed, loop.itemType) !== null);
6305
- this.loopParamStack.push(loop.param);
6309
+ const prevScope = this.scope;
6310
+ this.scope = prevScope.enterLoopRow(loop);
6306
6311
  let body = "";
6307
6312
  for (const item of items) {
6308
6313
  this.staticLoopItemStack.push({ param: loop.param, item });
@@ -6322,7 +6327,7 @@ ${goFields.join(`
6322
6327
  break;
6323
6328
  }
6324
6329
  }
6325
- this.loopParamStack.pop();
6330
+ this.scope = prevScope;
6326
6331
  this.loopScalarItemStack.pop();
6327
6332
  this.loopKeyDepthStack.pop();
6328
6333
  this.loopWrapperStack.pop();
@@ -6491,8 +6496,7 @@ ${children}`;
6491
6496
  }
6492
6497
  if (this.inLoop) {
6493
6498
  const trimmed = value.expr.trim();
6494
- const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
6495
- if (currentLoopParam && trimmed === currentLoopParam) {
6499
+ if (this.isCurrentLoopItem(trimmed)) {
6496
6500
  return `{{bf_spread_attrs (bf_js_keys .)}}`;
6497
6501
  }
6498
6502
  const restInfo = this.lookupRestExclude(trimmed);
@@ -6650,21 +6654,29 @@ var conformancePins = {
6650
6654
  "tag-cloud": [{ code: "BF021", severity: "error" }],
6651
6655
  "preamble-cells": [{ code: "BF021", severity: "error" }],
6652
6656
  "static-array-from-props": [
6653
- { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2321" }
6657
+ {
6658
+ code: "BF101",
6659
+ severity: "error",
6660
+ issue: "https://github.com/piconic-ai/barefootjs/issues/2321"
6661
+ }
6654
6662
  ],
6655
6663
  "static-array-from-props-with-component": [
6656
- { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2321" }
6664
+ {
6665
+ code: "BF101",
6666
+ severity: "error",
6667
+ issue: "https://github.com/piconic-ai/barefootjs/issues/2321"
6668
+ }
6657
6669
  ],
6658
6670
  "filter-nested-callback-predicate": [
6659
6671
  { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2320" }
6660
6672
  ],
6661
- "filter-nested-find-predicate": [
6662
- { code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2320" }
6663
- ],
6673
+ "filter-nested-find-predicate": [{ code: "BF101", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2320" }],
6664
6674
  "date-method-uncatalogued": [{ code: "BF021", severity: "error", issue: "https://github.com/piconic-ai/barefootjs/issues/2356" }]
6665
6675
  };
6666
6676
  // src/render-divergences.ts
6667
- var renderDivergences = {};
6677
+ var renderDivergences = {
6678
+ "static-array-from-props-with-component-precomputed": "prop-backed child-component loop renders the loop host empty at SSR (https://github.com/piconic-ai/barefootjs/issues/2630)"
6679
+ };
6668
6680
  export {
6669
6681
  renderDivergences,
6670
6682
  goTemplateAdapter,
@@ -13,9 +13,15 @@
13
13
  * the docs compatibility-matrix page. Graduating an entry means fixing
14
14
  * the adapter (or the shared compiler layer) and deleting the line.
15
15
  *
16
- * Empty — the file's last live entry (`aliased-destructured-prop`, #2525)
17
- * graduated: the Input struct field is now keyed by `sourceName ?? name`
18
- * (caller-facing), so the caller-side composite literal `go run`s clean.
16
+ * Empty — the file's last live entry (`static-array-from-props-with-
17
+ * component-client`, #2627) graduated: a clientOnly child-component loop
18
+ * whose array is neither a signal/memo nor a direct prop reference (e.g.
19
+ * `Object.entries(props.tags).filter(...)` feeding a `<Tag>` loop) is now
20
+ * excluded entirely from the Input/Props/NewProps codegen instead of
21
+ * colliding with the driving prop's own field — see
22
+ * `GoTemplateAdapter.isOrphanedClientOnlyNested`. The caller-side composite
23
+ * literal for the `tags` prop now type-checks against its own `interface{}`
24
+ * field and `go run`s clean.
19
25
  * Keep the file (and this header) when the set is empty — the next
20
26
  * divergence lands here, not in a re-created file.
21
27
  */
@@ -1 +1 @@
1
- {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAa/B,CAAA"}
1
+ {"version":3,"file":"render-divergences.d.ts","sourceRoot":"","sources":["../src/render-divergences.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD,eAAO,MAAM,iBAAiB,EAAE,iBAiC/B,CAAA"}