@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/adapter/expr/helper-inline.d.ts.map +1 -1
- package/dist/adapter/go-template-adapter.d.ts +1 -1
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +65 -11
- package/dist/adapter/memo/memo-compute.d.ts.map +1 -1
- package/dist/adapter/memo/memo-value.d.ts.map +1 -1
- package/dist/adapter/memo/template-interp.d.ts.map +1 -1
- package/dist/adapter/spread/spread-codegen.d.ts.map +1 -1
- package/dist/adapter/value/parsed-literal-to-go.d.ts.map +1 -1
- package/dist/adapter/value/value-lowering.d.ts +15 -12
- package/dist/adapter/value/value-lowering.d.ts.map +1 -1
- package/dist/conformance-pins.d.ts.map +1 -1
- package/dist/index.js +72 -13
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/vite.js +161 -48
- package/package.json +5 -5
- package/src/__tests__/go-template-adapter.test.ts +6 -0
- package/src/adapter/expr/helper-inline.ts +4 -2
- package/src/adapter/go-template-adapter.ts +42 -7
- package/src/adapter/memo/memo-compute.ts +12 -1
- package/src/adapter/memo/memo-value.ts +3 -0
- package/src/adapter/memo/template-interp.ts +5 -0
- package/src/adapter/spread/spread-codegen.ts +8 -0
- package/src/adapter/value/parsed-literal-to-go.ts +7 -0
- package/src/adapter/value/value-lowering.ts +96 -22
- package/src/conformance-pins.ts +7 -0
- package/src/render-divergences.ts +6 -1
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
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
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(
|
|
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 =
|
|
6471
|
-
const freeVars = freeVarsInBody(materialized, new Set(
|
|
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(
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
71
|
-
"@barefootjs/jsx": "0.
|
|
72
|
-
"@barefootjs/vite": "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
|
|
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
|