@barefootjs/go-template 0.32.0 → 0.33.1

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
@@ -209,7 +209,7 @@ function convertNode(node, raw) {
209
209
  }
210
210
  if (n === undefined || Number.isNaN(n)) {
211
211
  const parsedDepth = convertNode(depthNode, raw);
212
- if (checkSupport(parsedDepth).supported) {
212
+ if (checkSupport(parsedDepth, "rendered").supported) {
213
213
  depthExpr = parsedDepth;
214
214
  flatDepth = 1;
215
215
  } else {
@@ -303,10 +303,12 @@ function convertNode(node, raw) {
303
303
  const k = objectLiteralKeyName(prop.name);
304
304
  if (k === null)
305
305
  return { kind: "unsupported", raw, reason: `Unsupported syntax: ${ts.SyntaxKind[node.kind]}` };
306
- properties.push({ key: k.key, keyKind: k.keyKind, shorthand: false, value: convertNode(prop.initializer, raw) });
306
+ properties.push({ kind: "prop", key: k.key, keyKind: k.keyKind, shorthand: false, value: convertNode(prop.initializer, raw) });
307
307
  } else if (ts.isShorthandPropertyAssignment(prop)) {
308
308
  const key = prop.name.text;
309
- properties.push({ key, keyKind: "identifier", shorthand: true, value: { kind: "identifier", name: key } });
309
+ properties.push({ kind: "prop", key, keyKind: "identifier", shorthand: true, value: { kind: "identifier", name: key } });
310
+ } else if (ts.isSpreadAssignment(prop)) {
311
+ properties.push({ kind: "spread", expr: convertNode(prop.expression, raw) });
310
312
  } else {
311
313
  return { kind: "unsupported", raw, reason: `Unsupported syntax: ${ts.SyntaxKind[node.kind]}` };
312
314
  }
@@ -986,14 +988,26 @@ function getUnaryOperatorString(op) {
986
988
  }
987
989
  }
988
990
  function isSupported(expr) {
989
- return checkSupport(expr);
991
+ return checkSupport(expr, "rendered");
990
992
  }
991
- function checkSupport(expr) {
993
+ function isSupportedValue(expr) {
994
+ return checkSupport(expr, "value");
995
+ }
996
+ function checkSupport(expr, pos) {
992
997
  switch (expr.kind) {
993
998
  case "unsupported":
994
999
  return { supported: false, reason: expr.reason };
995
- case "object-literal":
996
- return { supported: false, reason: "Unsupported syntax: ObjectLiteralExpression" };
1000
+ case "object-literal": {
1001
+ if (pos !== "value") {
1002
+ return { supported: false, reason: "Unsupported syntax: ObjectLiteralExpression" };
1003
+ }
1004
+ for (const prop of expr.properties) {
1005
+ const propSupport = checkSupport(prop.kind === "spread" ? prop.expr : prop.value, pos);
1006
+ if (!propSupport.supported)
1007
+ return propSupport;
1008
+ }
1009
+ return { supported: true, level: "L2" };
1010
+ }
997
1011
  case "identifier":
998
1012
  return { supported: true, level: "L1" };
999
1013
  case "literal":
@@ -1003,7 +1017,7 @@ function checkSupport(expr) {
1003
1017
  return { supported: false, reason: "Standalone arrow functions / regex literals are not supported" };
1004
1018
  case "array-literal": {
1005
1019
  for (const el of expr.elements) {
1006
- const elSupport = checkSupport(el);
1020
+ const elSupport = checkSupport(el, pos);
1007
1021
  if (!elSupport.supported)
1008
1022
  return elSupport;
1009
1023
  }
@@ -1016,16 +1030,16 @@ function checkSupport(expr) {
1016
1030
  reason: `String.prototype.${expr.method} supports only a string pattern + string replacement (the regex form is deferred); use a string pattern or wrap the expression in /* @client */`
1017
1031
  };
1018
1032
  }
1019
- const objSupport = checkSupport(expr.object);
1033
+ const objSupport = checkSupport(expr.object, pos);
1020
1034
  if (!objSupport.supported)
1021
1035
  return objSupport;
1022
1036
  for (const arg of expr.args) {
1023
- const argSupport = checkSupport(arg);
1037
+ const argSupport = checkSupport(arg, pos);
1024
1038
  if (!argSupport.supported)
1025
1039
  return argSupport;
1026
1040
  }
1027
1041
  if (expr.method === "flat" && expr.depthExpr) {
1028
- const depthSupport = checkSupport(expr.depthExpr);
1042
+ const depthSupport = checkSupport(expr.depthExpr, pos);
1029
1043
  if (!depthSupport.supported)
1030
1044
  return depthSupport;
1031
1045
  }
@@ -1034,10 +1048,10 @@ function checkSupport(expr) {
1034
1048
  case "call": {
1035
1049
  const cb = asCallbackMethodCall(expr);
1036
1050
  if (cb) {
1037
- const objSupport = checkSupport(cb.object);
1051
+ const objSupport = checkSupport(cb.object, pos);
1038
1052
  if (!objSupport.supported)
1039
1053
  return objSupport;
1040
- const bodySupport = checkSupport(cb.arrow.body);
1054
+ const bodySupport = checkSupport(cb.arrow.body, pos);
1041
1055
  if (!bodySupport.supported) {
1042
1056
  return {
1043
1057
  supported: false,
@@ -1046,13 +1060,13 @@ function checkSupport(expr) {
1046
1060
  };
1047
1061
  }
1048
1062
  for (const rest of cb.args) {
1049
- const restSupport = checkSupport(rest);
1063
+ const restSupport = checkSupport(rest, pos);
1050
1064
  if (!restSupport.supported)
1051
1065
  return restSupport;
1052
1066
  }
1053
1067
  return { supported: true, level: "L5" };
1054
1068
  }
1055
- const calleeSupport = checkSupport(expr.callee);
1069
+ const calleeSupport = checkSupport(expr.callee, pos);
1056
1070
  if (!calleeSupport.supported) {
1057
1071
  return calleeSupport;
1058
1072
  }
@@ -1071,7 +1085,7 @@ function checkSupport(expr) {
1071
1085
  return { supported: true, level: "L1" };
1072
1086
  }
1073
1087
  for (const arg of expr.args) {
1074
- const argSupport = checkSupport(arg);
1088
+ const argSupport = checkSupport(arg, pos);
1075
1089
  if (!argSupport.supported) {
1076
1090
  return argSupport;
1077
1091
  }
@@ -1079,7 +1093,7 @@ function checkSupport(expr) {
1079
1093
  return { supported: true, level: "L2" };
1080
1094
  }
1081
1095
  case "member": {
1082
- const objSupport = checkSupport(expr.object);
1096
+ const objSupport = checkSupport(expr.object, pos);
1083
1097
  if (!objSupport.supported) {
1084
1098
  return objSupport;
1085
1099
  }
@@ -1089,19 +1103,19 @@ function checkSupport(expr) {
1089
1103
  return { supported: true, level: "L2" };
1090
1104
  }
1091
1105
  case "index-access": {
1092
- const objSupport = checkSupport(expr.object);
1106
+ const objSupport = checkSupport(expr.object, pos);
1093
1107
  if (!objSupport.supported)
1094
1108
  return objSupport;
1095
- const indexSupport = checkSupport(expr.index);
1109
+ const indexSupport = checkSupport(expr.index, pos);
1096
1110
  if (!indexSupport.supported)
1097
1111
  return indexSupport;
1098
1112
  return { supported: true, level: "L2" };
1099
1113
  }
1100
1114
  case "binary": {
1101
- const leftSupport = checkSupport(expr.left);
1115
+ const leftSupport = checkSupport(expr.left, pos);
1102
1116
  if (!leftSupport.supported)
1103
1117
  return leftSupport;
1104
- const rightSupport = checkSupport(expr.right);
1118
+ const rightSupport = checkSupport(expr.right, pos);
1105
1119
  if (!rightSupport.supported)
1106
1120
  return rightSupport;
1107
1121
  if (["===", "==", "!==", "!=", ">", "<", ">=", "<="].includes(expr.op)) {
@@ -1113,7 +1127,7 @@ function checkSupport(expr) {
1113
1127
  return { supported: false, reason: `Unknown operator: ${expr.op}` };
1114
1128
  }
1115
1129
  case "unary": {
1116
- const argSupport = checkSupport(expr.argument);
1130
+ const argSupport = checkSupport(expr.argument, pos);
1117
1131
  if (!argSupport.supported)
1118
1132
  return argSupport;
1119
1133
  if (expr.op === "!") {
@@ -1125,25 +1139,25 @@ function checkSupport(expr) {
1125
1139
  return { supported: false, reason: `Unsupported unary operator: ${expr.op}` };
1126
1140
  }
1127
1141
  case "logical": {
1128
- const leftSupport = checkSupport(expr.left);
1142
+ const leftSupport = checkSupport(expr.left, pos);
1129
1143
  if (!leftSupport.supported)
1130
1144
  return leftSupport;
1131
1145
  if (expr.op === "??" && expr.right.kind === "object-literal" && expr.right.properties.length === 0) {
1132
1146
  return { supported: true, level: "L4" };
1133
1147
  }
1134
- const rightSupport = checkSupport(expr.right);
1148
+ const rightSupport = checkSupport(expr.right, pos);
1135
1149
  if (!rightSupport.supported)
1136
1150
  return rightSupport;
1137
1151
  return { supported: true, level: "L4" };
1138
1152
  }
1139
1153
  case "conditional": {
1140
- const testSupport = checkSupport(expr.test);
1154
+ const testSupport = checkSupport(expr.test, pos);
1141
1155
  if (!testSupport.supported)
1142
1156
  return testSupport;
1143
- const consSupport = checkSupport(expr.consequent);
1157
+ const consSupport = checkSupport(expr.consequent, pos);
1144
1158
  if (!consSupport.supported)
1145
1159
  return consSupport;
1146
- const altSupport = checkSupport(expr.alternate);
1160
+ const altSupport = checkSupport(expr.alternate, pos);
1147
1161
  if (!altSupport.supported)
1148
1162
  return altSupport;
1149
1163
  return { supported: true, level: "L4" };
@@ -1151,7 +1165,7 @@ function checkSupport(expr) {
1151
1165
  case "template-literal": {
1152
1166
  for (const part of expr.parts) {
1153
1167
  if (part.type === "expression") {
1154
- const partSupport = checkSupport(part.expr);
1168
+ const partSupport = checkSupport(part.expr, pos);
1155
1169
  if (!partSupport.supported)
1156
1170
  return partSupport;
1157
1171
  }
@@ -1267,7 +1281,7 @@ function isPureInit(e, pureCallNames) {
1267
1281
  case "array-literal":
1268
1282
  return e.elements.every(pure);
1269
1283
  case "object-literal":
1270
- return e.properties.every((p) => pure(p.value));
1284
+ return e.properties.every((p) => pure(p.kind === "spread" ? p.expr : p.value));
1271
1285
  case "call":
1272
1286
  return e.callee.kind === "identifier" && e.args.length === 0 && pureCallNames !== undefined && pureCallNames.has(e.callee.name);
1273
1287
  case "array-method":
@@ -1321,7 +1335,7 @@ function usesPerPath(name, expr) {
1321
1335
  }
1322
1336
  return add(walk(e.object), sum(e.args));
1323
1337
  case "object-literal":
1324
- return sum(e.properties.map((p) => p.value));
1338
+ return sum(e.properties.map((p) => p.kind === "spread" ? p.expr : p.value));
1325
1339
  case "arrow":
1326
1340
  return walk(e.body).max > 0 ? { min: 0, max: Number.POSITIVE_INFINITY } : { min: 0, max: 0 };
1327
1341
  }
@@ -1387,7 +1401,7 @@ function inlineBinding(expr, name, value) {
1387
1401
  case "object-literal":
1388
1402
  return {
1389
1403
  kind: "object-literal",
1390
- properties: e.properties.map((p) => ({ ...p, value: walk(p.value, enclosing) })),
1404
+ properties: e.properties.map((p) => p.kind === "spread" ? { ...p, expr: walk(p.expr, enclosing) } : { ...p, value: walk(p.value, enclosing) }),
1391
1405
  raw: e.raw
1392
1406
  };
1393
1407
  case "literal":
@@ -1597,7 +1611,7 @@ function materializeGetterCalls(expr, names) {
1597
1611
  return {
1598
1612
  kind: "object-literal",
1599
1613
  raw: expr.raw,
1600
- properties: expr.properties.map((p) => ({ ...p, value: rw(p.value) }))
1614
+ properties: expr.properties.map((p) => p.kind === "spread" ? { ...p, expr: rw(p.expr) } : { ...p, value: rw(p.value) })
1601
1615
  };
1602
1616
  case "arrow":
1603
1617
  return { kind: "arrow", params: expr.params, body: rw(expr.body) };
@@ -1655,7 +1669,7 @@ function freeVarsInBody(body, params) {
1655
1669
  return;
1656
1670
  case "object-literal":
1657
1671
  for (const p of e.properties)
1658
- visit(p.value, bound);
1672
+ visit(p.kind === "spread" ? p.expr : p.value, bound);
1659
1673
  return;
1660
1674
  case "array-method":
1661
1675
  if (e.method === "includes" || e.method === "join") {
@@ -1730,7 +1744,7 @@ function freeIdentifiers(expr) {
1730
1744
  return true;
1731
1745
  case "object-literal":
1732
1746
  for (const p of e.properties)
1733
- if (!visit(p.value, bound))
1747
+ if (!visit(p.kind === "spread" ? p.expr : p.value, bound))
1734
1748
  return false;
1735
1749
  return true;
1736
1750
  case "arrow": {
@@ -1877,10 +1891,17 @@ function toEvalNode(e) {
1877
1891
  case "object-literal": {
1878
1892
  const properties = [];
1879
1893
  for (const p of e.properties) {
1894
+ if (p.kind === "spread") {
1895
+ const spreadExpr = toEvalNode(p.expr);
1896
+ if (!spreadExpr)
1897
+ return null;
1898
+ properties.push({ kind: "spread", expr: spreadExpr });
1899
+ continue;
1900
+ }
1880
1901
  const value = toEvalNode(p.value);
1881
1902
  if (!value)
1882
1903
  return null;
1883
- properties.push({ key: p.key, value });
1904
+ properties.push({ kind: "prop", key: p.key, value });
1884
1905
  }
1885
1906
  return { kind: "object-literal", properties };
1886
1907
  }
@@ -2322,6 +2343,7 @@ var ErrorCodes = {
2322
2343
  MISSING_KEY_IN_LIST: "BF023",
2323
2344
  MISSING_KEY_IN_NESTED_LIST: "BF024",
2324
2345
  UNSUPPORTED_DESTRUCTURE_REST: "BF025",
2346
+ RETURN_VALUE_NOT_JSX: "BF027",
2325
2347
  PROPS_DESTRUCTURING: "BF043",
2326
2348
  SIGNAL_GETTER_NOT_CALLED: "BF044",
2327
2349
  JSX_IN_LOCAL_FUNCTION: "BF045",
@@ -2353,6 +2375,7 @@ var errorMessages = {
2353
2375
  [ErrorCodes.MISSING_KEY_IN_LIST]: "Missing key attribute in list rendering. Add a key prop for efficient updates",
2354
2376
  [ErrorCodes.MISSING_KEY_IN_NESTED_LIST]: "Nested .map() loop requires key attribute for event delegation. Add a key prop to elements in the inner loop",
2355
2377
  [ErrorCodes.UNSUPPORTED_DESTRUCTURE_REST]: "Computed property key in .map() callback destructure is not supported. Rewrite the callback to destructure explicit bindings (e.g., `({ a, b }) => ...`) so the compiler can rewrite references to per-item signal accessors.",
2378
+ [ErrorCodes.RETURN_VALUE_NOT_JSX]: "Component's return value is not recognized as JSX — return the JSX expression directly instead of binding it to a local variable first.",
2356
2379
  [ErrorCodes.PROPS_DESTRUCTURING]: "Props destructuring in function parameters breaks reactivity. Use props object directly.",
2357
2380
  [ErrorCodes.SIGNAL_GETTER_NOT_CALLED]: "Signal/memo getter passed without calling it. Use getter() to read the value.",
2358
2381
  [ErrorCodes.JSX_IN_LOCAL_FUNCTION]: "Local function returns JSX but cannot be inlined. Extract it as a top-level PascalCase component or use a single return statement.",
@@ -2985,6 +3008,8 @@ function matchToLocaleDateStringCall(callee, args, metadata) {
2985
3008
  let tz = null;
2986
3009
  const probeOptions = {};
2987
3010
  for (const prop of options.properties) {
3011
+ if (prop.kind === "spread")
3012
+ return null;
2988
3013
  if (prop.value.kind !== "literal" || prop.value.literalType !== "string")
2989
3014
  return null;
2990
3015
  const value = String(prop.value.value);
@@ -3918,7 +3943,7 @@ function parseRecordIndexAccess(val, localConstants, propsParams, resolveKey) {
3918
3943
 
3919
3944
  // ../jsx/src/ssr-seed-plan.ts
3920
3945
  function classify(name, origin, expr, parsed, available) {
3921
- if (!isSupported(parsed).supported)
3946
+ if (!isSupportedValue(parsed).supported)
3922
3947
  return { kind: "opaque", name, origin };
3923
3948
  const frees = freeIdentifiers(parsed);
3924
3949
  if (frees === null)
@@ -3981,7 +4006,7 @@ function computeSsrSeedPlan(metadata) {
3981
4006
  }
3982
4007
  }
3983
4008
  const expr = signal.initialValue.trim();
3984
- steps.push(expr === "" ? { kind: "opaque", name: signal.getter, origin: "signal" } : classify(signal.getter, "signal", expr, resolveThroughLocalConsts(parseExpression(expr), localConsts), available));
4009
+ steps.push(expr === "" ? { kind: "opaque", name: signal.getter, origin: "signal" } : classify(signal.getter, "signal", expr, resolveThroughLocalConsts(signal.parsed ?? parseExpression(`(${expr})`), localConsts), available));
3985
4010
  available.add(signal.getter);
3986
4011
  }
3987
4012
  for (const memo of metadata.memos) {
@@ -4605,6 +4630,26 @@ function isStringTypedOperand(expr, isStringName) {
4605
4630
  function isStringConcatBinary(op, left, right, isStringName) {
4606
4631
  return op === "+" && (isStringTypedOperand(left, isStringName) || isStringTypedOperand(right, isStringName));
4607
4632
  }
4633
+ function groupObjectLiteralSegments(properties, literalOf, emit) {
4634
+ const segments = [];
4635
+ let run = [];
4636
+ const flushRun = () => {
4637
+ if (run.length > 0) {
4638
+ segments.push(literalOf(run));
4639
+ run = [];
4640
+ }
4641
+ };
4642
+ for (const p of properties) {
4643
+ if (p.kind === "spread") {
4644
+ flushRun();
4645
+ segments.push(emit(p.expr));
4646
+ } else {
4647
+ run.push(p);
4648
+ }
4649
+ }
4650
+ flushRun();
4651
+ return segments;
4652
+ }
4608
4653
  function emitParsedExpr(expr, emitter) {
4609
4654
  const emit = (child) => emitParsedExpr(child, emitter);
4610
4655
  switch (expr.kind) {
@@ -4751,6 +4796,17 @@ function evaluateStaticLiteral(expr, bindings) {
4751
4796
  case "object-literal": {
4752
4797
  const out = {};
4753
4798
  for (const prop of expr.properties) {
4799
+ if (prop.kind === "spread") {
4800
+ const resolved2 = evaluateStaticLiteral(prop.expr, bindings);
4801
+ if (!resolved2)
4802
+ return null;
4803
+ if (resolved2.value === null || resolved2.value === undefined)
4804
+ continue;
4805
+ if (typeof resolved2.value !== "object" || Array.isArray(resolved2.value))
4806
+ return null;
4807
+ Object.assign(out, resolved2.value);
4808
+ continue;
4809
+ }
4754
4810
  const resolved = evaluateStaticLiteral(prop.value, bindings);
4755
4811
  if (!resolved)
4756
4812
  return null;
@@ -4817,6 +4873,8 @@ function matchQueryHrefCall(callee, args, localNames) {
4817
4873
  return null;
4818
4874
  const triples = [];
4819
4875
  for (const p of obj.properties) {
4876
+ if (p.kind === "spread")
4877
+ return null;
4820
4878
  const v = p.value;
4821
4879
  if (v.kind === "conditional" && isOmitBranch(v.alternate)) {
4822
4880
  triples.push({ guard: v.test, key: p.key, value: v.consequent });
@@ -4980,6 +5038,8 @@ function htmlPropValue(parsed) {
4980
5038
  if (parsed.properties.length !== 1)
4981
5039
  return null;
4982
5040
  const [prop] = parsed.properties;
5041
+ if (prop.kind === "spread")
5042
+ return null;
4983
5043
  if (prop.key !== "__html")
4984
5044
  return null;
4985
5045
  return prop.value;
@@ -5849,7 +5909,7 @@ function isSubstituteSafeBody(body, paramNames) {
5849
5909
  if (n.kind === "identifier")
5850
5910
  return paramNames.has(n.name);
5851
5911
  if (n.kind === "object-literal") {
5852
- return n.properties.some((p) => p.shorthand && paramNames.has(p.key) || referencesParam(p.value));
5912
+ return n.properties.some((p) => p.kind === "spread" ? referencesParam(p.expr) : p.shorthand && paramNames.has(p.key) || referencesParam(p.value));
5853
5913
  }
5854
5914
  let hit = false;
5855
5915
  forEachValueChild(n, (c) => {
@@ -6204,6 +6264,8 @@ function bakeInlineObjectAsGoMap(ctx, expr) {
6204
6264
  return null;
6205
6265
  const entries = [];
6206
6266
  for (const prop of expr.properties) {
6267
+ if (prop.kind === "spread")
6268
+ return null;
6207
6269
  if (prop.shorthand)
6208
6270
  return null;
6209
6271
  const go = prop.value.kind === "object-literal" ? bakeInlineObjectAsGoMap(ctx, prop.value) : parsedLiteralToGo(ctx, prop.value);
@@ -6259,6 +6321,8 @@ function parsedLiteralToGo(ctx, expr, typeInfo) {
6259
6321
  return null;
6260
6322
  const entries = [];
6261
6323
  for (const prop of expr.properties) {
6324
+ if (prop.kind === "spread")
6325
+ return null;
6262
6326
  const goField = structFields.get(prop.key);
6263
6327
  if (!goField)
6264
6328
  return null;
@@ -6380,6 +6444,8 @@ function objectLiteralToGoMap(ctx, expr) {
6380
6444
  return null;
6381
6445
  const entries = [];
6382
6446
  for (const prop of expr.properties) {
6447
+ if (prop.kind === "spread")
6448
+ return null;
6383
6449
  if (prop.shorthand)
6384
6450
  return null;
6385
6451
  const val = parsedLiteralToGo(ctx, prop.value);
@@ -6458,17 +6524,18 @@ function resolveMapJoinBaseAsGo(ctx, object, signals, propsParams) {
6458
6524
  }
6459
6525
  return null;
6460
6526
  }
6461
- function mapJoinChainToGo(ctx, chain, signals, propsParams, propFallbackVars) {
6462
- const itemsGo = resolveMapJoinBaseAsGo(ctx, chain.object, signals, propsParams);
6463
- if (itemsGo === null)
6527
+ var CALLBACK_EVAL_FUNC = { map: "bf.MapEval", filter: "bf.FilterEval" };
6528
+ function callbackStepToGo(ctx, method, itemsGo, arrow, signals, propsParams, propFallbackVars) {
6529
+ const evalFunc = CALLBACK_EVAL_FUNC[method];
6530
+ if (!evalFunc)
6464
6531
  return null;
6465
6532
  const knownGetterNames = new Set(signals.map((s) => s.getter));
6466
- const materialized = materializeGetterCalls(chain.arrow.body, knownGetterNames);
6533
+ const materialized = materializeGetterCalls(arrow.body, knownGetterNames);
6467
6534
  const projJSON = serializeParsedExpr(materialized);
6468
6535
  if (projJSON === null)
6469
6536
  return null;
6470
- const paramName = chain.arrow.params[0] ?? "_";
6471
- const freeVars = freeVarsInBody(materialized, new Set(chain.arrow.params));
6537
+ const paramName = arrow.params[0] ?? "_";
6538
+ const freeVars = freeVarsInBody(materialized, new Set(arrow.params));
6472
6539
  const envEntries = [];
6473
6540
  for (const name of freeVars) {
6474
6541
  const sig = signals.find((s) => s.getter === name);
@@ -6485,6 +6552,25 @@ function mapJoinChainToGo(ctx, chain, signals, propsParams, propFallbackVars) {
6485
6552
  envEntries.push(`${JSON.stringify(name)}: ${goExpr}`);
6486
6553
  }
6487
6554
  const envMap = `map[string]any{${envEntries.join(", ")}}`;
6555
+ return `${evalFunc}(${itemsGo}, "${escapeGoString(projJSON)}", ${JSON.stringify(paramName)}, ${envMap})`;
6556
+ }
6557
+ function resolveMapChainAsGo(ctx, expr, signals, propsParams, propFallbackVars) {
6558
+ const cb = asCallbackMethodCall(expr);
6559
+ if (cb && cb.method in CALLBACK_EVAL_FUNC) {
6560
+ const innerGo = resolveMapChainAsGo(ctx, cb.object, signals, propsParams, propFallbackVars);
6561
+ if (innerGo === null)
6562
+ return null;
6563
+ return callbackStepToGo(ctx, cb.method, innerGo, cb.arrow, signals, propsParams, propFallbackVars);
6564
+ }
6565
+ return resolveMapJoinBaseAsGo(ctx, expr, signals, propsParams);
6566
+ }
6567
+ function mapJoinChainToGo(ctx, chain, signals, propsParams, propFallbackVars) {
6568
+ const itemsGo = resolveMapChainAsGo(ctx, chain.object, signals, propsParams, propFallbackVars);
6569
+ if (itemsGo === null)
6570
+ return null;
6571
+ const mapGo = callbackStepToGo(ctx, "map", itemsGo, chain.arrow, signals, propsParams, propFallbackVars);
6572
+ if (mapGo === null)
6573
+ return null;
6488
6574
  let sepGo;
6489
6575
  if (!chain.sepArg) {
6490
6576
  sepGo = JSON.stringify(",");
@@ -6493,7 +6579,7 @@ function mapJoinChainToGo(ctx, chain, signals, propsParams, propFallbackVars) {
6493
6579
  } else {
6494
6580
  return null;
6495
6581
  }
6496
- return `bf.Join(bf.MapEval(${itemsGo}, "${escapeGoString(projJSON)}", ${JSON.stringify(paramName)}, ${envMap}), ${sepGo})`;
6582
+ return `bf.Join(${mapGo}, ${sepGo})`;
6497
6583
  }
6498
6584
 
6499
6585
  // src/adapter/memo/memo-type.ts
@@ -6780,6 +6866,8 @@ function computeObjectMemoInitialValue(ctx, memo) {
6780
6866
  const env = { searchParamsVars: new Set, params: new Map };
6781
6867
  const entries = [];
6782
6868
  for (const prop of retObj.properties) {
6869
+ if (prop.kind === "spread")
6870
+ return null;
6783
6871
  if (prop.shorthand)
6784
6872
  return null;
6785
6873
  if (prop.keyKind !== undefined && prop.keyKind !== "identifier" && prop.keyKind !== "string") {
@@ -6903,6 +6991,8 @@ function recordIndexInterpolationToGo(ctx, node, propNames, localKeyBindings) {
6903
6991
  return null;
6904
6992
  const entries = [];
6905
6993
  for (const prop of parsedConst.properties) {
6994
+ if (prop.kind === "spread")
6995
+ return null;
6906
6996
  const v = prop.value;
6907
6997
  if (v.kind === "literal" && v.literalType === "number") {
6908
6998
  entries.push({ key: prop.key, value: { kind: "number", text: v.raw ?? String(v.value) } });
@@ -7229,6 +7319,12 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
7229
7319
  if (concatGo !== null)
7230
7320
  return concatGo;
7231
7321
  }
7322
+ const bareChain = matchMapJoinChain(body);
7323
+ if (bareChain) {
7324
+ const chainGo = mapJoinChainToGo(ctx, bareChain, signals, propsParams, propFallbackVars);
7325
+ if (chainGo !== null)
7326
+ return chainGo;
7327
+ }
7232
7328
  return null;
7233
7329
  }
7234
7330
  function resolveStringConcatChainGo(ctx, expr, signals, propsParams, propFallbackVars, propRef) {
@@ -7400,7 +7496,7 @@ function collectPropsReadByCtorInit(body, propsObjectName, propNames) {
7400
7496
  return;
7401
7497
  case "object-literal":
7402
7498
  for (const p of e.properties)
7403
- visit(p.value, bound);
7499
+ visit(p.kind === "spread" ? p.expr : p.value, bound);
7404
7500
  return;
7405
7501
  case "array-method":
7406
7502
  visit(e.object, bound);
@@ -7511,6 +7607,8 @@ function parsedObjectLiteralToGoMap(parsed) {
7511
7607
  return null;
7512
7608
  const entries = [];
7513
7609
  for (const prop of parsed.properties) {
7610
+ if (prop.kind === "spread")
7611
+ return null;
7514
7612
  if (prop.keyKind === "numeric")
7515
7613
  return null;
7516
7614
  const v = prop.value;
@@ -7635,6 +7733,8 @@ function conditionToGoBool(condition, ir) {
7635
7733
  function objectLiteralToGoSpreadMap(ctx, obj, ir) {
7636
7734
  const entries = [];
7637
7735
  for (const prop of obj.properties) {
7736
+ if (prop.kind === "spread")
7737
+ return null;
7638
7738
  if (prop.shorthand)
7639
7739
  return null;
7640
7740
  if (prop.keyKind === "numeric")
@@ -8449,6 +8549,8 @@ ${goFields.join(`
8449
8549
  return null;
8450
8550
  const seen = new Set;
8451
8551
  for (const prop of el.properties) {
8552
+ if (prop.kind === "spread")
8553
+ return null;
8452
8554
  if (prop.shorthand)
8453
8555
  return null;
8454
8556
  const key = prop.key;
@@ -9620,6 +9722,8 @@ ${goFields.join(`
9620
9722
  return null;
9621
9723
  const entries = [];
9622
9724
  for (const prop of parsed.properties) {
9725
+ if (prop.kind === "spread")
9726
+ return null;
9623
9727
  if (prop.shorthand)
9624
9728
  return null;
9625
9729
  const goVal = this.lowerProviderMapMemberValue(prop.value, propsParams);
@@ -10386,7 +10490,16 @@ ${goFields.join(`
10386
10490
  });
10387
10491
  return `bf_arr ${parts.join(" ")}`;
10388
10492
  }
10389
- objectLiteral(_properties, raw, _emit) {
10493
+ objectLiteral(properties, raw, emit) {
10494
+ if (properties.length > 0) {
10495
+ const wrap = (rendered) => rendered.includes(" ") ? `(${rendered})` : rendered;
10496
+ const literalOf = (run) => `bf_map ${run.map((p) => `"${escapeGoString(p.key)}" ${wrap(emit(p.value))}`).join(" ")}`;
10497
+ if (!properties.some((p) => p.kind === "spread")) {
10498
+ return literalOf(properties);
10499
+ }
10500
+ const segments = groupObjectLiteralSegments(properties, literalOf, (e) => wrap(emit(e)));
10501
+ return `bf_merge ${segments.join(" ")}`;
10502
+ }
10390
10503
  this.state.errors.push({
10391
10504
  code: "BF101",
10392
10505
  severity: "error",
@@ -11109,7 +11222,7 @@ ${goFields.join(`
11109
11222
  return null;
11110
11223
  const carried = constInfo.parsed;
11111
11224
  if (carried?.kind === "object-literal") {
11112
- const hit = carried.properties.find((prop) => prop.key === key);
11225
+ const hit = carried.properties.find((prop) => prop.kind === "prop" && prop.key === key);
11113
11226
  if (hit && hit.value.kind === "literal") {
11114
11227
  if (hit.value.literalType === "string")
11115
11228
  return JSON.stringify(hit.value.value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/go-template",
3
- "version": "0.32.0",
3
+ "version": "0.33.1",
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.32.0"
52
+ "@barefootjs/shared": "0.33.1"
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.32.0",
71
- "@barefootjs/jsx": "0.32.0",
72
- "@barefootjs/vite": "0.32.0",
70
+ "@barefootjs/client": "0.33.1",
71
+ "@barefootjs/jsx": "0.33.1",
72
+ "@barefootjs/vite": "0.33.1",
73
73
  "vite": "^6.0.0"
74
74
  }
75
75
  }
@@ -118,6 +118,12 @@ runAdapterConformanceTests({
118
118
  'todo-app',
119
119
  // (#1897) data-table no longer skipped — loop body children + wrapper
120
120
  // struct + block-body memo baking render correctly on Go.
121
+ // Real-render divergence (see `render-divergences.ts` for the full
122
+ // description): Go's real SSR output drops the `header` prop's
123
+ // content entirely for this shape, so its rendered template has no
124
+ // `^s0` marker for the IR's conditional to match against. Paired
125
+ // with the `renderDivergences` entry, not an independent gap.
126
+ 'jsx-element-prop-fragment-conditional',
121
127
  ]),
122
128
  skipDataPoints: new Set<string>(),
123
129
  onRenderError: (err, id) => {
@@ -86,8 +86,10 @@ function isSubstituteSafeBody(body: ParsedExpr, paramNames: ReadonlySet<string>)
86
86
  const referencesParam = (n: ParsedExpr): boolean => {
87
87
  if (n.kind === 'identifier') return paramNames.has(n.name)
88
88
  if (n.kind === 'object-literal') {
89
- return n.properties.some(
90
- p => (p.shorthand && paramNames.has(p.key)) || referencesParam(p.value),
89
+ return n.properties.some(p =>
90
+ p.kind === 'spread'
91
+ ? referencesParam(p.expr)
92
+ : (p.shorthand && paramNames.has(p.key)) || referencesParam(p.value),
91
93
  )
92
94
  }
93
95
  let hit = false