@barefootjs/go-template 0.13.0 → 0.15.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 +295 -9
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +1282 -137
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +1283 -136
- package/dist/index.js +1282 -137
- package/dist/test-render.d.ts +8 -0
- package/dist/test-render.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/__tests__/build.test.ts +40 -1
- package/src/__tests__/derived-state-memo.test.ts +371 -0
- package/src/__tests__/go-template-adapter.test.ts +267 -7
- package/src/adapter/go-template-adapter.ts +2205 -188
- package/src/build.ts +4 -0
- package/src/test-render.ts +109 -27
package/dist/build.js
CHANGED
|
@@ -377,9 +377,12 @@ import {
|
|
|
377
377
|
augmentInheritedPropAccesses,
|
|
378
378
|
parseRecordIndexAccess,
|
|
379
379
|
collectContextConsumers,
|
|
380
|
-
isLowerableObjectRestDestructure
|
|
380
|
+
isLowerableObjectRestDestructure,
|
|
381
|
+
collectModuleStringConsts as collectModuleStringConstsShared,
|
|
382
|
+
searchParamsLocalNames
|
|
381
383
|
} from "@barefootjs/jsx";
|
|
382
384
|
import { findInterpolationEnd } from "@barefootjs/jsx/scanner";
|
|
385
|
+
import { BF_REGION } from "@barefootjs/shared";
|
|
383
386
|
function wrapIfMultiToken(rendered) {
|
|
384
387
|
if (rendered.startsWith("(") && rendered.endsWith(")"))
|
|
385
388
|
return rendered;
|
|
@@ -467,13 +470,29 @@ function buildUnsupportedSuggestion(support) {
|
|
|
467
470
|
|
|
468
471
|
${GO_REMEDIATION_OPTIONS}`;
|
|
469
472
|
}
|
|
473
|
+
function wrapGoArg(arg) {
|
|
474
|
+
if (!/\s/.test(arg))
|
|
475
|
+
return arg;
|
|
476
|
+
if (arg.startsWith("(") && arg.endsWith(")"))
|
|
477
|
+
return arg;
|
|
478
|
+
return `(${arg})`;
|
|
479
|
+
}
|
|
480
|
+
function stringTolerantEqOperands(l, r) {
|
|
481
|
+
const isStrLit = (x) => /^"(?:[^"\\]|\\.)*"$/.test(x);
|
|
482
|
+
if (isStrLit(l) === isStrLit(r))
|
|
483
|
+
return [l, r];
|
|
484
|
+
const wrap = (x) => isStrLit(x) ? x : `(bf_string ${wrapGoArg(x)})`;
|
|
485
|
+
return [wrap(l), wrap(r)];
|
|
486
|
+
}
|
|
470
487
|
var GO_TEMPLATE_PRIMITIVES = {
|
|
471
|
-
"JSON.stringify": { arity: 1, emit: (args) => `bf_json ${args[0]}` },
|
|
472
|
-
String: { arity: 1, emit: (args) => `bf_string ${args[0]}` },
|
|
473
|
-
Number: { arity: 1, emit: (args) => `bf_number ${args[0]}` },
|
|
474
|
-
"Math.floor": { arity: 1, emit: (args) => `bf_floor ${args[0]}` },
|
|
475
|
-
"Math.ceil": { arity: 1, emit: (args) => `bf_ceil ${args[0]}` },
|
|
476
|
-
"Math.round": { arity: 1, emit: (args) => `bf_round ${args[0]}` }
|
|
488
|
+
"JSON.stringify": { arity: 1, emit: (args) => `bf_json ${wrapGoArg(args[0])}` },
|
|
489
|
+
String: { arity: 1, emit: (args) => `bf_string ${wrapGoArg(args[0])}` },
|
|
490
|
+
Number: { arity: 1, emit: (args) => `bf_number ${wrapGoArg(args[0])}` },
|
|
491
|
+
"Math.floor": { arity: 1, emit: (args) => `bf_floor ${wrapGoArg(args[0])}` },
|
|
492
|
+
"Math.ceil": { arity: 1, emit: (args) => `bf_ceil ${wrapGoArg(args[0])}` },
|
|
493
|
+
"Math.round": { arity: 1, emit: (args) => `bf_round ${wrapGoArg(args[0])}` },
|
|
494
|
+
"Math.min": { arity: 2, emit: (args) => `bf_min ${wrapGoArg(args[0])} ${wrapGoArg(args[1])}` },
|
|
495
|
+
"Math.max": { arity: 2, emit: (args) => `bf_max ${wrapGoArg(args[0])} ${wrapGoArg(args[1])}` }
|
|
477
496
|
};
|
|
478
497
|
|
|
479
498
|
class GoTemplateAdapter extends BaseAdapter {
|
|
@@ -488,10 +507,12 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
488
507
|
componentName = "";
|
|
489
508
|
options;
|
|
490
509
|
inLoop = false;
|
|
510
|
+
pendingChildrenDefines = [];
|
|
491
511
|
loopParamStack = [];
|
|
492
512
|
loopVarRefCount = new Map;
|
|
493
513
|
loopBindingStack = [];
|
|
494
514
|
errors = [];
|
|
515
|
+
currentMemos = [];
|
|
495
516
|
propsObjectName = null;
|
|
496
517
|
restPropsName = null;
|
|
497
518
|
templateVarCounter = 0;
|
|
@@ -499,6 +520,7 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
499
520
|
localTypeAliases = new Map;
|
|
500
521
|
localStructFields = new Map;
|
|
501
522
|
synthStructTypes = new Map;
|
|
523
|
+
currentTypeDefinitions = [];
|
|
502
524
|
usesHtmlTemplate = false;
|
|
503
525
|
rootScopeNodes = new Set;
|
|
504
526
|
usesFmt = false;
|
|
@@ -506,6 +528,11 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
506
528
|
moduleStringConsts = new Map;
|
|
507
529
|
localConstants = [];
|
|
508
530
|
contextConsumers = [];
|
|
531
|
+
searchParamsLocals = new Set;
|
|
532
|
+
localHelperNames = new Set;
|
|
533
|
+
needsStringsImport = false;
|
|
534
|
+
referencedDerivedConsts = new Set;
|
|
535
|
+
memoBackedLoopSlice = new Map;
|
|
509
536
|
childContextConsumers = new Map;
|
|
510
537
|
nillablePropNames = new Set;
|
|
511
538
|
constructor(options = {}) {
|
|
@@ -519,12 +546,18 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
519
546
|
generate(ir, options) {
|
|
520
547
|
this.componentName = ir.metadata.componentName;
|
|
521
548
|
this.errors = [];
|
|
549
|
+
this.referencedDerivedConsts = new Set;
|
|
522
550
|
this.templateVarCounter = 0;
|
|
551
|
+
this.pendingChildrenDefines = [];
|
|
523
552
|
this.propsObjectName = ir.metadata.propsObjectName;
|
|
524
553
|
this.restPropsName = ir.metadata.restPropsName ?? null;
|
|
525
554
|
this.moduleStringConsts = this.collectModuleStringConsts(ir.metadata.localConstants);
|
|
526
555
|
this.localConstants = ir.metadata.localConstants ?? [];
|
|
556
|
+
this.localHelperNames = new Set(this.localConstants.filter((c) => !c.isModule && c.containsArrow).map((c) => c.name));
|
|
557
|
+
this.currentMemos = ir.metadata.memos ?? [];
|
|
558
|
+
this.currentTypeDefinitions = ir.metadata.typeDefinitions ?? [];
|
|
527
559
|
this.contextConsumers = collectContextConsumers(ir.metadata);
|
|
560
|
+
this.searchParamsLocals = searchParamsLocalNames(ir.metadata);
|
|
528
561
|
augmentInheritedPropAccesses(ir);
|
|
529
562
|
this.nillablePropNames = this.collectNillablePropNames(ir);
|
|
530
563
|
if (!options?.siblingTemplatesRegistered) {
|
|
@@ -534,12 +567,22 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
534
567
|
const isRootComponent = ir.root.type === "component";
|
|
535
568
|
const isIfStatement = ir.root.type === "if-statement";
|
|
536
569
|
this.rootScopeNodes = collectRootScopeNodes(ir.root);
|
|
570
|
+
this.memoBackedLoopSlice = new Map;
|
|
571
|
+
for (const nested of this.findNestedComponents(ir.root)) {
|
|
572
|
+
const memoName = this.extractMemoNameFromLoopArray(nested.loopArray);
|
|
573
|
+
if (memoName)
|
|
574
|
+
this.memoBackedLoopSlice.set(memoName, `${nested.name}s`);
|
|
575
|
+
}
|
|
537
576
|
const templateBody = isIfStatement ? this.renderIfStatement(ir.root, { isRootOfClientComponent: hasInteractivity }) : this.renderNode(ir.root, { isRootOfClientComponent: hasInteractivity && isRootComponent });
|
|
538
577
|
const scriptRegistrations = options?.skipScriptRegistration ? "" : this.generateScriptRegistrations(ir, options?.scriptBaseName);
|
|
539
|
-
|
|
578
|
+
let template = `{{define "${this.componentName}"}}
|
|
540
579
|
${scriptRegistrations}${templateBody}
|
|
541
580
|
{{end}}
|
|
542
581
|
`;
|
|
582
|
+
for (const d of this.pendingChildrenDefines) {
|
|
583
|
+
template += `{{define "${d.name}"}}${d.content}{{end}}
|
|
584
|
+
`;
|
|
585
|
+
}
|
|
543
586
|
const types = this.generateTypes(ir);
|
|
544
587
|
if (this.errors.length > 0) {
|
|
545
588
|
ir.errors.push(...this.errors);
|
|
@@ -776,10 +819,15 @@ ${scriptRegistrations}${templateBody}
|
|
|
776
819
|
this.usesHtmlTemplate = false;
|
|
777
820
|
this.usesFmt = false;
|
|
778
821
|
this.propsObjectName = ir.metadata.propsObjectName;
|
|
822
|
+
this.restPropsName = ir.metadata.restPropsName ?? null;
|
|
779
823
|
augmentInheritedPropAccesses(ir);
|
|
780
824
|
this.moduleStringConsts = this.collectModuleStringConsts(ir.metadata.localConstants);
|
|
781
825
|
this.localConstants = ir.metadata.localConstants ?? [];
|
|
826
|
+
this.localHelperNames = new Set(this.localConstants.filter((c) => !c.isModule && c.containsArrow).map((c) => c.name));
|
|
827
|
+
this.currentMemos = ir.metadata.memos ?? [];
|
|
828
|
+
this.currentTypeDefinitions = ir.metadata.typeDefinitions ?? [];
|
|
782
829
|
this.contextConsumers = collectContextConsumers(ir.metadata);
|
|
830
|
+
this.searchParamsLocals = searchParamsLocalNames(ir.metadata);
|
|
783
831
|
const lines = [];
|
|
784
832
|
const componentName = ir.metadata.componentName;
|
|
785
833
|
this.localTypeNames = new Set;
|
|
@@ -832,11 +880,39 @@ ${goFields.join(`
|
|
|
832
880
|
lines.push("");
|
|
833
881
|
}
|
|
834
882
|
const nestedComponents = this.findNestedComponents(ir.root);
|
|
883
|
+
for (const nested of nestedComponents) {
|
|
884
|
+
if (nested.loopItemType || !nested.loopArray)
|
|
885
|
+
continue;
|
|
886
|
+
const memoName = this.extractMemoNameFromLoopArray(nested.loopArray);
|
|
887
|
+
if (memoName) {
|
|
888
|
+
const memo = ir.metadata.memos.find((m) => m.name === memoName);
|
|
889
|
+
if (memo) {
|
|
890
|
+
const blockReturn = this.resolveBlockBodyMemoModuleConst(memo.computation, ir.metadata.signals);
|
|
891
|
+
if (blockReturn) {
|
|
892
|
+
const constant = (ir.metadata.localConstants ?? []).find((c) => c.name === blockReturn.constName && c.origin?.scope === "module");
|
|
893
|
+
if (constant?.type?.elementType) {
|
|
894
|
+
nested.loopItemType = constant.type.elementType;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
const directConst = (ir.metadata.localConstants ?? []).find((c) => c.name === nested.loopArray && c.origin?.scope === "module");
|
|
901
|
+
if (directConst?.type?.elementType) {
|
|
902
|
+
nested.loopItemType = directConst.type.elementType;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
for (const nested of nestedComponents) {
|
|
906
|
+
if (!nested.bodyChildren || nested.bodyChildren.length === 0)
|
|
907
|
+
continue;
|
|
908
|
+
this.generateLoopBodyWrapperStruct(lines, componentName, nested);
|
|
909
|
+
}
|
|
835
910
|
const propTypeOverrides = this.buildPropTypeOverrides(ir);
|
|
836
911
|
const spreadSlots = this.collectSpreadSlots(ir.root);
|
|
837
912
|
this.generateInputStruct(lines, ir, componentName, nestedComponents, propTypeOverrides, spreadSlots);
|
|
913
|
+
this.needsStringsImport = false;
|
|
838
914
|
this.generatePropsStruct(lines, ir, componentName, nestedComponents, propTypeOverrides, spreadSlots);
|
|
839
|
-
this.generateNewPropsFunction(lines, ir, componentName, nestedComponents, spreadSlots);
|
|
915
|
+
this.generateNewPropsFunction(lines, ir, componentName, nestedComponents, spreadSlots, propTypeOverrides);
|
|
840
916
|
const header = [];
|
|
841
917
|
header.push(`package ${this.options.packageName}`);
|
|
842
918
|
header.push("");
|
|
@@ -846,6 +922,8 @@ ${goFields.join(`
|
|
|
846
922
|
if (this.usesHtmlTemplate)
|
|
847
923
|
header.push('\t"html/template"');
|
|
848
924
|
header.push('\t"math/rand"');
|
|
925
|
+
if (this.needsStringsImport)
|
|
926
|
+
header.push('\t"strings"');
|
|
849
927
|
header.push("");
|
|
850
928
|
header.push('\tbf "github.com/barefootjs/runtime/bf"');
|
|
851
929
|
header.push(")");
|
|
@@ -1016,6 +1094,20 @@ ${goFields.join(`
|
|
|
1016
1094
|
}
|
|
1017
1095
|
return nillable;
|
|
1018
1096
|
}
|
|
1097
|
+
usesSearchParams(ir) {
|
|
1098
|
+
if (this.searchParamsLocals.size === 0)
|
|
1099
|
+
return false;
|
|
1100
|
+
const taken = new Set([
|
|
1101
|
+
...ir.metadata.propsParams.map((p) => this.capitalizeFieldName(p.name)),
|
|
1102
|
+
...ir.metadata.signals.map((s) => this.capitalizeFieldName(s.getter)),
|
|
1103
|
+
...ir.metadata.memos.map((m) => this.capitalizeFieldName(m.name)),
|
|
1104
|
+
...this.contextConsumers.map((c) => this.contextFieldName(c))
|
|
1105
|
+
]);
|
|
1106
|
+
if (ir.metadata.restPropsName) {
|
|
1107
|
+
taken.add(this.capitalizeFieldName(ir.metadata.restPropsName));
|
|
1108
|
+
}
|
|
1109
|
+
return !taken.has("SearchParams");
|
|
1110
|
+
}
|
|
1019
1111
|
generateInputStruct(lines, ir, componentName, nestedComponents, propTypeOverrides, spreadSlots) {
|
|
1020
1112
|
const inputTypeName = `${componentName}Input`;
|
|
1021
1113
|
lines.push(`// ${inputTypeName} is the user-facing input type.`);
|
|
@@ -1023,6 +1115,9 @@ ${goFields.join(`
|
|
|
1023
1115
|
lines.push("\tScopeID string // Optional: if empty, random ID is generated");
|
|
1024
1116
|
lines.push("\tBfParent string // Optional: parent scope id");
|
|
1025
1117
|
lines.push("\tBfMount string // Optional: slot id in parent");
|
|
1118
|
+
if (this.usesSearchParams(ir)) {
|
|
1119
|
+
lines.push("\tSearchParams bf.SearchParams // Optional: request query for searchParams()");
|
|
1120
|
+
}
|
|
1026
1121
|
const inputNested = nestedComponents.filter((n) => !n.isDynamic || n.isPropDerived);
|
|
1027
1122
|
const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
|
|
1028
1123
|
for (const param of ir.metadata.propsParams) {
|
|
@@ -1067,6 +1162,9 @@ ${goFields.join(`
|
|
|
1067
1162
|
lines.push('\tBfMount string `json:"-"`');
|
|
1068
1163
|
lines.push('\tBfDataKey string `json:"-"`');
|
|
1069
1164
|
lines.push('\tScripts *bf.ScriptCollector `json:"-"`');
|
|
1165
|
+
if (this.usesSearchParams(ir)) {
|
|
1166
|
+
lines.push('\tSearchParams bf.SearchParams `json:"-"`');
|
|
1167
|
+
}
|
|
1070
1168
|
const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
|
|
1071
1169
|
const propFieldNames = new Set;
|
|
1072
1170
|
for (const param of ir.metadata.propsParams) {
|
|
@@ -1113,10 +1211,20 @@ ${goFields.join(`
|
|
|
1113
1211
|
}
|
|
1114
1212
|
for (const memo of ir.metadata.memos) {
|
|
1115
1213
|
const fieldName = this.capitalizeFieldName(memo.name);
|
|
1214
|
+
if (propFieldNames.has(fieldName))
|
|
1215
|
+
continue;
|
|
1116
1216
|
const jsonTag = this.toJsonTag(memo.name);
|
|
1117
1217
|
const goType = this.inferMemoType(memo, ir.metadata.signals, propsParamMap);
|
|
1118
1218
|
lines.push(` ${fieldName} ${goType} \`json:"${jsonTag}"\``);
|
|
1119
1219
|
}
|
|
1220
|
+
const takenForDerivedConsts = new Set([
|
|
1221
|
+
...ir.metadata.propsParams.map((p) => this.capitalizeFieldName(p.name)),
|
|
1222
|
+
...ir.metadata.signals.map((s) => this.capitalizeFieldName(s.getter)),
|
|
1223
|
+
...ir.metadata.memos.map((m) => this.capitalizeFieldName(m.name))
|
|
1224
|
+
]);
|
|
1225
|
+
for (const f of this.computeDerivedConstFields(takenForDerivedConsts)) {
|
|
1226
|
+
lines.push(` ${f.name} string \`json:"-"\``);
|
|
1227
|
+
}
|
|
1120
1228
|
const takenProps = new Set([
|
|
1121
1229
|
...ir.metadata.propsParams.map((p) => this.capitalizeFieldName(p.name)),
|
|
1122
1230
|
...ir.metadata.signals.map((s) => this.capitalizeFieldName(s.getter)),
|
|
@@ -1127,11 +1235,12 @@ ${goFields.join(`
|
|
|
1127
1235
|
lines.push(` ${this.contextFieldName(c)} ${this.contextConsumerGoType(c)} \`json:"${jsonTag}"\``);
|
|
1128
1236
|
}
|
|
1129
1237
|
for (const nested of nestedComponents) {
|
|
1238
|
+
const elemType = nested.bodyChildren?.length ? this.loopBodyWrapperName(componentName, nested) : `${nested.name}Props`;
|
|
1130
1239
|
if (nested.isDynamic && !nested.isPropDerived) {
|
|
1131
|
-
lines.push(` ${nested.name}s []${
|
|
1240
|
+
lines.push(` ${nested.name}s []${elemType} \`json:"-"\``);
|
|
1132
1241
|
} else {
|
|
1133
1242
|
const jsonTag = this.toJsonTag(`${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`);
|
|
1134
|
-
lines.push(` ${nested.name}s []${
|
|
1243
|
+
lines.push(` ${nested.name}s []${elemType} \`json:"${jsonTag}"\``);
|
|
1135
1244
|
}
|
|
1136
1245
|
}
|
|
1137
1246
|
const staticChildren = this.collectStaticChildInstances(ir.root);
|
|
@@ -1145,10 +1254,73 @@ ${goFields.join(`
|
|
|
1145
1254
|
lines.push("}");
|
|
1146
1255
|
lines.push("");
|
|
1147
1256
|
}
|
|
1148
|
-
|
|
1257
|
+
generateLoopBodyWrapperStruct(lines, parentComponentName, nested) {
|
|
1258
|
+
const wrapperName = this.loopBodyWrapperName(parentComponentName, nested);
|
|
1259
|
+
const datumFields = this.resolveLoopDatumFields(nested.loopItemType);
|
|
1260
|
+
const bodyChildInstances = this.collectBodyChildInstances(nested.bodyChildren);
|
|
1261
|
+
lines.push(`// ${wrapperName} wraps ${nested.name}Props with per-row loop datum`);
|
|
1262
|
+
lines.push(`// fields and child component slots for the loop body children. (#1897)`);
|
|
1263
|
+
lines.push(`type ${wrapperName} struct {`);
|
|
1264
|
+
lines.push(` ${nested.name}Props`);
|
|
1265
|
+
for (const f of datumFields) {
|
|
1266
|
+
lines.push(` ${f.goName} ${f.goType} \`json:"-"\``);
|
|
1267
|
+
}
|
|
1268
|
+
for (const child of bodyChildInstances) {
|
|
1269
|
+
lines.push(` ${child.fieldName} ${child.name}Props \`json:"-"\``);
|
|
1270
|
+
}
|
|
1271
|
+
lines.push("}");
|
|
1272
|
+
lines.push("");
|
|
1273
|
+
}
|
|
1274
|
+
extractMemoNameFromLoopArray(loopArray) {
|
|
1275
|
+
if (!loopArray)
|
|
1276
|
+
return null;
|
|
1277
|
+
const match = loopArray.match(/^(\w+)\(\)$/);
|
|
1278
|
+
return match ? match[1] : null;
|
|
1279
|
+
}
|
|
1280
|
+
loopBodyWrapperName(parentName, nested) {
|
|
1281
|
+
return `${parentName}${nested.name}L${nested.loopMarkerId ?? "0"}Ctx`;
|
|
1282
|
+
}
|
|
1283
|
+
resolveLoopDatumFields(itemType) {
|
|
1284
|
+
if (!itemType)
|
|
1285
|
+
return [];
|
|
1286
|
+
const typeName = itemType.raw?.replace(/\[\]$/, "") ?? itemType.raw;
|
|
1287
|
+
if (!typeName)
|
|
1288
|
+
return [];
|
|
1289
|
+
for (const td of this.currentTypeDefinitions) {
|
|
1290
|
+
if (td.name === typeName) {
|
|
1291
|
+
const fields = [];
|
|
1292
|
+
for (const prop of td.properties ?? []) {
|
|
1293
|
+
if (!GoTemplateAdapter.GO_IDENTIFIER.test(prop.name))
|
|
1294
|
+
continue;
|
|
1295
|
+
fields.push({
|
|
1296
|
+
tsName: prop.name,
|
|
1297
|
+
goName: this.capitalizeFieldName(prop.name),
|
|
1298
|
+
goType: this.typeInfoToGo(prop.type)
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
if (fields.length > 0)
|
|
1302
|
+
return fields;
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
const structFields = this.localStructFields.get(typeName);
|
|
1307
|
+
if (structFields) {
|
|
1308
|
+
return Array.from(structFields, ([tsName, goName]) => ({ tsName, goName, goType: "interface{}" }));
|
|
1309
|
+
}
|
|
1310
|
+
return [];
|
|
1311
|
+
}
|
|
1312
|
+
collectBodyChildInstances(bodyChildren) {
|
|
1313
|
+
const result = [];
|
|
1314
|
+
for (const child of bodyChildren) {
|
|
1315
|
+
this.collectStaticChildInstancesRecursive(child, result, false, new Map);
|
|
1316
|
+
}
|
|
1317
|
+
return result;
|
|
1318
|
+
}
|
|
1319
|
+
generateNewPropsFunction(lines, ir, componentName, nestedComponents, spreadSlots, propTypeOverrides) {
|
|
1149
1320
|
const inputTypeName = `${componentName}Input`;
|
|
1150
1321
|
const propsTypeName = `${componentName}Props`;
|
|
1151
|
-
const
|
|
1322
|
+
const dynamicWithBody = nestedComponents.filter((n) => n.isDynamic && !n.isPropDerived && n.bodyChildren && n.bodyChildren.length > 0);
|
|
1323
|
+
const signalDynamicNested = nestedComponents.filter((n) => n.isDynamic && !n.isPropDerived && !(n.bodyChildren && n.bodyChildren.length > 0));
|
|
1152
1324
|
lines.push(`// New${componentName}Props creates ${propsTypeName} from ${inputTypeName}.`);
|
|
1153
1325
|
for (const nested of signalDynamicNested) {
|
|
1154
1326
|
const arrayField = `${nested.name}s`;
|
|
@@ -1173,22 +1345,77 @@ ${goFields.join(`
|
|
|
1173
1345
|
lines.push("\t}");
|
|
1174
1346
|
lines.push("");
|
|
1175
1347
|
const staticNested = nestedComponents.filter((n) => !n.isDynamic || n.isPropDerived);
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1348
|
+
const emittedWrapperVars = new Set;
|
|
1349
|
+
const staticWithBody = staticNested.filter((n) => n.bodyChildren && n.bodyChildren.length > 0);
|
|
1350
|
+
const staticWithoutBody = staticNested.filter((n) => !n.bodyChildren || n.bodyChildren.length === 0);
|
|
1351
|
+
for (const nested of staticWithoutBody) {
|
|
1352
|
+
const varName = `${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`;
|
|
1353
|
+
lines.push(` ${varName} := make([]${nested.name}Props, len(in.${nested.name}s))`);
|
|
1354
|
+
lines.push(` for i, item := range in.${nested.name}s {`);
|
|
1355
|
+
lines.push(` ${varName}[i] = New${nested.name}Props(item)`);
|
|
1356
|
+
lines.push(` ${varName}[i].BfParent = scopeID`);
|
|
1357
|
+
lines.push(` ${varName}[i].BfMount = "${nested.slotId}"`);
|
|
1358
|
+
const keyField = loopKeyToGoFieldPath(nested.loopKey, nested.loopParam);
|
|
1359
|
+
if (keyField) {
|
|
1360
|
+
lines.push(` ${varName}[i].BfDataKey = fmt.Sprint(${keyField})`);
|
|
1361
|
+
this.usesFmt = true;
|
|
1362
|
+
}
|
|
1363
|
+
lines.push("\t}");
|
|
1364
|
+
lines.push("");
|
|
1365
|
+
}
|
|
1366
|
+
for (const nested of staticWithBody) {
|
|
1367
|
+
const loopArray = nested.loopArray;
|
|
1368
|
+
const moduleConst = loopArray ? (ir.metadata.localConstants ?? []).find((c) => c.name === loopArray && c.origin?.scope === "module" && c.value && c.type) : null;
|
|
1369
|
+
const bakedValue = moduleConst?.type ? this.convertInitialValue(moduleConst.value, moduleConst.type, ir.metadata.propsParams) : null;
|
|
1370
|
+
if (!bakedValue || bakedValue === "nil" || bakedValue === "0")
|
|
1371
|
+
continue;
|
|
1372
|
+
const varName = `${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`;
|
|
1373
|
+
const wrapperType = this.loopBodyWrapperName(componentName, nested);
|
|
1374
|
+
const datumFields = this.resolveLoopDatumFields(nested.loopItemType);
|
|
1375
|
+
const bodyChildInstances = this.collectBodyChildInstances(nested.bodyChildren);
|
|
1376
|
+
for (const child of bodyChildInstances) {
|
|
1377
|
+
const childVar = `child_${child.fieldName}`;
|
|
1378
|
+
lines.push(` ${childVar} := New${child.name}Props(${child.name}Input{`);
|
|
1379
|
+
lines.push(` ScopeID: scopeID + "_${child.slotId}",`);
|
|
1380
|
+
lines.push(` BfParent: scopeID,`);
|
|
1381
|
+
lines.push(` BfMount: "${child.slotId}",`);
|
|
1382
|
+
for (const prop of child.props) {
|
|
1383
|
+
if (prop.value.kind === "literal") {
|
|
1384
|
+
lines.push(` ${this.capitalizeFieldName(prop.name)}: ${this.goLiteral(prop.value.value)},`);
|
|
1385
|
+
} else if (prop.value.kind === "boolean-shorthand" || prop.value.kind === "boolean-attr") {
|
|
1386
|
+
lines.push(` ${this.capitalizeFieldName(prop.name)}: true,`);
|
|
1387
|
+
}
|
|
1188
1388
|
}
|
|
1189
|
-
lines.push(
|
|
1190
|
-
lines.push("");
|
|
1389
|
+
lines.push(` })`);
|
|
1191
1390
|
}
|
|
1391
|
+
if (bodyChildInstances.length > 0)
|
|
1392
|
+
lines.push("");
|
|
1393
|
+
const dataVar = `${varName}Data`;
|
|
1394
|
+
lines.push(` ${dataVar} := ${bakedValue}`);
|
|
1395
|
+
lines.push(` ${varName} := make([]${wrapperType}, len(${dataVar}))`);
|
|
1396
|
+
lines.push(` for i, item := range ${dataVar} {`);
|
|
1397
|
+
lines.push(` ${varName}[i] = ${wrapperType}{`);
|
|
1398
|
+
lines.push(` ${nested.name}Props: New${nested.name}Props(${nested.name}Input{`);
|
|
1399
|
+
lines.push(` BfParent: scopeID,`);
|
|
1400
|
+
lines.push(` BfMount: "${nested.slotId}",`);
|
|
1401
|
+
lines.push(` }),`);
|
|
1402
|
+
for (const f of datumFields) {
|
|
1403
|
+
lines.push(` ${f.goName}: item.${f.goName},`);
|
|
1404
|
+
}
|
|
1405
|
+
for (const child of bodyChildInstances) {
|
|
1406
|
+
lines.push(` ${child.fieldName}: child_${child.fieldName},`);
|
|
1407
|
+
}
|
|
1408
|
+
lines.push(` }`);
|
|
1409
|
+
lines.push(` ${varName}[i].BfParent = scopeID`);
|
|
1410
|
+
lines.push(` ${varName}[i].BfMount = "${nested.slotId}"`);
|
|
1411
|
+
const keyField = loopKeyToGoFieldPath(nested.loopKey, nested.loopParam);
|
|
1412
|
+
if (keyField) {
|
|
1413
|
+
lines.push(` ${varName}[i].BfDataKey = fmt.Sprint(${keyField})`);
|
|
1414
|
+
this.usesFmt = true;
|
|
1415
|
+
}
|
|
1416
|
+
lines.push("\t}");
|
|
1417
|
+
lines.push("");
|
|
1418
|
+
emittedWrapperVars.add(varName);
|
|
1192
1419
|
}
|
|
1193
1420
|
const propFallbackVars = this.collectPropFallbackVars(ir);
|
|
1194
1421
|
for (const [, info] of propFallbackVars) {
|
|
@@ -1199,11 +1426,87 @@ ${goFields.join(`
|
|
|
1199
1426
|
}
|
|
1200
1427
|
if (propFallbackVars.size > 0)
|
|
1201
1428
|
lines.push("");
|
|
1429
|
+
const propsParamMap = new Map(ir.metadata.propsParams.map((p) => [p.name, p]));
|
|
1430
|
+
for (const nested of dynamicWithBody) {
|
|
1431
|
+
const memoName = this.extractMemoNameFromLoopArray(nested.loopArray);
|
|
1432
|
+
if (!memoName)
|
|
1433
|
+
continue;
|
|
1434
|
+
const memo = ir.metadata.memos.find((m) => m.name === memoName);
|
|
1435
|
+
if (!memo)
|
|
1436
|
+
continue;
|
|
1437
|
+
const goType = this.inferMemoType(memo, ir.metadata.signals, propsParamMap);
|
|
1438
|
+
const bakedValue = this.computeMemoInitialValue(memo, ir.metadata.signals, ir.metadata.propsParams, propFallbackVars, goType);
|
|
1439
|
+
if (bakedValue === "nil" || bakedValue === "0")
|
|
1440
|
+
continue;
|
|
1441
|
+
const wrapperType = this.loopBodyWrapperName(componentName, nested);
|
|
1442
|
+
const varName = `${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`;
|
|
1443
|
+
const datumFields = this.resolveLoopDatumFields(nested.loopItemType);
|
|
1444
|
+
const bodyChildInstances = this.collectBodyChildInstances(nested.bodyChildren);
|
|
1445
|
+
for (const child of bodyChildInstances) {
|
|
1446
|
+
const childVar = `child_${child.fieldName}`;
|
|
1447
|
+
lines.push(` ${childVar} := New${child.name}Props(${child.name}Input{`);
|
|
1448
|
+
lines.push(` ScopeID: scopeID + "_${child.slotId}",`);
|
|
1449
|
+
lines.push(` BfParent: scopeID,`);
|
|
1450
|
+
lines.push(` BfMount: "${child.slotId}",`);
|
|
1451
|
+
for (const prop of child.props) {
|
|
1452
|
+
if (prop.value.kind === "literal") {
|
|
1453
|
+
lines.push(` ${this.capitalizeFieldName(prop.name)}: ${this.goLiteral(prop.value.value)},`);
|
|
1454
|
+
} else if (prop.value.kind === "boolean-shorthand" || prop.value.kind === "boolean-attr") {
|
|
1455
|
+
lines.push(` ${this.capitalizeFieldName(prop.name)}: true,`);
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
lines.push(` })`);
|
|
1459
|
+
}
|
|
1460
|
+
if (bodyChildInstances.length > 0)
|
|
1461
|
+
lines.push("");
|
|
1462
|
+
lines.push(` bakedData := ${bakedValue}`);
|
|
1463
|
+
lines.push(` ${varName} := make([]${wrapperType}, len(bakedData))`);
|
|
1464
|
+
lines.push(` for i, item := range bakedData {`);
|
|
1465
|
+
lines.push(` ${varName}[i] = ${wrapperType}{`);
|
|
1466
|
+
lines.push(` ${nested.name}Props: New${nested.name}Props(${nested.name}Input{`);
|
|
1467
|
+
lines.push(` BfParent: scopeID,`);
|
|
1468
|
+
lines.push(` BfMount: "${nested.slotId}",`);
|
|
1469
|
+
lines.push(` }),`);
|
|
1470
|
+
for (const f of datumFields) {
|
|
1471
|
+
lines.push(` ${f.goName}: item.${f.goName},`);
|
|
1472
|
+
}
|
|
1473
|
+
for (const child of bodyChildInstances) {
|
|
1474
|
+
lines.push(` ${child.fieldName}: child_${child.fieldName},`);
|
|
1475
|
+
}
|
|
1476
|
+
lines.push(` }`);
|
|
1477
|
+
const keyField = loopKeyToGoFieldPath(nested.loopKey, nested.loopParam);
|
|
1478
|
+
if (keyField) {
|
|
1479
|
+
lines.push(` ${varName}[i].BfDataKey = fmt.Sprint(${keyField})`);
|
|
1480
|
+
this.usesFmt = true;
|
|
1481
|
+
}
|
|
1482
|
+
lines.push(` }`);
|
|
1483
|
+
lines.push("");
|
|
1484
|
+
emittedWrapperVars.add(varName);
|
|
1485
|
+
}
|
|
1202
1486
|
lines.push(` return ${propsTypeName}{`);
|
|
1203
1487
|
lines.push("\t\tScopeID: scopeID,");
|
|
1204
1488
|
lines.push("\t\tBfParent: in.BfParent,");
|
|
1205
1489
|
lines.push("\t\tBfMount: in.BfMount,");
|
|
1490
|
+
if (this.usesSearchParams(ir)) {
|
|
1491
|
+
lines.push("\t\tSearchParams: in.SearchParams,");
|
|
1492
|
+
}
|
|
1206
1493
|
const nestedArrayFields = new Set(nestedComponents.map((n) => `${n.name}s`));
|
|
1494
|
+
const memoFallbacks = new Map;
|
|
1495
|
+
for (const memo of ir.metadata.memos) {
|
|
1496
|
+
const stripped = memo.computation.replace(/^\(\)\s*=>\s*/, "");
|
|
1497
|
+
const m = this.extractPropFallback(stripped);
|
|
1498
|
+
if (!m)
|
|
1499
|
+
continue;
|
|
1500
|
+
if (this.capitalizeFieldName(m.propName) !== this.capitalizeFieldName(memo.name))
|
|
1501
|
+
continue;
|
|
1502
|
+
const param = ir.metadata.propsParams.find((p) => this.capitalizeFieldName(p.name) === this.capitalizeFieldName(memo.name));
|
|
1503
|
+
if (!param)
|
|
1504
|
+
continue;
|
|
1505
|
+
const goType = this.resolvePropGoType(param, propTypeOverrides);
|
|
1506
|
+
if (goType !== "string" && goType !== "interface{}")
|
|
1507
|
+
continue;
|
|
1508
|
+
memoFallbacks.set(this.capitalizeFieldName(memo.name), { goFallback: m.goFallback, goType });
|
|
1509
|
+
}
|
|
1207
1510
|
const propFieldNames = new Set;
|
|
1208
1511
|
for (const param of ir.metadata.propsParams) {
|
|
1209
1512
|
const fieldName = this.capitalizeFieldName(param.name);
|
|
@@ -1213,9 +1516,14 @@ ${goFields.join(`
|
|
|
1213
1516
|
if (hoisted) {
|
|
1214
1517
|
lines.push(` ${fieldName}: ${hoisted.varName},`);
|
|
1215
1518
|
} else {
|
|
1216
|
-
const
|
|
1217
|
-
|
|
1218
|
-
|
|
1519
|
+
const paramDefault = this.goPropDefault(param.defaultValue);
|
|
1520
|
+
const memoFold = memoFallbacks.get(fieldName);
|
|
1521
|
+
if (paramDefault !== null) {
|
|
1522
|
+
lines.push(` ${fieldName}: ${this.applyGoFallback(`in.${fieldName}`, paramDefault)},`);
|
|
1523
|
+
} else if (memoFold !== undefined && memoFold.goType === "string") {
|
|
1524
|
+
lines.push(` ${fieldName}: ${this.applyGoFallback(`in.${fieldName}`, memoFold.goFallback)},`);
|
|
1525
|
+
} else if (memoFold !== undefined) {
|
|
1526
|
+
lines.push(` ${fieldName}: func() interface{} { v := interface{}(in.${fieldName}); if v == nil || v == "" { return ${memoFold.goFallback} }; return v }(),`);
|
|
1219
1527
|
} else {
|
|
1220
1528
|
lines.push(` ${fieldName}: in.${fieldName},`);
|
|
1221
1529
|
}
|
|
@@ -1236,17 +1544,33 @@ ${goFields.join(`
|
|
|
1236
1544
|
lines.push(` ${fieldName}: ${initialValue},`);
|
|
1237
1545
|
}
|
|
1238
1546
|
}
|
|
1239
|
-
for (const nested of
|
|
1547
|
+
for (const nested of staticWithoutBody) {
|
|
1548
|
+
const varName = `${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`;
|
|
1549
|
+
lines.push(` ${nested.name}s: ${varName},`);
|
|
1550
|
+
}
|
|
1551
|
+
for (const nested of [...staticWithBody, ...dynamicWithBody]) {
|
|
1240
1552
|
const varName = `${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`;
|
|
1553
|
+
if (!emittedWrapperVars.has(varName))
|
|
1554
|
+
continue;
|
|
1241
1555
|
lines.push(` ${nested.name}s: ${varName},`);
|
|
1242
1556
|
}
|
|
1243
1557
|
const memoPropsParamMap = new Map(ir.metadata.propsParams.map((p) => [p.name, p]));
|
|
1244
1558
|
for (const memo of ir.metadata.memos) {
|
|
1245
1559
|
const fieldName = this.capitalizeFieldName(memo.name);
|
|
1560
|
+
if (propFieldNames.has(fieldName))
|
|
1561
|
+
continue;
|
|
1246
1562
|
const goType = this.inferMemoType(memo, ir.metadata.signals, memoPropsParamMap);
|
|
1247
1563
|
const memoValue = this.computeMemoInitialValue(memo, ir.metadata.signals, ir.metadata.propsParams, propFallbackVars, goType);
|
|
1248
1564
|
lines.push(` ${fieldName}: ${memoValue},`);
|
|
1249
1565
|
}
|
|
1566
|
+
const takenDerivedInit = new Set([
|
|
1567
|
+
...ir.metadata.propsParams.map((p) => this.capitalizeFieldName(p.name)),
|
|
1568
|
+
...ir.metadata.signals.map((s) => this.capitalizeFieldName(s.getter)),
|
|
1569
|
+
...ir.metadata.memos.map((m) => this.capitalizeFieldName(m.name))
|
|
1570
|
+
]);
|
|
1571
|
+
for (const f of this.computeDerivedConstFields(takenDerivedInit)) {
|
|
1572
|
+
lines.push(` ${f.name}: ${f.init},`);
|
|
1573
|
+
}
|
|
1250
1574
|
const takenInit = new Set([
|
|
1251
1575
|
...ir.metadata.propsParams.map((p) => this.capitalizeFieldName(p.name)),
|
|
1252
1576
|
...ir.metadata.signals.map((s) => this.capitalizeFieldName(s.getter)),
|
|
@@ -1279,6 +1603,8 @@ ${goFields.join(`
|
|
|
1279
1603
|
restBagEntries.push(`${JSON.stringify(jsxName)}: ${goValue}`);
|
|
1280
1604
|
return;
|
|
1281
1605
|
}
|
|
1606
|
+
if (jsxName.includes("-"))
|
|
1607
|
+
return;
|
|
1282
1608
|
lines.push(` ${this.capitalizeFieldName(jsxName)}: ${goValue},`);
|
|
1283
1609
|
};
|
|
1284
1610
|
for (const prop of child.props) {
|
|
@@ -1360,12 +1686,17 @@ ${goFields.join(`
|
|
|
1360
1686
|
const loop = node;
|
|
1361
1687
|
if (loop.childComponent) {
|
|
1362
1688
|
if (!result.some((c) => c.name === loop.childComponent.name)) {
|
|
1689
|
+
const hasBodyChildren = loop.childComponent.children.length > 0;
|
|
1363
1690
|
result.push({
|
|
1364
1691
|
...loop.childComponent,
|
|
1365
1692
|
isDynamic: !loop.isStaticArray,
|
|
1366
1693
|
isPropDerived: !!loop.isPropDerivedArray,
|
|
1367
1694
|
loopKey: loop.key ?? undefined,
|
|
1368
|
-
loopParam: loop.param ?? undefined
|
|
1695
|
+
loopParam: loop.param ?? undefined,
|
|
1696
|
+
bodyChildren: hasBodyChildren ? loop.childComponent.children : undefined,
|
|
1697
|
+
loopArray: loop.array,
|
|
1698
|
+
loopMarkerId: loop.markerId,
|
|
1699
|
+
loopItemType: loop.itemType
|
|
1369
1700
|
});
|
|
1370
1701
|
}
|
|
1371
1702
|
}
|
|
@@ -1388,6 +1719,17 @@ ${goFields.join(`
|
|
|
1388
1719
|
if (cond.whenFalse) {
|
|
1389
1720
|
this.collectNestedComponents(cond.whenFalse, result);
|
|
1390
1721
|
}
|
|
1722
|
+
} else if (node.type === "if-statement") {
|
|
1723
|
+
const stmt = node;
|
|
1724
|
+
this.collectNestedComponents(stmt.consequent, result);
|
|
1725
|
+
if (stmt.alternate) {
|
|
1726
|
+
this.collectNestedComponents(stmt.alternate, result);
|
|
1727
|
+
}
|
|
1728
|
+
} else if (node.type === "component") {
|
|
1729
|
+
const comp = node;
|
|
1730
|
+
for (const child of comp.children) {
|
|
1731
|
+
this.collectNestedComponents(child, result);
|
|
1732
|
+
}
|
|
1391
1733
|
}
|
|
1392
1734
|
}
|
|
1393
1735
|
collectStaticChildInstances(node) {
|
|
@@ -1459,6 +1801,9 @@ ${goFields.join(`
|
|
|
1459
1801
|
childrenScopedHtmlExpr: this.extractScopedHtmlChildren(effectiveChildren),
|
|
1460
1802
|
contextBindings: providerCtx.size > 0 ? providerCtx : undefined
|
|
1461
1803
|
});
|
|
1804
|
+
for (const child of effectiveChildren) {
|
|
1805
|
+
this.collectStaticChildInstancesRecursive(child, result, inLoop, providerCtx);
|
|
1806
|
+
}
|
|
1462
1807
|
}
|
|
1463
1808
|
if (comp.name === "Portal" && comp.children) {
|
|
1464
1809
|
for (const child of comp.children) {
|
|
@@ -1486,6 +1831,12 @@ ${goFields.join(`
|
|
|
1486
1831
|
if (cond.whenFalse) {
|
|
1487
1832
|
this.collectStaticChildInstancesRecursive(cond.whenFalse, result, inLoop, providerCtx);
|
|
1488
1833
|
}
|
|
1834
|
+
} else if (node.type === "if-statement") {
|
|
1835
|
+
const stmt = node;
|
|
1836
|
+
this.collectStaticChildInstancesRecursive(stmt.consequent, result, inLoop, providerCtx);
|
|
1837
|
+
if (stmt.alternate) {
|
|
1838
|
+
this.collectStaticChildInstancesRecursive(stmt.alternate, result, inLoop, providerCtx);
|
|
1839
|
+
}
|
|
1489
1840
|
} else if (node.type === "provider") {
|
|
1490
1841
|
const p = node;
|
|
1491
1842
|
const childCtx = this.extendProviderContext(providerCtx, p);
|
|
@@ -1993,8 +2344,8 @@ ${goFields.join(`
|
|
|
1993
2344
|
}
|
|
1994
2345
|
const lines = [];
|
|
1995
2346
|
lines.push("func() string {");
|
|
1996
|
-
|
|
1997
|
-
lines.push(
|
|
2347
|
+
this.usesFmt = true;
|
|
2348
|
+
lines.push(` switch fmt.Sprint(in.${fieldName}) {`);
|
|
1998
2349
|
for (const [k, v] of caseEntries) {
|
|
1999
2350
|
lines.push(` case ${JSON.stringify(k)}: return ${JSON.stringify(v)}`);
|
|
2000
2351
|
}
|
|
@@ -2012,6 +2363,22 @@ ${goFields.join(`
|
|
|
2012
2363
|
return segments.join(" + ");
|
|
2013
2364
|
}
|
|
2014
2365
|
resolveDynamicPropValue(expr, signals, memos, propsParams) {
|
|
2366
|
+
const cmpMatch = expr.match(/^(\w+)\(\)\s*([!=]==?)\s*(?:'([^']*)'|(-?\d+(?:\.\d+)?))\s*$/);
|
|
2367
|
+
if (cmpMatch) {
|
|
2368
|
+
const [, depName, op, strLit, numLit] = cmpMatch;
|
|
2369
|
+
const signal = signals.find((sg) => sg.getter === depName);
|
|
2370
|
+
const init = signal?.initialValue.trim();
|
|
2371
|
+
const initMatch = init !== undefined ? /^(?:'([^'\\]*)'|(-?\d+(?:\.\d+)?))$/.exec(init) : null;
|
|
2372
|
+
if (initMatch) {
|
|
2373
|
+
const initVal = initMatch[1] ?? initMatch[2];
|
|
2374
|
+
const litVal = strLit ?? numLit;
|
|
2375
|
+
const sameKind = initMatch[1] !== undefined === (strLit !== undefined);
|
|
2376
|
+
if (sameKind) {
|
|
2377
|
+
const equal = initVal === litVal;
|
|
2378
|
+
return String(op.startsWith("!") ? !equal : equal);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2015
2382
|
const getterMatch = expr.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\(\)$/);
|
|
2016
2383
|
if (getterMatch) {
|
|
2017
2384
|
const getterName = getterMatch[1];
|
|
@@ -2021,7 +2388,7 @@ ${goFields.join(`
|
|
|
2021
2388
|
}
|
|
2022
2389
|
const memo = memos.find((m) => m.name === getterName);
|
|
2023
2390
|
if (memo) {
|
|
2024
|
-
return this.
|
|
2391
|
+
return this.computeMemoInitialValueOrNull(memo, signals, propsParams);
|
|
2025
2392
|
}
|
|
2026
2393
|
}
|
|
2027
2394
|
return null;
|
|
@@ -2162,6 +2529,19 @@ ${goFields.join(`
|
|
|
2162
2529
|
return node.name.text;
|
|
2163
2530
|
}
|
|
2164
2531
|
computeMemoInitialValue(memo, signals, propsParams, propFallbackVars = GoTemplateAdapter.EMPTY_PROP_FALLBACK_VARS, goType) {
|
|
2532
|
+
const resolved = this.computeMemoInitialValueOrNull(memo, signals, propsParams, propFallbackVars);
|
|
2533
|
+
if (resolved !== null)
|
|
2534
|
+
return resolved;
|
|
2535
|
+
if (goType === "bool")
|
|
2536
|
+
return "false";
|
|
2537
|
+
if (goType === "string")
|
|
2538
|
+
return '""';
|
|
2539
|
+
if (goType !== undefined && (goType.startsWith("map[") || goType.startsWith("[]") || goType.startsWith("*") || goType.includes("interface{}") || goType === "any")) {
|
|
2540
|
+
return "nil";
|
|
2541
|
+
}
|
|
2542
|
+
return "0";
|
|
2543
|
+
}
|
|
2544
|
+
computeMemoInitialValueOrNull(memo, signals, propsParams, propFallbackVars = GoTemplateAdapter.EMPTY_PROP_FALLBACK_VARS) {
|
|
2165
2545
|
const computation = memo.computation;
|
|
2166
2546
|
const propRef = (propName) => {
|
|
2167
2547
|
const hoisted = propFallbackVars.get(propName);
|
|
@@ -2172,6 +2552,52 @@ ${goFields.join(`
|
|
|
2172
2552
|
const tmplMemo = this.computeTemplateLiteralMemoInitialValue(computation, propsParams);
|
|
2173
2553
|
if (tmplMemo !== null)
|
|
2174
2554
|
return tmplMemo;
|
|
2555
|
+
const eqMatch = computation.match(/^\(\)\s*=>\s*(\w+)\(\)\s*([!=]==?)\s*'([^']*)'\s*$/);
|
|
2556
|
+
if (eqMatch) {
|
|
2557
|
+
const [, depName, op, lit] = eqMatch;
|
|
2558
|
+
const signal = signals.find((sg) => sg.getter === depName);
|
|
2559
|
+
const initLit = signal ? /^'([^'\\]*)'$/.exec(signal.initialValue.trim()) : null;
|
|
2560
|
+
if (initLit) {
|
|
2561
|
+
const equal = initLit[1] === lit;
|
|
2562
|
+
return String(op.startsWith("!") ? !equal : equal);
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
const boolPassthrough = computation.match(/^\(\)\s*=>\s*props\.(\w+)\s*\?\?\s*false\s*$/);
|
|
2566
|
+
if (boolPassthrough) {
|
|
2567
|
+
return propRef(boolPassthrough[1]);
|
|
2568
|
+
}
|
|
2569
|
+
const ternMatch = computation.match(/^\(\)\s*=>\s*(\w+)\(\)\s*\?\s*([\w$]+|'[^']*')\s*:\s*([\w$]+|'[^']*')\s*$/);
|
|
2570
|
+
if (ternMatch) {
|
|
2571
|
+
const [, condName, whenTrue, whenFalse] = ternMatch;
|
|
2572
|
+
const resolveBranch = (b) => {
|
|
2573
|
+
const lit = /^'([^']*)'$/.exec(b);
|
|
2574
|
+
if (lit)
|
|
2575
|
+
return JSON.stringify(lit[1]);
|
|
2576
|
+
const constVal = this.moduleStringConsts.get(b);
|
|
2577
|
+
return constVal !== undefined ? JSON.stringify(constVal) : null;
|
|
2578
|
+
};
|
|
2579
|
+
const t = resolveBranch(whenTrue);
|
|
2580
|
+
const f = resolveBranch(whenFalse);
|
|
2581
|
+
if (t !== null && f !== null) {
|
|
2582
|
+
let condGo = null;
|
|
2583
|
+
const condSignal = signals.find((sg) => sg.getter === condName);
|
|
2584
|
+
if (condSignal) {
|
|
2585
|
+
condGo = this.getSignalInitialValueAsGo(condSignal.initialValue, propsParams, propFallbackVars);
|
|
2586
|
+
} else {
|
|
2587
|
+
const condMemo = (this.currentMemos ?? []).find((m) => m.name === condName);
|
|
2588
|
+
if (condMemo) {
|
|
2589
|
+
condGo = this.computeMemoInitialValueOrNull(condMemo, signals, propsParams, propFallbackVars);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
if (condGo === "true")
|
|
2593
|
+
return t;
|
|
2594
|
+
if (condGo === "false")
|
|
2595
|
+
return f;
|
|
2596
|
+
if (condGo !== null) {
|
|
2597
|
+
return `func() string { if ${condGo} { return ${t} }; return ${f} }()`;
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2175
2601
|
const arithmeticMatch = computation.match(/\(\)\s*=>\s*(\w+)\(\)\s*([*+\-/])\s*(\d+)/);
|
|
2176
2602
|
if (arithmeticMatch) {
|
|
2177
2603
|
const [, depName, operator, operand] = arithmeticMatch;
|
|
@@ -2191,8 +2617,8 @@ ${goFields.join(`
|
|
|
2191
2617
|
return `${hoisted.varName} ${operator} ${operand}`;
|
|
2192
2618
|
const fieldName = this.capitalizeFieldName(propName);
|
|
2193
2619
|
if (param.type) {
|
|
2194
|
-
const
|
|
2195
|
-
if (
|
|
2620
|
+
const goType = this.typeInfoToGo(param.type, param.defaultValue);
|
|
2621
|
+
if (goType === "interface{}")
|
|
2196
2622
|
return `in.${fieldName}.(int) ${operator} ${operand}`;
|
|
2197
2623
|
}
|
|
2198
2624
|
return `in.${fieldName} ${operator} ${operand}`;
|
|
@@ -2221,8 +2647,8 @@ ${goFields.join(`
|
|
|
2221
2647
|
if (param) {
|
|
2222
2648
|
const fieldName = this.capitalizeFieldName(varName);
|
|
2223
2649
|
if (param.type) {
|
|
2224
|
-
const
|
|
2225
|
-
if (
|
|
2650
|
+
const goType = this.typeInfoToGo(param.type, param.defaultValue);
|
|
2651
|
+
if (goType === "interface{}")
|
|
2226
2652
|
return `in.${fieldName}.(int) ${operator} ${operand}`;
|
|
2227
2653
|
}
|
|
2228
2654
|
return `in.${fieldName} ${operator} ${operand}`;
|
|
@@ -2236,11 +2662,283 @@ ${goFields.join(`
|
|
|
2236
2662
|
return `in.${this.capitalizeFieldName(varName)}`;
|
|
2237
2663
|
}
|
|
2238
2664
|
}
|
|
2239
|
-
|
|
2665
|
+
const blockReturn = this.resolveBlockBodyMemoModuleConst(computation, signals);
|
|
2666
|
+
if (blockReturn !== null && blockReturn.constValue && blockReturn.constType) {
|
|
2667
|
+
return this.convertInitialValue(blockReturn.constValue, blockReturn.constType, propsParams);
|
|
2668
|
+
}
|
|
2669
|
+
const objMemo = this.computeObjectMemoInitialValue(computation);
|
|
2670
|
+
if (objMemo !== null)
|
|
2671
|
+
return objMemo;
|
|
2672
|
+
return null;
|
|
2673
|
+
}
|
|
2674
|
+
resolveBlockBodyMemoModuleConst(computation, signals) {
|
|
2675
|
+
try {
|
|
2676
|
+
const sf = ts.createSourceFile("__memo.ts", `const __x = (${computation});`, ts.ScriptTarget.Latest, false);
|
|
2677
|
+
const stmt = sf.statements[0];
|
|
2678
|
+
if (!stmt || !ts.isVariableStatement(stmt))
|
|
2679
|
+
return null;
|
|
2680
|
+
let init = stmt.declarationList.declarations[0]?.initializer;
|
|
2681
|
+
while (init && ts.isParenthesizedExpression(init))
|
|
2682
|
+
init = init.expression;
|
|
2683
|
+
if (!init || !ts.isArrowFunction(init))
|
|
2684
|
+
return null;
|
|
2685
|
+
const body = init.body;
|
|
2686
|
+
if (!ts.isBlock(body))
|
|
2687
|
+
return null;
|
|
2688
|
+
const varToSignal = new Map;
|
|
2689
|
+
let guardSignalGetter = null;
|
|
2690
|
+
let returnedConst = null;
|
|
2691
|
+
for (const s of body.statements) {
|
|
2692
|
+
if (ts.isVariableStatement(s)) {
|
|
2693
|
+
for (const decl of s.declarationList.declarations) {
|
|
2694
|
+
if (ts.isIdentifier(decl.name) && decl.initializer && ts.isCallExpression(decl.initializer) && ts.isIdentifier(decl.initializer.expression)) {
|
|
2695
|
+
const callee = decl.initializer.expression.text;
|
|
2696
|
+
if (signals.some((sg) => sg.getter === callee)) {
|
|
2697
|
+
varToSignal.set(decl.name.text, callee);
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
if (ts.isIfStatement(s) && ts.isPrefixUnaryExpression(s.expression) && s.expression.operator === ts.SyntaxKind.ExclamationToken && ts.isIdentifier(s.expression.operand)) {
|
|
2703
|
+
const guardVar = s.expression.operand.text;
|
|
2704
|
+
const signalGetter = varToSignal.get(guardVar);
|
|
2705
|
+
if (!signalGetter)
|
|
2706
|
+
continue;
|
|
2707
|
+
const thenBlock = ts.isBlock(s.thenStatement) ? s.thenStatement.statements : [s.thenStatement];
|
|
2708
|
+
for (const rs of thenBlock) {
|
|
2709
|
+
if (ts.isReturnStatement(rs) && rs.expression && ts.isIdentifier(rs.expression)) {
|
|
2710
|
+
guardSignalGetter = signalGetter;
|
|
2711
|
+
returnedConst = rs.expression.text;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
if (guardSignalGetter && returnedConst)
|
|
2716
|
+
break;
|
|
2717
|
+
}
|
|
2718
|
+
if (!guardSignalGetter || !returnedConst)
|
|
2719
|
+
return null;
|
|
2720
|
+
const guardSignal = signals.find((sg) => sg.getter === guardSignalGetter);
|
|
2721
|
+
if (!guardSignal)
|
|
2722
|
+
return null;
|
|
2723
|
+
const iv = guardSignal.initialValue.trim();
|
|
2724
|
+
if (iv !== "null" && iv !== "''" && iv !== '""' && iv !== "0" && iv !== "false") {
|
|
2725
|
+
return null;
|
|
2726
|
+
}
|
|
2727
|
+
const constant = this.localConstants.find((c) => c.name === returnedConst && c.origin?.scope === "module");
|
|
2728
|
+
if (!constant)
|
|
2729
|
+
return null;
|
|
2730
|
+
return {
|
|
2731
|
+
constName: constant.name,
|
|
2732
|
+
constValue: constant.value,
|
|
2733
|
+
constType: constant.type ?? undefined
|
|
2734
|
+
};
|
|
2735
|
+
} catch {
|
|
2736
|
+
return null;
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
computeObjectMemoInitialValue(computation) {
|
|
2740
|
+
const arrow = this.parseLiteralExpression(computation);
|
|
2741
|
+
if (!arrow || !ts.isArrowFunction(arrow) || !ts.isBlock(arrow.body))
|
|
2742
|
+
return null;
|
|
2743
|
+
const searchParamsVars = new Set;
|
|
2744
|
+
let retObj = null;
|
|
2745
|
+
const statements = arrow.body.statements;
|
|
2746
|
+
for (let i = 0;i < statements.length; i++) {
|
|
2747
|
+
const s = statements[i];
|
|
2748
|
+
if (ts.isVariableStatement(s)) {
|
|
2749
|
+
for (const d of s.declarationList.declarations) {
|
|
2750
|
+
if (ts.isIdentifier(d.name) && d.initializer && ts.isCallExpression(d.initializer) && ts.isIdentifier(d.initializer.expression) && d.initializer.arguments.length === 0 && this.searchParamsLocals.has(d.initializer.expression.text)) {
|
|
2751
|
+
searchParamsVars.add(d.name.text);
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
continue;
|
|
2755
|
+
}
|
|
2756
|
+
if (ts.isReturnStatement(s)) {
|
|
2757
|
+
if (i !== statements.length - 1 || !s.expression)
|
|
2758
|
+
return null;
|
|
2759
|
+
let e = s.expression;
|
|
2760
|
+
while (ts.isParenthesizedExpression(e))
|
|
2761
|
+
e = e.expression;
|
|
2762
|
+
if (!ts.isObjectLiteralExpression(e))
|
|
2763
|
+
return null;
|
|
2764
|
+
retObj = e;
|
|
2765
|
+
continue;
|
|
2766
|
+
}
|
|
2767
|
+
return null;
|
|
2768
|
+
}
|
|
2769
|
+
if (!retObj || retObj.properties.length === 0)
|
|
2770
|
+
return null;
|
|
2771
|
+
const env = { searchParamsVars, params: new Map };
|
|
2772
|
+
const entries = [];
|
|
2773
|
+
for (const prop of retObj.properties) {
|
|
2774
|
+
if (!ts.isPropertyAssignment(prop))
|
|
2775
|
+
return null;
|
|
2776
|
+
const key = ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name) ? prop.name.text : null;
|
|
2777
|
+
if (!key)
|
|
2778
|
+
return null;
|
|
2779
|
+
const go = this.lowerCtorExpr(prop.initializer, env);
|
|
2780
|
+
if (go === null)
|
|
2781
|
+
return null;
|
|
2782
|
+
entries.push(`"${this.capitalizeFieldName(key)}": ${go}`);
|
|
2783
|
+
}
|
|
2784
|
+
return `map[string]interface{}{
|
|
2785
|
+
${entries.join(`,
|
|
2786
|
+
`)},
|
|
2787
|
+
}`;
|
|
2788
|
+
}
|
|
2789
|
+
lowerCtorExpr(node, env) {
|
|
2790
|
+
while (ts.isParenthesizedExpression(node))
|
|
2791
|
+
node = node.expression;
|
|
2792
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
2793
|
+
return JSON.stringify(node.text);
|
|
2794
|
+
}
|
|
2795
|
+
if (ts.isNumericLiteral(node))
|
|
2796
|
+
return node.text;
|
|
2797
|
+
if (ts.isIdentifier(node)) {
|
|
2798
|
+
const sub = env.params.get(node.text);
|
|
2799
|
+
if (sub !== undefined)
|
|
2800
|
+
return sub;
|
|
2801
|
+
const c = this.localConstants.find((lc) => lc.name === node.text);
|
|
2802
|
+
if (c?.value !== undefined) {
|
|
2803
|
+
if (c.isModule) {
|
|
2804
|
+
const lit = this.parseLiteralExpression(c.value);
|
|
2805
|
+
if (lit && (ts.isStringLiteral(lit) || ts.isNoSubstitutionTemplateLiteral(lit))) {
|
|
2806
|
+
return JSON.stringify(lit.text);
|
|
2807
|
+
}
|
|
2808
|
+
return null;
|
|
2809
|
+
}
|
|
2810
|
+
if (env.consts?.has(node.text))
|
|
2811
|
+
return null;
|
|
2812
|
+
const inner = this.parseLiteralExpression(c.value);
|
|
2813
|
+
if (!inner)
|
|
2814
|
+
return null;
|
|
2815
|
+
return this.lowerCtorExpr(inner, {
|
|
2816
|
+
...env,
|
|
2817
|
+
consts: new Set([...env.consts ?? [], node.text])
|
|
2818
|
+
});
|
|
2819
|
+
}
|
|
2820
|
+
return null;
|
|
2821
|
+
}
|
|
2822
|
+
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === this.propsObjectName) {
|
|
2823
|
+
return `in.${this.capitalizeFieldName(node.name.text)}`;
|
|
2824
|
+
}
|
|
2825
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
2826
|
+
const method = node.expression.name.text;
|
|
2827
|
+
const recv = node.expression.expression;
|
|
2828
|
+
if (method === "get" && ts.isIdentifier(recv) && env.searchParamsVars.has(recv.text) && node.arguments.length === 1 && ts.isStringLiteral(node.arguments[0])) {
|
|
2829
|
+
return `in.SearchParams.Get(${JSON.stringify(node.arguments[0].text)})`;
|
|
2830
|
+
}
|
|
2831
|
+
if (method === "includes" && node.arguments.length === 1) {
|
|
2832
|
+
const arr = this.lowerCtorStringArray(recv);
|
|
2833
|
+
const elem = this.lowerCtorExpr(node.arguments[0], env);
|
|
2834
|
+
if (arr !== null && elem !== null)
|
|
2835
|
+
return `bf.Includes(${arr}, ${elem})`;
|
|
2836
|
+
return null;
|
|
2837
|
+
}
|
|
2838
|
+
if (method === "replace" && node.arguments.length === 2 && node.arguments[0].kind === ts.SyntaxKind.RegularExpressionLiteral && node.arguments[0].getText() === "/\\/+$/" && (ts.isStringLiteral(node.arguments[1]) || ts.isNoSubstitutionTemplateLiteral(node.arguments[1])) && node.arguments[1].text === "") {
|
|
2839
|
+
const recvGo = this.lowerCtorExpr(recv, env);
|
|
2840
|
+
if (recvGo === null)
|
|
2841
|
+
return null;
|
|
2842
|
+
this.needsStringsImport = true;
|
|
2843
|
+
return `strings.TrimRight(${recvGo}, "/")`;
|
|
2844
|
+
}
|
|
2845
|
+
return null;
|
|
2846
|
+
}
|
|
2847
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
2848
|
+
const fnConst = this.localConstants.find((lc) => lc.name === node.expression.text && lc.isModule);
|
|
2849
|
+
if (fnConst?.value) {
|
|
2850
|
+
const fn = this.parseLiteralExpression(fnConst.value);
|
|
2851
|
+
if (fn && ts.isArrowFunction(fn) && !ts.isBlock(fn.body) && fn.parameters.length === node.arguments.length) {
|
|
2852
|
+
const params = new Map(env.params);
|
|
2853
|
+
for (let i = 0;i < fn.parameters.length; i++) {
|
|
2854
|
+
const p = fn.parameters[i];
|
|
2855
|
+
if (!ts.isIdentifier(p.name))
|
|
2856
|
+
return null;
|
|
2857
|
+
const argGo = this.lowerCtorExpr(node.arguments[i], env);
|
|
2858
|
+
if (argGo === null)
|
|
2859
|
+
return null;
|
|
2860
|
+
params.set(p.name.text, argGo);
|
|
2861
|
+
}
|
|
2862
|
+
return this.lowerCtorExpr(fn.body, { searchParamsVars: env.searchParamsVars, params });
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
return null;
|
|
2866
|
+
}
|
|
2867
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
|
|
2868
|
+
const left = this.lowerCtorExpr(node.left, env);
|
|
2869
|
+
if (left === null)
|
|
2870
|
+
return null;
|
|
2871
|
+
let r = node.right;
|
|
2872
|
+
while (ts.isParenthesizedExpression(r))
|
|
2873
|
+
r = r.expression;
|
|
2874
|
+
if ((ts.isStringLiteral(r) || ts.isNoSubstitutionTemplateLiteral(r)) && r.text === "") {
|
|
2875
|
+
return left;
|
|
2876
|
+
}
|
|
2877
|
+
const right = this.lowerCtorExpr(node.right, env);
|
|
2878
|
+
if (right === null)
|
|
2879
|
+
return null;
|
|
2880
|
+
return `func() string { v := ${left}; if v != "" { return v }; return ${right} }()`;
|
|
2881
|
+
}
|
|
2882
|
+
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
|
|
2883
|
+
const left = this.lowerCtorExpr(node.left, env);
|
|
2884
|
+
const right = this.lowerCtorExpr(node.right, env);
|
|
2885
|
+
if (left === null || right === null)
|
|
2886
|
+
return null;
|
|
2887
|
+
return `func() string { v := ${left}; if v != "" { return v }; return ${right} }()`;
|
|
2888
|
+
}
|
|
2889
|
+
if (ts.isConditionalExpression(node)) {
|
|
2890
|
+
const cond = this.lowerCtorCond(node.condition, env);
|
|
2891
|
+
const t = this.lowerCtorExpr(node.whenTrue, env);
|
|
2892
|
+
const f = this.lowerCtorExpr(node.whenFalse, env);
|
|
2893
|
+
if (cond !== null && t !== null && f !== null) {
|
|
2894
|
+
return `func() string { if ${cond} { return ${t} }; return ${f} }()`;
|
|
2895
|
+
}
|
|
2896
|
+
return null;
|
|
2897
|
+
}
|
|
2898
|
+
return null;
|
|
2899
|
+
}
|
|
2900
|
+
lowerCtorCond(node, env) {
|
|
2901
|
+
while (ts.isParenthesizedExpression(node))
|
|
2902
|
+
node = node.expression;
|
|
2903
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword)
|
|
2904
|
+
return "true";
|
|
2905
|
+
if (node.kind === ts.SyntaxKind.FalseKeyword)
|
|
2240
2906
|
return "false";
|
|
2241
|
-
if (
|
|
2242
|
-
|
|
2243
|
-
|
|
2907
|
+
if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.ExclamationToken) {
|
|
2908
|
+
const inner = this.lowerCtorCond(node.operand, env);
|
|
2909
|
+
return inner === null ? null : `!(${inner})`;
|
|
2910
|
+
}
|
|
2911
|
+
if (ts.isBinaryExpression(node)) {
|
|
2912
|
+
const op = node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ? "&&" : node.operatorToken.kind === ts.SyntaxKind.BarBarToken ? "||" : null;
|
|
2913
|
+
if (op) {
|
|
2914
|
+
const l = this.lowerCtorCond(node.left, env);
|
|
2915
|
+
const r = this.lowerCtorCond(node.right, env);
|
|
2916
|
+
return l !== null && r !== null ? `(${l} ${op} ${r})` : null;
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "includes") {
|
|
2920
|
+
return this.lowerCtorExpr(node, env);
|
|
2921
|
+
}
|
|
2922
|
+
return null;
|
|
2923
|
+
}
|
|
2924
|
+
lowerCtorStringArray(node) {
|
|
2925
|
+
let arr = node;
|
|
2926
|
+
if (ts.isIdentifier(node)) {
|
|
2927
|
+
const c = this.localConstants.find((lc) => lc.name === node.text && lc.isModule);
|
|
2928
|
+
if (!c?.value)
|
|
2929
|
+
return null;
|
|
2930
|
+
arr = this.parseLiteralExpression(c.value);
|
|
2931
|
+
}
|
|
2932
|
+
if (!arr || !ts.isArrayLiteralExpression(arr))
|
|
2933
|
+
return null;
|
|
2934
|
+
const elems = [];
|
|
2935
|
+
for (const el of arr.elements) {
|
|
2936
|
+
if (ts.isStringLiteral(el) || ts.isNoSubstitutionTemplateLiteral(el)) {
|
|
2937
|
+
elems.push(JSON.stringify(el.text));
|
|
2938
|
+
} else
|
|
2939
|
+
return null;
|
|
2940
|
+
}
|
|
2941
|
+
return `[]string{${elems.join(", ")}}`;
|
|
2244
2942
|
}
|
|
2245
2943
|
isTemplateLiteralMemo(computation) {
|
|
2246
2944
|
const sf = ts.createSourceFile("__memo.ts", `const __x = (${computation});`, ts.ScriptTarget.Latest, false);
|
|
@@ -2296,6 +2994,10 @@ ${goFields.join(`
|
|
|
2296
2994
|
if (this.typeInfoToGo(memo.type) === "interface{}" && this.isBooleanMemo(memo, signals, propsParamMap)) {
|
|
2297
2995
|
return "bool";
|
|
2298
2996
|
}
|
|
2997
|
+
const blockReturn = this.resolveBlockBodyMemoModuleConst(memo.computation, signals);
|
|
2998
|
+
if (blockReturn?.constType?.kind === "array") {
|
|
2999
|
+
return this.typeInfoToGo(blockReturn.constType);
|
|
3000
|
+
}
|
|
2299
3001
|
return this.typeInfoToGo(memo.type);
|
|
2300
3002
|
}
|
|
2301
3003
|
isBooleanMemo(memo, signals, propsParamMap) {
|
|
@@ -2575,6 +3277,9 @@ ${goFields.join(`
|
|
|
2575
3277
|
if (element.slotId) {
|
|
2576
3278
|
hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
|
|
2577
3279
|
}
|
|
3280
|
+
if (element.regionId) {
|
|
3281
|
+
hydrationAttrs += ` ${BF_REGION}="${element.regionId}"`;
|
|
3282
|
+
}
|
|
2578
3283
|
const voidElements = [
|
|
2579
3284
|
"area",
|
|
2580
3285
|
"base",
|
|
@@ -2603,8 +3308,9 @@ ${goFields.join(`
|
|
|
2603
3308
|
}
|
|
2604
3309
|
return "";
|
|
2605
3310
|
}
|
|
2606
|
-
const
|
|
2607
|
-
|
|
3311
|
+
const classify = {};
|
|
3312
|
+
const goExpr = this.convertExpressionToGo(expr.expr, classify);
|
|
3313
|
+
if (this.isTemplateFragment(goExpr, classify.parsed?.kind)) {
|
|
2608
3314
|
if (expr.slotId) {
|
|
2609
3315
|
return `{{bfTextStart "${expr.slotId}"}}${goExpr}{{bfTextEnd}}`;
|
|
2610
3316
|
}
|
|
@@ -2615,6 +3321,9 @@ ${goFields.join(`
|
|
|
2615
3321
|
}
|
|
2616
3322
|
return `{{${goExpr}}}`;
|
|
2617
3323
|
}
|
|
3324
|
+
isTemplateFragment(go, kind) {
|
|
3325
|
+
return go.startsWith("{{") || kind === "template-literal";
|
|
3326
|
+
}
|
|
2618
3327
|
renderClientOnlyConditional(cond) {
|
|
2619
3328
|
if (cond.slotId) {
|
|
2620
3329
|
return `{{bfComment "cond-start:${cond.slotId}"}}{{bfComment "cond-end:${cond.slotId}"}}`;
|
|
@@ -2625,6 +3334,8 @@ ${goFields.join(`
|
|
|
2625
3334
|
return emitParsedExpr(expr, this);
|
|
2626
3335
|
}
|
|
2627
3336
|
identifier(name) {
|
|
3337
|
+
if (name === "undefined" || name === "null")
|
|
3338
|
+
return '""';
|
|
2628
3339
|
for (let i = this.loopBindingStack.length - 1;i >= 0; i--) {
|
|
2629
3340
|
const acc = this.loopBindingStack[i].get(name);
|
|
2630
3341
|
if (acc !== undefined)
|
|
@@ -2633,6 +3344,9 @@ ${goFields.join(`
|
|
|
2633
3344
|
const inlined = this.resolveModuleStringConst(name);
|
|
2634
3345
|
if (inlined !== null)
|
|
2635
3346
|
return inlined;
|
|
3347
|
+
const inlinedNum = this.resolveModuleNumericConst(name);
|
|
3348
|
+
if (inlinedNum !== null)
|
|
3349
|
+
return inlinedNum;
|
|
2636
3350
|
const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
|
|
2637
3351
|
if (currentLoopParam && name === currentLoopParam)
|
|
2638
3352
|
return ".";
|
|
@@ -2640,7 +3354,86 @@ ${goFields.join(`
|
|
|
2640
3354
|
return `$${name}`;
|
|
2641
3355
|
if (this.loopVarRefCount.has(name))
|
|
2642
3356
|
return `$${name}`;
|
|
2643
|
-
|
|
3357
|
+
if (this.localConstants.some((c) => c.name === name && !c.isModule && !c.containsArrow)) {
|
|
3358
|
+
this.referencedDerivedConsts.add(name);
|
|
3359
|
+
}
|
|
3360
|
+
return this.searchParamsFieldRef(name) ?? this.rootFieldRef(name);
|
|
3361
|
+
}
|
|
3362
|
+
computeDerivedConstFields(takenFieldNames) {
|
|
3363
|
+
const fields = [];
|
|
3364
|
+
for (const name of this.referencedDerivedConsts) {
|
|
3365
|
+
const fieldName = this.capitalizeFieldName(name);
|
|
3366
|
+
if (takenFieldNames.has(fieldName))
|
|
3367
|
+
continue;
|
|
3368
|
+
const c = this.localConstants.find((lc) => lc.name === name && !lc.isModule && lc.value);
|
|
3369
|
+
if (!c?.value)
|
|
3370
|
+
continue;
|
|
3371
|
+
const expr = this.parseLiteralExpression(c.value);
|
|
3372
|
+
if (!expr)
|
|
3373
|
+
continue;
|
|
3374
|
+
if (!this.isStringExpr(expr, new Set))
|
|
3375
|
+
continue;
|
|
3376
|
+
const init = this.lowerCtorExpr(expr, {
|
|
3377
|
+
searchParamsVars: new Set,
|
|
3378
|
+
params: new Map,
|
|
3379
|
+
consts: new Set([name])
|
|
3380
|
+
});
|
|
3381
|
+
if (init === null)
|
|
3382
|
+
continue;
|
|
3383
|
+
fields.push({ name: fieldName, init });
|
|
3384
|
+
}
|
|
3385
|
+
return fields;
|
|
3386
|
+
}
|
|
3387
|
+
isStringExpr(node, seen) {
|
|
3388
|
+
while (ts.isParenthesizedExpression(node))
|
|
3389
|
+
node = node.expression;
|
|
3390
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node) || ts.isTemplateExpression(node)) {
|
|
3391
|
+
return true;
|
|
3392
|
+
}
|
|
3393
|
+
if (ts.isBinaryExpression(node)) {
|
|
3394
|
+
const op = node.operatorToken.kind;
|
|
3395
|
+
if (op === ts.SyntaxKind.PlusToken) {
|
|
3396
|
+
return this.isStringExpr(node.left, seen) || this.isStringExpr(node.right, seen);
|
|
3397
|
+
}
|
|
3398
|
+
if (op === ts.SyntaxKind.BarBarToken || op === ts.SyntaxKind.QuestionQuestionToken) {
|
|
3399
|
+
return this.isStringExpr(node.left, seen) && this.isStringExpr(node.right, seen);
|
|
3400
|
+
}
|
|
3401
|
+
return false;
|
|
3402
|
+
}
|
|
3403
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
3404
|
+
const m = node.expression.name.text;
|
|
3405
|
+
const STRING_METHODS = new Set([
|
|
3406
|
+
"replace",
|
|
3407
|
+
"trim",
|
|
3408
|
+
"trimStart",
|
|
3409
|
+
"trimEnd",
|
|
3410
|
+
"toLowerCase",
|
|
3411
|
+
"toUpperCase",
|
|
3412
|
+
"slice",
|
|
3413
|
+
"substring",
|
|
3414
|
+
"substr",
|
|
3415
|
+
"padStart",
|
|
3416
|
+
"padEnd",
|
|
3417
|
+
"concat",
|
|
3418
|
+
"repeat",
|
|
3419
|
+
"get"
|
|
3420
|
+
]);
|
|
3421
|
+
return STRING_METHODS.has(m);
|
|
3422
|
+
}
|
|
3423
|
+
if (ts.isConditionalExpression(node)) {
|
|
3424
|
+
return this.isStringExpr(node.whenTrue, seen) && this.isStringExpr(node.whenFalse, seen);
|
|
3425
|
+
}
|
|
3426
|
+
if (ts.isIdentifier(node)) {
|
|
3427
|
+
if (seen.has(node.text))
|
|
3428
|
+
return false;
|
|
3429
|
+
const c = this.localConstants.find((lc) => lc.name === node.text && !lc.isModule && lc.value);
|
|
3430
|
+
if (c?.value) {
|
|
3431
|
+
const inner = this.parseLiteralExpression(c.value);
|
|
3432
|
+
if (inner)
|
|
3433
|
+
return this.isStringExpr(inner, new Set([...seen, node.text]));
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
return false;
|
|
2644
3437
|
}
|
|
2645
3438
|
isOuterLoopParam(name) {
|
|
2646
3439
|
const top = this.loopParamStack.length - 1;
|
|
@@ -2654,70 +3447,26 @@ ${goFields.join(`
|
|
|
2654
3447
|
const prefix = this.loopParamStack.length > 0 ? "$." : ".";
|
|
2655
3448
|
return `${prefix}${this.capitalizeFieldName(name)}`;
|
|
2656
3449
|
}
|
|
3450
|
+
searchParamsFieldRef(name) {
|
|
3451
|
+
return this.searchParamsLocals.has(name) ? this.rootFieldRef("searchParams") : null;
|
|
3452
|
+
}
|
|
2657
3453
|
collectModuleStringConsts(constants) {
|
|
2658
|
-
|
|
2659
|
-
for (const c of constants ?? []) {
|
|
2660
|
-
if (!c.isModule)
|
|
2661
|
-
continue;
|
|
2662
|
-
if (c.value === undefined)
|
|
2663
|
-
continue;
|
|
2664
|
-
const literal = this.parsePureStringLiteral(c.value);
|
|
2665
|
-
if (literal !== null)
|
|
2666
|
-
map.set(c.name, literal);
|
|
2667
|
-
}
|
|
2668
|
-
return map;
|
|
3454
|
+
return collectModuleStringConstsShared(constants);
|
|
2669
3455
|
}
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
const stmt = sf.statements[0];
|
|
2673
|
-
if (!stmt || !ts.isVariableStatement(stmt))
|
|
2674
|
-
return null;
|
|
2675
|
-
const decl = stmt.declarationList.declarations[0];
|
|
2676
|
-
let init = decl?.initializer;
|
|
2677
|
-
while (init && ts.isParenthesizedExpression(init))
|
|
2678
|
-
init = init.expression;
|
|
2679
|
-
if (!init)
|
|
3456
|
+
resolveModuleStringConst(name) {
|
|
3457
|
+
if (this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name) {
|
|
2680
3458
|
return null;
|
|
2681
|
-
if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
2682
|
-
return init.text;
|
|
2683
3459
|
}
|
|
2684
|
-
|
|
2685
|
-
if (joined !== null)
|
|
2686
|
-
return joined;
|
|
2687
|
-
return null;
|
|
2688
|
-
}
|
|
2689
|
-
evalStringArrayJoin(node) {
|
|
2690
|
-
if (!ts.isCallExpression(node))
|
|
2691
|
-
return null;
|
|
2692
|
-
const callee = node.expression;
|
|
2693
|
-
if (!ts.isPropertyAccessExpression(callee))
|
|
3460
|
+
if (this.loopVarRefCount.has(name))
|
|
2694
3461
|
return null;
|
|
2695
|
-
if (
|
|
3462
|
+
if (this.isOuterLoopParam(name))
|
|
2696
3463
|
return null;
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
recv = recv.expression;
|
|
2700
|
-
if (!ts.isArrayLiteralExpression(recv))
|
|
3464
|
+
const value = this.moduleStringConsts.get(name);
|
|
3465
|
+
if (value === undefined)
|
|
2701
3466
|
return null;
|
|
2702
|
-
|
|
2703
|
-
for (const el of recv.elements) {
|
|
2704
|
-
if (ts.isStringLiteral(el) || ts.isNoSubstitutionTemplateLiteral(el)) {
|
|
2705
|
-
parts.push(el.text);
|
|
2706
|
-
} else {
|
|
2707
|
-
return null;
|
|
2708
|
-
}
|
|
2709
|
-
}
|
|
2710
|
-
let sep = ",";
|
|
2711
|
-
if (node.arguments.length >= 1) {
|
|
2712
|
-
const arg = node.arguments[0];
|
|
2713
|
-
if (ts.isStringLiteral(arg) || ts.isNoSubstitutionTemplateLiteral(arg))
|
|
2714
|
-
sep = arg.text;
|
|
2715
|
-
else
|
|
2716
|
-
return null;
|
|
2717
|
-
}
|
|
2718
|
-
return parts.join(sep);
|
|
3467
|
+
return `"${this.escapeGoString(value)}"`;
|
|
2719
3468
|
}
|
|
2720
|
-
|
|
3469
|
+
resolveModuleNumericConst(name) {
|
|
2721
3470
|
if (this.loopParamStack.length > 0 && this.loopParamStack[this.loopParamStack.length - 1] === name) {
|
|
2722
3471
|
return null;
|
|
2723
3472
|
}
|
|
@@ -2725,10 +3474,11 @@ ${goFields.join(`
|
|
|
2725
3474
|
return null;
|
|
2726
3475
|
if (this.isOuterLoopParam(name))
|
|
2727
3476
|
return null;
|
|
2728
|
-
const
|
|
2729
|
-
if (value === undefined)
|
|
3477
|
+
const c = this.localConstants.find((k) => k.name === name && k.isModule && !k.containsArrow);
|
|
3478
|
+
if (!c || c.value === undefined)
|
|
2730
3479
|
return null;
|
|
2731
|
-
|
|
3480
|
+
const v = c.value.trim().replace(/(?<=\d)_(?=\d)/g, "");
|
|
3481
|
+
return /^-?\d+(\.\d+)?$/.test(v) ? v : null;
|
|
2732
3482
|
}
|
|
2733
3483
|
literal(value, literalType) {
|
|
2734
3484
|
if (literalType === "string")
|
|
@@ -2739,7 +3489,7 @@ ${goFields.join(`
|
|
|
2739
3489
|
}
|
|
2740
3490
|
call(callee, args, emit) {
|
|
2741
3491
|
if (callee.kind === "identifier" && args.length === 0) {
|
|
2742
|
-
return this.rootFieldRef(callee.name);
|
|
3492
|
+
return this.searchParamsFieldRef(callee.name) ?? this.rootFieldRef(callee.name);
|
|
2743
3493
|
}
|
|
2744
3494
|
const path = identifierPath(callee);
|
|
2745
3495
|
if (path && this.templatePrimitives[path]) {
|
|
@@ -2770,6 +3520,13 @@ ${goFields.join(`
|
|
|
2770
3520
|
if (result)
|
|
2771
3521
|
return result;
|
|
2772
3522
|
}
|
|
3523
|
+
if (property === "length" && object.kind === "call" && object.callee.kind === "identifier" && object.args.length === 0) {
|
|
3524
|
+
const slice = this.memoBackedLoopSlice.get(object.callee.name);
|
|
3525
|
+
if (slice) {
|
|
3526
|
+
const prefix = this.loopParamStack.length > 0 ? "$." : ".";
|
|
3527
|
+
return `len ${prefix}${slice}`;
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
2773
3530
|
if (object.kind === "higher-order" && (object.method === "find" || object.method === "findLast")) {
|
|
2774
3531
|
const findResult = this.renderHigherOrderExpr(object, emit);
|
|
2775
3532
|
if (findResult) {
|
|
@@ -2782,6 +3539,11 @@ ${goFields.join(`
|
|
|
2782
3539
|
if (object.kind === "identifier" && this.propsObjectName && object.name === this.propsObjectName) {
|
|
2783
3540
|
return this.rootFieldRef(property);
|
|
2784
3541
|
}
|
|
3542
|
+
if (object.kind === "identifier") {
|
|
3543
|
+
const staticValue = this.resolveStaticRecordLiteralIndex(`${object.name}.${property}`);
|
|
3544
|
+
if (staticValue !== null)
|
|
3545
|
+
return staticValue;
|
|
3546
|
+
}
|
|
2785
3547
|
const currentLoopParam = this.loopParamStack[this.loopParamStack.length - 1];
|
|
2786
3548
|
if (object.kind === "identifier" && currentLoopParam && object.name === currentLoopParam) {
|
|
2787
3549
|
return `.${this.capitalizeFieldName(property)}`;
|
|
@@ -2791,34 +3553,43 @@ ${goFields.join(`
|
|
|
2791
3553
|
return `len ${obj}`;
|
|
2792
3554
|
return `${obj}.${this.capitalizeFieldName(property)}`;
|
|
2793
3555
|
}
|
|
3556
|
+
indexAccess(object, index, emit) {
|
|
3557
|
+
return `index ${wrapIfMultiToken(emit(object))} ${wrapIfMultiToken(emit(index))}`;
|
|
3558
|
+
}
|
|
2794
3559
|
binary(op, left, right, emit) {
|
|
2795
3560
|
const l = emit(left);
|
|
2796
3561
|
const r = emit(right);
|
|
3562
|
+
const wl = wrapIfMultiToken(l);
|
|
3563
|
+
const wr = wrapIfMultiToken(r);
|
|
2797
3564
|
switch (op) {
|
|
2798
3565
|
case "===":
|
|
2799
|
-
case "==":
|
|
2800
|
-
|
|
3566
|
+
case "==": {
|
|
3567
|
+
const [el, er] = stringTolerantEqOperands(l, r);
|
|
3568
|
+
return `eq ${wrapIfMultiToken(el)} ${wrapIfMultiToken(er)}`;
|
|
3569
|
+
}
|
|
2801
3570
|
case "!==":
|
|
2802
|
-
case "!=":
|
|
2803
|
-
|
|
3571
|
+
case "!=": {
|
|
3572
|
+
const [el, er] = stringTolerantEqOperands(l, r);
|
|
3573
|
+
return `ne ${wrapIfMultiToken(el)} ${wrapIfMultiToken(er)}`;
|
|
3574
|
+
}
|
|
2804
3575
|
case ">":
|
|
2805
|
-
return `gt ${
|
|
3576
|
+
return `gt ${wl} ${wr}`;
|
|
2806
3577
|
case "<":
|
|
2807
|
-
return `lt ${
|
|
3578
|
+
return `lt ${wl} ${wr}`;
|
|
2808
3579
|
case ">=":
|
|
2809
|
-
return `ge ${
|
|
3580
|
+
return `ge ${wl} ${wr}`;
|
|
2810
3581
|
case "<=":
|
|
2811
|
-
return `le ${
|
|
3582
|
+
return `le ${wl} ${wr}`;
|
|
2812
3583
|
case "+":
|
|
2813
|
-
return `bf_add ${
|
|
3584
|
+
return `bf_add ${wl} ${wr}`;
|
|
2814
3585
|
case "-":
|
|
2815
|
-
return `bf_sub ${
|
|
3586
|
+
return `bf_sub ${wl} ${wr}`;
|
|
2816
3587
|
case "*":
|
|
2817
|
-
return `bf_mul ${
|
|
3588
|
+
return `bf_mul ${wl} ${wr}`;
|
|
2818
3589
|
case "/":
|
|
2819
|
-
return `bf_div ${
|
|
3590
|
+
return `bf_div ${wl} ${wr}`;
|
|
2820
3591
|
case "%":
|
|
2821
|
-
return `bf_mod ${
|
|
3592
|
+
return `bf_mod ${wl} ${wr}`;
|
|
2822
3593
|
default:
|
|
2823
3594
|
return `${l} ${op} ${r}`;
|
|
2824
3595
|
}
|
|
@@ -2832,10 +3603,8 @@ ${goFields.join(`
|
|
|
2832
3603
|
return arg;
|
|
2833
3604
|
}
|
|
2834
3605
|
logical(op, left, right, emit) {
|
|
2835
|
-
const
|
|
2836
|
-
const
|
|
2837
|
-
const wrapLeft = this.needsParens(left) ? `(${l})` : l;
|
|
2838
|
-
const wrapRight = this.needsParens(right) ? `(${r})` : r;
|
|
3606
|
+
const wrapLeft = wrapIfMultiToken(emit(left));
|
|
3607
|
+
const wrapRight = wrapIfMultiToken(emit(right));
|
|
2839
3608
|
if (op === "&&")
|
|
2840
3609
|
return `and ${wrapLeft} ${wrapRight}`;
|
|
2841
3610
|
return `or ${wrapLeft} ${wrapRight}`;
|
|
@@ -2852,7 +3621,8 @@ ${goFields.join(`
|
|
|
2852
3621
|
if (part.type === "string") {
|
|
2853
3622
|
result += part.value;
|
|
2854
3623
|
} else {
|
|
2855
|
-
|
|
3624
|
+
const e = emit(part.expr);
|
|
3625
|
+
result += this.isTemplateFragment(e, part.expr.kind) ? e : `{{${e}}}`;
|
|
2856
3626
|
}
|
|
2857
3627
|
}
|
|
2858
3628
|
return result;
|
|
@@ -2953,6 +3723,11 @@ ${goFields.join(`
|
|
|
2953
3723
|
const recv = emit(object);
|
|
2954
3724
|
return `bf_trim ${wrapIfMultiToken(recv)}`;
|
|
2955
3725
|
}
|
|
3726
|
+
case "toFixed": {
|
|
3727
|
+
const recv = emit(object);
|
|
3728
|
+
const digits = args.length >= 1 ? emit(args[0]) : "0";
|
|
3729
|
+
return `bf_to_fixed ${wrapIfMultiToken(recv)} ${wrapIfMultiToken(digits)}`;
|
|
3730
|
+
}
|
|
2956
3731
|
case "split": {
|
|
2957
3732
|
const recv = emit(object);
|
|
2958
3733
|
if (args.length === 0) {
|
|
@@ -3438,11 +4213,33 @@ ${goFields.join(`
|
|
|
3438
4213
|
return false;
|
|
3439
4214
|
}
|
|
3440
4215
|
}
|
|
3441
|
-
convertExpressionToGo(jsExpr) {
|
|
4216
|
+
convertExpressionToGo(jsExpr, out) {
|
|
3442
4217
|
const trimmed = jsExpr.trim();
|
|
3443
4218
|
if (trimmed === "null" || trimmed === "undefined") {
|
|
3444
4219
|
return '""';
|
|
3445
4220
|
}
|
|
4221
|
+
const staticIndexed = this.resolveStaticRecordLiteralIndex(trimmed);
|
|
4222
|
+
if (staticIndexed !== null) {
|
|
4223
|
+
return staticIndexed;
|
|
4224
|
+
}
|
|
4225
|
+
if (/^[A-Za-z_$][\w$]*$/.test(trimmed)) {
|
|
4226
|
+
const litConst = (this.localConstants ?? []).find((c) => c.name === trimmed);
|
|
4227
|
+
if (litConst?.value !== undefined) {
|
|
4228
|
+
const v = litConst.value.trim();
|
|
4229
|
+
if (/^-?\d+(\.\d+)?$/.test(v))
|
|
4230
|
+
return v;
|
|
4231
|
+
const strLit = /^'([^'\\]*)'$/.exec(v) ?? /^"([^"\\]*)"$/.exec(v);
|
|
4232
|
+
if (strLit)
|
|
4233
|
+
return JSON.stringify(strLit[1]);
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
const urlBuilt = this.lowerUrlBuilderHelperCall(trimmed);
|
|
4237
|
+
if (urlBuilt !== null)
|
|
4238
|
+
return urlBuilt;
|
|
4239
|
+
const inlined = this.inlineLocalHelperCall(trimmed);
|
|
4240
|
+
if (inlined !== null) {
|
|
4241
|
+
return this.convertExpressionToGo(inlined, out);
|
|
4242
|
+
}
|
|
3446
4243
|
const parsed = parseExpression(trimmed);
|
|
3447
4244
|
const support = isSupported(parsed);
|
|
3448
4245
|
if (!support.supported) {
|
|
@@ -3457,8 +4254,270 @@ ${goFields.join(`
|
|
|
3457
4254
|
});
|
|
3458
4255
|
return `""`;
|
|
3459
4256
|
}
|
|
4257
|
+
if (out)
|
|
4258
|
+
out.parsed = parsed;
|
|
3460
4259
|
return this.renderParsedExpr(parsed);
|
|
3461
4260
|
}
|
|
4261
|
+
inlineLocalHelperCall(jsExpr) {
|
|
4262
|
+
if (this.localHelperNames.size === 0)
|
|
4263
|
+
return null;
|
|
4264
|
+
const head = /^\s*([A-Za-z_$][\w$]*)\s*\(/.exec(jsExpr);
|
|
4265
|
+
if (!head || !this.localHelperNames.has(head[1]))
|
|
4266
|
+
return null;
|
|
4267
|
+
const call = this.parseLiteralExpression(jsExpr);
|
|
4268
|
+
if (!call || !ts.isCallExpression(call) || !ts.isIdentifier(call.expression))
|
|
4269
|
+
return null;
|
|
4270
|
+
if (call.arguments.some((a) => ts.isSpreadElement(a)))
|
|
4271
|
+
return null;
|
|
4272
|
+
const fnConst = this.localConstants.find((c) => c.name === call.expression.text && !c.isModule && c.value);
|
|
4273
|
+
if (!fnConst?.value)
|
|
4274
|
+
return null;
|
|
4275
|
+
const fn = this.parseLiteralExpression(fnConst.value);
|
|
4276
|
+
if (!fn || !ts.isArrowFunction(fn) || ts.isBlock(fn.body))
|
|
4277
|
+
return null;
|
|
4278
|
+
if (fn.parameters.length !== call.arguments.length)
|
|
4279
|
+
return null;
|
|
4280
|
+
if (this.bodyCallsLocalHelper(fn.body))
|
|
4281
|
+
return null;
|
|
4282
|
+
const subs = new Map;
|
|
4283
|
+
for (let i = 0;i < fn.parameters.length; i++) {
|
|
4284
|
+
const p = fn.parameters[i];
|
|
4285
|
+
if (!ts.isIdentifier(p.name))
|
|
4286
|
+
return null;
|
|
4287
|
+
subs.set(p.name.text, `(${call.arguments[i].getText()})`);
|
|
4288
|
+
}
|
|
4289
|
+
if (!this.isSpliceSafeHelperBody(fn.body, new Set(subs.keys())))
|
|
4290
|
+
return null;
|
|
4291
|
+
return this.substituteHelperParams(fn.body, subs);
|
|
4292
|
+
}
|
|
4293
|
+
isSpliceSafeHelperBody(body, paramNames) {
|
|
4294
|
+
let safe = true;
|
|
4295
|
+
const visit = (n) => {
|
|
4296
|
+
if (!safe)
|
|
4297
|
+
return;
|
|
4298
|
+
if (ts.isArrowFunction(n) || ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n)) {
|
|
4299
|
+
safe = false;
|
|
4300
|
+
return;
|
|
4301
|
+
}
|
|
4302
|
+
if (ts.isShorthandPropertyAssignment(n) && paramNames.has(n.name.text)) {
|
|
4303
|
+
safe = false;
|
|
4304
|
+
return;
|
|
4305
|
+
}
|
|
4306
|
+
ts.forEachChild(n, visit);
|
|
4307
|
+
};
|
|
4308
|
+
visit(body);
|
|
4309
|
+
return safe;
|
|
4310
|
+
}
|
|
4311
|
+
bodyCallsLocalHelper(body) {
|
|
4312
|
+
let found = false;
|
|
4313
|
+
const visit = (n) => {
|
|
4314
|
+
if (found)
|
|
4315
|
+
return;
|
|
4316
|
+
if (ts.isCallExpression(n) && ts.isIdentifier(n.expression)) {
|
|
4317
|
+
const name = n.expression.text;
|
|
4318
|
+
if (this.localConstants.some((c) => c.name === name && !c.isModule && c.containsArrow)) {
|
|
4319
|
+
found = true;
|
|
4320
|
+
return;
|
|
4321
|
+
}
|
|
4322
|
+
}
|
|
4323
|
+
ts.forEachChild(n, visit);
|
|
4324
|
+
};
|
|
4325
|
+
visit(body);
|
|
4326
|
+
return found;
|
|
4327
|
+
}
|
|
4328
|
+
substituteHelperParams(body, subs) {
|
|
4329
|
+
const sf = body.getSourceFile();
|
|
4330
|
+
const base = body.getStart(sf);
|
|
4331
|
+
const repls = [];
|
|
4332
|
+
const visit = (node) => {
|
|
4333
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
4334
|
+
visit(node.expression);
|
|
4335
|
+
return;
|
|
4336
|
+
}
|
|
4337
|
+
if (ts.isPropertyAssignment(node)) {
|
|
4338
|
+
if (ts.isComputedPropertyName(node.name))
|
|
4339
|
+
visit(node.name);
|
|
4340
|
+
visit(node.initializer);
|
|
4341
|
+
return;
|
|
4342
|
+
}
|
|
4343
|
+
if (ts.isIdentifier(node)) {
|
|
4344
|
+
const sub = subs.get(node.text);
|
|
4345
|
+
if (sub !== undefined) {
|
|
4346
|
+
repls.push({ start: node.getStart(sf) - base, end: node.getEnd() - base, text: sub });
|
|
4347
|
+
return;
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
ts.forEachChild(node, visit);
|
|
4351
|
+
};
|
|
4352
|
+
visit(body);
|
|
4353
|
+
let text = body.getText(sf);
|
|
4354
|
+
for (const r of repls.sort((a, b) => b.start - a.start)) {
|
|
4355
|
+
text = text.slice(0, r.start) + r.text + text.slice(r.end);
|
|
4356
|
+
}
|
|
4357
|
+
return text;
|
|
4358
|
+
}
|
|
4359
|
+
lowerUrlBuilderHelperCall(jsExpr) {
|
|
4360
|
+
if (this.localHelperNames.size === 0)
|
|
4361
|
+
return null;
|
|
4362
|
+
const head = /^\s*([A-Za-z_$][\w$]*)\s*\(/.exec(jsExpr);
|
|
4363
|
+
if (!head || !this.localHelperNames.has(head[1]))
|
|
4364
|
+
return null;
|
|
4365
|
+
const call = this.parseLiteralExpression(jsExpr);
|
|
4366
|
+
if (!call || !ts.isCallExpression(call) || !ts.isIdentifier(call.expression))
|
|
4367
|
+
return null;
|
|
4368
|
+
if (call.arguments.some((a) => ts.isSpreadElement(a)))
|
|
4369
|
+
return null;
|
|
4370
|
+
const fnConst = this.localConstants.find((c) => c.name === call.expression.text && !c.isModule && c.value);
|
|
4371
|
+
if (!fnConst?.value)
|
|
4372
|
+
return null;
|
|
4373
|
+
const fn = this.parseLiteralExpression(fnConst.value);
|
|
4374
|
+
if (!fn || !ts.isArrowFunction(fn) || fn.parameters.length !== call.arguments.length)
|
|
4375
|
+
return null;
|
|
4376
|
+
const subs = new Map;
|
|
4377
|
+
for (let i = 0;i < fn.parameters.length; i++) {
|
|
4378
|
+
const p = fn.parameters[i];
|
|
4379
|
+
if (!ts.isIdentifier(p.name))
|
|
4380
|
+
return null;
|
|
4381
|
+
subs.set(p.name.text, `(${call.arguments[i].getText()})`);
|
|
4382
|
+
}
|
|
4383
|
+
const shape = this.extractUrlBuilder(fn);
|
|
4384
|
+
if (shape)
|
|
4385
|
+
return this.emitUrlBuilder(shape, subs);
|
|
4386
|
+
if (!ts.isBlock(fn.body)) {
|
|
4387
|
+
let body = fn.body;
|
|
4388
|
+
while (ts.isParenthesizedExpression(body))
|
|
4389
|
+
body = body.expression;
|
|
4390
|
+
if (ts.isCallExpression(body) && ts.isIdentifier(body.expression) && this.localHelperNames.has(body.expression.text)) {
|
|
4391
|
+
return this.lowerUrlBuilderHelperCall(this.substituteHelperParams(body, subs));
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
return null;
|
|
4395
|
+
}
|
|
4396
|
+
extractUrlBuilder(arrow) {
|
|
4397
|
+
if (!ts.isBlock(arrow.body))
|
|
4398
|
+
return null;
|
|
4399
|
+
let builderVar = null;
|
|
4400
|
+
let base = null;
|
|
4401
|
+
const sets = [];
|
|
4402
|
+
for (const s of arrow.body.statements) {
|
|
4403
|
+
if (ts.isVariableStatement(s)) {
|
|
4404
|
+
for (const d of s.declarationList.declarations) {
|
|
4405
|
+
if (ts.isIdentifier(d.name) && d.initializer && ts.isNewExpression(d.initializer) && ts.isIdentifier(d.initializer.expression) && d.initializer.expression.text === "URLSearchParams" && (d.initializer.arguments?.length ?? 0) === 0) {
|
|
4406
|
+
builderVar = d.name.text;
|
|
4407
|
+
}
|
|
4408
|
+
}
|
|
4409
|
+
continue;
|
|
4410
|
+
}
|
|
4411
|
+
if (ts.isIfStatement(s) && !s.elseStatement && builderVar) {
|
|
4412
|
+
const set = this.matchUrlSet(s.thenStatement, builderVar);
|
|
4413
|
+
if (!set)
|
|
4414
|
+
return null;
|
|
4415
|
+
sets.push({ guard: s.expression, key: set.key, value: set.value });
|
|
4416
|
+
continue;
|
|
4417
|
+
}
|
|
4418
|
+
if (ts.isExpressionStatement(s) && builderVar) {
|
|
4419
|
+
const set = this.matchUrlSet(s, builderVar);
|
|
4420
|
+
if (set) {
|
|
4421
|
+
sets.push({ guard: null, key: set.key, value: set.value });
|
|
4422
|
+
continue;
|
|
4423
|
+
}
|
|
4424
|
+
return null;
|
|
4425
|
+
}
|
|
4426
|
+
if (ts.isReturnStatement(s) && s.expression) {
|
|
4427
|
+
let e = s.expression;
|
|
4428
|
+
while (ts.isParenthesizedExpression(e))
|
|
4429
|
+
e = e.expression;
|
|
4430
|
+
if (!ts.isConditionalExpression(e))
|
|
4431
|
+
return null;
|
|
4432
|
+
base = e.whenFalse;
|
|
4433
|
+
continue;
|
|
4434
|
+
}
|
|
4435
|
+
return null;
|
|
4436
|
+
}
|
|
4437
|
+
if (!builderVar || !base || sets.length === 0)
|
|
4438
|
+
return null;
|
|
4439
|
+
return { base, sets };
|
|
4440
|
+
}
|
|
4441
|
+
matchUrlSet(node, builderVar) {
|
|
4442
|
+
const stmt = ts.isBlock(node) ? node.statements.length === 1 ? node.statements[0] : null : node;
|
|
4443
|
+
if (!stmt || !ts.isExpressionStatement(stmt))
|
|
4444
|
+
return null;
|
|
4445
|
+
const call = stmt.expression;
|
|
4446
|
+
if (ts.isCallExpression(call) && ts.isPropertyAccessExpression(call.expression) && ts.isIdentifier(call.expression.expression) && call.expression.expression.text === builderVar && call.expression.name.text === "set" && call.arguments.length === 2 && ts.isStringLiteral(call.arguments[0])) {
|
|
4447
|
+
return { key: call.arguments[0].text, value: call.arguments[1] };
|
|
4448
|
+
}
|
|
4449
|
+
return null;
|
|
4450
|
+
}
|
|
4451
|
+
emitUrlBuilder(shape, subs) {
|
|
4452
|
+
const lowerExpr = (n) => this.convertExpressionToGo(this.substituteHelperParams(n, subs));
|
|
4453
|
+
const baseGo = wrapIfMultiToken(lowerExpr(shape.base));
|
|
4454
|
+
const parts = [baseGo];
|
|
4455
|
+
for (const set of shape.sets) {
|
|
4456
|
+
const guardGo = set.guard ? this.lowerUrlGuard(set.guard, subs) : "true";
|
|
4457
|
+
parts.push(`(${guardGo})`);
|
|
4458
|
+
parts.push(JSON.stringify(set.key));
|
|
4459
|
+
parts.push(wrapIfMultiToken(lowerExpr(set.value)));
|
|
4460
|
+
}
|
|
4461
|
+
return `bf_query ${parts.join(" ")}`;
|
|
4462
|
+
}
|
|
4463
|
+
lowerUrlGuard(guard, subs) {
|
|
4464
|
+
let g = guard;
|
|
4465
|
+
while (ts.isParenthesizedExpression(g))
|
|
4466
|
+
g = g.expression;
|
|
4467
|
+
const isBoolShape = ts.isBinaryExpression(g) && [
|
|
4468
|
+
ts.SyntaxKind.EqualsEqualsToken,
|
|
4469
|
+
ts.SyntaxKind.EqualsEqualsEqualsToken,
|
|
4470
|
+
ts.SyntaxKind.ExclamationEqualsToken,
|
|
4471
|
+
ts.SyntaxKind.ExclamationEqualsEqualsToken,
|
|
4472
|
+
ts.SyntaxKind.LessThanToken,
|
|
4473
|
+
ts.SyntaxKind.GreaterThanToken,
|
|
4474
|
+
ts.SyntaxKind.LessThanEqualsToken,
|
|
4475
|
+
ts.SyntaxKind.GreaterThanEqualsToken
|
|
4476
|
+
].includes(g.operatorToken.kind) || ts.isPrefixUnaryExpression(g) && g.operator === ts.SyntaxKind.ExclamationToken || g.kind === ts.SyntaxKind.TrueKeyword || g.kind === ts.SyntaxKind.FalseKeyword;
|
|
4477
|
+
if (isBoolShape) {
|
|
4478
|
+
return this.convertConditionToGo(this.substituteHelperParams(guard, subs)).condition;
|
|
4479
|
+
}
|
|
4480
|
+
const valueGo = wrapIfMultiToken(this.convertExpressionToGo(this.substituteHelperParams(guard, subs)));
|
|
4481
|
+
return `ne ${valueGo} ""`;
|
|
4482
|
+
}
|
|
4483
|
+
resolveStaticRecordLiteralIndex(jsExpr) {
|
|
4484
|
+
const m = /^([A-Za-z_$][\w$]*)\[\s*(?:'([^']*)'|"([^"]*)")\s*\]$/.exec(jsExpr) ?? /^([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)$/.exec(jsExpr);
|
|
4485
|
+
if (!m)
|
|
4486
|
+
return null;
|
|
4487
|
+
const key = m[2] ?? m[3];
|
|
4488
|
+
const constInfo = (this.localConstants ?? []).find((c) => c.name === m[1] && c.isModule);
|
|
4489
|
+
if (constInfo?.value === undefined)
|
|
4490
|
+
return null;
|
|
4491
|
+
const sf = ts.createSourceFile("__rec.ts", `(${constInfo.value})`, ts.ScriptTarget.Latest, true);
|
|
4492
|
+
if (sf.statements.length !== 1)
|
|
4493
|
+
return null;
|
|
4494
|
+
const stmt = sf.statements[0];
|
|
4495
|
+
if (!ts.isExpressionStatement(stmt))
|
|
4496
|
+
return null;
|
|
4497
|
+
let parsed = stmt.expression;
|
|
4498
|
+
while (ts.isParenthesizedExpression(parsed))
|
|
4499
|
+
parsed = parsed.expression;
|
|
4500
|
+
if (!ts.isObjectLiteralExpression(parsed))
|
|
4501
|
+
return null;
|
|
4502
|
+
for (const prop of parsed.properties) {
|
|
4503
|
+
if (!ts.isPropertyAssignment(prop))
|
|
4504
|
+
continue;
|
|
4505
|
+
const name = prop.name;
|
|
4506
|
+
const propKey = ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNoSubstitutionTemplateLiteral(name) ? name.text : null;
|
|
4507
|
+
if (propKey !== key)
|
|
4508
|
+
continue;
|
|
4509
|
+
let v = prop.initializer;
|
|
4510
|
+
while (ts.isParenthesizedExpression(v))
|
|
4511
|
+
v = v.expression;
|
|
4512
|
+
if (ts.isNumericLiteral(v))
|
|
4513
|
+
return v.text;
|
|
4514
|
+
if (ts.isStringLiteral(v) || ts.isNoSubstitutionTemplateLiteral(v)) {
|
|
4515
|
+
return JSON.stringify(v.text);
|
|
4516
|
+
}
|
|
4517
|
+
return null;
|
|
4518
|
+
}
|
|
4519
|
+
return null;
|
|
4520
|
+
}
|
|
3462
4521
|
makeLoc() {
|
|
3463
4522
|
return {
|
|
3464
4523
|
file: this.componentName + ".tsx",
|
|
@@ -3632,6 +4691,14 @@ ${goFields.join(`
|
|
|
3632
4691
|
}
|
|
3633
4692
|
return { preamble: obj.preamble, expr: `${obj.expr}.${this.capitalizeFieldName(expr.property)}` };
|
|
3634
4693
|
}
|
|
4694
|
+
case "index-access": {
|
|
4695
|
+
const obj = this.renderConditionExpr(expr.object);
|
|
4696
|
+
const idx = this.renderConditionExpr(expr.index);
|
|
4697
|
+
return {
|
|
4698
|
+
preamble: obj.preamble + idx.preamble,
|
|
4699
|
+
expr: `index ${wrapIfMultiToken(obj.expr)} ${wrapIfMultiToken(idx.expr)}`
|
|
4700
|
+
};
|
|
4701
|
+
}
|
|
3635
4702
|
case "binary": {
|
|
3636
4703
|
const leftNeedsParens = this.needsParensInGoTemplate(expr.left);
|
|
3637
4704
|
const leftResult = this.renderConditionExpr(expr.left);
|
|
@@ -3643,13 +4710,17 @@ ${goFields.join(`
|
|
|
3643
4710
|
let result;
|
|
3644
4711
|
switch (expr.op) {
|
|
3645
4712
|
case "===":
|
|
3646
|
-
case "==":
|
|
3647
|
-
|
|
4713
|
+
case "==": {
|
|
4714
|
+
const [el, er] = stringTolerantEqOperands(left, right);
|
|
4715
|
+
result = `eq ${el} ${er}`;
|
|
3648
4716
|
break;
|
|
4717
|
+
}
|
|
3649
4718
|
case "!==":
|
|
3650
|
-
case "!=":
|
|
3651
|
-
|
|
4719
|
+
case "!=": {
|
|
4720
|
+
const [el, er] = stringTolerantEqOperands(left, right);
|
|
4721
|
+
result = `ne ${el} ${er}`;
|
|
3652
4722
|
break;
|
|
4723
|
+
}
|
|
3653
4724
|
case ">":
|
|
3654
4725
|
result = `gt ${left} ${right}`;
|
|
3655
4726
|
break;
|
|
@@ -3840,6 +4911,45 @@ ${goFields.join(`
|
|
|
3840
4911
|
}
|
|
3841
4912
|
return null;
|
|
3842
4913
|
}
|
|
4914
|
+
queueDynamicChildrenDefine(comp) {
|
|
4915
|
+
const effectiveChildren = comp.children.length > 0 ? comp.children : this.jsxChildrenPropNodes(comp.props);
|
|
4916
|
+
if (effectiveChildren.length === 0)
|
|
4917
|
+
return null;
|
|
4918
|
+
if (this.extractTextChildren(effectiveChildren) !== null)
|
|
4919
|
+
return null;
|
|
4920
|
+
if (this.extractHtmlChildren(effectiveChildren) !== null)
|
|
4921
|
+
return null;
|
|
4922
|
+
if (this.extractScopedHtmlChildren(effectiveChildren) !== null)
|
|
4923
|
+
return null;
|
|
4924
|
+
const name = `${this.componentName}__children_${comp.slotId}`;
|
|
4925
|
+
if (!this.pendingChildrenDefines.some((d) => d.name === name)) {
|
|
4926
|
+
this.pendingChildrenDefines.push({
|
|
4927
|
+
name,
|
|
4928
|
+
content: this.renderChildren(effectiveChildren)
|
|
4929
|
+
});
|
|
4930
|
+
}
|
|
4931
|
+
return name;
|
|
4932
|
+
}
|
|
4933
|
+
queueLoopBodyChildrenDefine(comp) {
|
|
4934
|
+
const effectiveChildren = comp.children.length > 0 ? comp.children : this.jsxChildrenPropNodes(comp.props);
|
|
4935
|
+
if (effectiveChildren.length === 0)
|
|
4936
|
+
return null;
|
|
4937
|
+
if (this.extractTextChildren(effectiveChildren) !== null)
|
|
4938
|
+
return null;
|
|
4939
|
+
if (this.extractHtmlChildren(effectiveChildren) !== null)
|
|
4940
|
+
return null;
|
|
4941
|
+
if (this.extractScopedHtmlChildren(effectiveChildren) !== null)
|
|
4942
|
+
return null;
|
|
4943
|
+
const name = `${this.componentName}__loop_children_${comp.slotId}`;
|
|
4944
|
+
if (!this.pendingChildrenDefines.some((d) => d.name === name)) {
|
|
4945
|
+
const wasInLoop = this.inLoop;
|
|
4946
|
+
this.inLoop = false;
|
|
4947
|
+
const content = this.renderChildren(effectiveChildren);
|
|
4948
|
+
this.inLoop = wasInLoop;
|
|
4949
|
+
this.pendingChildrenDefines.push({ name, content });
|
|
4950
|
+
}
|
|
4951
|
+
return name;
|
|
4952
|
+
}
|
|
3843
4953
|
renderComponent(comp, ctx) {
|
|
3844
4954
|
if (comp.name === "Portal") {
|
|
3845
4955
|
return this.renderPortalComponent(comp);
|
|
@@ -3849,10 +4959,16 @@ ${goFields.join(`
|
|
|
3849
4959
|
}
|
|
3850
4960
|
let templateCall;
|
|
3851
4961
|
if (this.inLoop) {
|
|
3852
|
-
|
|
4962
|
+
const loopBodyDefine = this.queueLoopBodyChildrenDefine(comp);
|
|
4963
|
+
if (loopBodyDefine) {
|
|
4964
|
+
templateCall = `{{template "${comp.name}" (bf_with_children . (bf_tmpl "${loopBodyDefine}" .))}}`;
|
|
4965
|
+
} else {
|
|
4966
|
+
templateCall = `{{template "${comp.name}" .}}`;
|
|
4967
|
+
}
|
|
3853
4968
|
} else if (comp.slotId) {
|
|
3854
4969
|
const suffix = slotIdToFieldSuffix(comp.slotId);
|
|
3855
|
-
|
|
4970
|
+
const childrenDefine = this.queueDynamicChildrenDefine(comp);
|
|
4971
|
+
templateCall = childrenDefine ? `{{template "${comp.name}" (bf_with_children .${comp.name}${suffix} (bf_tmpl "${childrenDefine}" .))}}` : `{{template "${comp.name}" .${comp.name}${suffix}}}`;
|
|
3856
4972
|
} else {
|
|
3857
4973
|
templateCall = `{{template "${comp.name}" .${comp.name}}}`;
|
|
3858
4974
|
}
|
|
@@ -3899,10 +5015,22 @@ ${children}`;
|
|
|
3899
5015
|
}
|
|
3900
5016
|
if (isBooleanAttr(name) || value.presenceOrUndefined) {
|
|
3901
5017
|
const { condition: goCond, preamble } = this.convertConditionToGo(value.expr);
|
|
3902
|
-
|
|
5018
|
+
const body = name.startsWith("aria-") ? `${name}="true"` : name;
|
|
5019
|
+
return `${preamble}{{if ${goCond}}}${body}{{end}}`;
|
|
3903
5020
|
}
|
|
3904
5021
|
const parsed = parseExpression(value.expr.trim());
|
|
3905
|
-
if (parsed.kind === "conditional"
|
|
5022
|
+
if (parsed.kind === "conditional") {
|
|
5023
|
+
const undef = (e) => e.kind === "identifier" && (e.name === "undefined" || e.name === "null") || e.kind === "literal" && (e.value === null || e.value === undefined);
|
|
5024
|
+
const test = parsed.test;
|
|
5025
|
+
if (undef(parsed.alternate) && !undef(parsed.consequent)) {
|
|
5026
|
+
const { condition: goCond, preamble } = this.convertConditionToGo(this.isTemplateFragment(this.renderParsedExpr(test), test.kind) ? value.expr : value.expr.slice(0, value.expr.indexOf("?")).trim());
|
|
5027
|
+
const valueExpr = this.renderParsedExpr(parsed.consequent);
|
|
5028
|
+
const body = `${name}="{{${valueExpr}}}"`;
|
|
5029
|
+
return `${preamble}{{if ${goCond}}}${body}{{end}}`;
|
|
5030
|
+
}
|
|
5031
|
+
return `${name}="${this.renderParsedExpr(parsed)}"`;
|
|
5032
|
+
}
|
|
5033
|
+
if (parsed.kind === "template-literal") {
|
|
3906
5034
|
return `${name}="${this.renderParsedExpr(parsed)}"`;
|
|
3907
5035
|
}
|
|
3908
5036
|
const bareId = value.expr.trim();
|
|
@@ -3911,7 +5039,9 @@ ${children}`;
|
|
|
3911
5039
|
const field = `.${this.capitalizeFieldName(propName)}`;
|
|
3912
5040
|
return `{{if ne ${field} nil}}${name}="{{${this.convertExpressionToGo(value.expr)}}}"{{end}}`;
|
|
3913
5041
|
}
|
|
3914
|
-
|
|
5042
|
+
const exprOut = {};
|
|
5043
|
+
const go = this.convertExpressionToGo(value.expr, exprOut);
|
|
5044
|
+
return this.isTemplateFragment(go, exprOut.parsed?.kind) ? `${name}="${go}"` : `${name}="{{${go}}}"`;
|
|
3915
5045
|
},
|
|
3916
5046
|
emitBooleanAttr: (_value, name) => name,
|
|
3917
5047
|
emitSpread: (value) => {
|
|
@@ -3985,7 +5115,21 @@ ${children}`;
|
|
|
3985
5115
|
}
|
|
3986
5116
|
const inner = s.slice(open + 2, close).trim();
|
|
3987
5117
|
if (inner) {
|
|
3988
|
-
|
|
5118
|
+
const cls = {};
|
|
5119
|
+
const goExpr = this.convertExpressionToGo(inner, cls);
|
|
5120
|
+
const parsed = cls.parsed;
|
|
5121
|
+
if (parsed?.kind === "template-literal") {
|
|
5122
|
+
for (const part of parsed.parts) {
|
|
5123
|
+
if (part.type === "string") {
|
|
5124
|
+
out += this.escapeAttrText(part.value);
|
|
5125
|
+
} else {
|
|
5126
|
+
const e = this.renderParsedExpr(part.expr);
|
|
5127
|
+
out += this.isTemplateFragment(e, part.expr.kind) ? e : `{{${e}}}`;
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5130
|
+
} else {
|
|
5131
|
+
out += this.isTemplateFragment(goExpr, parsed?.kind) ? goExpr : `{{${goExpr}}}`;
|
|
5132
|
+
}
|
|
3989
5133
|
} else {
|
|
3990
5134
|
out += s.slice(open, close + 1);
|
|
3991
5135
|
}
|
|
@@ -4005,7 +5149,8 @@ ${children}`;
|
|
|
4005
5149
|
const { condition: goCond, preamble } = this.convertConditionToGo(part.condition);
|
|
4006
5150
|
output += `${preamble}{{if ${goCond}}}${part.whenTrue}{{else}}${part.whenFalse}{{end}}`;
|
|
4007
5151
|
} else if (part.type === "lookup") {
|
|
4008
|
-
const
|
|
5152
|
+
const rawKeyExpr = this.convertExpressionToGo(part.key);
|
|
5153
|
+
const keyExpr = /\s/.test(rawKeyExpr) ? `(${rawKeyExpr})` : rawKeyExpr;
|
|
4009
5154
|
const caseEntries = Object.entries(part.cases);
|
|
4010
5155
|
if (caseEntries.length === 0)
|
|
4011
5156
|
continue;
|
|
@@ -4154,6 +5299,8 @@ function combineGoTypes(options) {
|
|
|
4154
5299
|
const stdlibImports = [` "math/rand"`];
|
|
4155
5300
|
if (/\bfmt\./.test(usageScan))
|
|
4156
5301
|
stdlibImports.push(` "fmt"`);
|
|
5302
|
+
if (/\bstrings\./.test(usageScan))
|
|
5303
|
+
stdlibImports.push(` "strings"`);
|
|
4157
5304
|
if (/\btemplate\.HTML\b/.test(usageScan))
|
|
4158
5305
|
stdlibImports.push(` "html/template"`);
|
|
4159
5306
|
stdlibImports.sort();
|