@barefootjs/go-template 0.7.0 → 0.9.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/go-template-adapter.d.ts +222 -0
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +516 -26
- package/dist/build.js +516 -26
- package/dist/index.js +516 -26
- package/dist/test-render.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/go-template-adapter.test.ts +273 -22
- package/src/adapter/go-template-adapter.ts +907 -24
- package/src/test-render.ts +189 -10
package/dist/build.js
CHANGED
|
@@ -372,7 +372,10 @@ import {
|
|
|
372
372
|
identifierPath,
|
|
373
373
|
emitParsedExpr,
|
|
374
374
|
emitIRNode,
|
|
375
|
-
emitAttrValue
|
|
375
|
+
emitAttrValue,
|
|
376
|
+
augmentInheritedPropAccesses,
|
|
377
|
+
parseRecordIndexAccess,
|
|
378
|
+
collectContextConsumers
|
|
376
379
|
} from "@barefootjs/jsx";
|
|
377
380
|
import { findInterpolationEnd } from "@barefootjs/jsx/scanner";
|
|
378
381
|
function wrapIfMultiToken(rendered) {
|
|
@@ -457,7 +460,13 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
457
460
|
localStructFields = new Map;
|
|
458
461
|
synthStructTypes = new Map;
|
|
459
462
|
usesHtmlTemplate = false;
|
|
463
|
+
usesFmt = false;
|
|
464
|
+
childComponentShapes = new Map;
|
|
460
465
|
moduleStringConsts = new Map;
|
|
466
|
+
localConstants = [];
|
|
467
|
+
contextConsumers = [];
|
|
468
|
+
childContextConsumers = new Map;
|
|
469
|
+
nillablePropNames = new Set;
|
|
461
470
|
constructor(options = {}) {
|
|
462
471
|
super();
|
|
463
472
|
this.options = {
|
|
@@ -473,6 +482,10 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
473
482
|
this.propsObjectName = ir.metadata.propsObjectName;
|
|
474
483
|
this.restPropsName = ir.metadata.restPropsName ?? null;
|
|
475
484
|
this.moduleStringConsts = this.collectModuleStringConsts(ir.metadata.localConstants);
|
|
485
|
+
this.localConstants = ir.metadata.localConstants ?? [];
|
|
486
|
+
this.contextConsumers = collectContextConsumers(ir.metadata);
|
|
487
|
+
augmentInheritedPropAccesses(ir);
|
|
488
|
+
this.nillablePropNames = this.collectNillablePropNames(ir);
|
|
476
489
|
if (!options?.siblingTemplatesRegistered) {
|
|
477
490
|
this.checkImportedLoopChildComponents(ir);
|
|
478
491
|
}
|
|
@@ -685,8 +698,46 @@ ${scriptRegistrations}${templateBody}
|
|
|
685
698
|
return `{{if .Scripts}}${registrations.join("")}{{end}}
|
|
686
699
|
`;
|
|
687
700
|
}
|
|
701
|
+
registerChildComponentShape(ir) {
|
|
702
|
+
const name = ir.metadata.componentName;
|
|
703
|
+
if (!name)
|
|
704
|
+
return;
|
|
705
|
+
const paramNames = new Set((ir.metadata.propsParams ?? []).map((p) => p.name));
|
|
706
|
+
const restPropsName = ir.metadata.restPropsName ?? null;
|
|
707
|
+
const restBagField = restPropsName ? this.capitalizeFieldName(restPropsName) : null;
|
|
708
|
+
this.childComponentShapes.set(name, { paramNames, restBagField });
|
|
709
|
+
this.childContextConsumers.set(name, collectContextConsumers(ir.metadata));
|
|
710
|
+
}
|
|
711
|
+
contextFieldName(c) {
|
|
712
|
+
return this.capitalizeFieldName(c.localName);
|
|
713
|
+
}
|
|
714
|
+
contextConsumerGoType(c) {
|
|
715
|
+
if (typeof c.defaultValue === "number")
|
|
716
|
+
return "int";
|
|
717
|
+
if (typeof c.defaultValue === "boolean")
|
|
718
|
+
return "bool";
|
|
719
|
+
return "string";
|
|
720
|
+
}
|
|
721
|
+
contextConsumerGoDefault(c) {
|
|
722
|
+
if (typeof c.defaultValue === "number")
|
|
723
|
+
return String(c.defaultValue);
|
|
724
|
+
if (typeof c.defaultValue === "boolean")
|
|
725
|
+
return String(c.defaultValue);
|
|
726
|
+
if (typeof c.defaultValue === "string")
|
|
727
|
+
return `"${this.escapeGoString(c.defaultValue)}"`;
|
|
728
|
+
return '""';
|
|
729
|
+
}
|
|
730
|
+
nonCollidingContextConsumers(taken) {
|
|
731
|
+
return this.contextConsumers.filter((c) => !taken.has(this.contextFieldName(c)));
|
|
732
|
+
}
|
|
688
733
|
generateTypes(ir) {
|
|
689
734
|
this.usesHtmlTemplate = false;
|
|
735
|
+
this.usesFmt = false;
|
|
736
|
+
this.propsObjectName = ir.metadata.propsObjectName;
|
|
737
|
+
augmentInheritedPropAccesses(ir);
|
|
738
|
+
this.moduleStringConsts = this.collectModuleStringConsts(ir.metadata.localConstants);
|
|
739
|
+
this.localConstants = ir.metadata.localConstants ?? [];
|
|
740
|
+
this.contextConsumers = collectContextConsumers(ir.metadata);
|
|
690
741
|
const lines = [];
|
|
691
742
|
const componentName = ir.metadata.componentName;
|
|
692
743
|
this.localTypeNames = new Set;
|
|
@@ -748,6 +799,8 @@ ${goFields.join(`
|
|
|
748
799
|
header.push(`package ${this.options.packageName}`);
|
|
749
800
|
header.push("");
|
|
750
801
|
header.push("import (");
|
|
802
|
+
if (this.usesFmt)
|
|
803
|
+
header.push('\t"fmt"');
|
|
751
804
|
if (this.usesHtmlTemplate)
|
|
752
805
|
header.push('\t"html/template"');
|
|
753
806
|
header.push('\t"math/rand"');
|
|
@@ -908,6 +961,19 @@ ${goFields.join(`
|
|
|
908
961
|
}
|
|
909
962
|
return overrides;
|
|
910
963
|
}
|
|
964
|
+
resolvePropGoType(param, propTypeOverrides) {
|
|
965
|
+
return propTypeOverrides.get(param.name) ?? this.typeInfoToGo(param.type, param.defaultValue);
|
|
966
|
+
}
|
|
967
|
+
collectNillablePropNames(ir) {
|
|
968
|
+
const propTypeOverrides = this.buildPropTypeOverrides(ir);
|
|
969
|
+
const nillable = new Set;
|
|
970
|
+
for (const param of ir.metadata.propsParams) {
|
|
971
|
+
if (this.resolvePropGoType(param, propTypeOverrides) === "interface{}") {
|
|
972
|
+
nillable.add(param.name);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
return nillable;
|
|
976
|
+
}
|
|
911
977
|
generateInputStruct(lines, ir, componentName, nestedComponents, propTypeOverrides, spreadSlots) {
|
|
912
978
|
const inputTypeName = `${componentName}Input`;
|
|
913
979
|
lines.push(`// ${inputTypeName} is the user-facing input type.`);
|
|
@@ -921,12 +987,16 @@ ${goFields.join(`
|
|
|
921
987
|
const fieldName = this.capitalizeFieldName(param.name);
|
|
922
988
|
if (nestedArrayFields.has(fieldName))
|
|
923
989
|
continue;
|
|
924
|
-
const goType =
|
|
990
|
+
const goType = this.resolvePropGoType(param, propTypeOverrides);
|
|
925
991
|
lines.push(` ${fieldName} ${goType}`);
|
|
926
992
|
}
|
|
927
993
|
for (const nested of inputNested) {
|
|
928
994
|
lines.push(` ${nested.name}s []${nested.name}Input`);
|
|
929
995
|
}
|
|
996
|
+
const takenInput = new Set(ir.metadata.propsParams.map((p) => this.capitalizeFieldName(p.name)));
|
|
997
|
+
for (const c of this.nonCollidingContextConsumers(takenInput)) {
|
|
998
|
+
lines.push(` ${this.contextFieldName(c)} ${this.contextConsumerGoType(c)}`);
|
|
999
|
+
}
|
|
930
1000
|
const restPropsName = ir.metadata.restPropsName;
|
|
931
1001
|
if (restPropsName) {
|
|
932
1002
|
const seen = new Set;
|
|
@@ -960,7 +1030,7 @@ ${goFields.join(`
|
|
|
960
1030
|
const fieldName = this.capitalizeFieldName(param.name);
|
|
961
1031
|
if (nestedArrayFields.has(fieldName))
|
|
962
1032
|
continue;
|
|
963
|
-
const goType =
|
|
1033
|
+
const goType = this.resolvePropGoType(param, propTypeOverrides);
|
|
964
1034
|
const jsonTag = this.toJsonTag(param.name);
|
|
965
1035
|
lines.push(` ${fieldName} ${goType} \`json:"${jsonTag}"\``);
|
|
966
1036
|
propFieldNames.add(fieldName);
|
|
@@ -1004,6 +1074,15 @@ ${goFields.join(`
|
|
|
1004
1074
|
const goType = this.inferMemoType(memo, ir.metadata.signals, propsParamMap);
|
|
1005
1075
|
lines.push(` ${fieldName} ${goType} \`json:"${jsonTag}"\``);
|
|
1006
1076
|
}
|
|
1077
|
+
const takenProps = new Set([
|
|
1078
|
+
...ir.metadata.propsParams.map((p) => this.capitalizeFieldName(p.name)),
|
|
1079
|
+
...ir.metadata.signals.map((s) => this.capitalizeFieldName(s.getter)),
|
|
1080
|
+
...ir.metadata.memos.map((m) => this.capitalizeFieldName(m.name))
|
|
1081
|
+
]);
|
|
1082
|
+
for (const c of this.nonCollidingContextConsumers(takenProps)) {
|
|
1083
|
+
const jsonTag = this.toJsonTag(c.localName);
|
|
1084
|
+
lines.push(` ${this.contextFieldName(c)} ${this.contextConsumerGoType(c)} \`json:"${jsonTag}"\``);
|
|
1085
|
+
}
|
|
1007
1086
|
for (const nested of nestedComponents) {
|
|
1008
1087
|
if (nested.isDynamic && !nested.isPropDerived) {
|
|
1009
1088
|
lines.push(` ${nested.name}s []${nested.name}Props \`json:"-"\``);
|
|
@@ -1113,25 +1192,55 @@ ${goFields.join(`
|
|
|
1113
1192
|
const varName = `${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`;
|
|
1114
1193
|
lines.push(` ${nested.name}s: ${varName},`);
|
|
1115
1194
|
}
|
|
1195
|
+
const memoPropsParamMap = new Map(ir.metadata.propsParams.map((p) => [p.name, p]));
|
|
1116
1196
|
for (const memo of ir.metadata.memos) {
|
|
1117
1197
|
const fieldName = this.capitalizeFieldName(memo.name);
|
|
1118
|
-
const
|
|
1198
|
+
const goType = this.inferMemoType(memo, ir.metadata.signals, memoPropsParamMap);
|
|
1199
|
+
const memoValue = this.computeMemoInitialValue(memo, ir.metadata.signals, ir.metadata.propsParams, propFallbackVars, goType);
|
|
1119
1200
|
lines.push(` ${fieldName}: ${memoValue},`);
|
|
1120
1201
|
}
|
|
1202
|
+
const takenInit = new Set([
|
|
1203
|
+
...ir.metadata.propsParams.map((p) => this.capitalizeFieldName(p.name)),
|
|
1204
|
+
...ir.metadata.signals.map((s) => this.capitalizeFieldName(s.getter)),
|
|
1205
|
+
...ir.metadata.memos.map((m) => this.capitalizeFieldName(m.name))
|
|
1206
|
+
]);
|
|
1207
|
+
for (const c of this.nonCollidingContextConsumers(takenInit)) {
|
|
1208
|
+
const field = this.contextFieldName(c);
|
|
1209
|
+
const def = this.contextConsumerGoDefault(c);
|
|
1210
|
+
const defaulted = c.defaultValue === null || def === '""' || def === "0" || def === "false" ? `in.${field}` : this.applyGoFallback(`in.${field}`, def);
|
|
1211
|
+
lines.push(` ${field}: ${defaulted},`);
|
|
1212
|
+
}
|
|
1121
1213
|
const staticChildren = this.collectStaticChildInstances(ir.root);
|
|
1122
1214
|
for (const child of staticChildren) {
|
|
1123
1215
|
lines.push(` ${child.fieldName}: New${child.name}Props(${child.name}Input{`);
|
|
1124
1216
|
lines.push(` ScopeID: scopeID + "_${child.slotId}",`);
|
|
1125
1217
|
lines.push(` BfParent: scopeID,`);
|
|
1126
1218
|
lines.push(` BfMount: "${child.slotId}",`);
|
|
1219
|
+
if (child.contextBindings) {
|
|
1220
|
+
for (const consumer of this.childContextConsumers.get(child.name) ?? []) {
|
|
1221
|
+
const goVal = child.contextBindings.get(consumer.contextName);
|
|
1222
|
+
if (goVal !== undefined) {
|
|
1223
|
+
lines.push(` ${this.contextFieldName(consumer)}: ${goVal},`);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
const childShape = this.childComponentShapes.get(child.name);
|
|
1228
|
+
const restBagEntries = [];
|
|
1229
|
+
const emitChildField = (jsxName, goValue) => {
|
|
1230
|
+
if (childShape && childShape.restBagField && !childShape.paramNames.has(jsxName)) {
|
|
1231
|
+
restBagEntries.push(`${JSON.stringify(jsxName)}: ${goValue}`);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
lines.push(` ${this.capitalizeFieldName(jsxName)}: ${goValue},`);
|
|
1235
|
+
};
|
|
1127
1236
|
for (const prop of child.props) {
|
|
1128
1237
|
switch (prop.value.kind) {
|
|
1129
1238
|
case "literal":
|
|
1130
|
-
|
|
1239
|
+
emitChildField(prop.name, this.goLiteral(prop.value.value));
|
|
1131
1240
|
break;
|
|
1132
1241
|
case "boolean-shorthand":
|
|
1133
1242
|
case "boolean-attr":
|
|
1134
|
-
|
|
1243
|
+
emitChildField(prop.name, "true");
|
|
1135
1244
|
break;
|
|
1136
1245
|
case "expression":
|
|
1137
1246
|
case "spread":
|
|
@@ -1140,7 +1249,7 @@ ${goFields.join(`
|
|
|
1140
1249
|
if (parts) {
|
|
1141
1250
|
const goExpr = this.templatePartsToGoCode(parts, ir.metadata.propsParams);
|
|
1142
1251
|
if (goExpr !== null) {
|
|
1143
|
-
|
|
1252
|
+
emitChildField(prop.name, goExpr);
|
|
1144
1253
|
break;
|
|
1145
1254
|
}
|
|
1146
1255
|
}
|
|
@@ -1149,7 +1258,7 @@ ${goFields.join(`
|
|
|
1149
1258
|
break;
|
|
1150
1259
|
const resolvedValue = this.resolveDynamicPropValue(exprText, ir.metadata.signals, ir.metadata.memos, ir.metadata.propsParams);
|
|
1151
1260
|
if (resolvedValue !== null) {
|
|
1152
|
-
|
|
1261
|
+
emitChildField(prop.name, resolvedValue);
|
|
1153
1262
|
}
|
|
1154
1263
|
break;
|
|
1155
1264
|
}
|
|
@@ -1157,6 +1266,9 @@ ${goFields.join(`
|
|
|
1157
1266
|
break;
|
|
1158
1267
|
}
|
|
1159
1268
|
}
|
|
1269
|
+
if (childShape?.restBagField && restBagEntries.length > 0) {
|
|
1270
|
+
lines.push(` ${childShape.restBagField}: map[string]any{${restBagEntries.join(", ")}},`);
|
|
1271
|
+
}
|
|
1160
1272
|
if (child.childrenText !== null) {
|
|
1161
1273
|
lines.push(` Children: ${JSON.stringify(child.childrenText)},`);
|
|
1162
1274
|
} else if (child.childrenHtml !== null) {
|
|
@@ -1227,7 +1339,7 @@ ${goFields.join(`
|
|
|
1227
1339
|
}
|
|
1228
1340
|
collectStaticChildInstances(node) {
|
|
1229
1341
|
const result = [];
|
|
1230
|
-
this.collectStaticChildInstancesRecursive(node, result, false);
|
|
1342
|
+
this.collectStaticChildInstancesRecursive(node, result, false, new Map);
|
|
1231
1343
|
return result;
|
|
1232
1344
|
}
|
|
1233
1345
|
extractTextChildren(children) {
|
|
@@ -1251,12 +1363,12 @@ ${goFields.join(`
|
|
|
1251
1363
|
return null;
|
|
1252
1364
|
return html;
|
|
1253
1365
|
}
|
|
1254
|
-
collectStaticChildInstancesRecursive(node, result, inLoop) {
|
|
1366
|
+
collectStaticChildInstancesRecursive(node, result, inLoop, providerCtx) {
|
|
1255
1367
|
if (node.type === "component") {
|
|
1256
1368
|
const comp = node;
|
|
1257
1369
|
if (comp.dynamicTag) {
|
|
1258
1370
|
for (const child of comp.children) {
|
|
1259
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1371
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, providerCtx);
|
|
1260
1372
|
}
|
|
1261
1373
|
return;
|
|
1262
1374
|
}
|
|
@@ -1268,48 +1380,65 @@ ${goFields.join(`
|
|
|
1268
1380
|
props: comp.props,
|
|
1269
1381
|
fieldName: `${comp.name}${suffix}`,
|
|
1270
1382
|
childrenText: this.extractTextChildren(comp.children),
|
|
1271
|
-
childrenHtml: this.extractHtmlChildren(comp.children)
|
|
1383
|
+
childrenHtml: this.extractHtmlChildren(comp.children),
|
|
1384
|
+
contextBindings: providerCtx.size > 0 ? providerCtx : undefined
|
|
1272
1385
|
});
|
|
1273
1386
|
}
|
|
1274
1387
|
if (comp.name === "Portal" && comp.children) {
|
|
1275
1388
|
for (const child of comp.children) {
|
|
1276
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1389
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, providerCtx);
|
|
1277
1390
|
}
|
|
1278
1391
|
}
|
|
1279
1392
|
} else if (node.type === "loop") {
|
|
1280
1393
|
const loop = node;
|
|
1281
1394
|
for (const child of loop.children) {
|
|
1282
|
-
this.collectStaticChildInstancesRecursive(child, result, true);
|
|
1395
|
+
this.collectStaticChildInstancesRecursive(child, result, true, providerCtx);
|
|
1283
1396
|
}
|
|
1284
1397
|
} else if (node.type === "element") {
|
|
1285
1398
|
const element = node;
|
|
1286
1399
|
for (const child of element.children) {
|
|
1287
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1400
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, providerCtx);
|
|
1288
1401
|
}
|
|
1289
1402
|
} else if (node.type === "fragment") {
|
|
1290
1403
|
const fragment = node;
|
|
1291
1404
|
for (const child of fragment.children) {
|
|
1292
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1405
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, providerCtx);
|
|
1293
1406
|
}
|
|
1294
1407
|
} else if (node.type === "conditional") {
|
|
1295
1408
|
const cond = node;
|
|
1296
|
-
this.collectStaticChildInstancesRecursive(cond.whenTrue, result, inLoop);
|
|
1409
|
+
this.collectStaticChildInstancesRecursive(cond.whenTrue, result, inLoop, providerCtx);
|
|
1297
1410
|
if (cond.whenFalse) {
|
|
1298
|
-
this.collectStaticChildInstancesRecursive(cond.whenFalse, result, inLoop);
|
|
1411
|
+
this.collectStaticChildInstancesRecursive(cond.whenFalse, result, inLoop, providerCtx);
|
|
1299
1412
|
}
|
|
1300
1413
|
} else if (node.type === "provider") {
|
|
1301
1414
|
const p = node;
|
|
1415
|
+
const childCtx = this.extendProviderContext(providerCtx, p);
|
|
1302
1416
|
for (const child of p.children) {
|
|
1303
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1417
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, childCtx);
|
|
1304
1418
|
}
|
|
1305
1419
|
} else if (node.type === "async") {
|
|
1306
1420
|
const a = node;
|
|
1307
|
-
this.collectStaticChildInstancesRecursive(a.fallback, result, inLoop);
|
|
1421
|
+
this.collectStaticChildInstancesRecursive(a.fallback, result, inLoop, providerCtx);
|
|
1308
1422
|
for (const child of a.children) {
|
|
1309
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1423
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, providerCtx);
|
|
1310
1424
|
}
|
|
1311
1425
|
}
|
|
1312
1426
|
}
|
|
1427
|
+
extendProviderContext(current, p) {
|
|
1428
|
+
const v = p.valueProp?.value;
|
|
1429
|
+
if (!v || v.kind !== "literal")
|
|
1430
|
+
return current;
|
|
1431
|
+
let goLit = null;
|
|
1432
|
+
if (typeof v.value === "string")
|
|
1433
|
+
goLit = `"${this.escapeGoString(v.value)}"`;
|
|
1434
|
+
else if (typeof v.value === "number" || typeof v.value === "boolean")
|
|
1435
|
+
goLit = String(v.value);
|
|
1436
|
+
if (goLit === null)
|
|
1437
|
+
return current;
|
|
1438
|
+
const next = new Map(current);
|
|
1439
|
+
next.set(p.contextName, goLit);
|
|
1440
|
+
return next;
|
|
1441
|
+
}
|
|
1313
1442
|
collectSpreadSlots(node) {
|
|
1314
1443
|
const result = [];
|
|
1315
1444
|
this.collectSpreadSlotsRecursive(node, result);
|
|
@@ -1434,6 +1563,9 @@ ${goFields.join(`
|
|
|
1434
1563
|
}
|
|
1435
1564
|
buildSpreadInitializer(spreadExpr, ir) {
|
|
1436
1565
|
const trimmed = spreadExpr.trim();
|
|
1566
|
+
const conditional = this.buildConditionalSpreadInitializer(trimmed, ir);
|
|
1567
|
+
if (conditional !== undefined)
|
|
1568
|
+
return conditional;
|
|
1437
1569
|
const callMatch = /^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\s*\)$/.exec(trimmed);
|
|
1438
1570
|
if (callMatch) {
|
|
1439
1571
|
const getterName = callMatch[1];
|
|
@@ -1457,9 +1589,127 @@ ${goFields.join(`
|
|
|
1457
1589
|
if (ir.metadata.restPropsName === trimmed) {
|
|
1458
1590
|
return `in.${this.capitalizeFieldName(trimmed)}`;
|
|
1459
1591
|
}
|
|
1592
|
+
const localConst = (ir.metadata.localConstants ?? []).find((c) => c.name === trimmed && !c.isModule);
|
|
1593
|
+
if (localConst?.value !== undefined) {
|
|
1594
|
+
const initTrimmed = localConst.value.trim();
|
|
1595
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(initTrimmed)) {
|
|
1596
|
+
const resolved = this.buildConditionalSpreadInitializer(initTrimmed, ir);
|
|
1597
|
+
if (resolved)
|
|
1598
|
+
return resolved;
|
|
1599
|
+
if (resolved === null)
|
|
1600
|
+
return null;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1460
1603
|
}
|
|
1461
1604
|
return null;
|
|
1462
1605
|
}
|
|
1606
|
+
buildConditionalSpreadInitializer(spreadExpr, ir) {
|
|
1607
|
+
const expr = this.parseLiteralExpression(spreadExpr);
|
|
1608
|
+
if (!expr || !ts.isConditionalExpression(expr))
|
|
1609
|
+
return;
|
|
1610
|
+
const whenTrue = this.unwrapParens(expr.whenTrue);
|
|
1611
|
+
const whenFalse = this.unwrapParens(expr.whenFalse);
|
|
1612
|
+
if (!ts.isObjectLiteralExpression(whenTrue) || !ts.isObjectLiteralExpression(whenFalse)) {
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
const goCond = this.conditionToGoBool(expr.condition, ir);
|
|
1616
|
+
if (goCond === null)
|
|
1617
|
+
return null;
|
|
1618
|
+
const trueMap = this.objectLiteralToGoSpreadMap(whenTrue, ir);
|
|
1619
|
+
const falseMap = this.objectLiteralToGoSpreadMap(whenFalse, ir);
|
|
1620
|
+
if (trueMap === null || falseMap === null)
|
|
1621
|
+
return null;
|
|
1622
|
+
return `func() map[string]any {
|
|
1623
|
+
` + ` if ${goCond} {
|
|
1624
|
+
` + ` return ${trueMap}
|
|
1625
|
+
` + ` }
|
|
1626
|
+
` + ` return ${falseMap}
|
|
1627
|
+
` + ` }()`;
|
|
1628
|
+
}
|
|
1629
|
+
unwrapParens(node) {
|
|
1630
|
+
let e = node;
|
|
1631
|
+
while (ts.isParenthesizedExpression(e))
|
|
1632
|
+
e = e.expression;
|
|
1633
|
+
return e;
|
|
1634
|
+
}
|
|
1635
|
+
conditionToGoBool(condition, ir) {
|
|
1636
|
+
let node = this.unwrapParens(condition);
|
|
1637
|
+
let negate = false;
|
|
1638
|
+
if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
1639
|
+
negate = true;
|
|
1640
|
+
node = this.unwrapParens(node.operand);
|
|
1641
|
+
}
|
|
1642
|
+
if (!ts.isIdentifier(node))
|
|
1643
|
+
return null;
|
|
1644
|
+
const param = ir.metadata.propsParams.find((p) => p.name === node.text);
|
|
1645
|
+
if (!param)
|
|
1646
|
+
return null;
|
|
1647
|
+
const field = `in.${this.capitalizeFieldName(param.name)}`;
|
|
1648
|
+
const prim = param.type.kind === "primitive" ? param.type.primitive : undefined;
|
|
1649
|
+
let truthy;
|
|
1650
|
+
if (prim === "boolean") {
|
|
1651
|
+
truthy = field;
|
|
1652
|
+
} else if (prim === "number") {
|
|
1653
|
+
truthy = `${field} != 0`;
|
|
1654
|
+
} else if (prim === "string") {
|
|
1655
|
+
truthy = `${field} != ""`;
|
|
1656
|
+
} else {
|
|
1657
|
+
truthy = `bf.Truthy(${field})`;
|
|
1658
|
+
}
|
|
1659
|
+
if (!negate)
|
|
1660
|
+
return truthy;
|
|
1661
|
+
if (prim === "boolean")
|
|
1662
|
+
return `!${field}`;
|
|
1663
|
+
if (prim === "number")
|
|
1664
|
+
return `${field} == 0`;
|
|
1665
|
+
if (prim === "string")
|
|
1666
|
+
return `${field} == ""`;
|
|
1667
|
+
return `!bf.Truthy(${field})`;
|
|
1668
|
+
}
|
|
1669
|
+
objectLiteralToGoSpreadMap(obj, ir) {
|
|
1670
|
+
const entries = [];
|
|
1671
|
+
for (const prop of obj.properties) {
|
|
1672
|
+
if (!ts.isPropertyAssignment(prop))
|
|
1673
|
+
return null;
|
|
1674
|
+
let key;
|
|
1675
|
+
if (ts.isIdentifier(prop.name)) {
|
|
1676
|
+
key = prop.name.text;
|
|
1677
|
+
} else if (ts.isStringLiteral(prop.name) || ts.isNoSubstitutionTemplateLiteral(prop.name)) {
|
|
1678
|
+
key = prop.name.text;
|
|
1679
|
+
} else {
|
|
1680
|
+
return null;
|
|
1681
|
+
}
|
|
1682
|
+
const val = this.unwrapParens(prop.initializer);
|
|
1683
|
+
let goVal;
|
|
1684
|
+
if (ts.isStringLiteral(val) || ts.isNoSubstitutionTemplateLiteral(val)) {
|
|
1685
|
+
goVal = JSON.stringify(val.text);
|
|
1686
|
+
} else if (ts.isIdentifier(val)) {
|
|
1687
|
+
const param = ir.metadata.propsParams.find((p) => p.name === val.text);
|
|
1688
|
+
if (!param)
|
|
1689
|
+
return null;
|
|
1690
|
+
goVal = `in.${this.capitalizeFieldName(param.name)}`;
|
|
1691
|
+
} else {
|
|
1692
|
+
const indexed = this.recordIndexAccessToGoMap(val, ir);
|
|
1693
|
+
if (indexed === null)
|
|
1694
|
+
return null;
|
|
1695
|
+
goVal = indexed;
|
|
1696
|
+
}
|
|
1697
|
+
entries.push(`${JSON.stringify(key)}: ${goVal}`);
|
|
1698
|
+
}
|
|
1699
|
+
return `map[string]any{${entries.join(", ")}}`;
|
|
1700
|
+
}
|
|
1701
|
+
recordIndexAccessToGoMap(val, ir) {
|
|
1702
|
+
const parsed = parseRecordIndexAccess(val, ir.metadata.localConstants ?? [], ir.metadata.propsParams);
|
|
1703
|
+
if (!parsed)
|
|
1704
|
+
return null;
|
|
1705
|
+
const entries = parsed.entries.map((e) => {
|
|
1706
|
+
const mapVal = e.value.kind === "number" ? e.value.text : JSON.stringify(e.value.text);
|
|
1707
|
+
return `${JSON.stringify(e.key)}: ${mapVal}`;
|
|
1708
|
+
});
|
|
1709
|
+
this.usesFmt = true;
|
|
1710
|
+
const field = `in.${this.capitalizeFieldName(parsed.indexPropName)}`;
|
|
1711
|
+
return `map[string]any{${entries.join(", ")}}[fmt.Sprint(${field})]`;
|
|
1712
|
+
}
|
|
1463
1713
|
convertInitialValue(value, typeInfo, propsParams) {
|
|
1464
1714
|
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value)) {
|
|
1465
1715
|
if (propsParams?.some((p) => p.name === value)) {
|
|
@@ -1700,7 +1950,142 @@ ${goFields.join(`
|
|
|
1700
1950
|
}
|
|
1701
1951
|
return null;
|
|
1702
1952
|
}
|
|
1703
|
-
|
|
1953
|
+
computeTemplateLiteralMemoInitialValue(computation, propsParams) {
|
|
1954
|
+
const sf = ts.createSourceFile("__memo.ts", `const __x = (${computation});`, ts.ScriptTarget.Latest, false);
|
|
1955
|
+
const stmt = sf.statements[0];
|
|
1956
|
+
if (!stmt || !ts.isVariableStatement(stmt))
|
|
1957
|
+
return null;
|
|
1958
|
+
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
1959
|
+
while (init && ts.isParenthesizedExpression(init))
|
|
1960
|
+
init = init.expression;
|
|
1961
|
+
if (!init || !ts.isArrowFunction(init))
|
|
1962
|
+
return null;
|
|
1963
|
+
let body = init.body;
|
|
1964
|
+
while (ts.isParenthesizedExpression(body)) {
|
|
1965
|
+
body = body.expression;
|
|
1966
|
+
}
|
|
1967
|
+
const localKeyBindings = new Map;
|
|
1968
|
+
if (ts.isBlock(body)) {
|
|
1969
|
+
let returned = null;
|
|
1970
|
+
for (const s of body.statements) {
|
|
1971
|
+
if (ts.isVariableStatement(s)) {
|
|
1972
|
+
for (const d of s.declarationList.declarations) {
|
|
1973
|
+
if (!ts.isIdentifier(d.name) || !d.initializer)
|
|
1974
|
+
continue;
|
|
1975
|
+
const binding = this.parseLocalKeyBinding(d.initializer);
|
|
1976
|
+
if (binding)
|
|
1977
|
+
localKeyBindings.set(d.name.text, binding);
|
|
1978
|
+
}
|
|
1979
|
+
} else if (ts.isReturnStatement(s) && s.expression) {
|
|
1980
|
+
returned = s.expression;
|
|
1981
|
+
} else if (ts.isExpressionStatement(s) || ts.isEmptyStatement(s)) {} else {
|
|
1982
|
+
return null;
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
if (!returned)
|
|
1986
|
+
return null;
|
|
1987
|
+
body = returned;
|
|
1988
|
+
while (ts.isParenthesizedExpression(body)) {
|
|
1989
|
+
body = body.expression;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
if (!ts.isTemplateExpression(body) && !ts.isNoSubstitutionTemplateLiteral(body)) {
|
|
1993
|
+
return null;
|
|
1994
|
+
}
|
|
1995
|
+
const propNames = new Set(propsParams.map((p) => p.name));
|
|
1996
|
+
const escGo = (s) => `"${this.escapeGoString(s)}"`;
|
|
1997
|
+
const segments = [];
|
|
1998
|
+
if (ts.isNoSubstitutionTemplateLiteral(body)) {
|
|
1999
|
+
return escGo(body.text);
|
|
2000
|
+
}
|
|
2001
|
+
if (body.head.text)
|
|
2002
|
+
segments.push(escGo(body.head.text));
|
|
2003
|
+
for (const span of body.templateSpans) {
|
|
2004
|
+
const resolved = this.resolveTemplateInterpolation(span.expression, propNames, localKeyBindings);
|
|
2005
|
+
if (resolved === null)
|
|
2006
|
+
return null;
|
|
2007
|
+
segments.push(resolved);
|
|
2008
|
+
if (span.literal.text)
|
|
2009
|
+
segments.push(escGo(span.literal.text));
|
|
2010
|
+
}
|
|
2011
|
+
if (segments.length === 0)
|
|
2012
|
+
return '""';
|
|
2013
|
+
return segments.join(" + ");
|
|
2014
|
+
}
|
|
2015
|
+
resolveTemplateInterpolation(expr, propNames, localKeyBindings = new Map) {
|
|
2016
|
+
let node = expr;
|
|
2017
|
+
while (ts.isParenthesizedExpression(node))
|
|
2018
|
+
node = node.expression;
|
|
2019
|
+
if (ts.isIdentifier(node)) {
|
|
2020
|
+
const inlined = this.resolveModuleStringConst(node.text);
|
|
2021
|
+
if (inlined !== null)
|
|
2022
|
+
return inlined;
|
|
2023
|
+
return null;
|
|
2024
|
+
}
|
|
2025
|
+
if (ts.isElementAccessExpression(node)) {
|
|
2026
|
+
const indexed = this.recordIndexInterpolationToGo(node, propNames, localKeyBindings);
|
|
2027
|
+
if (indexed !== null)
|
|
2028
|
+
return indexed;
|
|
2029
|
+
return null;
|
|
2030
|
+
}
|
|
2031
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
2032
|
+
const right = node.right;
|
|
2033
|
+
const isEmptyStr = (ts.isStringLiteral(right) || ts.isNoSubstitutionTemplateLiteral(right)) && right.text === "";
|
|
2034
|
+
const propName2 = this.propsAccessName(node.left);
|
|
2035
|
+
if (propName2 && propNames.has(propName2) && isEmptyStr) {
|
|
2036
|
+
return `in.${this.capitalizeFieldName(propName2)}`;
|
|
2037
|
+
}
|
|
2038
|
+
return null;
|
|
2039
|
+
}
|
|
2040
|
+
const propName = this.propsAccessName(node);
|
|
2041
|
+
if (propName && propNames.has(propName)) {
|
|
2042
|
+
return `in.${this.capitalizeFieldName(propName)}`;
|
|
2043
|
+
}
|
|
2044
|
+
return null;
|
|
2045
|
+
}
|
|
2046
|
+
parseLocalKeyBinding(init) {
|
|
2047
|
+
let node = init;
|
|
2048
|
+
while (ts.isParenthesizedExpression(node))
|
|
2049
|
+
node = node.expression;
|
|
2050
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
2051
|
+
const propName2 = this.propsAccessName(node.left);
|
|
2052
|
+
const right = node.right;
|
|
2053
|
+
if (propName2 && (ts.isStringLiteral(right) || ts.isNoSubstitutionTemplateLiteral(right))) {
|
|
2054
|
+
return { propName: propName2, defaultLiteral: right.text };
|
|
2055
|
+
}
|
|
2056
|
+
return null;
|
|
2057
|
+
}
|
|
2058
|
+
const propName = this.propsAccessName(node);
|
|
2059
|
+
if (propName)
|
|
2060
|
+
return { propName };
|
|
2061
|
+
return null;
|
|
2062
|
+
}
|
|
2063
|
+
recordIndexInterpolationToGo(node, propNames, localKeyBindings) {
|
|
2064
|
+
const parsed = parseRecordIndexAccess(node, this.localConstants ?? [], [...propNames].map((name) => ({ name })), (name) => localKeyBindings.get(name) ?? null);
|
|
2065
|
+
if (!parsed)
|
|
2066
|
+
return null;
|
|
2067
|
+
const goVal = (v) => v.kind === "number" ? v.text : JSON.stringify(v.text);
|
|
2068
|
+
const entries = parsed.entries.map((e) => `${JSON.stringify(e.key)}: ${goVal(e.value)}`);
|
|
2069
|
+
if (parsed.defaultKey !== undefined) {
|
|
2070
|
+
const def = parsed.entries.find((e) => e.key === parsed.defaultKey);
|
|
2071
|
+
if (def)
|
|
2072
|
+
entries.unshift(`"": ${goVal(def.value)}`);
|
|
2073
|
+
}
|
|
2074
|
+
const allString = parsed.entries.every((e) => e.value.kind === "string");
|
|
2075
|
+
const mapType = allString ? "map[string]string" : "map[string]any";
|
|
2076
|
+
this.usesFmt = true;
|
|
2077
|
+
return `${mapType}{${entries.join(", ")}}[fmt.Sprint(in.${this.capitalizeFieldName(parsed.indexPropName)})]`;
|
|
2078
|
+
}
|
|
2079
|
+
propsAccessName(node) {
|
|
2080
|
+
if (!ts.isPropertyAccessExpression(node))
|
|
2081
|
+
return null;
|
|
2082
|
+
if (!ts.isIdentifier(node.expression))
|
|
2083
|
+
return null;
|
|
2084
|
+
if (!this.propsObjectName || node.expression.text !== this.propsObjectName)
|
|
2085
|
+
return null;
|
|
2086
|
+
return node.name.text;
|
|
2087
|
+
}
|
|
2088
|
+
computeMemoInitialValue(memo, signals, propsParams, propFallbackVars = GoTemplateAdapter.EMPTY_PROP_FALLBACK_VARS, goType) {
|
|
1704
2089
|
const computation = memo.computation;
|
|
1705
2090
|
const propRef = (propName) => {
|
|
1706
2091
|
const hoisted = propFallbackVars.get(propName);
|
|
@@ -1708,6 +2093,9 @@ ${goFields.join(`
|
|
|
1708
2093
|
return hoisted.varName;
|
|
1709
2094
|
return `in.${this.capitalizeFieldName(propName)}`;
|
|
1710
2095
|
};
|
|
2096
|
+
const tmplMemo = this.computeTemplateLiteralMemoInitialValue(computation, propsParams);
|
|
2097
|
+
if (tmplMemo !== null)
|
|
2098
|
+
return tmplMemo;
|
|
1711
2099
|
const arithmeticMatch = computation.match(/\(\)\s*=>\s*(\w+)\(\)\s*([*+\-/])\s*(\d+)/);
|
|
1712
2100
|
if (arithmeticMatch) {
|
|
1713
2101
|
const [, depName, operator, operand] = arithmeticMatch;
|
|
@@ -1727,8 +2115,8 @@ ${goFields.join(`
|
|
|
1727
2115
|
return `${hoisted.varName} ${operator} ${operand}`;
|
|
1728
2116
|
const fieldName = this.capitalizeFieldName(propName);
|
|
1729
2117
|
if (param.type) {
|
|
1730
|
-
const
|
|
1731
|
-
if (
|
|
2118
|
+
const goType2 = this.typeInfoToGo(param.type, param.defaultValue);
|
|
2119
|
+
if (goType2 === "interface{}")
|
|
1732
2120
|
return `in.${fieldName}.(int) ${operator} ${operand}`;
|
|
1733
2121
|
}
|
|
1734
2122
|
return `in.${fieldName} ${operator} ${operand}`;
|
|
@@ -1757,8 +2145,8 @@ ${goFields.join(`
|
|
|
1757
2145
|
if (param) {
|
|
1758
2146
|
const fieldName = this.capitalizeFieldName(varName);
|
|
1759
2147
|
if (param.type) {
|
|
1760
|
-
const
|
|
1761
|
-
if (
|
|
2148
|
+
const goType2 = this.typeInfoToGo(param.type, param.defaultValue);
|
|
2149
|
+
if (goType2 === "interface{}")
|
|
1762
2150
|
return `in.${fieldName}.(int) ${operator} ${operand}`;
|
|
1763
2151
|
}
|
|
1764
2152
|
return `in.${fieldName} ${operator} ${operand}`;
|
|
@@ -1772,9 +2160,40 @@ ${goFields.join(`
|
|
|
1772
2160
|
return `in.${this.capitalizeFieldName(varName)}`;
|
|
1773
2161
|
}
|
|
1774
2162
|
}
|
|
2163
|
+
if (goType === "bool")
|
|
2164
|
+
return "false";
|
|
2165
|
+
if (goType === "string")
|
|
2166
|
+
return '""';
|
|
1775
2167
|
return "0";
|
|
1776
2168
|
}
|
|
2169
|
+
isTemplateLiteralMemo(computation) {
|
|
2170
|
+
const sf = ts.createSourceFile("__memo.ts", `const __x = (${computation});`, ts.ScriptTarget.Latest, false);
|
|
2171
|
+
const stmt = sf.statements[0];
|
|
2172
|
+
if (!stmt || !ts.isVariableStatement(stmt))
|
|
2173
|
+
return false;
|
|
2174
|
+
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
2175
|
+
while (init && ts.isParenthesizedExpression(init))
|
|
2176
|
+
init = init.expression;
|
|
2177
|
+
if (!init || !ts.isArrowFunction(init))
|
|
2178
|
+
return false;
|
|
2179
|
+
let body = init.body;
|
|
2180
|
+
while (ts.isParenthesizedExpression(body)) {
|
|
2181
|
+
body = body.expression;
|
|
2182
|
+
}
|
|
2183
|
+
if (ts.isBlock(body)) {
|
|
2184
|
+
const ret = body.statements.find(ts.isReturnStatement);
|
|
2185
|
+
if (!ret || !ret.expression)
|
|
2186
|
+
return false;
|
|
2187
|
+
body = ret.expression;
|
|
2188
|
+
while (ts.isParenthesizedExpression(body)) {
|
|
2189
|
+
body = body.expression;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
return ts.isTemplateExpression(body) || ts.isNoSubstitutionTemplateLiteral(body);
|
|
2193
|
+
}
|
|
1777
2194
|
inferMemoType(memo, signals, propsParamMap) {
|
|
2195
|
+
if (this.isTemplateLiteralMemo(memo.computation))
|
|
2196
|
+
return "string";
|
|
1778
2197
|
if (memo.computation.includes("*") || memo.computation.includes("/") || memo.computation.includes("+") || memo.computation.includes("-")) {
|
|
1779
2198
|
for (const dep of memo.deps) {
|
|
1780
2199
|
const signal = signals.find((s) => s.getter === dep);
|
|
@@ -1798,8 +2217,39 @@ ${goFields.join(`
|
|
|
1798
2217
|
}
|
|
1799
2218
|
}
|
|
1800
2219
|
}
|
|
2220
|
+
if (this.typeInfoToGo(memo.type) === "interface{}" && this.isBooleanMemo(memo, signals, propsParamMap)) {
|
|
2221
|
+
return "bool";
|
|
2222
|
+
}
|
|
1801
2223
|
return this.typeInfoToGo(memo.type);
|
|
1802
2224
|
}
|
|
2225
|
+
isBooleanMemo(memo, signals, propsParamMap) {
|
|
2226
|
+
const c = memo.computation;
|
|
2227
|
+
if (/(!==|===|!=(?!=)|==(?!=))/.test(c))
|
|
2228
|
+
return true;
|
|
2229
|
+
if (/=>\s*!/.test(c))
|
|
2230
|
+
return true;
|
|
2231
|
+
const isBoolGetter = (name) => {
|
|
2232
|
+
const sig = signals.find((s) => s.getter === name);
|
|
2233
|
+
if (sig) {
|
|
2234
|
+
if (this.typeInfoToGo(sig.type) === "bool")
|
|
2235
|
+
return true;
|
|
2236
|
+
if (/\?\?\s*(true|false)\b/.test(sig.initialValue))
|
|
2237
|
+
return true;
|
|
2238
|
+
const propName = this.extractPropNameFromInitialValue(sig.initialValue) ?? sig.initialValue;
|
|
2239
|
+
const prop2 = propsParamMap.get(propName);
|
|
2240
|
+
if (prop2 && this.typeInfoToGo(prop2.type, prop2.defaultValue) === "bool")
|
|
2241
|
+
return true;
|
|
2242
|
+
return false;
|
|
2243
|
+
}
|
|
2244
|
+
const prop = propsParamMap.get(name);
|
|
2245
|
+
return !!prop && this.typeInfoToGo(prop.type, prop.defaultValue) === "bool";
|
|
2246
|
+
};
|
|
2247
|
+
const ternary = c.match(/=>\s*\w+\(\)\s*\?\s*(\w+)\(\)\s*:\s*(\w+)\(\)/);
|
|
2248
|
+
if (ternary) {
|
|
2249
|
+
return isBoolGetter(ternary[1]) && isBoolGetter(ternary[2]);
|
|
2250
|
+
}
|
|
2251
|
+
return false;
|
|
2252
|
+
}
|
|
1803
2253
|
inferTypeFromValue(value) {
|
|
1804
2254
|
if (value === "true" || value === "false")
|
|
1805
2255
|
return "bool";
|
|
@@ -2147,8 +2597,42 @@ ${goFields.join(`
|
|
|
2147
2597
|
if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
2148
2598
|
return init.text;
|
|
2149
2599
|
}
|
|
2600
|
+
const joined = this.evalStringArrayJoin(init);
|
|
2601
|
+
if (joined !== null)
|
|
2602
|
+
return joined;
|
|
2150
2603
|
return null;
|
|
2151
2604
|
}
|
|
2605
|
+
evalStringArrayJoin(node) {
|
|
2606
|
+
if (!ts.isCallExpression(node))
|
|
2607
|
+
return null;
|
|
2608
|
+
const callee = node.expression;
|
|
2609
|
+
if (!ts.isPropertyAccessExpression(callee))
|
|
2610
|
+
return null;
|
|
2611
|
+
if (callee.name.text !== "join")
|
|
2612
|
+
return null;
|
|
2613
|
+
let recv = callee.expression;
|
|
2614
|
+
while (ts.isParenthesizedExpression(recv))
|
|
2615
|
+
recv = recv.expression;
|
|
2616
|
+
if (!ts.isArrayLiteralExpression(recv))
|
|
2617
|
+
return null;
|
|
2618
|
+
const parts = [];
|
|
2619
|
+
for (const el of recv.elements) {
|
|
2620
|
+
if (ts.isStringLiteral(el) || ts.isNoSubstitutionTemplateLiteral(el)) {
|
|
2621
|
+
parts.push(el.text);
|
|
2622
|
+
} else {
|
|
2623
|
+
return null;
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
let sep = ",";
|
|
2627
|
+
if (node.arguments.length >= 1) {
|
|
2628
|
+
const arg = node.arguments[0];
|
|
2629
|
+
if (ts.isStringLiteral(arg) || ts.isNoSubstitutionTemplateLiteral(arg))
|
|
2630
|
+
sep = arg.text;
|
|
2631
|
+
else
|
|
2632
|
+
return null;
|
|
2633
|
+
}
|
|
2634
|
+
return parts.join(sep);
|
|
2635
|
+
}
|
|
2152
2636
|
resolveModuleStringConst(name) {
|
|
2153
2637
|
if (this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name) {
|
|
2154
2638
|
return null;
|
|
@@ -3308,6 +3792,12 @@ ${children}`;
|
|
|
3308
3792
|
if (parsed.kind === "conditional" || parsed.kind === "template-literal") {
|
|
3309
3793
|
return `${name}="${this.renderParsedExpr(parsed)}"`;
|
|
3310
3794
|
}
|
|
3795
|
+
const bareId = value.expr.trim();
|
|
3796
|
+
const propName = this.propsObjectName && bareId.startsWith(`${this.propsObjectName}.`) ? bareId.slice(this.propsObjectName.length + 1) : bareId;
|
|
3797
|
+
if (/^[A-Za-z_$][\w$]*$/.test(propName) && this.nillablePropNames.has(propName)) {
|
|
3798
|
+
const field = `.${this.capitalizeFieldName(propName)}`;
|
|
3799
|
+
return `{{if ne ${field} nil}}${name}="{{${this.convertExpressionToGo(value.expr)}}}"{{end}}`;
|
|
3800
|
+
}
|
|
3311
3801
|
return `${name}="{{${this.convertExpressionToGo(value.expr)}}}"`;
|
|
3312
3802
|
},
|
|
3313
3803
|
emitBooleanAttr: (_value, name) => name,
|