@barefootjs/go-template 0.33.6 → 0.35.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/adapter/expr/url-builder.d.ts +53 -1
- package/dist/adapter/expr/url-builder.d.ts.map +1 -1
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +68 -18
- package/dist/adapter/lib/compile-state.d.ts +20 -0
- package/dist/adapter/lib/compile-state.d.ts.map +1 -1
- package/dist/index.js +69 -22
- package/dist/render-divergences.d.ts.map +1 -1
- package/dist/vite.js +213 -70
- package/package.json +5 -5
- package/src/__tests__/go-template-adapter.test.ts +0 -8
- package/src/__tests__/lowering-plugin.test.ts +38 -0
- package/src/__tests__/query-href.test.ts +126 -0
- package/src/adapter/expr/url-builder.ts +114 -8
- package/src/adapter/go-template-adapter.ts +81 -8
- package/src/adapter/lib/compile-state.ts +22 -0
- package/src/render-divergences.ts +1 -6
package/dist/vite.js
CHANGED
|
@@ -12,6 +12,41 @@ import ts10 from "typescript";
|
|
|
12
12
|
|
|
13
13
|
// ../jsx/src/expression-parser.ts
|
|
14
14
|
import ts from "typescript";
|
|
15
|
+
|
|
16
|
+
// ../jsx/src/lowering-registry.ts
|
|
17
|
+
var plugins = [];
|
|
18
|
+
function registerLoweringPlugin(plugin) {
|
|
19
|
+
const existing = plugins.findIndex((p) => p.name === plugin.name);
|
|
20
|
+
if (existing >= 0)
|
|
21
|
+
plugins[existing] = plugin;
|
|
22
|
+
else
|
|
23
|
+
plugins.push(plugin);
|
|
24
|
+
}
|
|
25
|
+
function prepareLoweringMatchers(metadata) {
|
|
26
|
+
const matchers = [];
|
|
27
|
+
for (const plugin of plugins) {
|
|
28
|
+
const matcher = plugin.prepare(metadata);
|
|
29
|
+
if (matcher)
|
|
30
|
+
matchers.push(matcher);
|
|
31
|
+
}
|
|
32
|
+
return matchers;
|
|
33
|
+
}
|
|
34
|
+
function loweringNodeChildren(node) {
|
|
35
|
+
if (node.kind === "helper-call")
|
|
36
|
+
return [...node.args];
|
|
37
|
+
const children = [node.base];
|
|
38
|
+
for (const t of node.triples) {
|
|
39
|
+
if (t.guard)
|
|
40
|
+
children.push(t.guard);
|
|
41
|
+
children.push(t.value);
|
|
42
|
+
}
|
|
43
|
+
return children;
|
|
44
|
+
}
|
|
45
|
+
function isValidHelperId(helper) {
|
|
46
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(helper);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ../jsx/src/expression-parser.ts
|
|
15
50
|
var UNSUPPORTED_METHODS = new Set([
|
|
16
51
|
"filter",
|
|
17
52
|
"map",
|
|
@@ -209,7 +244,7 @@ function convertNode(node, raw) {
|
|
|
209
244
|
}
|
|
210
245
|
if (n === undefined || Number.isNaN(n)) {
|
|
211
246
|
const parsedDepth = convertNode(depthNode, raw);
|
|
212
|
-
if (checkSupport(parsedDepth, "rendered").supported) {
|
|
247
|
+
if (checkSupport(parsedDepth, "rendered", []).supported) {
|
|
213
248
|
depthExpr = parsedDepth;
|
|
214
249
|
flatDepth = 1;
|
|
215
250
|
} else {
|
|
@@ -987,13 +1022,13 @@ function getUnaryOperatorString(op) {
|
|
|
987
1022
|
return "unknown";
|
|
988
1023
|
}
|
|
989
1024
|
}
|
|
990
|
-
function isSupported(expr) {
|
|
991
|
-
return checkSupport(expr, "rendered");
|
|
1025
|
+
function isSupported(expr, opts) {
|
|
1026
|
+
return checkSupport(expr, "rendered", opts?.loweringMatchers ?? []);
|
|
992
1027
|
}
|
|
993
|
-
function isSupportedValue(expr) {
|
|
994
|
-
return checkSupport(expr, "value");
|
|
1028
|
+
function isSupportedValue(expr, opts) {
|
|
1029
|
+
return checkSupport(expr, "value", opts?.loweringMatchers ?? []);
|
|
995
1030
|
}
|
|
996
|
-
function checkSupport(expr, pos) {
|
|
1031
|
+
function checkSupport(expr, pos, matchers) {
|
|
997
1032
|
switch (expr.kind) {
|
|
998
1033
|
case "unsupported":
|
|
999
1034
|
return { supported: false, reason: expr.reason };
|
|
@@ -1002,7 +1037,7 @@ function checkSupport(expr, pos) {
|
|
|
1002
1037
|
return { supported: false, reason: "Unsupported syntax: ObjectLiteralExpression" };
|
|
1003
1038
|
}
|
|
1004
1039
|
for (const prop of expr.properties) {
|
|
1005
|
-
const propSupport = checkSupport(prop.kind === "spread" ? prop.expr : prop.value, pos);
|
|
1040
|
+
const propSupport = checkSupport(prop.kind === "spread" ? prop.expr : prop.value, pos, matchers);
|
|
1006
1041
|
if (!propSupport.supported)
|
|
1007
1042
|
return propSupport;
|
|
1008
1043
|
}
|
|
@@ -1017,7 +1052,7 @@ function checkSupport(expr, pos) {
|
|
|
1017
1052
|
return { supported: false, reason: "Standalone arrow functions / regex literals are not supported" };
|
|
1018
1053
|
case "array-literal": {
|
|
1019
1054
|
for (const el of expr.elements) {
|
|
1020
|
-
const elSupport = checkSupport(el, pos);
|
|
1055
|
+
const elSupport = checkSupport(el, pos, matchers);
|
|
1021
1056
|
if (!elSupport.supported)
|
|
1022
1057
|
return elSupport;
|
|
1023
1058
|
}
|
|
@@ -1030,28 +1065,39 @@ function checkSupport(expr, pos) {
|
|
|
1030
1065
|
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 */`
|
|
1031
1066
|
};
|
|
1032
1067
|
}
|
|
1033
|
-
const objSupport = checkSupport(expr.object, pos);
|
|
1068
|
+
const objSupport = checkSupport(expr.object, pos, matchers);
|
|
1034
1069
|
if (!objSupport.supported)
|
|
1035
1070
|
return objSupport;
|
|
1036
1071
|
for (const arg of expr.args) {
|
|
1037
|
-
const argSupport = checkSupport(arg, pos);
|
|
1072
|
+
const argSupport = checkSupport(arg, pos, matchers);
|
|
1038
1073
|
if (!argSupport.supported)
|
|
1039
1074
|
return argSupport;
|
|
1040
1075
|
}
|
|
1041
1076
|
if (expr.method === "flat" && expr.depthExpr) {
|
|
1042
|
-
const depthSupport = checkSupport(expr.depthExpr, pos);
|
|
1077
|
+
const depthSupport = checkSupport(expr.depthExpr, pos, matchers);
|
|
1043
1078
|
if (!depthSupport.supported)
|
|
1044
1079
|
return depthSupport;
|
|
1045
1080
|
}
|
|
1046
1081
|
return { supported: true, level: "L2" };
|
|
1047
1082
|
}
|
|
1048
1083
|
case "call": {
|
|
1084
|
+
for (const matcher of matchers) {
|
|
1085
|
+
const node = matcher(expr.callee, expr.args);
|
|
1086
|
+
if (!node)
|
|
1087
|
+
continue;
|
|
1088
|
+
for (const child of loweringNodeChildren(node)) {
|
|
1089
|
+
const childSupport = checkSupport(child, pos, matchers);
|
|
1090
|
+
if (!childSupport.supported)
|
|
1091
|
+
return childSupport;
|
|
1092
|
+
}
|
|
1093
|
+
return { supported: true, level: "L2" };
|
|
1094
|
+
}
|
|
1049
1095
|
const cb = asCallbackMethodCall(expr);
|
|
1050
1096
|
if (cb) {
|
|
1051
|
-
const objSupport = checkSupport(cb.object, pos);
|
|
1097
|
+
const objSupport = checkSupport(cb.object, pos, matchers);
|
|
1052
1098
|
if (!objSupport.supported)
|
|
1053
1099
|
return objSupport;
|
|
1054
|
-
const bodySupport = checkSupport(cb.arrow.body, pos);
|
|
1100
|
+
const bodySupport = checkSupport(cb.arrow.body, pos, matchers);
|
|
1055
1101
|
if (!bodySupport.supported) {
|
|
1056
1102
|
return {
|
|
1057
1103
|
supported: false,
|
|
@@ -1060,13 +1106,13 @@ function checkSupport(expr, pos) {
|
|
|
1060
1106
|
};
|
|
1061
1107
|
}
|
|
1062
1108
|
for (const rest of cb.args) {
|
|
1063
|
-
const restSupport = checkSupport(rest, pos);
|
|
1109
|
+
const restSupport = checkSupport(rest, pos, matchers);
|
|
1064
1110
|
if (!restSupport.supported)
|
|
1065
1111
|
return restSupport;
|
|
1066
1112
|
}
|
|
1067
1113
|
return { supported: true, level: "L5" };
|
|
1068
1114
|
}
|
|
1069
|
-
const calleeSupport = checkSupport(expr.callee, pos);
|
|
1115
|
+
const calleeSupport = checkSupport(expr.callee, pos, matchers);
|
|
1070
1116
|
if (!calleeSupport.supported) {
|
|
1071
1117
|
return calleeSupport;
|
|
1072
1118
|
}
|
|
@@ -1085,7 +1131,7 @@ function checkSupport(expr, pos) {
|
|
|
1085
1131
|
return { supported: true, level: "L1" };
|
|
1086
1132
|
}
|
|
1087
1133
|
for (const arg of expr.args) {
|
|
1088
|
-
const argSupport = checkSupport(arg, pos);
|
|
1134
|
+
const argSupport = checkSupport(arg, pos, matchers);
|
|
1089
1135
|
if (!argSupport.supported) {
|
|
1090
1136
|
return argSupport;
|
|
1091
1137
|
}
|
|
@@ -1093,7 +1139,7 @@ function checkSupport(expr, pos) {
|
|
|
1093
1139
|
return { supported: true, level: "L2" };
|
|
1094
1140
|
}
|
|
1095
1141
|
case "member": {
|
|
1096
|
-
const objSupport = checkSupport(expr.object, pos);
|
|
1142
|
+
const objSupport = checkSupport(expr.object, pos, matchers);
|
|
1097
1143
|
if (!objSupport.supported) {
|
|
1098
1144
|
return objSupport;
|
|
1099
1145
|
}
|
|
@@ -1103,19 +1149,19 @@ function checkSupport(expr, pos) {
|
|
|
1103
1149
|
return { supported: true, level: "L2" };
|
|
1104
1150
|
}
|
|
1105
1151
|
case "index-access": {
|
|
1106
|
-
const objSupport = checkSupport(expr.object, pos);
|
|
1152
|
+
const objSupport = checkSupport(expr.object, pos, matchers);
|
|
1107
1153
|
if (!objSupport.supported)
|
|
1108
1154
|
return objSupport;
|
|
1109
|
-
const indexSupport = checkSupport(expr.index, pos);
|
|
1155
|
+
const indexSupport = checkSupport(expr.index, pos, matchers);
|
|
1110
1156
|
if (!indexSupport.supported)
|
|
1111
1157
|
return indexSupport;
|
|
1112
1158
|
return { supported: true, level: "L2" };
|
|
1113
1159
|
}
|
|
1114
1160
|
case "binary": {
|
|
1115
|
-
const leftSupport = checkSupport(expr.left, pos);
|
|
1161
|
+
const leftSupport = checkSupport(expr.left, pos, matchers);
|
|
1116
1162
|
if (!leftSupport.supported)
|
|
1117
1163
|
return leftSupport;
|
|
1118
|
-
const rightSupport = checkSupport(expr.right, pos);
|
|
1164
|
+
const rightSupport = checkSupport(expr.right, pos, matchers);
|
|
1119
1165
|
if (!rightSupport.supported)
|
|
1120
1166
|
return rightSupport;
|
|
1121
1167
|
if (["===", "==", "!==", "!=", ">", "<", ">=", "<="].includes(expr.op)) {
|
|
@@ -1127,7 +1173,7 @@ function checkSupport(expr, pos) {
|
|
|
1127
1173
|
return { supported: false, reason: `Unknown operator: ${expr.op}` };
|
|
1128
1174
|
}
|
|
1129
1175
|
case "unary": {
|
|
1130
|
-
const argSupport = checkSupport(expr.argument, pos);
|
|
1176
|
+
const argSupport = checkSupport(expr.argument, pos, matchers);
|
|
1131
1177
|
if (!argSupport.supported)
|
|
1132
1178
|
return argSupport;
|
|
1133
1179
|
if (expr.op === "!") {
|
|
@@ -1139,25 +1185,25 @@ function checkSupport(expr, pos) {
|
|
|
1139
1185
|
return { supported: false, reason: `Unsupported unary operator: ${expr.op}` };
|
|
1140
1186
|
}
|
|
1141
1187
|
case "logical": {
|
|
1142
|
-
const leftSupport = checkSupport(expr.left, pos);
|
|
1188
|
+
const leftSupport = checkSupport(expr.left, pos, matchers);
|
|
1143
1189
|
if (!leftSupport.supported)
|
|
1144
1190
|
return leftSupport;
|
|
1145
1191
|
if (expr.op === "??" && expr.right.kind === "object-literal" && expr.right.properties.length === 0) {
|
|
1146
1192
|
return { supported: true, level: "L4" };
|
|
1147
1193
|
}
|
|
1148
|
-
const rightSupport = checkSupport(expr.right, pos);
|
|
1194
|
+
const rightSupport = checkSupport(expr.right, pos, matchers);
|
|
1149
1195
|
if (!rightSupport.supported)
|
|
1150
1196
|
return rightSupport;
|
|
1151
1197
|
return { supported: true, level: "L4" };
|
|
1152
1198
|
}
|
|
1153
1199
|
case "conditional": {
|
|
1154
|
-
const testSupport = checkSupport(expr.test, pos);
|
|
1200
|
+
const testSupport = checkSupport(expr.test, pos, matchers);
|
|
1155
1201
|
if (!testSupport.supported)
|
|
1156
1202
|
return testSupport;
|
|
1157
|
-
const consSupport = checkSupport(expr.consequent, pos);
|
|
1203
|
+
const consSupport = checkSupport(expr.consequent, pos, matchers);
|
|
1158
1204
|
if (!consSupport.supported)
|
|
1159
1205
|
return consSupport;
|
|
1160
|
-
const altSupport = checkSupport(expr.alternate, pos);
|
|
1206
|
+
const altSupport = checkSupport(expr.alternate, pos, matchers);
|
|
1161
1207
|
if (!altSupport.supported)
|
|
1162
1208
|
return altSupport;
|
|
1163
1209
|
return { supported: true, level: "L4" };
|
|
@@ -1165,7 +1211,7 @@ function checkSupport(expr, pos) {
|
|
|
1165
1211
|
case "template-literal": {
|
|
1166
1212
|
for (const part of expr.parts) {
|
|
1167
1213
|
if (part.type === "expression") {
|
|
1168
|
-
const partSupport = checkSupport(part.expr, pos);
|
|
1214
|
+
const partSupport = checkSupport(part.expr, pos, matchers);
|
|
1169
1215
|
if (!partSupport.supported)
|
|
1170
1216
|
return partSupport;
|
|
1171
1217
|
}
|
|
@@ -2204,7 +2250,33 @@ function propsDestructureBinding(p) {
|
|
|
2204
2250
|
const binding = callerKey === localName ? localName : `${isIdentifierName(callerKey) ? callerKey : JSON.stringify(callerKey)}: ${localName}`;
|
|
2205
2251
|
return p.defaultValue ? `${binding} = ${p.defaultValue}` : binding;
|
|
2206
2252
|
}
|
|
2253
|
+
function resolveBodyDestructuredPropAliases(localConstants, propsObjectName) {
|
|
2254
|
+
const aliases = new Map;
|
|
2255
|
+
if (propsObjectName === null)
|
|
2256
|
+
return aliases;
|
|
2257
|
+
for (const c of localConstants) {
|
|
2258
|
+
if (c.isModule)
|
|
2259
|
+
continue;
|
|
2260
|
+
const m = c.parsed;
|
|
2261
|
+
if (m?.kind === "member" && !m.computed && m.object.kind === "identifier" && m.object.name === propsObjectName) {
|
|
2262
|
+
aliases.set(c.name, m.property);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
return aliases;
|
|
2266
|
+
}
|
|
2207
2267
|
var EMPTY_SET = new Set;
|
|
2268
|
+
function resolveAliasOrigin(constantValues, name, terminal) {
|
|
2269
|
+
const visited = new Set;
|
|
2270
|
+
let current = name.trim();
|
|
2271
|
+
while (current !== undefined && !visited.has(current)) {
|
|
2272
|
+
const hit = terminal(current);
|
|
2273
|
+
if (hit !== null)
|
|
2274
|
+
return hit;
|
|
2275
|
+
visited.add(current);
|
|
2276
|
+
current = constantValues.get(current)?.trim();
|
|
2277
|
+
}
|
|
2278
|
+
return null;
|
|
2279
|
+
}
|
|
2208
2280
|
|
|
2209
2281
|
// ../jsx/src/ir-to-client-js/csr-substitute.ts
|
|
2210
2282
|
function extractFreeIdentifiersFromText(text) {
|
|
@@ -2217,6 +2289,35 @@ function extractFreeIdentifiersFromText(text) {
|
|
|
2217
2289
|
const expr = ts5.isParenthesizedExpression(stmt.expression) ? stmt.expression.expression : stmt.expression;
|
|
2218
2290
|
return extractFreeIdentifiersFromNode(expr);
|
|
2219
2291
|
}
|
|
2292
|
+
function resolveGetterAliases(localConstants, isGetter) {
|
|
2293
|
+
const constantValues = new Map;
|
|
2294
|
+
for (const c of localConstants) {
|
|
2295
|
+
if (c.isModule)
|
|
2296
|
+
continue;
|
|
2297
|
+
constantValues.set(c.name, c.value);
|
|
2298
|
+
}
|
|
2299
|
+
const aliases = new Map;
|
|
2300
|
+
for (const c of localConstants) {
|
|
2301
|
+
if (c.isModule || isGetter(c.name))
|
|
2302
|
+
continue;
|
|
2303
|
+
const origin = resolveAliasOrigin(constantValues, c.name, (current) => isGetter(current) ? current : null);
|
|
2304
|
+
if (origin !== null && origin !== c.name)
|
|
2305
|
+
aliases.set(c.name, origin);
|
|
2306
|
+
}
|
|
2307
|
+
return aliases;
|
|
2308
|
+
}
|
|
2309
|
+
function collectAliasableGetterNames(signals, memos) {
|
|
2310
|
+
const getterNames = new Set;
|
|
2311
|
+
for (const sig of signals) {
|
|
2312
|
+
if (sig.getter && !sig.isModule && !sig.envReader)
|
|
2313
|
+
getterNames.add(sig.getter);
|
|
2314
|
+
}
|
|
2315
|
+
for (const memo of memos) {
|
|
2316
|
+
if (!memo.isModule)
|
|
2317
|
+
getterNames.add(memo.name);
|
|
2318
|
+
}
|
|
2319
|
+
return getterNames;
|
|
2320
|
+
}
|
|
2220
2321
|
|
|
2221
2322
|
// ../jsx/src/ir-to-client-js/rewrite-props-object.ts
|
|
2222
2323
|
import ts6 from "typescript";
|
|
@@ -2298,6 +2399,12 @@ class BindingScope {
|
|
|
2298
2399
|
}
|
|
2299
2400
|
}
|
|
2300
2401
|
|
|
2402
|
+
// ../jsx/src/ir-to-client-js/safe-html.ts
|
|
2403
|
+
function safeHtml(expr) {
|
|
2404
|
+
return expr;
|
|
2405
|
+
}
|
|
2406
|
+
var EMPTY_MARKUP = safeHtml("''");
|
|
2407
|
+
|
|
2301
2408
|
// ../jsx/src/ir-to-client-js/html-template.ts
|
|
2302
2409
|
var VOID_ELEMENTS = new Set([
|
|
2303
2410
|
"area",
|
|
@@ -2584,6 +2691,7 @@ var CLIENT_EXPORTS = new Set([
|
|
|
2584
2691
|
"isSSRPortal",
|
|
2585
2692
|
"findSiblingSlot",
|
|
2586
2693
|
"cleanupPortalPlaceholder",
|
|
2694
|
+
"trackPosition",
|
|
2587
2695
|
"createSearchParams",
|
|
2588
2696
|
"queryHref",
|
|
2589
2697
|
"formatDate",
|
|
@@ -2644,7 +2752,8 @@ var BROWSER_ONLY_CLIENT_APIS = new Set([
|
|
|
2644
2752
|
"createPortal",
|
|
2645
2753
|
"isSSRPortal",
|
|
2646
2754
|
"findSiblingSlot",
|
|
2647
|
-
"cleanupPortalPlaceholder"
|
|
2755
|
+
"cleanupPortalPlaceholder",
|
|
2756
|
+
"trackPosition"
|
|
2648
2757
|
]);
|
|
2649
2758
|
var REACTIVE_PRIMITIVES = new Set([
|
|
2650
2759
|
"createSignal",
|
|
@@ -3243,30 +3352,6 @@ import ts16 from "typescript";
|
|
|
3243
3352
|
|
|
3244
3353
|
// ../jsx/src/relocate.ts
|
|
3245
3354
|
import ts18 from "typescript";
|
|
3246
|
-
|
|
3247
|
-
// ../jsx/src/lowering-registry.ts
|
|
3248
|
-
var plugins = [];
|
|
3249
|
-
function registerLoweringPlugin(plugin) {
|
|
3250
|
-
const existing = plugins.findIndex((p) => p.name === plugin.name);
|
|
3251
|
-
if (existing >= 0)
|
|
3252
|
-
plugins[existing] = plugin;
|
|
3253
|
-
else
|
|
3254
|
-
plugins.push(plugin);
|
|
3255
|
-
}
|
|
3256
|
-
function prepareLoweringMatchers(metadata) {
|
|
3257
|
-
const matchers = [];
|
|
3258
|
-
for (const plugin of plugins) {
|
|
3259
|
-
const matcher = plugin.prepare(metadata);
|
|
3260
|
-
if (matcher)
|
|
3261
|
-
matchers.push(matcher);
|
|
3262
|
-
}
|
|
3263
|
-
return matchers;
|
|
3264
|
-
}
|
|
3265
|
-
function isValidHelperId(helper) {
|
|
3266
|
-
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(helper);
|
|
3267
|
-
}
|
|
3268
|
-
|
|
3269
|
-
// ../jsx/src/relocate.ts
|
|
3270
3355
|
var REGISTRY_SAFE_BINDING_KINDS = new Set([
|
|
3271
3356
|
"global",
|
|
3272
3357
|
"module-import",
|
|
@@ -4694,6 +4779,17 @@ function emitParsedExpr(expr, emitter) {
|
|
|
4694
4779
|
case "literal":
|
|
4695
4780
|
return emitter.literal(expr.value, expr.literalType);
|
|
4696
4781
|
case "call": {
|
|
4782
|
+
if (emitter.lowering) {
|
|
4783
|
+
for (const matcher of emitter.lowering.matchers) {
|
|
4784
|
+
const node = matcher(expr.callee, expr.args);
|
|
4785
|
+
if (!node)
|
|
4786
|
+
continue;
|
|
4787
|
+
const rendered = emitter.lowering.render(node, emit);
|
|
4788
|
+
if (rendered !== null)
|
|
4789
|
+
return rendered;
|
|
4790
|
+
break;
|
|
4791
|
+
}
|
|
4792
|
+
}
|
|
4697
4793
|
const cb = asCallbackMethodCall(expr);
|
|
4698
4794
|
if (cb)
|
|
4699
4795
|
return emitter.callbackMethod(cb.method, cb.object, cb.arrow, cb.args, emit);
|
|
@@ -5535,6 +5631,8 @@ class CompileState {
|
|
|
5535
5631
|
restPropsName = null;
|
|
5536
5632
|
moduleStringConsts = new Map;
|
|
5537
5633
|
localConstants = [];
|
|
5634
|
+
getterAliases = new Map;
|
|
5635
|
+
propDestructureAliases = new Map;
|
|
5538
5636
|
staticLoopSourceBoundNames = new Set;
|
|
5539
5637
|
localHelperNames = new Set;
|
|
5540
5638
|
currentMemos = [];
|
|
@@ -6103,9 +6201,22 @@ function lowerTernaryTest(ctx, test) {
|
|
|
6103
6201
|
const isBoolShape = test.kind === "binary" && BOOL_COMPARISON_OPS.has(test.op) || test.kind === "unary" && test.op === "!" || test.kind === "literal" && test.literalType === "boolean";
|
|
6104
6202
|
return isBoolShape ? go : `(bf_truthy ${go})`;
|
|
6105
6203
|
}
|
|
6204
|
+
function matchRegisteredCall(ctx, callee, args) {
|
|
6205
|
+
for (const matcher of ctx.state.loweringMatchers) {
|
|
6206
|
+
const node = matcher(callee, args);
|
|
6207
|
+
if (node)
|
|
6208
|
+
return node;
|
|
6209
|
+
}
|
|
6210
|
+
return null;
|
|
6211
|
+
}
|
|
6212
|
+
function lowerRegisteredCallNode(ctx, callee, args) {
|
|
6213
|
+
if (ctx.state.loweringMatchers.length === 0)
|
|
6214
|
+
return null;
|
|
6215
|
+
const node = matchRegisteredCall(ctx, callee, args);
|
|
6216
|
+
return node ? renderLoweringNode(ctx, node) : null;
|
|
6217
|
+
}
|
|
6106
6218
|
function lowerRegisteredCall(ctx, jsExpr, preParsed) {
|
|
6107
|
-
|
|
6108
|
-
if (matchers.length === 0)
|
|
6219
|
+
if (ctx.state.loweringMatchers.length === 0)
|
|
6109
6220
|
return null;
|
|
6110
6221
|
let call = preParsed?.kind === "call" ? preParsed : undefined;
|
|
6111
6222
|
if (!call) {
|
|
@@ -6116,15 +6227,32 @@ function lowerRegisteredCall(ctx, jsExpr, preParsed) {
|
|
|
6116
6227
|
return null;
|
|
6117
6228
|
call = parsed;
|
|
6118
6229
|
}
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6230
|
+
return lowerRegisteredCallNode(ctx, call.callee, call.args);
|
|
6231
|
+
}
|
|
6232
|
+
function lowerRegisteredAttrCall(ctx, attrName, parsed) {
|
|
6233
|
+
if (parsed.kind === "conditional") {
|
|
6234
|
+
if (!ternaryHasQueryBranch(ctx, parsed.consequent, parsed.alternate))
|
|
6235
|
+
return null;
|
|
6236
|
+
const rendered2 = lowerTernary(ctx, parsed.test, parsed.consequent, parsed.alternate);
|
|
6237
|
+
return `{{bf_attr ${JSON.stringify(attrName)} ${rendered2}}}`;
|
|
6126
6238
|
}
|
|
6127
|
-
|
|
6239
|
+
if (parsed.kind !== "call")
|
|
6240
|
+
return null;
|
|
6241
|
+
const node = matchRegisteredCall(ctx, parsed.callee, parsed.args);
|
|
6242
|
+
if (!node || node.kind !== "guard-list" || node.helper !== "query")
|
|
6243
|
+
return null;
|
|
6244
|
+
const rendered = renderLoweringNode(ctx, node);
|
|
6245
|
+
return rendered === null ? null : `{{bf_attr ${JSON.stringify(attrName)} (${rendered})}}`;
|
|
6246
|
+
}
|
|
6247
|
+
function isQueryGuardListCall(ctx, node) {
|
|
6248
|
+
if (node.kind !== "call")
|
|
6249
|
+
return false;
|
|
6250
|
+
const lowered = matchRegisteredCall(ctx, node.callee, node.args);
|
|
6251
|
+
return lowered?.kind === "guard-list" && lowered.helper === "query";
|
|
6252
|
+
}
|
|
6253
|
+
function ternaryHasQueryBranch(ctx, consequent, alternate) {
|
|
6254
|
+
const branchHasQuery = (n) => n.kind === "conditional" ? ternaryHasQueryBranch(ctx, n.consequent, n.alternate) : isQueryGuardListCall(ctx, n);
|
|
6255
|
+
return branchHasQuery(consequent) || branchHasQuery(alternate);
|
|
6128
6256
|
}
|
|
6129
6257
|
function renderLoweringNode(ctx, node) {
|
|
6130
6258
|
const helper = goHelperName(node.helper);
|
|
@@ -8132,6 +8260,11 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
8132
8260
|
this.state.objectTypedPropNames = new Set((ir.metadata.propsParams ?? []).filter((p) => p.type.kind === "object").map((p) => p.name));
|
|
8133
8261
|
this.state.moduleStringConsts = this.collectModuleStringConsts(ir.metadata.localConstants);
|
|
8134
8262
|
this.state.localConstants = ir.metadata.localConstants ?? [];
|
|
8263
|
+
{
|
|
8264
|
+
const getterNames = collectAliasableGetterNames(ir.metadata.signals ?? [], ir.metadata.memos ?? []);
|
|
8265
|
+
this.state.getterAliases = resolveGetterAliases(ir.metadata.localConstants ?? [], (n) => getterNames.has(n));
|
|
8266
|
+
}
|
|
8267
|
+
this.state.propDestructureAliases = resolveBodyDestructuredPropAliases(ir.metadata.localConstants ?? [], ir.metadata.propsObjectName);
|
|
8135
8268
|
this.state.staticLoopSourceBoundNames = collectLoopBoundNames(ir);
|
|
8136
8269
|
this.bakedStaticChildLoopCache = new Map;
|
|
8137
8270
|
this.state.localHelperNames = new Set(this.state.localConstants.filter((c) => !c.isModule && c.containsArrow).map((c) => c.name));
|
|
@@ -10368,9 +10501,10 @@ ${goFields.join(`
|
|
|
10368
10501
|
return hit !== null && hit.depth > 0 && hit.binding.source === "item";
|
|
10369
10502
|
}
|
|
10370
10503
|
rootFieldRef(name) {
|
|
10371
|
-
this.state.
|
|
10504
|
+
const resolved = this.state.getterAliases.get(name) ?? this.state.propDestructureAliases.get(name) ?? name;
|
|
10505
|
+
this.state.templateReadRootFields.add(resolved);
|
|
10372
10506
|
const prefix = this.inLoop ? "$." : ".";
|
|
10373
|
-
return `${prefix}${capitalizeFieldName(
|
|
10507
|
+
return `${prefix}${capitalizeFieldName(resolved)}`;
|
|
10374
10508
|
}
|
|
10375
10509
|
searchParamsFieldRef(name) {
|
|
10376
10510
|
return this.state.searchParamsLocals.has(name) ? this.rootFieldRef("searchParams") : null;
|
|
@@ -10427,6 +10561,9 @@ ${goFields.join(`
|
|
|
10427
10561
|
return String(value);
|
|
10428
10562
|
}
|
|
10429
10563
|
call(callee, args, emit) {
|
|
10564
|
+
const lowered = lowerRegisteredCallNode(this.emitCtx, callee, args);
|
|
10565
|
+
if (lowered !== null)
|
|
10566
|
+
return lowered;
|
|
10430
10567
|
if (callee.kind === "identifier" && args.length === 0) {
|
|
10431
10568
|
return this.searchParamsFieldRef(callee.name) ?? this.rootFieldRef(callee.name);
|
|
10432
10569
|
}
|
|
@@ -10555,7 +10692,7 @@ ${goFields.join(`
|
|
|
10555
10692
|
unary(op, argument, emit) {
|
|
10556
10693
|
const arg = emit(argument);
|
|
10557
10694
|
if (op === "!")
|
|
10558
|
-
return `not ${arg}`;
|
|
10695
|
+
return `not ${wrapIfMultiToken(arg)}`;
|
|
10559
10696
|
if (op === "-")
|
|
10560
10697
|
return `bf_neg ${arg}`;
|
|
10561
10698
|
return arg;
|
|
@@ -11606,7 +11743,7 @@ ${goFields.join(`
|
|
|
11606
11743
|
case "unary": {
|
|
11607
11744
|
const arg = this.renderConditionExpr(expr.argument);
|
|
11608
11745
|
if (expr.op === "!")
|
|
11609
|
-
return { preamble: arg.preamble, expr: `not ${arg.expr}` };
|
|
11746
|
+
return { preamble: arg.preamble, expr: `not ${wrapIfMultiToken(arg.expr)}` };
|
|
11610
11747
|
if (expr.op === "-")
|
|
11611
11748
|
return { preamble: arg.preamble, expr: `bf_neg ${arg.expr}` };
|
|
11612
11749
|
return arg;
|
|
@@ -11851,7 +11988,7 @@ ${goFields.join(`
|
|
|
11851
11988
|
if (loop.bodyIsMultiRoot)
|
|
11852
11989
|
return `{{bfComment "loop-i"}}`;
|
|
11853
11990
|
if (loop.bodyIsItemConditional && loop.key) {
|
|
11854
|
-
return `{{bfComment (printf "loop-i:%v" ${this.convertExpressionToGo(loop.key)})}}`;
|
|
11991
|
+
return `{{bfComment (printf "loop-i:%v" (bfEscapeCommentKey ${this.convertExpressionToGo(loop.key)}))}}`;
|
|
11855
11992
|
}
|
|
11856
11993
|
return "";
|
|
11857
11994
|
}
|
|
@@ -12012,15 +12149,21 @@ ${children}`;
|
|
|
12012
12149
|
const test = parsed.test;
|
|
12013
12150
|
if (undef(parsed.alternate) && !undef(parsed.consequent)) {
|
|
12014
12151
|
const { condition: goCond, preamble } = this.convertConditionToGo(this.isTemplateFragment(this.renderParsedExpr(test), test.kind) ? value.expr : value.expr.slice(0, value.expr.indexOf("?")).trim());
|
|
12015
|
-
const
|
|
12016
|
-
const body = `${name}="{{${
|
|
12152
|
+
const attrConsequent = lowerRegisteredAttrCall(this.emitCtx, name, parsed.consequent);
|
|
12153
|
+
const body = attrConsequent !== null ? attrConsequent : `${name}="{{${this.renderParsedExpr(parsed.consequent)}}}"`;
|
|
12017
12154
|
return `${preamble}{{if ${goCond}}}${body}{{end}}`;
|
|
12018
12155
|
}
|
|
12156
|
+
const attrTernary = lowerRegisteredAttrCall(this.emitCtx, name, parsed);
|
|
12157
|
+
if (attrTernary !== null)
|
|
12158
|
+
return attrTernary;
|
|
12019
12159
|
return `${name}="{{${this.renderParsedExpr(parsed)}}}"`;
|
|
12020
12160
|
}
|
|
12021
12161
|
if (parsed.kind === "template-literal") {
|
|
12022
12162
|
return `${name}="${this.renderParsedExpr(parsed)}"`;
|
|
12023
12163
|
}
|
|
12164
|
+
const attrAction = lowerRegisteredAttrCall(this.emitCtx, name, parsed);
|
|
12165
|
+
if (attrAction !== null)
|
|
12166
|
+
return attrAction;
|
|
12024
12167
|
const bareId = value.expr.trim();
|
|
12025
12168
|
const propName = this.state.propsObjectName && bareId.startsWith(`${this.state.propsObjectName}.`) ? bareId.slice(this.state.propsObjectName.length + 1) : bareId;
|
|
12026
12169
|
if (/^[A-Za-z_$][\w$]*$/.test(propName) && this.state.nillablePropNames.has(propName)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/go-template",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.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.
|
|
52
|
+
"@barefootjs/shared": "0.35.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.
|
|
71
|
-
"@barefootjs/jsx": "0.
|
|
72
|
-
"@barefootjs/vite": "0.
|
|
70
|
+
"@barefootjs/client": "0.35.0",
|
|
71
|
+
"@barefootjs/jsx": "0.35.0",
|
|
72
|
+
"@barefootjs/vite": "0.35.0",
|
|
73
73
|
"vite": "^6.0.0"
|
|
74
74
|
}
|
|
75
75
|
}
|
|
@@ -129,14 +129,6 @@ runAdapterConformanceTests({
|
|
|
129
129
|
// produces no complete template.
|
|
130
130
|
'jsx-element-prop-rest-bag-dynamic',
|
|
131
131
|
]),
|
|
132
|
-
skipDataPoints: new Set<string>([
|
|
133
|
-
// #2743: html/template's URL-context autoescape percent-encodes the
|
|
134
|
-
// queryHref BASE in href position (`日本語` → `%e6%97%a5…`); the JS
|
|
135
|
-
// reference only HTML-escapes. The bf_query helper itself is faithful —
|
|
136
|
-
// the divergence is Go's contextual escaper on the whole href value.
|
|
137
|
-
'query-href:gen:base:markup',
|
|
138
|
-
'query-href:gen:base:multibyte',
|
|
139
|
-
]),
|
|
140
132
|
onRenderError: (err, id) => {
|
|
141
133
|
if (err instanceof GoNotAvailableError) {
|
|
142
134
|
console.log(`Skipping [${id}]: ${err.message}`)
|
|
@@ -149,6 +149,11 @@ export function P(props: { config: object }) {
|
|
|
149
149
|
// naming exactly — the formula generalises, it isn't a lookup table
|
|
150
150
|
// limited to `query`.
|
|
151
151
|
expect(template).toContain('bf_custom_serialize .Config')
|
|
152
|
+
// #2743: the whole-attribute `bf_attr` route is keyed on the neutral
|
|
153
|
+
// `guard-list` + `helper === 'query'` shape specifically — a
|
|
154
|
+
// `helper-call` node (this plugin's shape) is a different node kind and
|
|
155
|
+
// must not be routed through it.
|
|
156
|
+
expect(template).not.toContain('bf_attr')
|
|
152
157
|
})
|
|
153
158
|
|
|
154
159
|
test('without the plugin registered, the call falls back to the generic (unsupported) lowering', () => {
|
|
@@ -163,6 +168,39 @@ export function P(props: { config: object }) {
|
|
|
163
168
|
expect(template).not.toContain('bf_custom_serialize')
|
|
164
169
|
})
|
|
165
170
|
|
|
171
|
+
// #2842: a `helper-call` node (not the `query` guard-list) reached through
|
|
172
|
+
// the undef-alternate omission shape now routes through the fixed `call()`
|
|
173
|
+
// dispatcher — but NOT through `bf_attr` (only `query` needs the
|
|
174
|
+
// URL-context-escape bypass; a plain helper-call keeps the ordinary
|
|
175
|
+
// `name="{{…}}"` wrapper the `{{if}}` already builds around).
|
|
176
|
+
test('an undef-alternate helper-call consequent is registry-lowered without bf_attr (#2842)', () => {
|
|
177
|
+
registerLoweringPlugin(customSerializePlugin)
|
|
178
|
+
const src = `
|
|
179
|
+
'use client'
|
|
180
|
+
import { customSerialize } from './lib'
|
|
181
|
+
export function P(props: { on: boolean; config: object }) {
|
|
182
|
+
return <div data-config={props.on ? customSerialize(props.config) : undefined}>x</div>
|
|
183
|
+
}
|
|
184
|
+
`
|
|
185
|
+
const { template } = generate(src)
|
|
186
|
+
expect(template).toContain('{{if .On}}data-config="{{bf_custom_serialize .Config}}"{{end}}')
|
|
187
|
+
expect(template).not.toContain('bf_attr')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
// Same shape, plugin NOT registered — pins that an unmatched call keeps the
|
|
191
|
+
// existing generic Go-method-call convention (no new BF10x refusal).
|
|
192
|
+
test('an undef-alternate helper-call consequent with no plugin registered keeps the generic convention', () => {
|
|
193
|
+
const src = `
|
|
194
|
+
'use client'
|
|
195
|
+
import { customSerialize } from './lib'
|
|
196
|
+
export function P(props: { on: boolean; config: object }) {
|
|
197
|
+
return <div data-config={props.on ? customSerialize(props.config) : undefined}>x</div>
|
|
198
|
+
}
|
|
199
|
+
`
|
|
200
|
+
const { template } = generate(src)
|
|
201
|
+
expect(template).toContain('{{if .On}}data-config="{{.CustomSerialize .Config}}"{{end}}')
|
|
202
|
+
})
|
|
203
|
+
|
|
166
204
|
test('a CONDITIONAL helper-call arg renders as pipeline-position bf_ternary, not an {{if}} action', () => {
|
|
167
205
|
// The #2324 union stage lowers a union-typed locale to a ternary
|
|
168
206
|
// pattern arg. Go templates have no expression-level conditional, and
|