@barefootjs/go-template 0.18.7 → 0.19.1
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/emit-context.d.ts +11 -5
- package/dist/adapter/emit-context.d.ts.map +1 -1
- package/dist/adapter/go-template-adapter.d.ts +45 -6
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +326 -38
- package/dist/adapter/lib/compile-state.d.ts +32 -0
- package/dist/adapter/lib/compile-state.d.ts.map +1 -1
- package/dist/adapter/lib/types.d.ts +9 -0
- package/dist/adapter/lib/types.d.ts.map +1 -1
- package/dist/adapter/memo/memo-compute.d.ts +8 -0
- package/dist/adapter/memo/memo-compute.d.ts.map +1 -1
- package/dist/adapter/props/prop-types.d.ts +97 -0
- package/dist/adapter/props/prop-types.d.ts.map +1 -1
- package/dist/adapter/value/value-lowering.d.ts +9 -1
- package/dist/adapter/value/value-lowering.d.ts.map +1 -1
- package/dist/build.js +326 -38
- package/dist/index.js +326 -38
- package/package.json +3 -3
- package/src/__tests__/derived-state-memo.test.ts +10 -3
- package/src/__tests__/go-template-adapter.test.ts +282 -23
- package/src/adapter/emit-context.ts +14 -5
- package/src/adapter/go-template-adapter.ts +291 -50
- package/src/adapter/lib/compile-state.ts +36 -0
- package/src/adapter/lib/types.ts +9 -0
- package/src/adapter/memo/memo-compute.ts +123 -9
- package/src/adapter/props/prop-types.ts +310 -1
- package/src/adapter/value/value-lowering.ts +58 -5
- package/src/test-render.ts +18 -4
package/dist/build.js
CHANGED
|
@@ -364,7 +364,7 @@ var init_path = __esm(() => {
|
|
|
364
364
|
// src/adapter/go-template-adapter.ts
|
|
365
365
|
import {
|
|
366
366
|
BaseAdapter,
|
|
367
|
-
isBooleanAttr,
|
|
367
|
+
isBooleanAttr as isBooleanAttr2,
|
|
368
368
|
parseExpression as parseExpression4,
|
|
369
369
|
stringifyParsedExpr as stringifyParsedExpr2,
|
|
370
370
|
parseStyleObjectEntries,
|
|
@@ -706,6 +706,10 @@ class CompileState {
|
|
|
706
706
|
hoistedMemoLocals = new Map;
|
|
707
707
|
loweringMatchers = [];
|
|
708
708
|
nillablePropNames = new Set;
|
|
709
|
+
nullishConsumedPropNames = new Set;
|
|
710
|
+
omittableAttrConsumedPropNames = new Set;
|
|
711
|
+
textConsumedPropNames = new Set;
|
|
712
|
+
presenceCheckedPropNames = new Set;
|
|
709
713
|
stringValueNames = new Set;
|
|
710
714
|
rootScopeNodes = new Set;
|
|
711
715
|
memoBackedLoopSlice = new Map;
|
|
@@ -1449,15 +1453,28 @@ function parsedLiteralToGo(ctx, expr, typeInfo) {
|
|
|
1449
1453
|
|
|
1450
1454
|
// src/adapter/value/value-lowering.ts
|
|
1451
1455
|
var EMPTY_PROP_FALLBACK_VARS = new Map;
|
|
1456
|
+
function nillableAwarePropRef(ctx, propName, expectedType) {
|
|
1457
|
+
const fieldRef = `in.${capitalizeFieldName(propName)}`;
|
|
1458
|
+
const scalar = expectedType.kind === "primitive" ? expectedType : expectedType.kind === "union" && expectedType.unionTypes?.length === 2 ? expectedType.unionTypes.find((t) => t.primitive !== "undefined" && t.primitive !== "null") : undefined;
|
|
1459
|
+
if (ctx.state.nillablePropNames.has(propName) && scalar?.kind === "primitive") {
|
|
1460
|
+
const goType = scalar.primitive === "boolean" ? "bool" : scalar.primitive === "number" ? "float64" : scalar.primitive === "string" ? "string" : null;
|
|
1461
|
+
if (goType) {
|
|
1462
|
+
const zero = goType === "bool" ? "false" : goType === "string" ? '""' : "0";
|
|
1463
|
+
return `func() ${goType} { if v, ok := ${fieldRef}.(${goType}); ok { return v }; return ${zero} }()`;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
return fieldRef;
|
|
1467
|
+
}
|
|
1452
1468
|
function convertInitialValue(ctx, value, typeInfo, propsParams, preParsed) {
|
|
1469
|
+
const propRef = (propName2) => nillableAwarePropRef(ctx, propName2, typeInfo);
|
|
1453
1470
|
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value)) {
|
|
1454
1471
|
if (propsParams?.some((p) => p.name === value)) {
|
|
1455
|
-
return
|
|
1472
|
+
return propRef(value);
|
|
1456
1473
|
}
|
|
1457
1474
|
}
|
|
1458
|
-
const propName = ctx.extractPropNameFromInitialValue(value);
|
|
1475
|
+
const propName = ctx.extractPropNameFromInitialValue(value, preParsed);
|
|
1459
1476
|
if (propName && propsParams?.some((p) => p.name === propName)) {
|
|
1460
|
-
return
|
|
1477
|
+
return propRef(propName);
|
|
1461
1478
|
}
|
|
1462
1479
|
if (typeInfo.kind === "primitive") {
|
|
1463
1480
|
if (typeInfo.primitive === "boolean") {
|
|
@@ -1524,19 +1541,20 @@ function objectLiteralToGoMap(ctx, expr) {
|
|
|
1524
1541
|
return null;
|
|
1525
1542
|
return `map[string]interface{}{${entries.join(", ")}}`;
|
|
1526
1543
|
}
|
|
1527
|
-
function getSignalInitialValueAsGo(ctx, initialValue, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS) {
|
|
1544
|
+
function getSignalInitialValueAsGo(ctx, initialValue, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS, signalType) {
|
|
1545
|
+
const propRef = (propName2) => signalType ? nillableAwarePropRef(ctx, propName2, signalType) : `in.${capitalizeFieldName(propName2)}`;
|
|
1528
1546
|
if (propsParams.some((p) => p.name === initialValue)) {
|
|
1529
1547
|
const hoisted = propFallbackVars.get(initialValue);
|
|
1530
1548
|
if (hoisted)
|
|
1531
1549
|
return hoisted.varName;
|
|
1532
|
-
return
|
|
1550
|
+
return propRef(initialValue);
|
|
1533
1551
|
}
|
|
1534
1552
|
const propName = ctx.extractPropNameFromInitialValue(initialValue);
|
|
1535
1553
|
if (propName && propsParams.some((p) => p.name === propName)) {
|
|
1536
1554
|
const hoisted = propFallbackVars.get(propName);
|
|
1537
1555
|
if (hoisted)
|
|
1538
1556
|
return hoisted.varName;
|
|
1539
|
-
return
|
|
1557
|
+
return propRef(propName);
|
|
1540
1558
|
}
|
|
1541
1559
|
if (/^-?\d+$/.test(initialValue)) {
|
|
1542
1560
|
return initialValue;
|
|
@@ -2006,9 +2024,34 @@ var EMPTY_PROP_FALLBACK_VARS2 = new Map;
|
|
|
2006
2024
|
function getterCallName(e) {
|
|
2007
2025
|
return e.kind === "call" && e.callee.kind === "identifier" && e.args.length === 0 ? e.callee.name : null;
|
|
2008
2026
|
}
|
|
2027
|
+
function isBooleanTypeInfo(t) {
|
|
2028
|
+
if (t.kind === "primitive")
|
|
2029
|
+
return t.primitive === "boolean";
|
|
2030
|
+
if (t.kind === "union" && t.unionTypes?.length === 2) {
|
|
2031
|
+
const scalar = t.unionTypes.find((u) => u.primitive !== "undefined" && u.primitive !== "null");
|
|
2032
|
+
return scalar?.kind === "primitive" && scalar.primitive === "boolean";
|
|
2033
|
+
}
|
|
2034
|
+
return false;
|
|
2035
|
+
}
|
|
2036
|
+
function isBooleanTypedGetter(ctx, name, signals) {
|
|
2037
|
+
const signal = signals.find((s) => s.getter === name);
|
|
2038
|
+
if (signal)
|
|
2039
|
+
return signal.type !== undefined && isBooleanTypeInfo(signal.type);
|
|
2040
|
+
const memo = (ctx.state.currentMemos ?? []).find((m) => m.name === name);
|
|
2041
|
+
return memo?.type !== undefined && isBooleanTypeInfo(memo.type);
|
|
2042
|
+
}
|
|
2009
2043
|
function propsMemberName(e) {
|
|
2010
2044
|
return e.kind === "member" && !e.computed && e.object.kind === "identifier" && e.object.name === "props" ? e.property : null;
|
|
2011
2045
|
}
|
|
2046
|
+
function propNameForPropsBinding(ctx, e) {
|
|
2047
|
+
const propsObject = ctx.state.propsObjectName;
|
|
2048
|
+
if (e.kind === "member" && !e.computed && e.object.kind === "identifier" && e.object.name === propsObject) {
|
|
2049
|
+
return e.property;
|
|
2050
|
+
}
|
|
2051
|
+
if (!propsObject && e.kind === "identifier")
|
|
2052
|
+
return e.name;
|
|
2053
|
+
return null;
|
|
2054
|
+
}
|
|
2012
2055
|
function matchFilterArmMemo(ctx, body, signals, propsParams) {
|
|
2013
2056
|
const cb = asCallbackMethodCall2(body);
|
|
2014
2057
|
if (!cb || cb.method !== "filter")
|
|
@@ -2147,6 +2190,17 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
|
|
|
2147
2190
|
if (propName)
|
|
2148
2191
|
return propRef(propName);
|
|
2149
2192
|
}
|
|
2193
|
+
if (body.kind === "binary" && (body.op === "!==" || body.op === "!=" || body.op === "===" || body.op === "==")) {
|
|
2194
|
+
const other = body.right.kind === "identifier" && body.right.name === "undefined" ? body.left : body.left.kind === "identifier" && body.left.name === "undefined" ? body.right : null;
|
|
2195
|
+
const propName = other ? propNameForPropsBinding(ctx, other) : null;
|
|
2196
|
+
if (propName) {
|
|
2197
|
+
const param = propsParams.find((p) => p.name === propName);
|
|
2198
|
+
if (param && ctx.state.nillablePropNames.has(propName)) {
|
|
2199
|
+
const isNe = body.op === "!==" || body.op === "!=";
|
|
2200
|
+
return `in.${capitalizeFieldName(propName)} ${isNe ? "!=" : "=="} nil`;
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2150
2204
|
if (body.kind === "conditional") {
|
|
2151
2205
|
const condName = getterCallName(body.test);
|
|
2152
2206
|
if (condName) {
|
|
@@ -2181,6 +2235,18 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
|
|
|
2181
2235
|
}
|
|
2182
2236
|
}
|
|
2183
2237
|
}
|
|
2238
|
+
if (condName) {
|
|
2239
|
+
const tName = getterCallName(body.consequent);
|
|
2240
|
+
const fName = getterCallName(body.alternate);
|
|
2241
|
+
if (tName && fName && isBooleanTypedGetter(ctx, tName, signals) && isBooleanTypedGetter(ctx, fName, signals)) {
|
|
2242
|
+
const condGo = resolveGetterValueAsGo(ctx, condName, signals, propsParams, propFallbackVars, resolving);
|
|
2243
|
+
const tGo = resolveGetterValueAsGo(ctx, tName, signals, propsParams, propFallbackVars, resolving);
|
|
2244
|
+
const fGo = resolveGetterValueAsGo(ctx, fName, signals, propsParams, propFallbackVars, resolving);
|
|
2245
|
+
if (condGo !== null && tGo !== null && fGo !== null) {
|
|
2246
|
+
return `func() bool { if ${condGo} { return ${tGo} }; return ${fGo} }()`;
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2184
2250
|
}
|
|
2185
2251
|
if (body.kind === "binary" && ["*", "+", "-", "/"].includes(body.op) && body.right.kind === "literal" && body.right.literalType === "number" && typeof body.right.value === "number" && Number.isInteger(body.right.value) && body.right.value >= 0) {
|
|
2186
2252
|
const operator = body.op;
|
|
@@ -2269,7 +2335,7 @@ function computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFall
|
|
|
2269
2335
|
function resolveGetterValueAsGo(ctx, name, signals, propsParams, propFallbackVars, resolving = new Set) {
|
|
2270
2336
|
const signal = signals.find((s) => s.getter === name);
|
|
2271
2337
|
if (signal) {
|
|
2272
|
-
return getSignalInitialValueAsGo(ctx, signal.initialValue, propsParams, propFallbackVars);
|
|
2338
|
+
return getSignalInitialValueAsGo(ctx, signal.initialValue, propsParams, propFallbackVars, signal.type);
|
|
2273
2339
|
}
|
|
2274
2340
|
const memo = (ctx.state.currentMemos ?? []).find((m) => m.name === name);
|
|
2275
2341
|
if (memo) {
|
|
@@ -2599,11 +2665,12 @@ function recordIndexAccessToGoMap(ctx, val, ir) {
|
|
|
2599
2665
|
}
|
|
2600
2666
|
|
|
2601
2667
|
// src/adapter/props/prop-types.ts
|
|
2668
|
+
import { isBooleanAttr } from "@barefootjs/jsx";
|
|
2602
2669
|
function buildPropTypeOverrides(ctx, ir) {
|
|
2603
2670
|
const overrides = new Map;
|
|
2604
2671
|
for (const signal of ir.metadata.signals) {
|
|
2605
2672
|
const propNames = [signal.initialValue];
|
|
2606
|
-
const extracted = ctx.extractPropNameFromInitialValue(signal.initialValue);
|
|
2673
|
+
const extracted = ctx.extractPropNameFromInitialValue(signal.initialValue, signal.parsed);
|
|
2607
2674
|
if (extracted)
|
|
2608
2675
|
propNames.push(extracted);
|
|
2609
2676
|
for (const propName of propNames) {
|
|
@@ -2660,11 +2727,146 @@ function collectToFixedPropNames(root) {
|
|
|
2660
2727
|
walk(root);
|
|
2661
2728
|
return names;
|
|
2662
2729
|
}
|
|
2730
|
+
var NULLISH_SCALAR_GO_TYPES = new Set(["string", "int", "float64", "bool"]);
|
|
2731
|
+
function collectNullishConsumedPropNames(ctx, ir) {
|
|
2732
|
+
const names = new Set;
|
|
2733
|
+
const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null).map((p) => p.name));
|
|
2734
|
+
if (optionalParams.size === 0)
|
|
2735
|
+
return names;
|
|
2736
|
+
const propsObject = ctx.state.propsObjectName;
|
|
2737
|
+
const propNameOfLeft = (left) => {
|
|
2738
|
+
if (left.kind === "identifier")
|
|
2739
|
+
return left.name;
|
|
2740
|
+
if (left.kind === "member" && !left.computed && left.object.kind === "identifier") {
|
|
2741
|
+
if (left.object.name === propsObject)
|
|
2742
|
+
return left.property;
|
|
2743
|
+
return left.object.name;
|
|
2744
|
+
}
|
|
2745
|
+
return null;
|
|
2746
|
+
};
|
|
2747
|
+
const isZeroEquivalentLiteral = (right) => right.kind === "literal" && (right.value === "" || right.value === false || right.value === null || right.literalType === "number" && Number(right.value) === 0);
|
|
2748
|
+
const walk = (node) => {
|
|
2749
|
+
if (!node || typeof node !== "object")
|
|
2750
|
+
return;
|
|
2751
|
+
if (Array.isArray(node)) {
|
|
2752
|
+
for (const item of node)
|
|
2753
|
+
walk(item);
|
|
2754
|
+
return;
|
|
2755
|
+
}
|
|
2756
|
+
const rec = node;
|
|
2757
|
+
if (rec.kind === "logical" && rec.op === "??" && rec.left && rec.right) {
|
|
2758
|
+
const propName = propNameOfLeft(rec.left);
|
|
2759
|
+
if (propName && optionalParams.has(propName) && !isZeroEquivalentLiteral(rec.right)) {
|
|
2760
|
+
names.add(propName);
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
for (const value of Object.values(rec))
|
|
2764
|
+
walk(value);
|
|
2765
|
+
};
|
|
2766
|
+
walk(ir.root);
|
|
2767
|
+
for (const signal of ir.metadata.signals) {
|
|
2768
|
+
const match = ctx.extractPropFallback(signal.initialValue, signal.parsed);
|
|
2769
|
+
if (!match || !optionalParams.has(match.propName))
|
|
2770
|
+
continue;
|
|
2771
|
+
const f = match.goFallback;
|
|
2772
|
+
if (f === '""' || f === "false" || f === "nil" || Number(f) === 0)
|
|
2773
|
+
continue;
|
|
2774
|
+
names.add(match.propName);
|
|
2775
|
+
}
|
|
2776
|
+
return names;
|
|
2777
|
+
}
|
|
2778
|
+
function collectOmittableAttrConsumedPropNames(ctx, ir) {
|
|
2779
|
+
const names = new Set;
|
|
2780
|
+
const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null).map((p) => p.name));
|
|
2781
|
+
if (optionalParams.size === 0)
|
|
2782
|
+
return names;
|
|
2783
|
+
const propsObject = ctx.state.propsObjectName;
|
|
2784
|
+
const walk = (node) => {
|
|
2785
|
+
if (!node || typeof node !== "object")
|
|
2786
|
+
return;
|
|
2787
|
+
if (Array.isArray(node)) {
|
|
2788
|
+
for (const item of node)
|
|
2789
|
+
walk(item);
|
|
2790
|
+
return;
|
|
2791
|
+
}
|
|
2792
|
+
const rec = node;
|
|
2793
|
+
if (rec.type === "element" && Array.isArray(rec.attrs)) {
|
|
2794
|
+
for (const attr of rec.attrs) {
|
|
2795
|
+
if (attr.name === "class" || attr.name === "className" || attr.name === "style")
|
|
2796
|
+
continue;
|
|
2797
|
+
if (isBooleanAttr(attr.name))
|
|
2798
|
+
continue;
|
|
2799
|
+
if (attr.value?.kind !== "expression" || attr.value.presenceOrUndefined)
|
|
2800
|
+
continue;
|
|
2801
|
+
const bareId = String(attr.value.expr ?? "").trim();
|
|
2802
|
+
const propName = propsObject && bareId.startsWith(`${propsObject}.`) ? bareId.slice(propsObject.length + 1) : bareId;
|
|
2803
|
+
if (/^[A-Za-z_$][\w$]*$/.test(propName) && optionalParams.has(propName)) {
|
|
2804
|
+
names.add(propName);
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
for (const value of Object.values(rec))
|
|
2809
|
+
walk(value);
|
|
2810
|
+
};
|
|
2811
|
+
walk(ir.root);
|
|
2812
|
+
return names;
|
|
2813
|
+
}
|
|
2814
|
+
function collectTextConsumedPropNames(ctx, ir) {
|
|
2815
|
+
const names = new Set;
|
|
2816
|
+
const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null && p.type.kind === "primitive").map((p) => p.name));
|
|
2817
|
+
if (optionalParams.size === 0)
|
|
2818
|
+
return names;
|
|
2819
|
+
const propsObject = ctx.state.propsObjectName;
|
|
2820
|
+
const walk = (node) => {
|
|
2821
|
+
if (!node || typeof node !== "object")
|
|
2822
|
+
return;
|
|
2823
|
+
if (Array.isArray(node)) {
|
|
2824
|
+
for (const item of node)
|
|
2825
|
+
walk(item);
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
const rec = node;
|
|
2829
|
+
if (rec.type === "expression") {
|
|
2830
|
+
const bareId = String(rec.expr ?? "").trim();
|
|
2831
|
+
const propName = propsObject && bareId.startsWith(`${propsObject}.`) ? bareId.slice(propsObject.length + 1) : bareId;
|
|
2832
|
+
if (/^[A-Za-z_$][\w$]*$/.test(propName) && optionalParams.has(propName)) {
|
|
2833
|
+
names.add(propName);
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
for (const value of Object.values(rec))
|
|
2837
|
+
walk(value);
|
|
2838
|
+
};
|
|
2839
|
+
walk(ir.root);
|
|
2840
|
+
return names;
|
|
2841
|
+
}
|
|
2842
|
+
function collectPresenceCheckedPropNames(ctx, ir) {
|
|
2843
|
+
const names = new Set;
|
|
2844
|
+
const optionalParams = new Set(ir.metadata.propsParams.filter((p) => p.optional && p.defaultValue == null && p.type.kind === "primitive").map((p) => p.name));
|
|
2845
|
+
if (optionalParams.size === 0)
|
|
2846
|
+
return names;
|
|
2847
|
+
const propsObject = ctx.state.propsObjectName;
|
|
2848
|
+
const isUndefinedCheck = (op) => op === "!==" || op === "!=" || op === "===" || op === "==";
|
|
2849
|
+
for (const memo of ir.metadata.memos) {
|
|
2850
|
+
const body = memo.parsed;
|
|
2851
|
+
if (!body || body.kind !== "binary" || !isUndefinedCheck(body.op))
|
|
2852
|
+
continue;
|
|
2853
|
+
const other = body.right.kind === "identifier" && body.right.name === "undefined" ? body.left : body.left.kind === "identifier" && body.left.name === "undefined" ? body.right : null;
|
|
2854
|
+
if (!other)
|
|
2855
|
+
continue;
|
|
2856
|
+
const propName = other.kind === "member" && !other.computed && other.object.kind === "identifier" && other.object.name === propsObject ? other.property : !propsObject && other.kind === "identifier" ? other.name : null;
|
|
2857
|
+
if (propName && optionalParams.has(propName))
|
|
2858
|
+
names.add(propName);
|
|
2859
|
+
}
|
|
2860
|
+
return names;
|
|
2861
|
+
}
|
|
2663
2862
|
function resolvePropGoType(ctx, param, propTypeOverrides) {
|
|
2664
2863
|
const base = propTypeOverrides.get(param.name) ?? typeInfoToGo(ctx, param.type, param.defaultValue);
|
|
2665
2864
|
if (param.optional && ctx.state.localStructFields.has(base)) {
|
|
2666
2865
|
return "map[string]interface{}";
|
|
2667
2866
|
}
|
|
2867
|
+
if (param.optional && param.type.kind === "primitive" && (ctx.state.nullishConsumedPropNames.has(param.name) || ctx.state.omittableAttrConsumedPropNames.has(param.name) || ctx.state.textConsumedPropNames.has(param.name) || ctx.state.presenceCheckedPropNames.has(param.name)) && NULLISH_SCALAR_GO_TYPES.has(base)) {
|
|
2868
|
+
return "interface{}";
|
|
2869
|
+
}
|
|
2668
2870
|
return base;
|
|
2669
2871
|
}
|
|
2670
2872
|
function collectNillablePropNames(ctx, ir) {
|
|
@@ -2743,8 +2945,8 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2743
2945
|
state: this.state,
|
|
2744
2946
|
convertExpressionToGo: (jsExpr, out, preParsed) => this.convertExpressionToGo(jsExpr, out, preParsed),
|
|
2745
2947
|
convertConditionToGo: (jsCondition, preParsed) => this.convertConditionToGo(jsCondition, preParsed),
|
|
2746
|
-
extractPropNameFromInitialValue: (initialValue) => this.extractPropNameFromInitialValue(initialValue),
|
|
2747
|
-
extractPropFallback: (initialValue) => this.extractPropFallback(initialValue),
|
|
2948
|
+
extractPropNameFromInitialValue: (initialValue, preParsed) => this.extractPropNameFromInitialValue(initialValue, preParsed),
|
|
2949
|
+
extractPropFallback: (initialValue, preParsed) => this.extractPropFallback(initialValue, preParsed),
|
|
2748
2950
|
resolveModuleStringConst: (name) => this.resolveModuleStringConst(name)
|
|
2749
2951
|
};
|
|
2750
2952
|
get errors() {
|
|
@@ -2796,6 +2998,12 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2796
2998
|
}
|
|
2797
2999
|
this.state.loweringMatchers = prepareLoweringMatchers(ir.metadata);
|
|
2798
3000
|
augmentInheritedPropAccesses(ir);
|
|
3001
|
+
this.buildLocalTypeTables(ir, ir.metadata.componentName);
|
|
3002
|
+
this.state.nullishConsumedPropNames = collectNullishConsumedPropNames(this.emitCtx, ir);
|
|
3003
|
+
this.state.omittableAttrConsumedPropNames = collectOmittableAttrConsumedPropNames(this.emitCtx, ir);
|
|
3004
|
+
this.state.textConsumedPropNames = collectTextConsumedPropNames(this.emitCtx, ir);
|
|
3005
|
+
this.state.presenceCheckedPropNames = collectPresenceCheckedPropNames(this.emitCtx, ir);
|
|
3006
|
+
this.state.nillablePropNames = collectNillablePropNames(this.emitCtx, ir);
|
|
2799
3007
|
}
|
|
2800
3008
|
generate(ir, options) {
|
|
2801
3009
|
this.state.componentName = ir.metadata.componentName;
|
|
@@ -2804,7 +3012,6 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2804
3012
|
this.state.templateVarCounter = 0;
|
|
2805
3013
|
this.state.pendingChildrenDefines = [];
|
|
2806
3014
|
this.primeCompileState(ir);
|
|
2807
|
-
this.state.nillablePropNames = collectNillablePropNames(this.emitCtx, ir);
|
|
2808
3015
|
this.state.stringValueNames = collectStringValueNames(ir);
|
|
2809
3016
|
if (!options?.siblingTemplatesRegistered) {
|
|
2810
3017
|
this.checkImportedLoopChildComponents(ir);
|
|
@@ -3323,10 +3530,18 @@ ${goFields.join(`
|
|
|
3323
3530
|
this.emitStaticBodyWrappers(lines, ir, componentName, staticWithBody, emittedWrapperVars);
|
|
3324
3531
|
const propFallbackVars = this.collectPropFallbackVars(ir);
|
|
3325
3532
|
for (const [, info] of propFallbackVars) {
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3533
|
+
if (info.assertType) {
|
|
3534
|
+
const deref = info.assertType === "int" ? `bf.ToInt(in.${info.fieldName})` : info.assertType === "float64" ? `bf.ToFloat64(in.${info.fieldName})` : `in.${info.fieldName}.(${info.assertType})`;
|
|
3535
|
+
lines.push(` var ${info.varName} ${info.assertType} = ${info.goFallback}`);
|
|
3536
|
+
lines.push(` if in.${info.fieldName} != nil {`);
|
|
3537
|
+
lines.push(` ${info.varName} = ${deref}`);
|
|
3538
|
+
lines.push(` }`);
|
|
3539
|
+
} else {
|
|
3540
|
+
lines.push(` ${info.varName} := in.${info.fieldName}`);
|
|
3541
|
+
lines.push(` if ${info.varName} == ${info.zeroLiteral} {`);
|
|
3542
|
+
lines.push(` ${info.varName} = ${info.goFallback}`);
|
|
3543
|
+
lines.push(` }`);
|
|
3544
|
+
}
|
|
3330
3545
|
}
|
|
3331
3546
|
if (propFallbackVars.size > 0)
|
|
3332
3547
|
lines.push("");
|
|
@@ -3406,7 +3621,7 @@ ${goFields.join(`
|
|
|
3406
3621
|
const fieldName = capitalizeFieldName(signal.getter);
|
|
3407
3622
|
if (propFieldNames.has(fieldName))
|
|
3408
3623
|
continue;
|
|
3409
|
-
const fallbackMatch = this.extractPropFallback(signal.initialValue);
|
|
3624
|
+
const fallbackMatch = this.extractPropFallback(signal.initialValue, signal.parsed);
|
|
3410
3625
|
const hoisted = fallbackMatch ? propFallbackVars.get(fallbackMatch.propName) : undefined;
|
|
3411
3626
|
if (hoisted) {
|
|
3412
3627
|
lines.push(` ${fieldName}: ${hoisted.varName},`);
|
|
@@ -4252,8 +4467,9 @@ ${goFields.join(`
|
|
|
4252
4467
|
for (const nested of findNestedComponents(ir.root)) {
|
|
4253
4468
|
localTaken.add(`${nested.name.charAt(0).toLowerCase()}${nested.name.slice(1)}s`);
|
|
4254
4469
|
}
|
|
4470
|
+
const propTypeOverrides = buildPropTypeOverrides(this.emitCtx, ir);
|
|
4255
4471
|
for (const signal of ir.metadata.signals) {
|
|
4256
|
-
const match = this.extractPropFallback(signal.initialValue);
|
|
4472
|
+
const match = this.extractPropFallback(signal.initialValue, signal.parsed);
|
|
4257
4473
|
if (!match)
|
|
4258
4474
|
continue;
|
|
4259
4475
|
if (result.has(match.propName))
|
|
@@ -4264,6 +4480,8 @@ ${goFields.join(`
|
|
|
4264
4480
|
if (goPropDefault(param.defaultValue) !== null)
|
|
4265
4481
|
continue;
|
|
4266
4482
|
const fieldName = capitalizeFieldName(match.propName);
|
|
4483
|
+
const concreteType = propTypeOverrides.get(param.name) ?? typeInfoToGo(this.emitCtx, param.type, param.defaultValue);
|
|
4484
|
+
const nullishLowered = NULLISH_SCALAR_GO_TYPES.has(concreteType) && resolvePropGoType(this.emitCtx, param, propTypeOverrides) === "interface{}";
|
|
4267
4485
|
let zeroLiteral;
|
|
4268
4486
|
if (match.goFallback === "true" || match.goFallback === "false") {
|
|
4269
4487
|
zeroLiteral = "false";
|
|
@@ -4274,20 +4492,31 @@ ${goFields.join(`
|
|
|
4274
4492
|
} else {
|
|
4275
4493
|
continue;
|
|
4276
4494
|
}
|
|
4277
|
-
if (
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4495
|
+
if (!nullishLowered) {
|
|
4496
|
+
if (match.goFallback === zeroLiteral)
|
|
4497
|
+
continue;
|
|
4498
|
+
if (zeroLiteral === "0" && Number(match.goFallback) === 0)
|
|
4499
|
+
continue;
|
|
4500
|
+
}
|
|
4281
4501
|
let varName = match.propName;
|
|
4282
4502
|
while (localTaken.has(varName) || GO_KEYWORDS.has(varName)) {
|
|
4283
4503
|
varName += "_";
|
|
4284
4504
|
}
|
|
4285
4505
|
localTaken.add(varName);
|
|
4286
|
-
result.set(match.propName, {
|
|
4506
|
+
result.set(match.propName, {
|
|
4507
|
+
varName,
|
|
4508
|
+
fieldName,
|
|
4509
|
+
goFallback: match.goFallback,
|
|
4510
|
+
zeroLiteral,
|
|
4511
|
+
...nullishLowered ? { assertType: concreteType } : {}
|
|
4512
|
+
});
|
|
4287
4513
|
}
|
|
4288
4514
|
return result;
|
|
4289
4515
|
}
|
|
4290
|
-
extractPropFallback(initialValue) {
|
|
4516
|
+
extractPropFallback(initialValue, preParsed) {
|
|
4517
|
+
const structural = preParsed ? this.extractPropFallbackFromParsed(preParsed) : null;
|
|
4518
|
+
if (structural)
|
|
4519
|
+
return structural;
|
|
4291
4520
|
if (!this.state.propsObjectName)
|
|
4292
4521
|
return null;
|
|
4293
4522
|
const trimmed = initialValue.trim();
|
|
@@ -4301,9 +4530,38 @@ ${goFields.join(`
|
|
|
4301
4530
|
return null;
|
|
4302
4531
|
return { propName: m[1], goFallback };
|
|
4303
4532
|
}
|
|
4304
|
-
|
|
4305
|
-
if (
|
|
4533
|
+
extractPropFallbackFromParsed(preParsed) {
|
|
4534
|
+
if (preParsed.kind !== "logical" || preParsed.op !== "??")
|
|
4535
|
+
return null;
|
|
4536
|
+
const left = preParsed.left;
|
|
4537
|
+
const propName = left.kind === "identifier" && !this.state.propsObjectName ? left.name : left.kind === "member" && !left.computed && left.object.kind === "identifier" && left.object.name === this.state.propsObjectName ? left.property : null;
|
|
4538
|
+
if (!propName)
|
|
4539
|
+
return null;
|
|
4540
|
+
let right = preParsed.right;
|
|
4541
|
+
let negate = "";
|
|
4542
|
+
if (right.kind === "unary" && right.op === "-") {
|
|
4543
|
+
negate = "-";
|
|
4544
|
+
right = right.argument;
|
|
4545
|
+
}
|
|
4546
|
+
if (right.kind !== "literal")
|
|
4547
|
+
return null;
|
|
4548
|
+
if (negate && right.literalType !== "number")
|
|
4549
|
+
return null;
|
|
4550
|
+
if (right.literalType === "string") {
|
|
4551
|
+
return { propName, goFallback: JSON.stringify(right.value) };
|
|
4552
|
+
}
|
|
4553
|
+
const goFallback = goPropDefault(negate + (right.raw ?? String(right.value)));
|
|
4554
|
+
if (goFallback === null)
|
|
4555
|
+
return null;
|
|
4556
|
+
return { propName, goFallback };
|
|
4557
|
+
}
|
|
4558
|
+
extractPropNameFromInitialValue(initialValue, preParsed) {
|
|
4559
|
+
if (!this.state.propsObjectName) {
|
|
4560
|
+
if (preParsed?.kind === "logical" && (preParsed.op === "??" || preParsed.op === "||") && preParsed.left.kind === "identifier") {
|
|
4561
|
+
return preParsed.left.name;
|
|
4562
|
+
}
|
|
4306
4563
|
return null;
|
|
4564
|
+
}
|
|
4307
4565
|
const trimmed = initialValue.trim();
|
|
4308
4566
|
const name = this.state.propsObjectName;
|
|
4309
4567
|
const direct = new RegExp(`^${name}\\.(\\w+)(?:\\s*(?:\\?\\?|\\|\\|)\\s*.+)?$`);
|
|
@@ -4427,10 +4685,22 @@ ${goFields.join(`
|
|
|
4427
4685
|
}
|
|
4428
4686
|
return goExpr;
|
|
4429
4687
|
}
|
|
4688
|
+
const finalExpr = this.textNillablePropNameOf(classify.parsed) !== null ? `bf_string ${wrapIfMultiToken(goExpr)}` : goExpr;
|
|
4430
4689
|
if (expr.slotId) {
|
|
4431
|
-
return `{{bfTextStart "${expr.slotId}"}}{{${
|
|
4690
|
+
return `{{bfTextStart "${expr.slotId}"}}{{${finalExpr}}}{{bfTextEnd}}`;
|
|
4432
4691
|
}
|
|
4433
|
-
return `{{${
|
|
4692
|
+
return `{{${finalExpr}}}`;
|
|
4693
|
+
}
|
|
4694
|
+
textNillablePropNameOf(expr) {
|
|
4695
|
+
if (!expr)
|
|
4696
|
+
return null;
|
|
4697
|
+
let name = null;
|
|
4698
|
+
if (expr.kind === "identifier") {
|
|
4699
|
+
name = expr.name;
|
|
4700
|
+
} else if (expr.kind === "member" && !expr.computed && expr.object.kind === "identifier" && expr.object.name === this.state.propsObjectName) {
|
|
4701
|
+
name = expr.property;
|
|
4702
|
+
}
|
|
4703
|
+
return name !== null && this.state.textConsumedPropNames.has(name) && this.state.nillablePropNames.has(name) ? name : null;
|
|
4434
4704
|
}
|
|
4435
4705
|
isTemplateFragment(go, kind) {
|
|
4436
4706
|
return go.startsWith("{{") || kind === "template-literal";
|
|
@@ -4646,7 +4916,7 @@ ${goFields.join(`
|
|
|
4646
4916
|
}
|
|
4647
4917
|
const obj = emit(object);
|
|
4648
4918
|
if (property === "length")
|
|
4649
|
-
return `
|
|
4919
|
+
return `bf_length ${wrapIfMultiToken(obj)}`;
|
|
4650
4920
|
if (optional) {
|
|
4651
4921
|
return `bf_get ${wrapIfMultiToken(obj)} ${JSON.stringify(goFieldNameForKey(property))}`;
|
|
4652
4922
|
}
|
|
@@ -4715,8 +4985,24 @@ ${goFields.join(`
|
|
|
4715
4985
|
const wrapRight = wrapIfMultiToken(emit(right));
|
|
4716
4986
|
if (op === "&&")
|
|
4717
4987
|
return `and ${wrapLeft} ${wrapRight}`;
|
|
4988
|
+
if (op === "??" && this.nillablePropNameOf(left) !== null) {
|
|
4989
|
+
return `bf_nullish ${wrapLeft} ${wrapRight}`;
|
|
4990
|
+
}
|
|
4718
4991
|
return `or ${wrapLeft} ${wrapRight}`;
|
|
4719
4992
|
}
|
|
4993
|
+
nillablePropNameOf(expr) {
|
|
4994
|
+
let name = null;
|
|
4995
|
+
if (expr.kind === "identifier") {
|
|
4996
|
+
name = expr.name;
|
|
4997
|
+
} else if (expr.kind === "member" && !expr.computed && expr.object.kind === "identifier") {
|
|
4998
|
+
if (expr.object.name === this.state.propsObjectName) {
|
|
4999
|
+
name = expr.property;
|
|
5000
|
+
} else {
|
|
5001
|
+
name = expr.object.name;
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
return name !== null && this.state.nullishConsumedPropNames.has(name) && this.state.nillablePropNames.has(name) ? name : null;
|
|
5005
|
+
}
|
|
4720
5006
|
conditional(test, consequent, alternate, emit) {
|
|
4721
5007
|
const t = emit(test);
|
|
4722
5008
|
const c = this.renderConditionalBranch(consequent);
|
|
@@ -5148,9 +5434,6 @@ ${goFields.join(`
|
|
|
5148
5434
|
renderPredicateCondition(pred, param, datumField) {
|
|
5149
5435
|
return this.renderFilterExpr(pred, param, new Map, datumField ?? undefined);
|
|
5150
5436
|
}
|
|
5151
|
-
needsParens(expr) {
|
|
5152
|
-
return expr.kind === "logical" || expr.kind === "unary" || expr.kind === "conditional";
|
|
5153
|
-
}
|
|
5154
5437
|
splitPreamble(rendered) {
|
|
5155
5438
|
if (!rendered.includes("{{"))
|
|
5156
5439
|
return null;
|
|
@@ -5563,7 +5846,8 @@ ${goFields.join(`
|
|
|
5563
5846
|
return plain(this.rootFieldRef(expr.callee.name));
|
|
5564
5847
|
}
|
|
5565
5848
|
if (expr.callee.kind === "identifier" && (identifierPath(expr.callee) ?? expr.callee.name) === "isValidElement" && expr.args.length === 1) {
|
|
5566
|
-
|
|
5849
|
+
const inner = this.renderConditionExpr(expr.args[0]);
|
|
5850
|
+
return { preamble: inner.preamble, expr: `(bf_is_element ${wrapIfMultiToken(inner.expr)})` };
|
|
5567
5851
|
}
|
|
5568
5852
|
if (expr.callee.kind === "identifier" && !this.templatePrimitives[identifierPath(expr.callee) ?? ""]) {
|
|
5569
5853
|
const path = identifierPath(expr.callee) ?? expr.callee.name;
|
|
@@ -5680,9 +5964,9 @@ ${goFields.join(`
|
|
|
5680
5964
|
const leftResult = this.renderConditionExpr(expr.left);
|
|
5681
5965
|
const rightResult = this.renderConditionExpr(expr.right);
|
|
5682
5966
|
const preamble = leftResult.preamble + rightResult.preamble;
|
|
5683
|
-
const wrapLeft =
|
|
5684
|
-
const wrapRight =
|
|
5685
|
-
const result = expr.op === "&&" ? `and ${wrapLeft} ${wrapRight}` : `or ${wrapLeft} ${wrapRight}`;
|
|
5967
|
+
const wrapLeft = wrapIfMultiToken(leftResult.expr);
|
|
5968
|
+
const wrapRight = wrapIfMultiToken(rightResult.expr);
|
|
5969
|
+
const result = expr.op === "&&" ? `and ${wrapLeft} ${wrapRight}` : expr.op === "??" && this.nillablePropNameOf(expr.left) !== null ? `bf_nullish ${wrapLeft} ${wrapRight}` : `or ${wrapLeft} ${wrapRight}`;
|
|
5686
5970
|
return { preamble, expr: result };
|
|
5687
5971
|
}
|
|
5688
5972
|
case "conditional": {
|
|
@@ -6022,7 +6306,7 @@ ${children}`;
|
|
|
6022
6306
|
if (css !== null)
|
|
6023
6307
|
return `style="${css}"`;
|
|
6024
6308
|
}
|
|
6025
|
-
if (
|
|
6309
|
+
if (isBooleanAttr2(name) || value.presenceOrUndefined) {
|
|
6026
6310
|
const { condition: goCond, preamble } = this.convertConditionToGo(value.expr, value.parsed);
|
|
6027
6311
|
const body = name.startsWith("aria-") ? `${name}="true"` : name;
|
|
6028
6312
|
return `${preamble}{{if ${goCond}}}${body}{{end}}`;
|
|
@@ -6095,7 +6379,11 @@ ${children}`;
|
|
|
6095
6379
|
if (e.kind === "expr" && !isSupported(parseExpression4(e.expr)).supported)
|
|
6096
6380
|
return null;
|
|
6097
6381
|
}
|
|
6098
|
-
|
|
6382
|
+
const args = entries.flatMap((e) => [
|
|
6383
|
+
JSON.stringify(e.cssKey),
|
|
6384
|
+
e.kind === "literal" ? JSON.stringify(e.value) : wrapIfMultiToken(this.convertExpressionToGo(e.expr))
|
|
6385
|
+
]);
|
|
6386
|
+
return `{{bf_style_object ${args.join(" ")}}}`;
|
|
6099
6387
|
}
|
|
6100
6388
|
renderAttributes(element) {
|
|
6101
6389
|
const parts = [];
|