@barefootjs/go-template 0.31.0 → 0.31.2

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.
Files changed (35) hide show
  1. package/dist/adapter/go-template-adapter.d.ts +49 -41
  2. package/dist/adapter/go-template-adapter.d.ts.map +1 -1
  3. package/dist/adapter/index.js +167 -101
  4. package/dist/adapter/lib/types.d.ts +4 -1
  5. package/dist/adapter/lib/types.d.ts.map +1 -1
  6. package/dist/adapter/memo/memo-compute.d.ts +9 -0
  7. package/dist/adapter/memo/memo-compute.d.ts.map +1 -1
  8. package/dist/adapter/memo/memo-type.d.ts +2 -0
  9. package/dist/adapter/memo/memo-type.d.ts.map +1 -1
  10. package/dist/adapter/props/prop-types.d.ts +2 -1
  11. package/dist/adapter/props/prop-types.d.ts.map +1 -1
  12. package/dist/adapter/spread/spread-codegen.d.ts.map +1 -1
  13. package/dist/adapter/type/type-codegen.d.ts +22 -5
  14. package/dist/adapter/type/type-codegen.d.ts.map +1 -1
  15. package/dist/adapter/value/parsed-literal-to-go.d.ts +12 -0
  16. package/dist/adapter/value/parsed-literal-to-go.d.ts.map +1 -1
  17. package/dist/adapter/value/value-lowering.d.ts +3 -0
  18. package/dist/adapter/value/value-lowering.d.ts.map +1 -1
  19. package/dist/index.js +168 -104
  20. package/dist/render-divergences.d.ts +6 -0
  21. package/dist/render-divergences.d.ts.map +1 -1
  22. package/dist/vite.js +493 -192
  23. package/package.json +5 -5
  24. package/src/__tests__/go-template-adapter.test.ts +155 -10
  25. package/src/adapter/go-template-adapter.ts +213 -128
  26. package/src/adapter/lib/types.ts +4 -1
  27. package/src/adapter/memo/memo-compute.ts +21 -17
  28. package/src/adapter/memo/memo-type.ts +5 -5
  29. package/src/adapter/props/prop-types.ts +6 -5
  30. package/src/adapter/spread/spread-codegen.ts +12 -5
  31. package/src/adapter/type/type-codegen.ts +86 -25
  32. package/src/adapter/value/parsed-literal-to-go.ts +28 -10
  33. package/src/adapter/value/value-lowering.ts +65 -29
  34. package/src/render-divergences.ts +6 -15
  35. package/src/test-render.ts +6 -1
@@ -975,15 +975,43 @@ function collapseLiteralUnion(typeInfo) {
975
975
  }
976
976
  return { kind: "primitive", raw: typeInfo.raw, primitive: first };
977
977
  }
978
- function typeInfoToGo(ctx, _typeInfo, defaultValue) {
978
+ function literalNumberValue(expr) {
979
+ if (expr.kind === "literal" && expr.literalType === "number" && typeof expr.value === "number") {
980
+ return expr.value;
981
+ }
982
+ if (expr.kind === "unary" && expr.op === "-" && expr.argument.kind === "literal" && expr.argument.literalType === "number" && typeof expr.argument.value === "number") {
983
+ return -expr.argument.value;
984
+ }
985
+ return null;
986
+ }
987
+ function inferGoTypeFromParsed(expr) {
988
+ const n = literalNumberValue(expr);
989
+ if (n !== null)
990
+ return Number.isInteger(n) ? "int" : "float64";
991
+ if (expr.kind === "literal") {
992
+ if (expr.literalType === "boolean")
993
+ return "bool";
994
+ if (expr.literalType === "string")
995
+ return "string";
996
+ return null;
997
+ }
998
+ if (expr.kind === "array-literal")
999
+ return "[]interface{}";
1000
+ return null;
1001
+ }
1002
+ function typeInfoToGo(ctx, _typeInfo, defaultValue, preParsed) {
979
1003
  const typeInfo = collapseLiteralUnion(_typeInfo);
980
1004
  switch (typeInfo.kind) {
981
1005
  case "primitive":
982
1006
  switch (typeInfo.primitive) {
983
1007
  case "string":
984
1008
  return "string";
985
- case "number":
1009
+ case "number": {
1010
+ const n = preParsed ? literalNumberValue(preParsed) : null;
1011
+ if (n !== null)
1012
+ return Number.isInteger(n) ? "int" : "float64";
986
1013
  return defaultValue !== undefined ? numberPrimitiveGoType(defaultValue) : "int";
1014
+ }
987
1015
  case "boolean":
988
1016
  return "bool";
989
1017
  default:
@@ -1006,30 +1034,21 @@ function typeInfoToGo(ctx, _typeInfo, defaultValue) {
1006
1034
  return resolved;
1007
1035
  }
1008
1036
  return "interface{}";
1009
- case "unknown":
1037
+ case "unknown": {
1038
+ const inferred = preParsed ? inferGoTypeFromParsed(preParsed) : null;
1039
+ if (inferred)
1040
+ return inferred;
1010
1041
  if (defaultValue !== undefined) {
1011
1042
  return inferTypeFromValue(defaultValue);
1012
1043
  }
1013
1044
  return "interface{}";
1045
+ }
1014
1046
  default:
1015
1047
  return "interface{}";
1016
1048
  }
1017
1049
  }
1018
1050
  function tsTypeStringToGo(ctx, tsType) {
1019
1051
  const t = tsType.trim();
1020
- if (t === "number")
1021
- return "int";
1022
- if (t === "string")
1023
- return "string";
1024
- if (t === "boolean" || t === "bool")
1025
- return "bool";
1026
- if (t.endsWith("[]")) {
1027
- const elem = t.slice(0, -2);
1028
- return `[]${tsTypeStringToGo(ctx, elem)}`;
1029
- }
1030
- const arrayMatch = t.match(/^Array<(.+)>$/);
1031
- if (arrayMatch)
1032
- return `[]${tsTypeStringToGo(ctx, arrayMatch[1])}`;
1033
1052
  if (ctx.state.localStructFields.has(t) || ctx.state.localTypeAliases.has(t))
1034
1053
  return t;
1035
1054
  return "interface{}";
@@ -1073,16 +1092,25 @@ function bakeInlineObjectAsGoMap(ctx, expr) {
1073
1092
  }
1074
1093
  return `map[string]interface{}{${entries.join(", ")}}`;
1075
1094
  }
1076
- function parsedLiteralToGo(ctx, expr, typeInfo) {
1095
+ function numberLiteralRawGo(expr) {
1077
1096
  if (expr.kind === "unary" && expr.op === "-" && expr.argument.kind === "literal" && expr.argument.literalType === "number") {
1078
1097
  return expr.argument.raw !== undefined ? `-${expr.argument.raw}` : null;
1079
1098
  }
1099
+ if (expr.kind === "literal" && expr.literalType === "number") {
1100
+ return expr.raw ?? null;
1101
+ }
1102
+ return null;
1103
+ }
1104
+ function parsedLiteralToGo(ctx, expr, typeInfo) {
1105
+ const numberGo = numberLiteralRawGo(expr);
1106
+ if (numberGo !== null)
1107
+ return numberGo;
1080
1108
  if (expr.kind === "literal") {
1081
1109
  switch (expr.literalType) {
1082
1110
  case "string":
1083
1111
  return JSON.stringify(expr.value);
1084
1112
  case "number":
1085
- return expr.raw ?? null;
1113
+ return null;
1086
1114
  case "boolean":
1087
1115
  return expr.value ? "true" : "false";
1088
1116
  case "null":
@@ -1134,10 +1162,10 @@ function parsedLiteralToGo(ctx, expr, typeInfo) {
1134
1162
 
1135
1163
  // src/adapter/value/value-lowering.ts
1136
1164
  var EMPTY_PROP_FALLBACK_VARS = new Map;
1137
- function nillableAwarePropRef(ctx, propName, expectedType) {
1138
- const fieldRef = `in.${capitalizeFieldName(propName)}`;
1165
+ function nillableAwarePropRef(ctx, param, expectedType) {
1166
+ const fieldRef = `in.${capitalizeFieldName(param.sourceName ?? param.name)}`;
1139
1167
  const scalar = expectedType.kind === "primitive" ? expectedType : expectedType.kind === "union" && expectedType.unionTypes?.length === 2 ? expectedType.unionTypes.find((t) => t.primitive !== "undefined" && t.primitive !== "null") : undefined;
1140
- if (ctx.state.nillablePropNames.has(propName) && scalar?.kind === "primitive") {
1168
+ if (ctx.state.nillablePropNames.has(param.name) && scalar?.kind === "primitive") {
1141
1169
  const goType = scalar.primitive === "boolean" ? "bool" : scalar.primitive === "number" ? "float64" : scalar.primitive === "string" ? "string" : null;
1142
1170
  if (goType) {
1143
1171
  const zero = goType === "bool" ? "false" : goType === "string" ? '""' : "0";
@@ -1148,21 +1176,29 @@ function nillableAwarePropRef(ctx, propName, expectedType) {
1148
1176
  }
1149
1177
  function convertInitialValue(ctx, value, _typeInfo, propsParams, preParsed) {
1150
1178
  const typeInfo = collapseLiteralUnion(_typeInfo);
1151
- const propRef = (propName2) => nillableAwarePropRef(ctx, propName2, typeInfo);
1179
+ const propRef = (param2) => nillableAwarePropRef(ctx, param2, typeInfo);
1152
1180
  if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value)) {
1153
- if (propsParams?.some((p) => p.name === value)) {
1154
- return propRef(value);
1181
+ const param2 = propsParams?.find((p) => p.name === value);
1182
+ if (param2) {
1183
+ return propRef(param2);
1155
1184
  }
1156
1185
  }
1157
1186
  const propName = ctx.extractPropNameFromInitialValue(value, preParsed);
1158
- if (propName && propsParams?.some((p) => p.name === propName)) {
1159
- return propRef(propName);
1187
+ const param = propName ? propsParams?.find((p) => p.name === propName) : undefined;
1188
+ if (param) {
1189
+ return propRef(param);
1160
1190
  }
1161
1191
  if (typeInfo.kind === "primitive") {
1162
1192
  if (typeInfo.primitive === "boolean") {
1193
+ if (preParsed?.kind === "literal" && preParsed.literalType === "boolean" && typeof preParsed.value === "boolean") {
1194
+ return preParsed.value ? "true" : "false";
1195
+ }
1163
1196
  return value === "true" ? "true" : "false";
1164
1197
  }
1165
1198
  if (typeInfo.primitive === "number") {
1199
+ const numGo = preParsed ? numberLiteralRawGo(preParsed) : null;
1200
+ if (numGo !== null)
1201
+ return numGo;
1166
1202
  if (/^-?\d+$/.test(value))
1167
1203
  return value;
1168
1204
  if (/^-?\d+\.\d+$/.test(value))
@@ -1170,7 +1206,10 @@ function convertInitialValue(ctx, value, _typeInfo, propsParams, preParsed) {
1170
1206
  return "0";
1171
1207
  }
1172
1208
  if (typeInfo.primitive === "string") {
1173
- if (value.startsWith("'") || value.endsWith("'")) {
1209
+ if (preParsed?.kind === "literal" && preParsed.literalType === "string" && typeof preParsed.value === "string") {
1210
+ return JSON.stringify(preParsed.value);
1211
+ }
1212
+ if (value.startsWith("'") && value.endsWith("'")) {
1174
1213
  return value.replace(/'/g, '"');
1175
1214
  }
1176
1215
  if (value.startsWith('"') && value.endsWith('"')) {
@@ -1232,19 +1271,21 @@ function objectLiteralToGoMap(ctx, expr) {
1232
1271
  return `map[string]interface{}{${entries.join(", ")}}`;
1233
1272
  }
1234
1273
  function getSignalInitialValueAsGo(ctx, initialValue, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS, signalType) {
1235
- const propRef = (propName2) => signalType ? nillableAwarePropRef(ctx, propName2, signalType) : `in.${capitalizeFieldName(propName2)}`;
1236
- if (propsParams.some((p) => p.name === initialValue)) {
1274
+ const propRef = (param2) => signalType ? nillableAwarePropRef(ctx, param2, signalType) : `in.${capitalizeFieldName(param2.sourceName ?? param2.name)}`;
1275
+ const directParam = propsParams.find((p) => p.name === initialValue);
1276
+ if (directParam) {
1237
1277
  const hoisted = propFallbackVars.get(initialValue);
1238
1278
  if (hoisted)
1239
1279
  return hoisted.varName;
1240
- return propRef(initialValue);
1280
+ return propRef(directParam);
1241
1281
  }
1242
1282
  const propName = ctx.extractPropNameFromInitialValue(initialValue);
1243
- if (propName && propsParams.some((p) => p.name === propName)) {
1283
+ const param = propName ? propsParams.find((p) => p.name === propName) : undefined;
1284
+ if (param) {
1244
1285
  const hoisted = propFallbackVars.get(propName);
1245
1286
  if (hoisted)
1246
1287
  return hoisted.varName;
1247
- return propRef(propName);
1288
+ return propRef(param);
1248
1289
  }
1249
1290
  if (/^-?\d+$/.test(initialValue)) {
1250
1291
  return initialValue;
@@ -1278,8 +1319,9 @@ function resolveMapJoinBaseAsGo(ctx, object, signals, propsParams) {
1278
1319
  }
1279
1320
  }
1280
1321
  const propName = object.kind === "member" && !object.computed && object.object.kind === "identifier" && object.object.name === "props" ? object.property : object.kind === "identifier" ? object.name : null;
1281
- if (propName && propsParams.some((p) => p.name === propName)) {
1282
- return `in.${capitalizeFieldName(propName)}`;
1322
+ const param = propName ? propsParams.find((p) => p.name === propName) : undefined;
1323
+ if (param) {
1324
+ return `in.${capitalizeFieldName(param.sourceName ?? param.name)}`;
1283
1325
  }
1284
1326
  if (object.kind === "array-literal") {
1285
1327
  if (object.elements.length === 0)
@@ -1310,11 +1352,12 @@ function mapJoinChainToGo(ctx, chain, signals, propsParams, propFallbackVars) {
1310
1352
  for (const name of freeVars) {
1311
1353
  const sig = signals.find((s) => s.getter === name);
1312
1354
  let goExpr = null;
1355
+ const freeVarParam = propsParams.find((p) => p.name === name);
1313
1356
  if (sig) {
1314
1357
  goExpr = getSignalInitialValueAsGo(ctx, sig.initialValue, propsParams, propFallbackVars, sig.type);
1315
- } else if (propsParams.some((p) => p.name === name)) {
1358
+ } else if (freeVarParam) {
1316
1359
  const hoisted = propFallbackVars.get(name);
1317
- goExpr = hoisted ? hoisted.varName : `in.${capitalizeFieldName(name)}`;
1360
+ goExpr = hoisted ? hoisted.varName : `in.${capitalizeFieldName(freeVarParam.sourceName ?? name)}`;
1318
1361
  }
1319
1362
  if (goExpr === null)
1320
1363
  return null;
@@ -1357,14 +1400,14 @@ function isBooleanMemo(ctx, memo, signals, propsParamMap) {
1357
1400
  return true;
1358
1401
  if (/\?\?\s*(true|false)\b/.test(sig.initialValue))
1359
1402
  return true;
1360
- const propName = ctx.extractPropNameFromInitialValue(sig.initialValue) ?? sig.initialValue;
1403
+ const propName = ctx.extractPropNameFromInitialValue(sig.initialValue, sig.parsed) ?? sig.initialValue;
1361
1404
  const prop2 = propsParamMap.get(propName);
1362
- if (prop2 && typeInfoToGo(ctx, prop2.type, prop2.defaultValue) === "bool")
1405
+ if (prop2 && typeInfoToGo(ctx, prop2.type, prop2.defaultValue, prop2.parsed) === "bool")
1363
1406
  return true;
1364
1407
  return false;
1365
1408
  }
1366
1409
  const prop = propsParamMap.get(name);
1367
- return !!prop && typeInfoToGo(ctx, prop.type, prop.defaultValue) === "bool";
1410
+ return !!prop && typeInfoToGo(ctx, prop.type, prop.defaultValue, prop.parsed) === "bool";
1368
1411
  };
1369
1412
  const ternary = c.match(/=>\s*\w+\(\)\s*\?\s*(\w+)\(\)\s*:\s*(\w+)\(\)/);
1370
1413
  if (ternary) {
@@ -1866,7 +1909,8 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
1866
1909
  const hoisted = propFallbackVars.get(propName);
1867
1910
  if (hoisted)
1868
1911
  return hoisted.varName;
1869
- return `in.${capitalizeFieldName(propName)}`;
1912
+ const param = propsParams.find((p) => p.name === propName);
1913
+ return `in.${capitalizeFieldName(param?.sourceName ?? propName)}`;
1870
1914
  };
1871
1915
  const envGetKey = (e) => {
1872
1916
  if (e.kind !== "call" || e.callee.kind !== "member" || e.callee.computed)
@@ -1958,7 +2002,7 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
1958
2002
  const param = propsParams.find((p) => p.name === propName);
1959
2003
  if (param && ctx.state.nillablePropNames.has(propName)) {
1960
2004
  const isNe = body.op === "!==" || body.op === "!=";
1961
- return `in.${capitalizeFieldName(propName)} ${isNe ? "!=" : "=="} nil`;
2005
+ return `in.${capitalizeFieldName(param.sourceName ?? propName)} ${isNe ? "!=" : "=="} nil`;
1962
2006
  }
1963
2007
  }
1964
2008
  }
@@ -2030,7 +2074,7 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
2030
2074
  return `${hoisted.varName} ${operator} ${operand}`;
2031
2075
  const fieldName = capitalizeFieldName(propName);
2032
2076
  if (param.type) {
2033
- const goType = typeInfoToGo(ctx, param.type, param.defaultValue);
2077
+ const goType = typeInfoToGo(ctx, param.type, param.defaultValue, param.parsed);
2034
2078
  if (goType === "interface{}")
2035
2079
  return `in.${fieldName}.(int) ${operator} ${operand}`;
2036
2080
  }
@@ -2041,9 +2085,9 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
2041
2085
  const varName = body.left.name;
2042
2086
  const param = propsParams.find((p) => p.name === varName);
2043
2087
  if (param) {
2044
- const fieldName = capitalizeFieldName(varName);
2088
+ const fieldName = capitalizeFieldName(param.sourceName ?? varName);
2045
2089
  if (param.type) {
2046
- const goType = typeInfoToGo(ctx, param.type, param.defaultValue);
2090
+ const goType = typeInfoToGo(ctx, param.type, param.defaultValue, param.parsed);
2047
2091
  if (goType === "interface{}")
2048
2092
  return `in.${fieldName}.(int) ${operator} ${operand}`;
2049
2093
  }
@@ -2067,7 +2111,7 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
2067
2111
  if (body.kind === "identifier") {
2068
2112
  const param = propsParams.find((p) => p.name === body.name);
2069
2113
  if (param)
2070
- return `in.${capitalizeFieldName(body.name)}`;
2114
+ return `in.${capitalizeFieldName(param.sourceName ?? body.name)}`;
2071
2115
  }
2072
2116
  if (body.kind === "binary" && body.op === "+") {
2073
2117
  const concatGo = resolveStringConcatChainGo(ctx, body, signals, propsParams, propFallbackVars, propRef);
@@ -2129,7 +2173,8 @@ function resolveGetterValueAsGo(ctx, name, signals, propsParams, propFallbackVar
2129
2173
  const stripped = memo.computation.replace(/^\(\)\s*=>\s*/, "");
2130
2174
  const fb = ctx.extractPropFallback(stripped);
2131
2175
  if (fb && capitalizeFieldName(fb.propName) === capitalizeFieldName(memo.name)) {
2132
- const field = `in.${capitalizeFieldName(fb.propName)}`;
2176
+ const fbParam = propsParams.find((p) => p.name === fb.propName);
2177
+ const field = `in.${capitalizeFieldName(fbParam?.sourceName ?? fb.propName)}`;
2133
2178
  return `func() interface{} { v := interface{}(${field}); if v == nil || v == "" { return ${fb.goFallback} }; return v }()`;
2134
2179
  }
2135
2180
  return computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars, new Set([...resolving, memo.name]));
@@ -2137,7 +2182,7 @@ function resolveGetterValueAsGo(ctx, name, signals, propsParams, propFallbackVar
2137
2182
  const param = propsParams.find((p) => p.name === name);
2138
2183
  if (param) {
2139
2184
  const hoisted = propFallbackVars.get(name);
2140
- return hoisted ? hoisted.varName : `in.${capitalizeFieldName(name)}`;
2185
+ return hoisted ? hoisted.varName : `in.${capitalizeFieldName(param.sourceName ?? name)}`;
2141
2186
  }
2142
2187
  return null;
2143
2188
  }
@@ -2398,10 +2443,10 @@ function buildSpreadInitializer(ctx, spreadExpr, ir, parsed) {
2398
2443
  if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
2399
2444
  const param = ir.metadata.propsParams.find((p) => p.name === trimmed);
2400
2445
  if (param) {
2401
- return `in.${capitalizeFieldName(param.name)}`;
2446
+ return `in.${capitalizeFieldName(param.sourceName ?? param.name)}`;
2402
2447
  }
2403
2448
  if (ir.metadata.propsObjectName === trimmed) {
2404
- const entries = ir.metadata.propsParams.map((p) => `${JSON.stringify(p.name)}: in.${capitalizeFieldName(p.name)}`);
2449
+ const entries = ir.metadata.propsParams.map((p) => `${JSON.stringify(p.sourceName ?? p.name)}: in.${capitalizeFieldName(p.sourceName ?? p.name)}`);
2405
2450
  return `map[string]any{${entries.join(", ")}}`;
2406
2451
  }
2407
2452
  if (ir.metadata.restPropsName === trimmed) {
@@ -2455,7 +2500,7 @@ function conditionToGoBool(condition, ir) {
2455
2500
  const param = ir.metadata.propsParams.find((p) => p.name === node.name);
2456
2501
  if (!param)
2457
2502
  return null;
2458
- const field = `in.${capitalizeFieldName(param.name)}`;
2503
+ const field = `in.${capitalizeFieldName(param.sourceName ?? param.name)}`;
2459
2504
  const prim = param.type.kind === "primitive" ? param.type.primitive : undefined;
2460
2505
  let truthy;
2461
2506
  if (prim === "boolean") {
@@ -2493,7 +2538,7 @@ function objectLiteralToGoSpreadMap(ctx, obj, ir) {
2493
2538
  const param = ir.metadata.propsParams.find((p) => p.name === val.name);
2494
2539
  if (!param)
2495
2540
  return null;
2496
- goVal = `in.${capitalizeFieldName(param.name)}`;
2541
+ goVal = `in.${capitalizeFieldName(param.sourceName ?? param.name)}`;
2497
2542
  } else {
2498
2543
  const indexed = recordIndexAccessToGoMap(ctx, val, ir);
2499
2544
  if (indexed === null)
@@ -2517,7 +2562,8 @@ function recordIndexAccessToGoMap(ctx, val, ir) {
2517
2562
  return `${JSON.stringify(e.key)}: ${mapVal}`;
2518
2563
  });
2519
2564
  ctx.state.usesFmt = true;
2520
- const field = `in.${capitalizeFieldName(parsed.indexPropName)}`;
2565
+ const indexParam = ir.metadata.propsParams.find((p) => p.name === parsed.indexPropName);
2566
+ const field = `in.${capitalizeFieldName(indexParam?.sourceName ?? parsed.indexPropName)}`;
2521
2567
  return `map[string]any{${entries.join(", ")}}[fmt.Sprint(${field})]`;
2522
2568
  }
2523
2569
 
@@ -2534,9 +2580,9 @@ function buildPropTypeOverrides(ctx, ir) {
2534
2580
  const param = ir.metadata.propsParams.find((p) => p.name === propName);
2535
2581
  if (!param)
2536
2582
  continue;
2537
- const propGoType = typeInfoToGo(ctx, param.type, param.defaultValue);
2583
+ const propGoType = typeInfoToGo(ctx, param.type, param.defaultValue, param.parsed);
2538
2584
  if (propGoType.includes("interface{}")) {
2539
- const signalGoType = typeInfoToGo(ctx, signal.type, signal.initialValue);
2585
+ const signalGoType = typeInfoToGo(ctx, signal.type, signal.initialValue, signal.parsed);
2540
2586
  if (!signalGoType.includes("interface{}")) {
2541
2587
  overrides.set(propName, signalGoType);
2542
2588
  }
@@ -2547,7 +2593,7 @@ function buildPropTypeOverrides(ctx, ir) {
2547
2593
  const param = ir.metadata.propsParams.find((p) => p.name === propName);
2548
2594
  if (!param)
2549
2595
  continue;
2550
- const resolved = overrides.get(propName) ?? typeInfoToGo(ctx, param.type, param.defaultValue);
2596
+ const resolved = overrides.get(propName) ?? typeInfoToGo(ctx, param.type, param.defaultValue, param.parsed);
2551
2597
  if (resolved === "int") {
2552
2598
  overrides.set(propName, "float64");
2553
2599
  }
@@ -2717,7 +2763,7 @@ function collectPresenceCheckedPropNames(ctx, ir) {
2717
2763
  return names;
2718
2764
  }
2719
2765
  function resolvePropGoType(ctx, param, propTypeOverrides) {
2720
- const base = propTypeOverrides.get(param.name) ?? typeInfoToGo(ctx, param.type, param.defaultValue);
2766
+ const base = propTypeOverrides.get(param.name) ?? typeInfoToGo(ctx, param.type, param.defaultValue, param.parsed);
2721
2767
  if (param.optional && ctx.state.localStructFields.has(base)) {
2722
2768
  return "map[string]interface{}";
2723
2769
  }
@@ -3105,6 +3151,18 @@ ${scriptRegistrations}${templateBody}
3105
3151
  nonCollidingContextConsumers(taken) {
3106
3152
  return this.state.contextConsumers.filter((c) => !taken.has(this.contextFieldName(c)));
3107
3153
  }
3154
+ isNestedArrayShadowed(param, nestedArrayFields) {
3155
+ return nestedArrayFields.has(capitalizeFieldName(param.name)) || nestedArrayFields.has(capitalizeFieldName(param.sourceName ?? param.name));
3156
+ }
3157
+ propParamFieldNamesUnion(params) {
3158
+ return params.flatMap((p) => [capitalizeFieldName(p.name), capitalizeFieldName(p.sourceName ?? p.name)]);
3159
+ }
3160
+ claimJsonTag(desired, taken) {
3161
+ if (taken.has(desired))
3162
+ return "-";
3163
+ taken.add(desired);
3164
+ return desired;
3165
+ }
3108
3166
  generateTypes(ir) {
3109
3167
  this.state.usesHtmlTemplate = false;
3110
3168
  this.state.usesFmt = false;
@@ -3135,13 +3193,16 @@ ${scriptRegistrations}${templateBody}
3135
3193
  if (!this.childDerivedFieldDeps.has(componentName))
3136
3194
  return;
3137
3195
  const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
3138
- const params = (ir.metadata.propsParams ?? []).filter((p) => !nestedArrayFields.has(capitalizeFieldName(p.name)));
3139
- const takenInput = new Set((ir.metadata.propsParams ?? []).map((p) => capitalizeFieldName(p.name)));
3196
+ const params = (ir.metadata.propsParams ?? []).filter((p) => !this.isNestedArrayShadowed(p, nestedArrayFields));
3197
+ const takenInput = new Set(this.propParamFieldNamesUnion(ir.metadata.propsParams ?? []));
3140
3198
  const eligible = nestedComponents.every((n) => n.isDynamic && !n.isPropDerived) && spreadSlots.length === 0 && !ir.metadata.restPropsName && this.nonCollidingContextConsumers(takenInput).length === 0;
3141
3199
  if (!eligible)
3142
3200
  return;
3143
3201
  this.childRepropsReady.set(componentName, {
3144
- params: params.map((p) => capitalizeFieldName(p.name)),
3202
+ params: params.map((p) => ({
3203
+ propsField: capitalizeFieldName(p.name),
3204
+ inputField: capitalizeFieldName(p.sourceName ?? p.name)
3205
+ })),
3145
3206
  usesSearchParams: this.usesSearchParams(ir)
3146
3207
  });
3147
3208
  }
@@ -3177,17 +3238,17 @@ ${scriptRegistrations}${templateBody}
3177
3238
  lines.push("\t\t\tBfMount: b.BfMount,");
3178
3239
  if (usesSearchParams)
3179
3240
  lines.push("\t\t\tSearchParams: b.SearchParams,");
3180
- for (const field of params) {
3181
- lines.push(` ${field}: b.${field},`);
3241
+ for (const { propsField, inputField } of params) {
3242
+ lines.push(` ${inputField}: b.${propsField},`);
3182
3243
  }
3183
3244
  lines.push("\t\t}");
3184
3245
  lines.push("\t\tfor i := 0; i < len(kv); i += 2 {");
3185
3246
  lines.push("\t\t\tname, _ := kv[i].(string)");
3186
3247
  lines.push("\t\t\tvar err error");
3187
3248
  lines.push("\t\t\tswitch name {");
3188
- for (const field of params) {
3189
- lines.push(` case ${JSON.stringify(field)}:`);
3190
- lines.push(` err = bf.RepropsAssign(${q}, ${JSON.stringify(field)}, &in.${field}, kv[i+1])`);
3249
+ for (const { propsField, inputField } of params) {
3250
+ lines.push(` case ${JSON.stringify(propsField)}:`);
3251
+ lines.push(` err = bf.RepropsAssign(${q}, ${JSON.stringify(propsField)}, &in.${inputField}, kv[i+1])`);
3191
3252
  }
3192
3253
  lines.push("\t\t\tdefault:");
3193
3254
  lines.push(` err = bf.RepropsUnknownFieldError(${q}, name)`);
@@ -3325,6 +3386,7 @@ ${goFields.join(`
3325
3386
  return false;
3326
3387
  const taken = new Set([
3327
3388
  ...ir.metadata.propsParams.map((p) => capitalizeFieldName(p.name)),
3389
+ ...ir.metadata.propsParams.map((p) => capitalizeFieldName(p.sourceName ?? p.name)),
3328
3390
  ...ir.metadata.signals.filter((s) => !s.envReader).map((s) => capitalizeFieldName(s.getter)),
3329
3391
  ...ir.metadata.memos.map((m) => capitalizeFieldName(m.name)),
3330
3392
  ...this.state.contextConsumers.map((c) => this.contextFieldName(c))
@@ -3347,8 +3409,8 @@ ${goFields.join(`
3347
3409
  const inputNested = nestedComponents.filter((n) => !n.isDynamic || n.isPropDerived);
3348
3410
  const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
3349
3411
  for (const param of ir.metadata.propsParams) {
3350
- const fieldName = capitalizeFieldName(param.name);
3351
- if (nestedArrayFields.has(fieldName))
3412
+ const fieldName = capitalizeFieldName(param.sourceName ?? param.name);
3413
+ if (this.isNestedArrayShadowed(param, nestedArrayFields))
3352
3414
  continue;
3353
3415
  const goType = resolvePropGoType(this.emitCtx, param, propTypeOverrides);
3354
3416
  lines.push(` ${fieldName} ${goType}`);
@@ -3358,7 +3420,7 @@ ${goFields.join(`
3358
3420
  continue;
3359
3421
  lines.push(` ${nested.name}s []${nested.name}Input`);
3360
3422
  }
3361
- const takenInput = new Set(ir.metadata.propsParams.map((p) => capitalizeFieldName(p.name)));
3423
+ const takenInput = new Set(this.propParamFieldNamesUnion(ir.metadata.propsParams));
3362
3424
  for (const c of this.nonCollidingContextConsumers(takenInput)) {
3363
3425
  lines.push(` ${this.contextFieldName(c)} ${this.contextConsumerGoType(c)}`);
3364
3426
  }
@@ -3382,8 +3444,9 @@ ${goFields.join(`
3382
3444
  generatePropsStruct(lines, ir, componentName, nestedComponents, propTypeOverrides, spreadSlots) {
3383
3445
  const propsTypeName = `${componentName}Props`;
3384
3446
  this.emitPropsStructHeader(lines, ir, propsTypeName, componentName);
3385
- this.emitPropsDataFields(lines, ir, nestedComponents, propTypeOverrides);
3386
- this.emitPropsAuxFields(lines, ir, componentName, nestedComponents, spreadSlots);
3447
+ const takenJsonTags = new Set;
3448
+ this.emitPropsDataFields(lines, ir, nestedComponents, propTypeOverrides, takenJsonTags);
3449
+ this.emitPropsAuxFields(lines, ir, componentName, nestedComponents, spreadSlots, takenJsonTags);
3387
3450
  lines.push("}");
3388
3451
  lines.push("");
3389
3452
  }
@@ -3420,7 +3483,7 @@ ${goFields.join(`
3420
3483
  resolveLoopDatumFields(itemType) {
3421
3484
  if (!itemType)
3422
3485
  return [];
3423
- const typeName = itemType.raw?.replace(/\[\]$/, "") ?? itemType.raw;
3486
+ const typeName = itemType.kind === "array" ? itemType.elementType?.raw ?? itemType.raw : itemType.raw;
3424
3487
  if (!typeName)
3425
3488
  return [];
3426
3489
  for (const td of this.state.currentTypeDefinitions) {
@@ -3583,7 +3646,8 @@ ${goFields.join(`
3583
3646
  const propFieldNames = new Set;
3584
3647
  for (const param of ir.metadata.propsParams) {
3585
3648
  const fieldName = capitalizeFieldName(param.name);
3586
- if (nestedArrayFields.has(fieldName))
3649
+ const inputField = capitalizeFieldName(param.sourceName ?? param.name);
3650
+ if (this.isNestedArrayShadowed(param, nestedArrayFields))
3587
3651
  continue;
3588
3652
  const hoisted = propFallbackVars.get(param.name);
3589
3653
  if (hoisted) {
@@ -3592,13 +3656,13 @@ ${goFields.join(`
3592
3656
  const paramDefault = goPropDefault(param.defaultValue);
3593
3657
  const memoFold = memoFallbacks.get(fieldName);
3594
3658
  if (paramDefault !== null) {
3595
- lines.push(` ${fieldName}: ${applyGoFallback(`in.${fieldName}`, paramDefault)},`);
3659
+ lines.push(` ${fieldName}: ${applyGoFallback(`in.${inputField}`, paramDefault)},`);
3596
3660
  } else if (memoFold !== undefined && memoFold.goType === "string") {
3597
- lines.push(` ${fieldName}: ${applyGoFallback(`in.${fieldName}`, memoFold.goFallback)},`);
3661
+ lines.push(` ${fieldName}: ${applyGoFallback(`in.${inputField}`, memoFold.goFallback)},`);
3598
3662
  } else if (memoFold !== undefined) {
3599
- lines.push(` ${fieldName}: func() interface{} { v := interface{}(in.${fieldName}); if v == nil || v == "" { return ${memoFold.goFallback} }; return v }(),`);
3663
+ lines.push(` ${fieldName}: func() interface{} { v := interface{}(in.${inputField}); if v == nil || v == "" { return ${memoFold.goFallback} }; return v }(),`);
3600
3664
  } else {
3601
- lines.push(` ${fieldName}: in.${fieldName},`);
3665
+ lines.push(` ${fieldName}: in.${inputField},`);
3602
3666
  }
3603
3667
  }
3604
3668
  propFieldNames.add(fieldName);
@@ -3651,7 +3715,7 @@ ${goFields.join(`
3651
3715
  lines.push(` ${f.name}: ${f.init},`);
3652
3716
  }
3653
3717
  const takenInit = new Set([
3654
- ...ir.metadata.propsParams.map((p) => capitalizeFieldName(p.name)),
3718
+ ...this.propParamFieldNamesUnion(ir.metadata.propsParams),
3655
3719
  ...ir.metadata.signals.map((s) => capitalizeFieldName(s.getter)),
3656
3720
  ...ir.metadata.memos.map((m) => capitalizeFieldName(m.name))
3657
3721
  ]);
@@ -3690,7 +3754,7 @@ ${goFields.join(`
3690
3754
  }
3691
3755
  if (jsxName.includes("-"))
3692
3756
  return;
3693
- const fieldName = this.childPropFieldNames.get(child.name)?.get(jsxName) ?? capitalizeFieldName(jsxName);
3757
+ const fieldName = capitalizeFieldName(jsxName);
3694
3758
  lines.push(` ${fieldName}: ${goValue},`);
3695
3759
  };
3696
3760
  for (const prop of child.props) {
@@ -4059,15 +4123,15 @@ ${goFields.join(`
4059
4123
  lines.push('\tSearchParams bf.SearchParams `json:"-"`');
4060
4124
  }
4061
4125
  }
4062
- emitPropsDataFields(lines, ir, nestedComponents, propTypeOverrides) {
4126
+ emitPropsDataFields(lines, ir, nestedComponents, propTypeOverrides, takenJsonTags) {
4063
4127
  const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
4064
4128
  const propFieldNames = new Set;
4065
4129
  for (const param of ir.metadata.propsParams) {
4066
4130
  const fieldName = capitalizeFieldName(param.name);
4067
- if (nestedArrayFields.has(fieldName))
4131
+ if (this.isNestedArrayShadowed(param, nestedArrayFields))
4068
4132
  continue;
4069
4133
  const goType = resolvePropGoType(this.emitCtx, param, propTypeOverrides);
4070
- const jsonTag = param.name === "children" ? "-" : this.toJsonTag(param.name);
4134
+ const jsonTag = param.name === "children" ? "-" : this.claimJsonTag(this.toJsonTag(param.sourceName ?? param.name), takenJsonTags);
4071
4135
  lines.push(` ${fieldName} ${goType} \`json:"${jsonTag}"\``);
4072
4136
  propFieldNames.add(fieldName);
4073
4137
  }
@@ -4078,7 +4142,7 @@ ${goFields.join(`
4078
4142
  const fieldName = capitalizeFieldName(signal.getter);
4079
4143
  if (propFieldNames.has(fieldName))
4080
4144
  continue;
4081
- const jsonTag = this.toJsonTag(signal.getter);
4145
+ const jsonTag = this.claimJsonTag(this.toJsonTag(signal.getter), takenJsonTags);
4082
4146
  const synthType = this.state.synthStructTypes.get(signal.getter);
4083
4147
  if (synthType) {
4084
4148
  lines.push(` ${fieldName} ${typeInfoToGo(this.emitCtx, synthType)} \`json:"${jsonTag}"\``);
@@ -4087,13 +4151,13 @@ ${goFields.join(`
4087
4151
  let goType;
4088
4152
  let referencedProp = propsParamMap.get(signal.initialValue);
4089
4153
  if (!referencedProp) {
4090
- const propName = this.extractPropNameFromInitialValue(signal.initialValue);
4154
+ const propName = this.extractPropNameFromInitialValue(signal.initialValue, signal.parsed);
4091
4155
  if (propName)
4092
4156
  referencedProp = propsParamMap.get(propName);
4093
4157
  }
4094
4158
  if (referencedProp) {
4095
- const propGoType = typeInfoToGo(this.emitCtx, referencedProp.type, referencedProp.defaultValue);
4096
- const signalGoType = typeInfoToGo(this.emitCtx, signal.type, signal.initialValue);
4159
+ const propGoType = typeInfoToGo(this.emitCtx, referencedProp.type, referencedProp.defaultValue, referencedProp.parsed);
4160
+ const signalGoType = typeInfoToGo(this.emitCtx, signal.type, signal.initialValue, signal.parsed);
4097
4161
  if (propGoType.includes("interface{}")) {
4098
4162
  goType = signalGoType;
4099
4163
  } else if (!signalGoType.includes("interface{}") && signalGoType !== propGoType) {
@@ -4102,7 +4166,7 @@ ${goFields.join(`
4102
4166
  goType = propGoType;
4103
4167
  }
4104
4168
  } else {
4105
- goType = typeInfoToGo(this.emitCtx, signal.type, signal.initialValue);
4169
+ goType = typeInfoToGo(this.emitCtx, signal.type, signal.initialValue, signal.parsed);
4106
4170
  }
4107
4171
  lines.push(` ${fieldName} ${goType} \`json:"${jsonTag}"\``);
4108
4172
  }
@@ -4110,12 +4174,12 @@ ${goFields.join(`
4110
4174
  const fieldName = capitalizeFieldName(memo.name);
4111
4175
  if (propFieldNames.has(fieldName))
4112
4176
  continue;
4113
- const jsonTag = this.toJsonTag(memo.name);
4177
+ const jsonTag = this.claimJsonTag(this.toJsonTag(memo.name), takenJsonTags);
4114
4178
  const goType = this.inferMemoType(memo, ir.metadata.signals, propsParamMap);
4115
4179
  lines.push(` ${fieldName} ${goType} \`json:"${jsonTag}"\``);
4116
4180
  }
4117
4181
  }
4118
- emitPropsAuxFields(lines, ir, componentName, nestedComponents, spreadSlots) {
4182
+ emitPropsAuxFields(lines, ir, componentName, nestedComponents, spreadSlots, takenJsonTags) {
4119
4183
  const takenForDerivedConsts = new Set([
4120
4184
  ...ir.metadata.propsParams.map((p) => capitalizeFieldName(p.name)),
4121
4185
  ...ir.metadata.signals.map((s) => capitalizeFieldName(s.getter)),
@@ -4125,12 +4189,12 @@ ${goFields.join(`
4125
4189
  lines.push(` ${f.name} string \`json:"-"\``);
4126
4190
  }
4127
4191
  const takenProps = new Set([
4128
- ...ir.metadata.propsParams.map((p) => capitalizeFieldName(p.name)),
4192
+ ...this.propParamFieldNamesUnion(ir.metadata.propsParams),
4129
4193
  ...ir.metadata.signals.map((s) => capitalizeFieldName(s.getter)),
4130
4194
  ...ir.metadata.memos.map((m) => capitalizeFieldName(m.name))
4131
4195
  ]);
4132
4196
  for (const c of this.nonCollidingContextConsumers(takenProps)) {
4133
- const jsonTag = this.toJsonTag(c.localName);
4197
+ const jsonTag = this.claimJsonTag(this.toJsonTag(c.localName), takenJsonTags);
4134
4198
  lines.push(` ${this.contextFieldName(c)} ${this.contextConsumerGoType(c)} \`json:"${jsonTag}"\``);
4135
4199
  }
4136
4200
  for (const nested of nestedComponents) {
@@ -4138,7 +4202,7 @@ ${goFields.join(`
4138
4202
  if (nested.isDynamic && !nested.isPropDerived) {
4139
4203
  lines.push(` ${nested.name}s []${elemType} \`json:"-"\``);
4140
4204
  } else {
4141
- const jsonTag = this.toJsonTag(`${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`);
4205
+ const jsonTag = this.claimJsonTag(this.toJsonTag(`${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`), takenJsonTags);
4142
4206
  lines.push(` ${nested.name}s []${elemType} \`json:"${jsonTag}"\``);
4143
4207
  }
4144
4208
  }
@@ -4147,7 +4211,7 @@ ${goFields.join(`
4147
4211
  lines.push(` ${child.fieldName} ${child.name}Props \`json:"-"\``);
4148
4212
  }
4149
4213
  for (const slot of spreadSlots) {
4150
- const jsonTag = this.toJsonTag(slot.slotId);
4214
+ const jsonTag = this.claimJsonTag(this.toJsonTag(slot.slotId), takenJsonTags);
4151
4215
  lines.push(` ${slot.slotId} map[string]any \`json:"${jsonTag}"\``);
4152
4216
  }
4153
4217
  }
@@ -4323,8 +4387,9 @@ ${goFields.join(`
4323
4387
  return literal;
4324
4388
  if (node.kind === "logical" && node.op === "??" && node.right.kind === "object-literal" && node.right.properties.length === 0 && node.left.kind === "member" && !node.left.computed && node.left.object.kind === "identifier" && node.left.object.name === this.state.propsObjectName) {
4325
4389
  const propName = node.left.property;
4326
- if (propsParams.some((param) => param.name === propName)) {
4327
- const fieldRef = `in.${capitalizeFieldName(propName)}`;
4390
+ const matchedParam = propsParams.find((param) => param.name === propName);
4391
+ if (matchedParam) {
4392
+ const fieldRef = `in.${capitalizeFieldName(matchedParam.sourceName ?? matchedParam.name)}`;
4328
4393
  return `func() map[string]interface{} { ` + `if m := bf.AsMap(${fieldRef}); m != nil { return m }; ` + `return map[string]interface{}{} }()`;
4329
4394
  }
4330
4395
  }
@@ -4342,7 +4407,7 @@ ${goFields.join(`
4342
4407
  const param = propsParams.find((p) => p.name === keyExpr);
4343
4408
  if (!param)
4344
4409
  return null;
4345
- const fieldName = capitalizeFieldName(keyExpr);
4410
+ const fieldName = capitalizeFieldName(param.sourceName ?? keyExpr);
4346
4411
  const caseEntries = Object.entries(part.cases);
4347
4412
  if (caseEntries.length === 0) {
4348
4413
  segments.push('""');
@@ -4405,8 +4470,9 @@ ${goFields.join(`
4405
4470
  const localConst = this.state.localConstants.find((c) => c.name === passthroughName);
4406
4471
  const isPropsDestructureAlias = localConst !== undefined && propsObjectName !== null && localConst.value === `${propsObjectName}.${passthroughName}`;
4407
4472
  const shadowedByLocal = passthroughName !== null && (localConst !== undefined && !isPropsDestructureAlias || this.state.localHelperNames.has(passthroughName));
4408
- if (passthroughName && !shadowedByLocal && propsParams.some((p) => p.name === passthroughName)) {
4409
- return `in.${capitalizeFieldName(passthroughName)}`;
4473
+ const passthroughParam = passthroughName && !shadowedByLocal ? propsParams.find((p) => p.name === passthroughName) : undefined;
4474
+ if (passthroughParam) {
4475
+ return `in.${capitalizeFieldName(passthroughParam.sourceName ?? passthroughParam.name)}`;
4410
4476
  }
4411
4477
  return null;
4412
4478
  }
@@ -4421,17 +4487,17 @@ ${goFields.join(`
4421
4487
  if (signal) {
4422
4488
  let referencedProp = propsParamMap.get(signal.initialValue);
4423
4489
  if (!referencedProp) {
4424
- const propName = this.extractPropNameFromInitialValue(signal.initialValue);
4490
+ const propName = this.extractPropNameFromInitialValue(signal.initialValue, signal.parsed);
4425
4491
  if (propName)
4426
4492
  referencedProp = propsParamMap.get(propName);
4427
4493
  }
4428
4494
  if (referencedProp) {
4429
- const propType = typeInfoToGo(this.emitCtx, referencedProp.type, referencedProp.defaultValue);
4495
+ const propType = typeInfoToGo(this.emitCtx, referencedProp.type, referencedProp.defaultValue, referencedProp.parsed);
4430
4496
  if (propType === "int" || propType === "float64") {
4431
4497
  return "int";
4432
4498
  }
4433
4499
  }
4434
- const signalType = typeInfoToGo(this.emitCtx, signal.type, signal.initialValue);
4500
+ const signalType = typeInfoToGo(this.emitCtx, signal.type, signal.initialValue, signal.parsed);
4435
4501
  if (signalType === "int" || signalType === "float64") {
4436
4502
  return "int";
4437
4503
  }
@@ -4468,8 +4534,8 @@ ${goFields.join(`
4468
4534
  continue;
4469
4535
  if (goPropDefault(param.defaultValue) !== null)
4470
4536
  continue;
4471
- const fieldName = capitalizeFieldName(match.propName);
4472
- const concreteType = propTypeOverrides.get(param.name) ?? typeInfoToGo(this.emitCtx, param.type, param.defaultValue);
4537
+ const fieldName = capitalizeFieldName(param.sourceName ?? match.propName);
4538
+ const concreteType = propTypeOverrides.get(param.name) ?? typeInfoToGo(this.emitCtx, param.type, param.defaultValue, param.parsed);
4473
4539
  const nullishLowered = NULLISH_SCALAR_GO_TYPES.has(concreteType) && resolvePropGoType(this.emitCtx, param, propTypeOverrides) === "interface{}";
4474
4540
  let zeroLiteral;
4475
4541
  if (match.goFallback === "true" || match.goFallback === "false") {