@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/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
- const matchers = ctx.state.loweringMatchers;
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
- for (const matcher of matchers) {
6120
- const node = matcher(call.callee, call.args);
6121
- if (!node)
6122
- continue;
6123
- const rendered = renderLoweringNode(ctx, node);
6124
- if (rendered !== null)
6125
- return rendered;
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
- return null;
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.templateReadRootFields.add(name);
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(name)}`;
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 valueExpr = this.renderParsedExpr(parsed.consequent);
12016
- const body = `${name}="{{${valueExpr}}}"`;
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.33.6",
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.33.6"
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.33.6",
71
- "@barefootjs/jsx": "0.33.6",
72
- "@barefootjs/vite": "0.33.6",
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