@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/adapter/index.js
CHANGED
|
@@ -32,7 +32,10 @@ import {
|
|
|
32
32
|
identifierPath,
|
|
33
33
|
emitParsedExpr,
|
|
34
34
|
emitIRNode,
|
|
35
|
-
emitAttrValue
|
|
35
|
+
emitAttrValue,
|
|
36
|
+
augmentInheritedPropAccesses,
|
|
37
|
+
parseRecordIndexAccess,
|
|
38
|
+
collectContextConsumers
|
|
36
39
|
} from "@barefootjs/jsx";
|
|
37
40
|
import { findInterpolationEnd } from "@barefootjs/jsx/scanner";
|
|
38
41
|
function wrapIfMultiToken(rendered) {
|
|
@@ -117,7 +120,13 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
117
120
|
localStructFields = new Map;
|
|
118
121
|
synthStructTypes = new Map;
|
|
119
122
|
usesHtmlTemplate = false;
|
|
123
|
+
usesFmt = false;
|
|
124
|
+
childComponentShapes = new Map;
|
|
120
125
|
moduleStringConsts = new Map;
|
|
126
|
+
localConstants = [];
|
|
127
|
+
contextConsumers = [];
|
|
128
|
+
childContextConsumers = new Map;
|
|
129
|
+
nillablePropNames = new Set;
|
|
121
130
|
constructor(options = {}) {
|
|
122
131
|
super();
|
|
123
132
|
this.options = {
|
|
@@ -133,6 +142,10 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
133
142
|
this.propsObjectName = ir.metadata.propsObjectName;
|
|
134
143
|
this.restPropsName = ir.metadata.restPropsName ?? null;
|
|
135
144
|
this.moduleStringConsts = this.collectModuleStringConsts(ir.metadata.localConstants);
|
|
145
|
+
this.localConstants = ir.metadata.localConstants ?? [];
|
|
146
|
+
this.contextConsumers = collectContextConsumers(ir.metadata);
|
|
147
|
+
augmentInheritedPropAccesses(ir);
|
|
148
|
+
this.nillablePropNames = this.collectNillablePropNames(ir);
|
|
136
149
|
if (!options?.siblingTemplatesRegistered) {
|
|
137
150
|
this.checkImportedLoopChildComponents(ir);
|
|
138
151
|
}
|
|
@@ -345,8 +358,46 @@ ${scriptRegistrations}${templateBody}
|
|
|
345
358
|
return `{{if .Scripts}}${registrations.join("")}{{end}}
|
|
346
359
|
`;
|
|
347
360
|
}
|
|
361
|
+
registerChildComponentShape(ir) {
|
|
362
|
+
const name = ir.metadata.componentName;
|
|
363
|
+
if (!name)
|
|
364
|
+
return;
|
|
365
|
+
const paramNames = new Set((ir.metadata.propsParams ?? []).map((p) => p.name));
|
|
366
|
+
const restPropsName = ir.metadata.restPropsName ?? null;
|
|
367
|
+
const restBagField = restPropsName ? this.capitalizeFieldName(restPropsName) : null;
|
|
368
|
+
this.childComponentShapes.set(name, { paramNames, restBagField });
|
|
369
|
+
this.childContextConsumers.set(name, collectContextConsumers(ir.metadata));
|
|
370
|
+
}
|
|
371
|
+
contextFieldName(c) {
|
|
372
|
+
return this.capitalizeFieldName(c.localName);
|
|
373
|
+
}
|
|
374
|
+
contextConsumerGoType(c) {
|
|
375
|
+
if (typeof c.defaultValue === "number")
|
|
376
|
+
return "int";
|
|
377
|
+
if (typeof c.defaultValue === "boolean")
|
|
378
|
+
return "bool";
|
|
379
|
+
return "string";
|
|
380
|
+
}
|
|
381
|
+
contextConsumerGoDefault(c) {
|
|
382
|
+
if (typeof c.defaultValue === "number")
|
|
383
|
+
return String(c.defaultValue);
|
|
384
|
+
if (typeof c.defaultValue === "boolean")
|
|
385
|
+
return String(c.defaultValue);
|
|
386
|
+
if (typeof c.defaultValue === "string")
|
|
387
|
+
return `"${this.escapeGoString(c.defaultValue)}"`;
|
|
388
|
+
return '""';
|
|
389
|
+
}
|
|
390
|
+
nonCollidingContextConsumers(taken) {
|
|
391
|
+
return this.contextConsumers.filter((c) => !taken.has(this.contextFieldName(c)));
|
|
392
|
+
}
|
|
348
393
|
generateTypes(ir) {
|
|
349
394
|
this.usesHtmlTemplate = false;
|
|
395
|
+
this.usesFmt = false;
|
|
396
|
+
this.propsObjectName = ir.metadata.propsObjectName;
|
|
397
|
+
augmentInheritedPropAccesses(ir);
|
|
398
|
+
this.moduleStringConsts = this.collectModuleStringConsts(ir.metadata.localConstants);
|
|
399
|
+
this.localConstants = ir.metadata.localConstants ?? [];
|
|
400
|
+
this.contextConsumers = collectContextConsumers(ir.metadata);
|
|
350
401
|
const lines = [];
|
|
351
402
|
const componentName = ir.metadata.componentName;
|
|
352
403
|
this.localTypeNames = new Set;
|
|
@@ -408,6 +459,8 @@ ${goFields.join(`
|
|
|
408
459
|
header.push(`package ${this.options.packageName}`);
|
|
409
460
|
header.push("");
|
|
410
461
|
header.push("import (");
|
|
462
|
+
if (this.usesFmt)
|
|
463
|
+
header.push('\t"fmt"');
|
|
411
464
|
if (this.usesHtmlTemplate)
|
|
412
465
|
header.push('\t"html/template"');
|
|
413
466
|
header.push('\t"math/rand"');
|
|
@@ -568,6 +621,19 @@ ${goFields.join(`
|
|
|
568
621
|
}
|
|
569
622
|
return overrides;
|
|
570
623
|
}
|
|
624
|
+
resolvePropGoType(param, propTypeOverrides) {
|
|
625
|
+
return propTypeOverrides.get(param.name) ?? this.typeInfoToGo(param.type, param.defaultValue);
|
|
626
|
+
}
|
|
627
|
+
collectNillablePropNames(ir) {
|
|
628
|
+
const propTypeOverrides = this.buildPropTypeOverrides(ir);
|
|
629
|
+
const nillable = new Set;
|
|
630
|
+
for (const param of ir.metadata.propsParams) {
|
|
631
|
+
if (this.resolvePropGoType(param, propTypeOverrides) === "interface{}") {
|
|
632
|
+
nillable.add(param.name);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return nillable;
|
|
636
|
+
}
|
|
571
637
|
generateInputStruct(lines, ir, componentName, nestedComponents, propTypeOverrides, spreadSlots) {
|
|
572
638
|
const inputTypeName = `${componentName}Input`;
|
|
573
639
|
lines.push(`// ${inputTypeName} is the user-facing input type.`);
|
|
@@ -581,12 +647,16 @@ ${goFields.join(`
|
|
|
581
647
|
const fieldName = this.capitalizeFieldName(param.name);
|
|
582
648
|
if (nestedArrayFields.has(fieldName))
|
|
583
649
|
continue;
|
|
584
|
-
const goType =
|
|
650
|
+
const goType = this.resolvePropGoType(param, propTypeOverrides);
|
|
585
651
|
lines.push(` ${fieldName} ${goType}`);
|
|
586
652
|
}
|
|
587
653
|
for (const nested of inputNested) {
|
|
588
654
|
lines.push(` ${nested.name}s []${nested.name}Input`);
|
|
589
655
|
}
|
|
656
|
+
const takenInput = new Set(ir.metadata.propsParams.map((p) => this.capitalizeFieldName(p.name)));
|
|
657
|
+
for (const c of this.nonCollidingContextConsumers(takenInput)) {
|
|
658
|
+
lines.push(` ${this.contextFieldName(c)} ${this.contextConsumerGoType(c)}`);
|
|
659
|
+
}
|
|
590
660
|
const restPropsName = ir.metadata.restPropsName;
|
|
591
661
|
if (restPropsName) {
|
|
592
662
|
const seen = new Set;
|
|
@@ -620,7 +690,7 @@ ${goFields.join(`
|
|
|
620
690
|
const fieldName = this.capitalizeFieldName(param.name);
|
|
621
691
|
if (nestedArrayFields.has(fieldName))
|
|
622
692
|
continue;
|
|
623
|
-
const goType =
|
|
693
|
+
const goType = this.resolvePropGoType(param, propTypeOverrides);
|
|
624
694
|
const jsonTag = this.toJsonTag(param.name);
|
|
625
695
|
lines.push(` ${fieldName} ${goType} \`json:"${jsonTag}"\``);
|
|
626
696
|
propFieldNames.add(fieldName);
|
|
@@ -664,6 +734,15 @@ ${goFields.join(`
|
|
|
664
734
|
const goType = this.inferMemoType(memo, ir.metadata.signals, propsParamMap);
|
|
665
735
|
lines.push(` ${fieldName} ${goType} \`json:"${jsonTag}"\``);
|
|
666
736
|
}
|
|
737
|
+
const takenProps = new Set([
|
|
738
|
+
...ir.metadata.propsParams.map((p) => this.capitalizeFieldName(p.name)),
|
|
739
|
+
...ir.metadata.signals.map((s) => this.capitalizeFieldName(s.getter)),
|
|
740
|
+
...ir.metadata.memos.map((m) => this.capitalizeFieldName(m.name))
|
|
741
|
+
]);
|
|
742
|
+
for (const c of this.nonCollidingContextConsumers(takenProps)) {
|
|
743
|
+
const jsonTag = this.toJsonTag(c.localName);
|
|
744
|
+
lines.push(` ${this.contextFieldName(c)} ${this.contextConsumerGoType(c)} \`json:"${jsonTag}"\``);
|
|
745
|
+
}
|
|
667
746
|
for (const nested of nestedComponents) {
|
|
668
747
|
if (nested.isDynamic && !nested.isPropDerived) {
|
|
669
748
|
lines.push(` ${nested.name}s []${nested.name}Props \`json:"-"\``);
|
|
@@ -773,25 +852,55 @@ ${goFields.join(`
|
|
|
773
852
|
const varName = `${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`;
|
|
774
853
|
lines.push(` ${nested.name}s: ${varName},`);
|
|
775
854
|
}
|
|
855
|
+
const memoPropsParamMap = new Map(ir.metadata.propsParams.map((p) => [p.name, p]));
|
|
776
856
|
for (const memo of ir.metadata.memos) {
|
|
777
857
|
const fieldName = this.capitalizeFieldName(memo.name);
|
|
778
|
-
const
|
|
858
|
+
const goType = this.inferMemoType(memo, ir.metadata.signals, memoPropsParamMap);
|
|
859
|
+
const memoValue = this.computeMemoInitialValue(memo, ir.metadata.signals, ir.metadata.propsParams, propFallbackVars, goType);
|
|
779
860
|
lines.push(` ${fieldName}: ${memoValue},`);
|
|
780
861
|
}
|
|
862
|
+
const takenInit = new Set([
|
|
863
|
+
...ir.metadata.propsParams.map((p) => this.capitalizeFieldName(p.name)),
|
|
864
|
+
...ir.metadata.signals.map((s) => this.capitalizeFieldName(s.getter)),
|
|
865
|
+
...ir.metadata.memos.map((m) => this.capitalizeFieldName(m.name))
|
|
866
|
+
]);
|
|
867
|
+
for (const c of this.nonCollidingContextConsumers(takenInit)) {
|
|
868
|
+
const field = this.contextFieldName(c);
|
|
869
|
+
const def = this.contextConsumerGoDefault(c);
|
|
870
|
+
const defaulted = c.defaultValue === null || def === '""' || def === "0" || def === "false" ? `in.${field}` : this.applyGoFallback(`in.${field}`, def);
|
|
871
|
+
lines.push(` ${field}: ${defaulted},`);
|
|
872
|
+
}
|
|
781
873
|
const staticChildren = this.collectStaticChildInstances(ir.root);
|
|
782
874
|
for (const child of staticChildren) {
|
|
783
875
|
lines.push(` ${child.fieldName}: New${child.name}Props(${child.name}Input{`);
|
|
784
876
|
lines.push(` ScopeID: scopeID + "_${child.slotId}",`);
|
|
785
877
|
lines.push(` BfParent: scopeID,`);
|
|
786
878
|
lines.push(` BfMount: "${child.slotId}",`);
|
|
879
|
+
if (child.contextBindings) {
|
|
880
|
+
for (const consumer of this.childContextConsumers.get(child.name) ?? []) {
|
|
881
|
+
const goVal = child.contextBindings.get(consumer.contextName);
|
|
882
|
+
if (goVal !== undefined) {
|
|
883
|
+
lines.push(` ${this.contextFieldName(consumer)}: ${goVal},`);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
const childShape = this.childComponentShapes.get(child.name);
|
|
888
|
+
const restBagEntries = [];
|
|
889
|
+
const emitChildField = (jsxName, goValue) => {
|
|
890
|
+
if (childShape && childShape.restBagField && !childShape.paramNames.has(jsxName)) {
|
|
891
|
+
restBagEntries.push(`${JSON.stringify(jsxName)}: ${goValue}`);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
lines.push(` ${this.capitalizeFieldName(jsxName)}: ${goValue},`);
|
|
895
|
+
};
|
|
787
896
|
for (const prop of child.props) {
|
|
788
897
|
switch (prop.value.kind) {
|
|
789
898
|
case "literal":
|
|
790
|
-
|
|
899
|
+
emitChildField(prop.name, this.goLiteral(prop.value.value));
|
|
791
900
|
break;
|
|
792
901
|
case "boolean-shorthand":
|
|
793
902
|
case "boolean-attr":
|
|
794
|
-
|
|
903
|
+
emitChildField(prop.name, "true");
|
|
795
904
|
break;
|
|
796
905
|
case "expression":
|
|
797
906
|
case "spread":
|
|
@@ -800,7 +909,7 @@ ${goFields.join(`
|
|
|
800
909
|
if (parts) {
|
|
801
910
|
const goExpr = this.templatePartsToGoCode(parts, ir.metadata.propsParams);
|
|
802
911
|
if (goExpr !== null) {
|
|
803
|
-
|
|
912
|
+
emitChildField(prop.name, goExpr);
|
|
804
913
|
break;
|
|
805
914
|
}
|
|
806
915
|
}
|
|
@@ -809,7 +918,7 @@ ${goFields.join(`
|
|
|
809
918
|
break;
|
|
810
919
|
const resolvedValue = this.resolveDynamicPropValue(exprText, ir.metadata.signals, ir.metadata.memos, ir.metadata.propsParams);
|
|
811
920
|
if (resolvedValue !== null) {
|
|
812
|
-
|
|
921
|
+
emitChildField(prop.name, resolvedValue);
|
|
813
922
|
}
|
|
814
923
|
break;
|
|
815
924
|
}
|
|
@@ -817,6 +926,9 @@ ${goFields.join(`
|
|
|
817
926
|
break;
|
|
818
927
|
}
|
|
819
928
|
}
|
|
929
|
+
if (childShape?.restBagField && restBagEntries.length > 0) {
|
|
930
|
+
lines.push(` ${childShape.restBagField}: map[string]any{${restBagEntries.join(", ")}},`);
|
|
931
|
+
}
|
|
820
932
|
if (child.childrenText !== null) {
|
|
821
933
|
lines.push(` Children: ${JSON.stringify(child.childrenText)},`);
|
|
822
934
|
} else if (child.childrenHtml !== null) {
|
|
@@ -887,7 +999,7 @@ ${goFields.join(`
|
|
|
887
999
|
}
|
|
888
1000
|
collectStaticChildInstances(node) {
|
|
889
1001
|
const result = [];
|
|
890
|
-
this.collectStaticChildInstancesRecursive(node, result, false);
|
|
1002
|
+
this.collectStaticChildInstancesRecursive(node, result, false, new Map);
|
|
891
1003
|
return result;
|
|
892
1004
|
}
|
|
893
1005
|
extractTextChildren(children) {
|
|
@@ -911,12 +1023,12 @@ ${goFields.join(`
|
|
|
911
1023
|
return null;
|
|
912
1024
|
return html;
|
|
913
1025
|
}
|
|
914
|
-
collectStaticChildInstancesRecursive(node, result, inLoop) {
|
|
1026
|
+
collectStaticChildInstancesRecursive(node, result, inLoop, providerCtx) {
|
|
915
1027
|
if (node.type === "component") {
|
|
916
1028
|
const comp = node;
|
|
917
1029
|
if (comp.dynamicTag) {
|
|
918
1030
|
for (const child of comp.children) {
|
|
919
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1031
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, providerCtx);
|
|
920
1032
|
}
|
|
921
1033
|
return;
|
|
922
1034
|
}
|
|
@@ -928,48 +1040,65 @@ ${goFields.join(`
|
|
|
928
1040
|
props: comp.props,
|
|
929
1041
|
fieldName: `${comp.name}${suffix}`,
|
|
930
1042
|
childrenText: this.extractTextChildren(comp.children),
|
|
931
|
-
childrenHtml: this.extractHtmlChildren(comp.children)
|
|
1043
|
+
childrenHtml: this.extractHtmlChildren(comp.children),
|
|
1044
|
+
contextBindings: providerCtx.size > 0 ? providerCtx : undefined
|
|
932
1045
|
});
|
|
933
1046
|
}
|
|
934
1047
|
if (comp.name === "Portal" && comp.children) {
|
|
935
1048
|
for (const child of comp.children) {
|
|
936
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1049
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, providerCtx);
|
|
937
1050
|
}
|
|
938
1051
|
}
|
|
939
1052
|
} else if (node.type === "loop") {
|
|
940
1053
|
const loop = node;
|
|
941
1054
|
for (const child of loop.children) {
|
|
942
|
-
this.collectStaticChildInstancesRecursive(child, result, true);
|
|
1055
|
+
this.collectStaticChildInstancesRecursive(child, result, true, providerCtx);
|
|
943
1056
|
}
|
|
944
1057
|
} else if (node.type === "element") {
|
|
945
1058
|
const element = node;
|
|
946
1059
|
for (const child of element.children) {
|
|
947
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1060
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, providerCtx);
|
|
948
1061
|
}
|
|
949
1062
|
} else if (node.type === "fragment") {
|
|
950
1063
|
const fragment = node;
|
|
951
1064
|
for (const child of fragment.children) {
|
|
952
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1065
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, providerCtx);
|
|
953
1066
|
}
|
|
954
1067
|
} else if (node.type === "conditional") {
|
|
955
1068
|
const cond = node;
|
|
956
|
-
this.collectStaticChildInstancesRecursive(cond.whenTrue, result, inLoop);
|
|
1069
|
+
this.collectStaticChildInstancesRecursive(cond.whenTrue, result, inLoop, providerCtx);
|
|
957
1070
|
if (cond.whenFalse) {
|
|
958
|
-
this.collectStaticChildInstancesRecursive(cond.whenFalse, result, inLoop);
|
|
1071
|
+
this.collectStaticChildInstancesRecursive(cond.whenFalse, result, inLoop, providerCtx);
|
|
959
1072
|
}
|
|
960
1073
|
} else if (node.type === "provider") {
|
|
961
1074
|
const p = node;
|
|
1075
|
+
const childCtx = this.extendProviderContext(providerCtx, p);
|
|
962
1076
|
for (const child of p.children) {
|
|
963
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1077
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, childCtx);
|
|
964
1078
|
}
|
|
965
1079
|
} else if (node.type === "async") {
|
|
966
1080
|
const a = node;
|
|
967
|
-
this.collectStaticChildInstancesRecursive(a.fallback, result, inLoop);
|
|
1081
|
+
this.collectStaticChildInstancesRecursive(a.fallback, result, inLoop, providerCtx);
|
|
968
1082
|
for (const child of a.children) {
|
|
969
|
-
this.collectStaticChildInstancesRecursive(child, result, inLoop);
|
|
1083
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, providerCtx);
|
|
970
1084
|
}
|
|
971
1085
|
}
|
|
972
1086
|
}
|
|
1087
|
+
extendProviderContext(current, p) {
|
|
1088
|
+
const v = p.valueProp?.value;
|
|
1089
|
+
if (!v || v.kind !== "literal")
|
|
1090
|
+
return current;
|
|
1091
|
+
let goLit = null;
|
|
1092
|
+
if (typeof v.value === "string")
|
|
1093
|
+
goLit = `"${this.escapeGoString(v.value)}"`;
|
|
1094
|
+
else if (typeof v.value === "number" || typeof v.value === "boolean")
|
|
1095
|
+
goLit = String(v.value);
|
|
1096
|
+
if (goLit === null)
|
|
1097
|
+
return current;
|
|
1098
|
+
const next = new Map(current);
|
|
1099
|
+
next.set(p.contextName, goLit);
|
|
1100
|
+
return next;
|
|
1101
|
+
}
|
|
973
1102
|
collectSpreadSlots(node) {
|
|
974
1103
|
const result = [];
|
|
975
1104
|
this.collectSpreadSlotsRecursive(node, result);
|
|
@@ -1094,6 +1223,9 @@ ${goFields.join(`
|
|
|
1094
1223
|
}
|
|
1095
1224
|
buildSpreadInitializer(spreadExpr, ir) {
|
|
1096
1225
|
const trimmed = spreadExpr.trim();
|
|
1226
|
+
const conditional = this.buildConditionalSpreadInitializer(trimmed, ir);
|
|
1227
|
+
if (conditional !== undefined)
|
|
1228
|
+
return conditional;
|
|
1097
1229
|
const callMatch = /^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\s*\)$/.exec(trimmed);
|
|
1098
1230
|
if (callMatch) {
|
|
1099
1231
|
const getterName = callMatch[1];
|
|
@@ -1117,9 +1249,127 @@ ${goFields.join(`
|
|
|
1117
1249
|
if (ir.metadata.restPropsName === trimmed) {
|
|
1118
1250
|
return `in.${this.capitalizeFieldName(trimmed)}`;
|
|
1119
1251
|
}
|
|
1252
|
+
const localConst = (ir.metadata.localConstants ?? []).find((c) => c.name === trimmed && !c.isModule);
|
|
1253
|
+
if (localConst?.value !== undefined) {
|
|
1254
|
+
const initTrimmed = localConst.value.trim();
|
|
1255
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(initTrimmed)) {
|
|
1256
|
+
const resolved = this.buildConditionalSpreadInitializer(initTrimmed, ir);
|
|
1257
|
+
if (resolved)
|
|
1258
|
+
return resolved;
|
|
1259
|
+
if (resolved === null)
|
|
1260
|
+
return null;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1120
1263
|
}
|
|
1121
1264
|
return null;
|
|
1122
1265
|
}
|
|
1266
|
+
buildConditionalSpreadInitializer(spreadExpr, ir) {
|
|
1267
|
+
const expr = this.parseLiteralExpression(spreadExpr);
|
|
1268
|
+
if (!expr || !ts.isConditionalExpression(expr))
|
|
1269
|
+
return;
|
|
1270
|
+
const whenTrue = this.unwrapParens(expr.whenTrue);
|
|
1271
|
+
const whenFalse = this.unwrapParens(expr.whenFalse);
|
|
1272
|
+
if (!ts.isObjectLiteralExpression(whenTrue) || !ts.isObjectLiteralExpression(whenFalse)) {
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
const goCond = this.conditionToGoBool(expr.condition, ir);
|
|
1276
|
+
if (goCond === null)
|
|
1277
|
+
return null;
|
|
1278
|
+
const trueMap = this.objectLiteralToGoSpreadMap(whenTrue, ir);
|
|
1279
|
+
const falseMap = this.objectLiteralToGoSpreadMap(whenFalse, ir);
|
|
1280
|
+
if (trueMap === null || falseMap === null)
|
|
1281
|
+
return null;
|
|
1282
|
+
return `func() map[string]any {
|
|
1283
|
+
` + ` if ${goCond} {
|
|
1284
|
+
` + ` return ${trueMap}
|
|
1285
|
+
` + ` }
|
|
1286
|
+
` + ` return ${falseMap}
|
|
1287
|
+
` + ` }()`;
|
|
1288
|
+
}
|
|
1289
|
+
unwrapParens(node) {
|
|
1290
|
+
let e = node;
|
|
1291
|
+
while (ts.isParenthesizedExpression(e))
|
|
1292
|
+
e = e.expression;
|
|
1293
|
+
return e;
|
|
1294
|
+
}
|
|
1295
|
+
conditionToGoBool(condition, ir) {
|
|
1296
|
+
let node = this.unwrapParens(condition);
|
|
1297
|
+
let negate = false;
|
|
1298
|
+
if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
1299
|
+
negate = true;
|
|
1300
|
+
node = this.unwrapParens(node.operand);
|
|
1301
|
+
}
|
|
1302
|
+
if (!ts.isIdentifier(node))
|
|
1303
|
+
return null;
|
|
1304
|
+
const param = ir.metadata.propsParams.find((p) => p.name === node.text);
|
|
1305
|
+
if (!param)
|
|
1306
|
+
return null;
|
|
1307
|
+
const field = `in.${this.capitalizeFieldName(param.name)}`;
|
|
1308
|
+
const prim = param.type.kind === "primitive" ? param.type.primitive : undefined;
|
|
1309
|
+
let truthy;
|
|
1310
|
+
if (prim === "boolean") {
|
|
1311
|
+
truthy = field;
|
|
1312
|
+
} else if (prim === "number") {
|
|
1313
|
+
truthy = `${field} != 0`;
|
|
1314
|
+
} else if (prim === "string") {
|
|
1315
|
+
truthy = `${field} != ""`;
|
|
1316
|
+
} else {
|
|
1317
|
+
truthy = `bf.Truthy(${field})`;
|
|
1318
|
+
}
|
|
1319
|
+
if (!negate)
|
|
1320
|
+
return truthy;
|
|
1321
|
+
if (prim === "boolean")
|
|
1322
|
+
return `!${field}`;
|
|
1323
|
+
if (prim === "number")
|
|
1324
|
+
return `${field} == 0`;
|
|
1325
|
+
if (prim === "string")
|
|
1326
|
+
return `${field} == ""`;
|
|
1327
|
+
return `!bf.Truthy(${field})`;
|
|
1328
|
+
}
|
|
1329
|
+
objectLiteralToGoSpreadMap(obj, ir) {
|
|
1330
|
+
const entries = [];
|
|
1331
|
+
for (const prop of obj.properties) {
|
|
1332
|
+
if (!ts.isPropertyAssignment(prop))
|
|
1333
|
+
return null;
|
|
1334
|
+
let key;
|
|
1335
|
+
if (ts.isIdentifier(prop.name)) {
|
|
1336
|
+
key = prop.name.text;
|
|
1337
|
+
} else if (ts.isStringLiteral(prop.name) || ts.isNoSubstitutionTemplateLiteral(prop.name)) {
|
|
1338
|
+
key = prop.name.text;
|
|
1339
|
+
} else {
|
|
1340
|
+
return null;
|
|
1341
|
+
}
|
|
1342
|
+
const val = this.unwrapParens(prop.initializer);
|
|
1343
|
+
let goVal;
|
|
1344
|
+
if (ts.isStringLiteral(val) || ts.isNoSubstitutionTemplateLiteral(val)) {
|
|
1345
|
+
goVal = JSON.stringify(val.text);
|
|
1346
|
+
} else if (ts.isIdentifier(val)) {
|
|
1347
|
+
const param = ir.metadata.propsParams.find((p) => p.name === val.text);
|
|
1348
|
+
if (!param)
|
|
1349
|
+
return null;
|
|
1350
|
+
goVal = `in.${this.capitalizeFieldName(param.name)}`;
|
|
1351
|
+
} else {
|
|
1352
|
+
const indexed = this.recordIndexAccessToGoMap(val, ir);
|
|
1353
|
+
if (indexed === null)
|
|
1354
|
+
return null;
|
|
1355
|
+
goVal = indexed;
|
|
1356
|
+
}
|
|
1357
|
+
entries.push(`${JSON.stringify(key)}: ${goVal}`);
|
|
1358
|
+
}
|
|
1359
|
+
return `map[string]any{${entries.join(", ")}}`;
|
|
1360
|
+
}
|
|
1361
|
+
recordIndexAccessToGoMap(val, ir) {
|
|
1362
|
+
const parsed = parseRecordIndexAccess(val, ir.metadata.localConstants ?? [], ir.metadata.propsParams);
|
|
1363
|
+
if (!parsed)
|
|
1364
|
+
return null;
|
|
1365
|
+
const entries = parsed.entries.map((e) => {
|
|
1366
|
+
const mapVal = e.value.kind === "number" ? e.value.text : JSON.stringify(e.value.text);
|
|
1367
|
+
return `${JSON.stringify(e.key)}: ${mapVal}`;
|
|
1368
|
+
});
|
|
1369
|
+
this.usesFmt = true;
|
|
1370
|
+
const field = `in.${this.capitalizeFieldName(parsed.indexPropName)}`;
|
|
1371
|
+
return `map[string]any{${entries.join(", ")}}[fmt.Sprint(${field})]`;
|
|
1372
|
+
}
|
|
1123
1373
|
convertInitialValue(value, typeInfo, propsParams) {
|
|
1124
1374
|
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value)) {
|
|
1125
1375
|
if (propsParams?.some((p) => p.name === value)) {
|
|
@@ -1360,7 +1610,142 @@ ${goFields.join(`
|
|
|
1360
1610
|
}
|
|
1361
1611
|
return null;
|
|
1362
1612
|
}
|
|
1363
|
-
|
|
1613
|
+
computeTemplateLiteralMemoInitialValue(computation, propsParams) {
|
|
1614
|
+
const sf = ts.createSourceFile("__memo.ts", `const __x = (${computation});`, ts.ScriptTarget.Latest, false);
|
|
1615
|
+
const stmt = sf.statements[0];
|
|
1616
|
+
if (!stmt || !ts.isVariableStatement(stmt))
|
|
1617
|
+
return null;
|
|
1618
|
+
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
1619
|
+
while (init && ts.isParenthesizedExpression(init))
|
|
1620
|
+
init = init.expression;
|
|
1621
|
+
if (!init || !ts.isArrowFunction(init))
|
|
1622
|
+
return null;
|
|
1623
|
+
let body = init.body;
|
|
1624
|
+
while (ts.isParenthesizedExpression(body)) {
|
|
1625
|
+
body = body.expression;
|
|
1626
|
+
}
|
|
1627
|
+
const localKeyBindings = new Map;
|
|
1628
|
+
if (ts.isBlock(body)) {
|
|
1629
|
+
let returned = null;
|
|
1630
|
+
for (const s of body.statements) {
|
|
1631
|
+
if (ts.isVariableStatement(s)) {
|
|
1632
|
+
for (const d of s.declarationList.declarations) {
|
|
1633
|
+
if (!ts.isIdentifier(d.name) || !d.initializer)
|
|
1634
|
+
continue;
|
|
1635
|
+
const binding = this.parseLocalKeyBinding(d.initializer);
|
|
1636
|
+
if (binding)
|
|
1637
|
+
localKeyBindings.set(d.name.text, binding);
|
|
1638
|
+
}
|
|
1639
|
+
} else if (ts.isReturnStatement(s) && s.expression) {
|
|
1640
|
+
returned = s.expression;
|
|
1641
|
+
} else if (ts.isExpressionStatement(s) || ts.isEmptyStatement(s)) {} else {
|
|
1642
|
+
return null;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
if (!returned)
|
|
1646
|
+
return null;
|
|
1647
|
+
body = returned;
|
|
1648
|
+
while (ts.isParenthesizedExpression(body)) {
|
|
1649
|
+
body = body.expression;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
if (!ts.isTemplateExpression(body) && !ts.isNoSubstitutionTemplateLiteral(body)) {
|
|
1653
|
+
return null;
|
|
1654
|
+
}
|
|
1655
|
+
const propNames = new Set(propsParams.map((p) => p.name));
|
|
1656
|
+
const escGo = (s) => `"${this.escapeGoString(s)}"`;
|
|
1657
|
+
const segments = [];
|
|
1658
|
+
if (ts.isNoSubstitutionTemplateLiteral(body)) {
|
|
1659
|
+
return escGo(body.text);
|
|
1660
|
+
}
|
|
1661
|
+
if (body.head.text)
|
|
1662
|
+
segments.push(escGo(body.head.text));
|
|
1663
|
+
for (const span of body.templateSpans) {
|
|
1664
|
+
const resolved = this.resolveTemplateInterpolation(span.expression, propNames, localKeyBindings);
|
|
1665
|
+
if (resolved === null)
|
|
1666
|
+
return null;
|
|
1667
|
+
segments.push(resolved);
|
|
1668
|
+
if (span.literal.text)
|
|
1669
|
+
segments.push(escGo(span.literal.text));
|
|
1670
|
+
}
|
|
1671
|
+
if (segments.length === 0)
|
|
1672
|
+
return '""';
|
|
1673
|
+
return segments.join(" + ");
|
|
1674
|
+
}
|
|
1675
|
+
resolveTemplateInterpolation(expr, propNames, localKeyBindings = new Map) {
|
|
1676
|
+
let node = expr;
|
|
1677
|
+
while (ts.isParenthesizedExpression(node))
|
|
1678
|
+
node = node.expression;
|
|
1679
|
+
if (ts.isIdentifier(node)) {
|
|
1680
|
+
const inlined = this.resolveModuleStringConst(node.text);
|
|
1681
|
+
if (inlined !== null)
|
|
1682
|
+
return inlined;
|
|
1683
|
+
return null;
|
|
1684
|
+
}
|
|
1685
|
+
if (ts.isElementAccessExpression(node)) {
|
|
1686
|
+
const indexed = this.recordIndexInterpolationToGo(node, propNames, localKeyBindings);
|
|
1687
|
+
if (indexed !== null)
|
|
1688
|
+
return indexed;
|
|
1689
|
+
return null;
|
|
1690
|
+
}
|
|
1691
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
1692
|
+
const right = node.right;
|
|
1693
|
+
const isEmptyStr = (ts.isStringLiteral(right) || ts.isNoSubstitutionTemplateLiteral(right)) && right.text === "";
|
|
1694
|
+
const propName2 = this.propsAccessName(node.left);
|
|
1695
|
+
if (propName2 && propNames.has(propName2) && isEmptyStr) {
|
|
1696
|
+
return `in.${this.capitalizeFieldName(propName2)}`;
|
|
1697
|
+
}
|
|
1698
|
+
return null;
|
|
1699
|
+
}
|
|
1700
|
+
const propName = this.propsAccessName(node);
|
|
1701
|
+
if (propName && propNames.has(propName)) {
|
|
1702
|
+
return `in.${this.capitalizeFieldName(propName)}`;
|
|
1703
|
+
}
|
|
1704
|
+
return null;
|
|
1705
|
+
}
|
|
1706
|
+
parseLocalKeyBinding(init) {
|
|
1707
|
+
let node = init;
|
|
1708
|
+
while (ts.isParenthesizedExpression(node))
|
|
1709
|
+
node = node.expression;
|
|
1710
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
1711
|
+
const propName2 = this.propsAccessName(node.left);
|
|
1712
|
+
const right = node.right;
|
|
1713
|
+
if (propName2 && (ts.isStringLiteral(right) || ts.isNoSubstitutionTemplateLiteral(right))) {
|
|
1714
|
+
return { propName: propName2, defaultLiteral: right.text };
|
|
1715
|
+
}
|
|
1716
|
+
return null;
|
|
1717
|
+
}
|
|
1718
|
+
const propName = this.propsAccessName(node);
|
|
1719
|
+
if (propName)
|
|
1720
|
+
return { propName };
|
|
1721
|
+
return null;
|
|
1722
|
+
}
|
|
1723
|
+
recordIndexInterpolationToGo(node, propNames, localKeyBindings) {
|
|
1724
|
+
const parsed = parseRecordIndexAccess(node, this.localConstants ?? [], [...propNames].map((name) => ({ name })), (name) => localKeyBindings.get(name) ?? null);
|
|
1725
|
+
if (!parsed)
|
|
1726
|
+
return null;
|
|
1727
|
+
const goVal = (v) => v.kind === "number" ? v.text : JSON.stringify(v.text);
|
|
1728
|
+
const entries = parsed.entries.map((e) => `${JSON.stringify(e.key)}: ${goVal(e.value)}`);
|
|
1729
|
+
if (parsed.defaultKey !== undefined) {
|
|
1730
|
+
const def = parsed.entries.find((e) => e.key === parsed.defaultKey);
|
|
1731
|
+
if (def)
|
|
1732
|
+
entries.unshift(`"": ${goVal(def.value)}`);
|
|
1733
|
+
}
|
|
1734
|
+
const allString = parsed.entries.every((e) => e.value.kind === "string");
|
|
1735
|
+
const mapType = allString ? "map[string]string" : "map[string]any";
|
|
1736
|
+
this.usesFmt = true;
|
|
1737
|
+
return `${mapType}{${entries.join(", ")}}[fmt.Sprint(in.${this.capitalizeFieldName(parsed.indexPropName)})]`;
|
|
1738
|
+
}
|
|
1739
|
+
propsAccessName(node) {
|
|
1740
|
+
if (!ts.isPropertyAccessExpression(node))
|
|
1741
|
+
return null;
|
|
1742
|
+
if (!ts.isIdentifier(node.expression))
|
|
1743
|
+
return null;
|
|
1744
|
+
if (!this.propsObjectName || node.expression.text !== this.propsObjectName)
|
|
1745
|
+
return null;
|
|
1746
|
+
return node.name.text;
|
|
1747
|
+
}
|
|
1748
|
+
computeMemoInitialValue(memo, signals, propsParams, propFallbackVars = GoTemplateAdapter.EMPTY_PROP_FALLBACK_VARS, goType) {
|
|
1364
1749
|
const computation = memo.computation;
|
|
1365
1750
|
const propRef = (propName) => {
|
|
1366
1751
|
const hoisted = propFallbackVars.get(propName);
|
|
@@ -1368,6 +1753,9 @@ ${goFields.join(`
|
|
|
1368
1753
|
return hoisted.varName;
|
|
1369
1754
|
return `in.${this.capitalizeFieldName(propName)}`;
|
|
1370
1755
|
};
|
|
1756
|
+
const tmplMemo = this.computeTemplateLiteralMemoInitialValue(computation, propsParams);
|
|
1757
|
+
if (tmplMemo !== null)
|
|
1758
|
+
return tmplMemo;
|
|
1371
1759
|
const arithmeticMatch = computation.match(/\(\)\s*=>\s*(\w+)\(\)\s*([*+\-/])\s*(\d+)/);
|
|
1372
1760
|
if (arithmeticMatch) {
|
|
1373
1761
|
const [, depName, operator, operand] = arithmeticMatch;
|
|
@@ -1387,8 +1775,8 @@ ${goFields.join(`
|
|
|
1387
1775
|
return `${hoisted.varName} ${operator} ${operand}`;
|
|
1388
1776
|
const fieldName = this.capitalizeFieldName(propName);
|
|
1389
1777
|
if (param.type) {
|
|
1390
|
-
const
|
|
1391
|
-
if (
|
|
1778
|
+
const goType2 = this.typeInfoToGo(param.type, param.defaultValue);
|
|
1779
|
+
if (goType2 === "interface{}")
|
|
1392
1780
|
return `in.${fieldName}.(int) ${operator} ${operand}`;
|
|
1393
1781
|
}
|
|
1394
1782
|
return `in.${fieldName} ${operator} ${operand}`;
|
|
@@ -1417,8 +1805,8 @@ ${goFields.join(`
|
|
|
1417
1805
|
if (param) {
|
|
1418
1806
|
const fieldName = this.capitalizeFieldName(varName);
|
|
1419
1807
|
if (param.type) {
|
|
1420
|
-
const
|
|
1421
|
-
if (
|
|
1808
|
+
const goType2 = this.typeInfoToGo(param.type, param.defaultValue);
|
|
1809
|
+
if (goType2 === "interface{}")
|
|
1422
1810
|
return `in.${fieldName}.(int) ${operator} ${operand}`;
|
|
1423
1811
|
}
|
|
1424
1812
|
return `in.${fieldName} ${operator} ${operand}`;
|
|
@@ -1432,9 +1820,40 @@ ${goFields.join(`
|
|
|
1432
1820
|
return `in.${this.capitalizeFieldName(varName)}`;
|
|
1433
1821
|
}
|
|
1434
1822
|
}
|
|
1823
|
+
if (goType === "bool")
|
|
1824
|
+
return "false";
|
|
1825
|
+
if (goType === "string")
|
|
1826
|
+
return '""';
|
|
1435
1827
|
return "0";
|
|
1436
1828
|
}
|
|
1829
|
+
isTemplateLiteralMemo(computation) {
|
|
1830
|
+
const sf = ts.createSourceFile("__memo.ts", `const __x = (${computation});`, ts.ScriptTarget.Latest, false);
|
|
1831
|
+
const stmt = sf.statements[0];
|
|
1832
|
+
if (!stmt || !ts.isVariableStatement(stmt))
|
|
1833
|
+
return false;
|
|
1834
|
+
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
1835
|
+
while (init && ts.isParenthesizedExpression(init))
|
|
1836
|
+
init = init.expression;
|
|
1837
|
+
if (!init || !ts.isArrowFunction(init))
|
|
1838
|
+
return false;
|
|
1839
|
+
let body = init.body;
|
|
1840
|
+
while (ts.isParenthesizedExpression(body)) {
|
|
1841
|
+
body = body.expression;
|
|
1842
|
+
}
|
|
1843
|
+
if (ts.isBlock(body)) {
|
|
1844
|
+
const ret = body.statements.find(ts.isReturnStatement);
|
|
1845
|
+
if (!ret || !ret.expression)
|
|
1846
|
+
return false;
|
|
1847
|
+
body = ret.expression;
|
|
1848
|
+
while (ts.isParenthesizedExpression(body)) {
|
|
1849
|
+
body = body.expression;
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
return ts.isTemplateExpression(body) || ts.isNoSubstitutionTemplateLiteral(body);
|
|
1853
|
+
}
|
|
1437
1854
|
inferMemoType(memo, signals, propsParamMap) {
|
|
1855
|
+
if (this.isTemplateLiteralMemo(memo.computation))
|
|
1856
|
+
return "string";
|
|
1438
1857
|
if (memo.computation.includes("*") || memo.computation.includes("/") || memo.computation.includes("+") || memo.computation.includes("-")) {
|
|
1439
1858
|
for (const dep of memo.deps) {
|
|
1440
1859
|
const signal = signals.find((s) => s.getter === dep);
|
|
@@ -1458,8 +1877,39 @@ ${goFields.join(`
|
|
|
1458
1877
|
}
|
|
1459
1878
|
}
|
|
1460
1879
|
}
|
|
1880
|
+
if (this.typeInfoToGo(memo.type) === "interface{}" && this.isBooleanMemo(memo, signals, propsParamMap)) {
|
|
1881
|
+
return "bool";
|
|
1882
|
+
}
|
|
1461
1883
|
return this.typeInfoToGo(memo.type);
|
|
1462
1884
|
}
|
|
1885
|
+
isBooleanMemo(memo, signals, propsParamMap) {
|
|
1886
|
+
const c = memo.computation;
|
|
1887
|
+
if (/(!==|===|!=(?!=)|==(?!=))/.test(c))
|
|
1888
|
+
return true;
|
|
1889
|
+
if (/=>\s*!/.test(c))
|
|
1890
|
+
return true;
|
|
1891
|
+
const isBoolGetter = (name) => {
|
|
1892
|
+
const sig = signals.find((s) => s.getter === name);
|
|
1893
|
+
if (sig) {
|
|
1894
|
+
if (this.typeInfoToGo(sig.type) === "bool")
|
|
1895
|
+
return true;
|
|
1896
|
+
if (/\?\?\s*(true|false)\b/.test(sig.initialValue))
|
|
1897
|
+
return true;
|
|
1898
|
+
const propName = this.extractPropNameFromInitialValue(sig.initialValue) ?? sig.initialValue;
|
|
1899
|
+
const prop2 = propsParamMap.get(propName);
|
|
1900
|
+
if (prop2 && this.typeInfoToGo(prop2.type, prop2.defaultValue) === "bool")
|
|
1901
|
+
return true;
|
|
1902
|
+
return false;
|
|
1903
|
+
}
|
|
1904
|
+
const prop = propsParamMap.get(name);
|
|
1905
|
+
return !!prop && this.typeInfoToGo(prop.type, prop.defaultValue) === "bool";
|
|
1906
|
+
};
|
|
1907
|
+
const ternary = c.match(/=>\s*\w+\(\)\s*\?\s*(\w+)\(\)\s*:\s*(\w+)\(\)/);
|
|
1908
|
+
if (ternary) {
|
|
1909
|
+
return isBoolGetter(ternary[1]) && isBoolGetter(ternary[2]);
|
|
1910
|
+
}
|
|
1911
|
+
return false;
|
|
1912
|
+
}
|
|
1463
1913
|
inferTypeFromValue(value) {
|
|
1464
1914
|
if (value === "true" || value === "false")
|
|
1465
1915
|
return "bool";
|
|
@@ -1807,8 +2257,42 @@ ${goFields.join(`
|
|
|
1807
2257
|
if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
1808
2258
|
return init.text;
|
|
1809
2259
|
}
|
|
2260
|
+
const joined = this.evalStringArrayJoin(init);
|
|
2261
|
+
if (joined !== null)
|
|
2262
|
+
return joined;
|
|
1810
2263
|
return null;
|
|
1811
2264
|
}
|
|
2265
|
+
evalStringArrayJoin(node) {
|
|
2266
|
+
if (!ts.isCallExpression(node))
|
|
2267
|
+
return null;
|
|
2268
|
+
const callee = node.expression;
|
|
2269
|
+
if (!ts.isPropertyAccessExpression(callee))
|
|
2270
|
+
return null;
|
|
2271
|
+
if (callee.name.text !== "join")
|
|
2272
|
+
return null;
|
|
2273
|
+
let recv = callee.expression;
|
|
2274
|
+
while (ts.isParenthesizedExpression(recv))
|
|
2275
|
+
recv = recv.expression;
|
|
2276
|
+
if (!ts.isArrayLiteralExpression(recv))
|
|
2277
|
+
return null;
|
|
2278
|
+
const parts = [];
|
|
2279
|
+
for (const el of recv.elements) {
|
|
2280
|
+
if (ts.isStringLiteral(el) || ts.isNoSubstitutionTemplateLiteral(el)) {
|
|
2281
|
+
parts.push(el.text);
|
|
2282
|
+
} else {
|
|
2283
|
+
return null;
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
let sep = ",";
|
|
2287
|
+
if (node.arguments.length >= 1) {
|
|
2288
|
+
const arg = node.arguments[0];
|
|
2289
|
+
if (ts.isStringLiteral(arg) || ts.isNoSubstitutionTemplateLiteral(arg))
|
|
2290
|
+
sep = arg.text;
|
|
2291
|
+
else
|
|
2292
|
+
return null;
|
|
2293
|
+
}
|
|
2294
|
+
return parts.join(sep);
|
|
2295
|
+
}
|
|
1812
2296
|
resolveModuleStringConst(name) {
|
|
1813
2297
|
if (this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name) {
|
|
1814
2298
|
return null;
|
|
@@ -2968,6 +3452,12 @@ ${children}`;
|
|
|
2968
3452
|
if (parsed.kind === "conditional" || parsed.kind === "template-literal") {
|
|
2969
3453
|
return `${name}="${this.renderParsedExpr(parsed)}"`;
|
|
2970
3454
|
}
|
|
3455
|
+
const bareId = value.expr.trim();
|
|
3456
|
+
const propName = this.propsObjectName && bareId.startsWith(`${this.propsObjectName}.`) ? bareId.slice(this.propsObjectName.length + 1) : bareId;
|
|
3457
|
+
if (/^[A-Za-z_$][\w$]*$/.test(propName) && this.nillablePropNames.has(propName)) {
|
|
3458
|
+
const field = `.${this.capitalizeFieldName(propName)}`;
|
|
3459
|
+
return `{{if ne ${field} nil}}${name}="{{${this.convertExpressionToGo(value.expr)}}}"{{end}}`;
|
|
3460
|
+
}
|
|
2971
3461
|
return `${name}="{{${this.convertExpressionToGo(value.expr)}}}"`;
|
|
2972
3462
|
},
|
|
2973
3463
|
emitBooleanAttr: (_value, name) => name,
|