@barefootjs/go-template 0.17.0 → 0.17.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/go-template-adapter.d.ts +10 -0
- package/dist/adapter/go-template-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +221 -36
- package/dist/adapter/lib/compile-state.d.ts +27 -1
- package/dist/adapter/lib/compile-state.d.ts.map +1 -1
- package/dist/adapter/lib/go-emit.d.ts +9 -0
- package/dist/adapter/lib/go-emit.d.ts.map +1 -1
- package/dist/adapter/memo/memo-compute.d.ts +54 -5
- package/dist/adapter/memo/memo-compute.d.ts.map +1 -1
- package/dist/adapter/memo/memo-type.d.ts +10 -0
- package/dist/adapter/memo/memo-type.d.ts.map +1 -1
- package/dist/build.js +221 -36
- package/dist/index.js +221 -36
- package/package.json +3 -3
- package/src/__tests__/go-template-adapter.test.ts +280 -0
- package/src/adapter/go-template-adapter.ts +126 -31
- package/src/adapter/lib/compile-state.ts +31 -0
- package/src/adapter/lib/go-emit.ts +20 -0
- package/src/adapter/memo/memo-compute.ts +228 -18
- package/src/adapter/memo/memo-type.ts +19 -0
- package/src/test-render.ts +46 -8
|
@@ -10,6 +10,46 @@
|
|
|
10
10
|
import type { ParsedExpr, ParsedStatement, TypeInfo } from '@barefootjs/jsx';
|
|
11
11
|
import type { GoEmitContext } from '../emit-context.ts';
|
|
12
12
|
import type { PropFallbackVar } from '../lib/types.ts';
|
|
13
|
+
/** A `() => props.X.filter((p) => <predicate>)` match: the array prop name,
|
|
14
|
+
* the predicate serialized to the runtime evaluator's ParsedExpr JSON, the
|
|
15
|
+
* arrow's param name, and the free variable names its predicate captures.
|
|
16
|
+
* Null when `body` isn't this shape, `propName` doesn't resolve to a known
|
|
17
|
+
* prop, or the predicate isn't representable (`serializeParsedExpr` refusal).
|
|
18
|
+
* Shared by the SSR-value emitter ({@link memoInitialFromParsedBody}) and the
|
|
19
|
+
* constructor's sibling-memo hoisting pre-pass so both walk the shape
|
|
20
|
+
* identically. */
|
|
21
|
+
export declare function matchFilterArmMemo(ctx: GoEmitContext, body: ParsedExpr, signals: {
|
|
22
|
+
getter: string;
|
|
23
|
+
initialValue: string;
|
|
24
|
+
}[], propsParams: {
|
|
25
|
+
name: string;
|
|
26
|
+
type?: TypeInfo;
|
|
27
|
+
defaultValue?: string;
|
|
28
|
+
}[]): {
|
|
29
|
+
propName: string;
|
|
30
|
+
predJSON: string;
|
|
31
|
+
paramName: string;
|
|
32
|
+
freeVars: string[];
|
|
33
|
+
} | null;
|
|
34
|
+
/**
|
|
35
|
+
* Names of EARLIER sibling memos (per `ctx.state.currentMemos` declaration
|
|
36
|
+
* order) that a filter-arm memo's predicate free variables resolve to — the
|
|
37
|
+
* constructor generator hoists these into a shared local so the sibling's
|
|
38
|
+
* expression isn't emitted twice (once for its own field, once inlined in
|
|
39
|
+
* this memo's `bf.FilterEval` env map, #2075/#2077 review finding 3). Empty
|
|
40
|
+
* when `memo` isn't a filter-arm memo or references nothing hoistable.
|
|
41
|
+
*/
|
|
42
|
+
export declare function filterArmEarlierSiblingRefs(ctx: GoEmitContext, memo: {
|
|
43
|
+
name: string;
|
|
44
|
+
parsed?: ParsedExpr;
|
|
45
|
+
}, signals: {
|
|
46
|
+
getter: string;
|
|
47
|
+
initialValue: string;
|
|
48
|
+
}[], propsParams: {
|
|
49
|
+
name: string;
|
|
50
|
+
type?: TypeInfo;
|
|
51
|
+
defaultValue?: string;
|
|
52
|
+
}[]): string[];
|
|
13
53
|
/**
|
|
14
54
|
* Compute a memo's SSR initial value as a Go expression — e.g.
|
|
15
55
|
* `() => count() * 2` → `in.Initial * 2`, `() => props.value * 10` →
|
|
@@ -49,7 +89,7 @@ export declare function memoInitialFromParsedBody(ctx: GoEmitContext, body: Pars
|
|
|
49
89
|
name: string;
|
|
50
90
|
type?: TypeInfo;
|
|
51
91
|
defaultValue?: string;
|
|
52
|
-
}[], propFallbackVars: ReadonlyMap<string, PropFallbackVar>): string | null;
|
|
92
|
+
}[], propFallbackVars: ReadonlyMap<string, PropFallbackVar>, currentMemoName: string, resolving?: ReadonlySet<string>): string | null;
|
|
53
93
|
/**
|
|
54
94
|
* Pattern-matching core of `computeMemoInitialValue`.
|
|
55
95
|
*
|
|
@@ -73,7 +113,16 @@ export declare function computeMemoInitialValueOrNull(ctx: GoEmitContext, memo:
|
|
|
73
113
|
name: string;
|
|
74
114
|
type?: TypeInfo;
|
|
75
115
|
defaultValue?: string;
|
|
76
|
-
}[], propFallbackVars?: ReadonlyMap<string, PropFallbackVar
|
|
116
|
+
}[], propFallbackVars?: ReadonlyMap<string, PropFallbackVar>,
|
|
117
|
+
/**
|
|
118
|
+
* Memo names currently being resolved on this call stack — guards against
|
|
119
|
+
* unbounded recursion when memos reference each other (mutually, or a
|
|
120
|
+
* self-reference). Callers that start a fresh top-level computation seed
|
|
121
|
+
* this with `memo.name`; recursive calls extend it with the sibling memo
|
|
122
|
+
* about to be entered. A candidate already in the set resolves to `null`
|
|
123
|
+
* (the shape's usual "unsupported" fallback) instead of recursing.
|
|
124
|
+
*/
|
|
125
|
+
resolving?: ReadonlySet<string>): string | null;
|
|
77
126
|
/**
|
|
78
127
|
* Resolve a signal/memo getter NAME to a Go value expression, for use as the
|
|
79
128
|
* condition operand of another memo's ternary. Handles a plain signal, a
|
|
@@ -89,7 +138,7 @@ export declare function resolveGetterValueAsGo(ctx: GoEmitContext, name: string,
|
|
|
89
138
|
name: string;
|
|
90
139
|
type?: TypeInfo;
|
|
91
140
|
defaultValue?: string;
|
|
92
|
-
}[], propFallbackVars: ReadonlyMap<string, PropFallbackVar>): string | null;
|
|
141
|
+
}[], propFallbackVars: ReadonlyMap<string, PropFallbackVar>, resolving?: ReadonlySet<string>): string | null;
|
|
93
142
|
/**
|
|
94
143
|
* Resolve a string-ternary memo whose condition is a literal comparison —
|
|
95
144
|
* `() => <operand> === 'lit' ? A : B` (or `!==`) — to a Go runtime conditional.
|
|
@@ -105,7 +154,7 @@ export declare function computeComparisonTernaryGo(ctx: GoEmitContext, parsed: P
|
|
|
105
154
|
name: string;
|
|
106
155
|
type?: TypeInfo;
|
|
107
156
|
defaultValue?: string;
|
|
108
|
-
}[], propFallbackVars: ReadonlyMap<string, PropFallbackVar>): string | null;
|
|
157
|
+
}[], propFallbackVars: ReadonlyMap<string, PropFallbackVar>, resolving?: ReadonlySet<string>): string | null;
|
|
109
158
|
/**
|
|
110
159
|
* Resolve the left operand of a string-ternary memo's comparison condition to a
|
|
111
160
|
* Go expression: a zero-arg getter call (a signal/prop-shadow memo), an inline
|
|
@@ -120,5 +169,5 @@ export declare function resolveComparisonOperandGo(ctx: GoEmitContext, node: Par
|
|
|
120
169
|
name: string;
|
|
121
170
|
type?: TypeInfo;
|
|
122
171
|
defaultValue?: string;
|
|
123
|
-
}[], propFallbackVars: ReadonlyMap<string, PropFallbackVar>): string | null;
|
|
172
|
+
}[], propFallbackVars: ReadonlyMap<string, PropFallbackVar>, resolving?: ReadonlySet<string>): string | null;
|
|
124
173
|
//# sourceMappingURL=memo-compute.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memo-compute.d.ts","sourceRoot":"","sources":["../../../src/adapter/memo/memo-compute.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"memo-compute.d.ts","sourceRoot":"","sources":["../../../src/adapter/memo/memo-compute.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,16 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { ParsedExpr, TypeInfo } from '@barefootjs/jsx';
|
|
9
9
|
import type { GoEmitContext } from '../emit-context.ts';
|
|
10
|
+
/**
|
|
11
|
+
* True when a memo's body is a `.filter(<arrow>)` callback-method call
|
|
12
|
+
* (#2075) — a LIST-valued derived memo (the blog PostList `visible` shape:
|
|
13
|
+
* `createMemo(() => props.items.filter((p) => …))`), not a scalar. Exported
|
|
14
|
+
* so both `isBooleanMemo` (guard below) and `inferMemoType`'s field-type
|
|
15
|
+
* decision (go-template-adapter.ts) share the one recognition point.
|
|
16
|
+
*/
|
|
17
|
+
export declare function isListFilterMemo(memo: {
|
|
18
|
+
parsed?: ParsedExpr;
|
|
19
|
+
}): boolean;
|
|
10
20
|
/**
|
|
11
21
|
* Heuristic: does this memo evaluate to a boolean? True when its computation is
|
|
12
22
|
* a comparison (`!==`/`===`/`!=`/`==`), a negation (`!x`), or a ternary whose
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memo-type.d.ts","sourceRoot":"","sources":["../../../src/adapter/memo/memo-type.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"memo-type.d.ts","sourceRoot":"","sources":["../../../src/adapter/memo/memo-type.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAGvD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAE,GAAG,OAAO,CAIvE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,UAAU,CAAA;CAAE,EAClE,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,EAAE,EACnE,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAClF,OAAO,CAmCT;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAS/F"}
|
package/dist/build.js
CHANGED
|
@@ -371,7 +371,7 @@ import {
|
|
|
371
371
|
isSupported,
|
|
372
372
|
exprToString,
|
|
373
373
|
identifierPath,
|
|
374
|
-
asCallbackMethodCall,
|
|
374
|
+
asCallbackMethodCall as asCallbackMethodCall3,
|
|
375
375
|
sortComparatorFromArrow,
|
|
376
376
|
emitParsedExpr,
|
|
377
377
|
emitIRNode,
|
|
@@ -380,8 +380,9 @@ import {
|
|
|
380
380
|
collectContextConsumers,
|
|
381
381
|
isLowerableObjectRestDestructure,
|
|
382
382
|
collectModuleStringConsts as collectModuleStringConstsShared,
|
|
383
|
-
|
|
384
|
-
|
|
383
|
+
prepareLoweringMatchers,
|
|
384
|
+
envSignalReaderFor,
|
|
385
|
+
computeSsrSeedPlan
|
|
385
386
|
} from "@barefootjs/jsx";
|
|
386
387
|
import { findInterpolationEnd } from "@barefootjs/jsx/scanner";
|
|
387
388
|
import { BF_REGION } from "@barefootjs/shared";
|
|
@@ -564,6 +565,13 @@ function emitFlatMapEval(recv, body, param, emit) {
|
|
|
564
565
|
const env = emitEvalEnvArg(body, [param], emit);
|
|
565
566
|
return `bf_flat_map_eval ${wrapIfMultiToken(recv)} "${escapeGoString(json)}" "${param}" ${env}`;
|
|
566
567
|
}
|
|
568
|
+
function emitMapEval(recv, body, param, emit) {
|
|
569
|
+
const json = serializeParsedExpr(body);
|
|
570
|
+
if (json === null)
|
|
571
|
+
return null;
|
|
572
|
+
const env = emitEvalEnvArg(body, [param], emit);
|
|
573
|
+
return `bf_map_eval ${wrapIfMultiToken(recv)} "${escapeGoString(json)}" "${param}" ${env}`;
|
|
574
|
+
}
|
|
567
575
|
function stringTolerantEqOperands(l, r) {
|
|
568
576
|
const isStrLit = (x) => /^"(?:[^"\\]|\\.)*"$/.test(x);
|
|
569
577
|
if (isStrLit(l) === isStrLit(r))
|
|
@@ -677,6 +685,9 @@ class CompileState {
|
|
|
677
685
|
currentTypeDefinitions = [];
|
|
678
686
|
contextConsumers = [];
|
|
679
687
|
searchParamsLocals = new Set;
|
|
688
|
+
envSignalReadersByLocal = new Map;
|
|
689
|
+
ssrSeedPlan = { baseScope: [], steps: [] };
|
|
690
|
+
hoistedMemoLocals = new Map;
|
|
680
691
|
loweringMatchers = [];
|
|
681
692
|
nillablePropNames = new Set;
|
|
682
693
|
rootScopeNodes = new Set;
|
|
@@ -1317,8 +1328,17 @@ function getSignalInitialValueAsGo(ctx, initialValue, propsParams, propFallbackV
|
|
|
1317
1328
|
}
|
|
1318
1329
|
|
|
1319
1330
|
// src/adapter/memo/memo-type.ts
|
|
1331
|
+
import { asCallbackMethodCall } from "@barefootjs/jsx";
|
|
1332
|
+
function isListFilterMemo(memo) {
|
|
1333
|
+
if (!memo.parsed)
|
|
1334
|
+
return false;
|
|
1335
|
+
const cb = asCallbackMethodCall(memo.parsed);
|
|
1336
|
+
return cb !== null && cb.method === "filter";
|
|
1337
|
+
}
|
|
1320
1338
|
function isBooleanMemo(ctx, memo, signals, propsParamMap) {
|
|
1321
1339
|
const c = memo.computation;
|
|
1340
|
+
if (isListFilterMemo(memo))
|
|
1341
|
+
return false;
|
|
1322
1342
|
if (isStringTernaryMemo(ctx, memo.parsed))
|
|
1323
1343
|
return false;
|
|
1324
1344
|
if (/(!==|===|!=(?!=)|==(?!=))/.test(c))
|
|
@@ -1609,6 +1629,14 @@ function computeObjectMemoInitialValue(ctx, memo) {
|
|
|
1609
1629
|
}`;
|
|
1610
1630
|
}
|
|
1611
1631
|
|
|
1632
|
+
// src/adapter/memo/memo-compute.ts
|
|
1633
|
+
import {
|
|
1634
|
+
asCallbackMethodCall as asCallbackMethodCall2,
|
|
1635
|
+
freeVarsInBody as freeVarsInBody2,
|
|
1636
|
+
materializeGetterCalls,
|
|
1637
|
+
serializeParsedExpr as serializeParsedExpr2
|
|
1638
|
+
} from "@barefootjs/jsx";
|
|
1639
|
+
|
|
1612
1640
|
// src/adapter/memo/template-interp.ts
|
|
1613
1641
|
import ts from "typescript";
|
|
1614
1642
|
function computeTemplateLiteralMemoInitialValue(ctx, memo, propsParams) {
|
|
@@ -1749,8 +1777,49 @@ function propsAccessNameFromParsed(ctx, node) {
|
|
|
1749
1777
|
|
|
1750
1778
|
// src/adapter/memo/memo-compute.ts
|
|
1751
1779
|
var EMPTY_PROP_FALLBACK_VARS2 = new Map;
|
|
1780
|
+
function getterCallName(e) {
|
|
1781
|
+
return e.kind === "call" && e.callee.kind === "identifier" && e.args.length === 0 ? e.callee.name : null;
|
|
1782
|
+
}
|
|
1783
|
+
function propsMemberName(e) {
|
|
1784
|
+
return e.kind === "member" && !e.computed && e.object.kind === "identifier" && e.object.name === "props" ? e.property : null;
|
|
1785
|
+
}
|
|
1786
|
+
function matchFilterArmMemo(ctx, body, signals, propsParams) {
|
|
1787
|
+
const cb = asCallbackMethodCall2(body);
|
|
1788
|
+
if (!cb || cb.method !== "filter")
|
|
1789
|
+
return null;
|
|
1790
|
+
const propName = propsMemberName(cb.object);
|
|
1791
|
+
const param = propName ? propsParams.find((p) => p.name === propName) : undefined;
|
|
1792
|
+
if (!propName || !param)
|
|
1793
|
+
return null;
|
|
1794
|
+
const knownGetterNames = new Set(ctx.state.ssrSeedPlan.steps.filter((s) => s.kind !== "env-reader").map((s) => s.name));
|
|
1795
|
+
const materialized = materializeGetterCalls(cb.arrow.body, knownGetterNames);
|
|
1796
|
+
const predJSON = serializeParsedExpr2(materialized);
|
|
1797
|
+
if (predJSON === null)
|
|
1798
|
+
return null;
|
|
1799
|
+
const paramName = cb.arrow.params[0] ?? "_";
|
|
1800
|
+
const freeVars = freeVarsInBody2(materialized, new Set(cb.arrow.params));
|
|
1801
|
+
return { propName, predJSON, paramName, freeVars };
|
|
1802
|
+
}
|
|
1803
|
+
function filterArmEarlierSiblingRefs(ctx, memo, signals, propsParams) {
|
|
1804
|
+
if (!memo.parsed)
|
|
1805
|
+
return [];
|
|
1806
|
+
const match = matchFilterArmMemo(ctx, memo.parsed, signals, propsParams);
|
|
1807
|
+
if (!match)
|
|
1808
|
+
return [];
|
|
1809
|
+
const memos = ctx.state.currentMemos ?? [];
|
|
1810
|
+
const currentIndex = memos.findIndex((m) => m.name === memo.name);
|
|
1811
|
+
if (currentIndex < 0)
|
|
1812
|
+
return [];
|
|
1813
|
+
const refs = [];
|
|
1814
|
+
for (const name of match.freeVars) {
|
|
1815
|
+
const idx = memos.findIndex((m) => m.name === name);
|
|
1816
|
+
if (idx >= 0 && idx < currentIndex)
|
|
1817
|
+
refs.push(name);
|
|
1818
|
+
}
|
|
1819
|
+
return refs;
|
|
1820
|
+
}
|
|
1752
1821
|
function computeMemoInitialValue(ctx, memo, signals, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS2, goType) {
|
|
1753
|
-
const resolved = computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars);
|
|
1822
|
+
const resolved = computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars, new Set([memo.name]));
|
|
1754
1823
|
if (resolved !== null)
|
|
1755
1824
|
return resolved;
|
|
1756
1825
|
if (goType === "bool")
|
|
@@ -1762,15 +1831,79 @@ function computeMemoInitialValue(ctx, memo, signals, propsParams, propFallbackVa
|
|
|
1762
1831
|
}
|
|
1763
1832
|
return "0";
|
|
1764
1833
|
}
|
|
1765
|
-
function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallbackVars) {
|
|
1834
|
+
function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallbackVars, currentMemoName, resolving = new Set) {
|
|
1766
1835
|
const propRef = (propName) => {
|
|
1767
1836
|
const hoisted = propFallbackVars.get(propName);
|
|
1768
1837
|
if (hoisted)
|
|
1769
1838
|
return hoisted.varName;
|
|
1770
1839
|
return `in.${capitalizeFieldName(propName)}`;
|
|
1771
1840
|
};
|
|
1772
|
-
const
|
|
1773
|
-
|
|
1841
|
+
const envGetKey = (e) => {
|
|
1842
|
+
if (e.kind !== "call" || e.callee.kind !== "member" || e.callee.computed)
|
|
1843
|
+
return null;
|
|
1844
|
+
const recvName = getterCallName(e.callee.object);
|
|
1845
|
+
if (recvName === null)
|
|
1846
|
+
return null;
|
|
1847
|
+
const reader = ctx.state.envSignalReadersByLocal.get(recvName);
|
|
1848
|
+
if (!reader || !reader.methods.has(e.callee.property))
|
|
1849
|
+
return null;
|
|
1850
|
+
const arg = e.args[0];
|
|
1851
|
+
if (!arg || arg.kind !== "literal" || arg.literalType !== "string")
|
|
1852
|
+
return null;
|
|
1853
|
+
return { key: String(arg.value), fieldName: capitalizeFieldName(reader.canonicalName) };
|
|
1854
|
+
};
|
|
1855
|
+
{
|
|
1856
|
+
const m = envGetKey(body);
|
|
1857
|
+
if (m !== null)
|
|
1858
|
+
return `in.${m.fieldName}.Get(${JSON.stringify(m.key)})`;
|
|
1859
|
+
}
|
|
1860
|
+
if (body.kind === "logical" && (body.op === "??" || body.op === "||") && body.right.kind === "literal" && body.right.literalType === "string") {
|
|
1861
|
+
const m = envGetKey(body.left);
|
|
1862
|
+
if (m !== null) {
|
|
1863
|
+
const def = JSON.stringify(String(body.right.value));
|
|
1864
|
+
return `func() string { if v := in.${m.fieldName}.Get(${JSON.stringify(m.key)}); v != "" { return v }; return ${def} }()`;
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
{
|
|
1868
|
+
const match = matchFilterArmMemo(ctx, body, signals, propsParams);
|
|
1869
|
+
if (match) {
|
|
1870
|
+
const { propName, predJSON, paramName, freeVars } = match;
|
|
1871
|
+
const currentIndex = (ctx.state.currentMemos ?? []).findIndex((m) => m.name === currentMemoName);
|
|
1872
|
+
const envEntries = [];
|
|
1873
|
+
let unresolved = false;
|
|
1874
|
+
for (const name of freeVars) {
|
|
1875
|
+
let goExpr = null;
|
|
1876
|
+
const siblingIndex = (ctx.state.currentMemos ?? []).findIndex((m) => m.name === name);
|
|
1877
|
+
if (siblingIndex >= 0) {
|
|
1878
|
+
const siblingMemo = ctx.state.currentMemos[siblingIndex];
|
|
1879
|
+
const eligible = currentIndex >= 0 && siblingIndex < currentIndex && !resolving.has(siblingMemo.name);
|
|
1880
|
+
if (eligible) {
|
|
1881
|
+
const hoisted = ctx.state.hoistedMemoLocals.get(siblingMemo.name);
|
|
1882
|
+
goExpr = hoisted ?? computeMemoInitialValueOrNull(ctx, siblingMemo, signals, propsParams, propFallbackVars, new Set([...resolving, siblingMemo.name]));
|
|
1883
|
+
}
|
|
1884
|
+
} else {
|
|
1885
|
+
const sig = signals.find((s) => s.getter === name);
|
|
1886
|
+
if (sig) {
|
|
1887
|
+
goExpr = getSignalInitialValueAsGo(ctx, sig.initialValue, propsParams, propFallbackVars);
|
|
1888
|
+
} else {
|
|
1889
|
+
const p = propsParams.find((pp) => pp.name === name);
|
|
1890
|
+
if (p)
|
|
1891
|
+
goExpr = propRef(name);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
if (goExpr === null) {
|
|
1895
|
+
unresolved = true;
|
|
1896
|
+
break;
|
|
1897
|
+
}
|
|
1898
|
+
envEntries.push(`${JSON.stringify(name)}: ${goExpr}`);
|
|
1899
|
+
}
|
|
1900
|
+
if (!unresolved) {
|
|
1901
|
+
const itemsField = `in.${capitalizeFieldName(propName)}`;
|
|
1902
|
+
const envMap = `map[string]any{${envEntries.join(", ")}}`;
|
|
1903
|
+
return `bf.FilterEval(${itemsField}, "${escapeGoString(predJSON)}", ${JSON.stringify(paramName)}, ${envMap})`;
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1774
1907
|
if (body.kind === "binary" && ["===", "!==", "==", "!="].includes(body.op) && body.right.kind === "literal" && body.right.literalType === "string") {
|
|
1775
1908
|
const depName = getterCallName(body.left);
|
|
1776
1909
|
if (depName) {
|
|
@@ -1809,8 +1942,8 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
|
|
|
1809
1942
|
condGo = getSignalInitialValueAsGo(ctx, condSignal.initialValue, propsParams, propFallbackVars);
|
|
1810
1943
|
} else {
|
|
1811
1944
|
const condMemo = (ctx.state.currentMemos ?? []).find((m) => m.name === condName);
|
|
1812
|
-
if (condMemo) {
|
|
1813
|
-
condGo = computeMemoInitialValueOrNull(ctx, condMemo, signals, propsParams, propFallbackVars);
|
|
1945
|
+
if (condMemo && !resolving.has(condMemo.name)) {
|
|
1946
|
+
condGo = computeMemoInitialValueOrNull(ctx, condMemo, signals, propsParams, propFallbackVars, new Set([...resolving, condMemo.name]));
|
|
1814
1947
|
}
|
|
1815
1948
|
}
|
|
1816
1949
|
if (condGo === "true")
|
|
@@ -1884,17 +2017,17 @@ function memoInitialFromParsedBody(ctx, body, signals, propsParams, propFallback
|
|
|
1884
2017
|
}
|
|
1885
2018
|
return null;
|
|
1886
2019
|
}
|
|
1887
|
-
function computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS2) {
|
|
2020
|
+
function computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars = EMPTY_PROP_FALLBACK_VARS2, resolving = new Set) {
|
|
1888
2021
|
const computation = memo.computation;
|
|
1889
2022
|
const tmplMemo = computeTemplateLiteralMemoInitialValue(ctx, memo, propsParams);
|
|
1890
2023
|
if (tmplMemo !== null)
|
|
1891
2024
|
return tmplMemo;
|
|
1892
2025
|
if (memo.parsed) {
|
|
1893
|
-
const fromParsed = memoInitialFromParsedBody(ctx, memo.parsed, signals, propsParams, propFallbackVars);
|
|
2026
|
+
const fromParsed = memoInitialFromParsedBody(ctx, memo.parsed, signals, propsParams, propFallbackVars, memo.name, resolving);
|
|
1894
2027
|
if (fromParsed !== null)
|
|
1895
2028
|
return fromParsed;
|
|
1896
2029
|
}
|
|
1897
|
-
const cmpTernary = computeComparisonTernaryGo(ctx, memo.parsed, signals, propsParams, propFallbackVars);
|
|
2030
|
+
const cmpTernary = computeComparisonTernaryGo(ctx, memo.parsed, signals, propsParams, propFallbackVars, resolving);
|
|
1898
2031
|
if (cmpTernary !== null)
|
|
1899
2032
|
return cmpTernary;
|
|
1900
2033
|
const blockReturn = resolveBlockBodyMemoModuleConst(ctx, memo, signals);
|
|
@@ -1906,20 +2039,22 @@ function computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFall
|
|
|
1906
2039
|
return objMemo;
|
|
1907
2040
|
return null;
|
|
1908
2041
|
}
|
|
1909
|
-
function resolveGetterValueAsGo(ctx, name, signals, propsParams, propFallbackVars) {
|
|
2042
|
+
function resolveGetterValueAsGo(ctx, name, signals, propsParams, propFallbackVars, resolving = new Set) {
|
|
1910
2043
|
const signal = signals.find((s) => s.getter === name);
|
|
1911
2044
|
if (signal) {
|
|
1912
2045
|
return getSignalInitialValueAsGo(ctx, signal.initialValue, propsParams, propFallbackVars);
|
|
1913
2046
|
}
|
|
1914
2047
|
const memo = (ctx.state.currentMemos ?? []).find((m) => m.name === name);
|
|
1915
2048
|
if (memo) {
|
|
2049
|
+
if (resolving.has(memo.name))
|
|
2050
|
+
return null;
|
|
1916
2051
|
const stripped = memo.computation.replace(/^\(\)\s*=>\s*/, "");
|
|
1917
2052
|
const fb = ctx.extractPropFallback(stripped);
|
|
1918
2053
|
if (fb && capitalizeFieldName(fb.propName) === capitalizeFieldName(memo.name)) {
|
|
1919
2054
|
const field = `in.${capitalizeFieldName(fb.propName)}`;
|
|
1920
2055
|
return `func() interface{} { v := interface{}(${field}); if v == nil || v == "" { return ${fb.goFallback} }; return v }()`;
|
|
1921
2056
|
}
|
|
1922
|
-
return computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars);
|
|
2057
|
+
return computeMemoInitialValueOrNull(ctx, memo, signals, propsParams, propFallbackVars, new Set([...resolving, memo.name]));
|
|
1923
2058
|
}
|
|
1924
2059
|
const param = propsParams.find((p) => p.name === name);
|
|
1925
2060
|
if (param) {
|
|
@@ -1928,7 +2063,7 @@ function resolveGetterValueAsGo(ctx, name, signals, propsParams, propFallbackVar
|
|
|
1928
2063
|
}
|
|
1929
2064
|
return null;
|
|
1930
2065
|
}
|
|
1931
|
-
function computeComparisonTernaryGo(ctx, parsed, signals, propsParams, propFallbackVars) {
|
|
2066
|
+
function computeComparisonTernaryGo(ctx, parsed, signals, propsParams, propFallbackVars, resolving = new Set) {
|
|
1932
2067
|
if (!parsed || parsed.kind !== "conditional")
|
|
1933
2068
|
return null;
|
|
1934
2069
|
const cond = parsed.test;
|
|
@@ -1953,16 +2088,16 @@ function computeComparisonTernaryGo(ctx, parsed, signals, propsParams, propFallb
|
|
|
1953
2088
|
const f = branch(parsed.alternate);
|
|
1954
2089
|
if (t === null || f === null)
|
|
1955
2090
|
return null;
|
|
1956
|
-
const condGo = resolveComparisonOperandGo(ctx, cond.left, signals, propsParams, propFallbackVars);
|
|
2091
|
+
const condGo = resolveComparisonOperandGo(ctx, cond.left, signals, propsParams, propFallbackVars, resolving);
|
|
1957
2092
|
if (condGo === null)
|
|
1958
2093
|
return null;
|
|
1959
2094
|
const eqBranch = isEq ? t : f;
|
|
1960
2095
|
const neBranch = isEq ? f : t;
|
|
1961
2096
|
return `func() string { if ${condGo} == ${JSON.stringify(cond.right.value)} { return ${eqBranch} }; return ${neBranch} }()`;
|
|
1962
2097
|
}
|
|
1963
|
-
function resolveComparisonOperandGo(ctx, node, signals, propsParams, propFallbackVars) {
|
|
2098
|
+
function resolveComparisonOperandGo(ctx, node, signals, propsParams, propFallbackVars, resolving = new Set) {
|
|
1964
2099
|
if (node.kind === "call" && node.callee.kind === "identifier" && node.args.length === 0) {
|
|
1965
|
-
return resolveGetterValueAsGo(ctx, node.callee.name, signals, propsParams, propFallbackVars);
|
|
2100
|
+
return resolveGetterValueAsGo(ctx, node.callee.name, signals, propsParams, propFallbackVars, resolving);
|
|
1966
2101
|
}
|
|
1967
2102
|
if (node.kind === "logical" && node.op === "??" && node.right.kind === "literal" && node.right.literalType === "string") {
|
|
1968
2103
|
const propName = propsAccessNameFromParsed2(ctx, node.left);
|
|
@@ -2341,7 +2476,18 @@ class GoTemplateAdapter extends BaseAdapter {
|
|
|
2341
2476
|
this.state.currentMemos = ir.metadata.memos ?? [];
|
|
2342
2477
|
this.state.currentTypeDefinitions = ir.metadata.typeDefinitions ?? [];
|
|
2343
2478
|
this.state.contextConsumers = collectContextConsumers(ir.metadata);
|
|
2344
|
-
this.state.
|
|
2479
|
+
this.state.ssrSeedPlan = ir.metadata.ssrSeedPlan ?? computeSsrSeedPlan(ir.metadata);
|
|
2480
|
+
this.state.envSignalReadersByLocal = new Map;
|
|
2481
|
+
this.state.searchParamsLocals = new Set;
|
|
2482
|
+
for (const step of this.state.ssrSeedPlan.steps) {
|
|
2483
|
+
if (step.kind !== "env-reader")
|
|
2484
|
+
continue;
|
|
2485
|
+
const reader = envSignalReaderFor(step.reader.key);
|
|
2486
|
+
if (reader)
|
|
2487
|
+
this.state.envSignalReadersByLocal.set(step.name, reader);
|
|
2488
|
+
if (step.reader.key === "search")
|
|
2489
|
+
this.state.searchParamsLocals.add(step.name);
|
|
2490
|
+
}
|
|
2345
2491
|
this.state.loweringMatchers = prepareLoweringMatchers(ir.metadata);
|
|
2346
2492
|
augmentInheritedPropAccesses(ir);
|
|
2347
2493
|
}
|
|
@@ -2845,6 +2991,28 @@ ${goFields.join(`
|
|
|
2845
2991
|
if (propFallbackVars.size > 0)
|
|
2846
2992
|
lines.push("");
|
|
2847
2993
|
this.emitDynamicBodyWrappers(lines, ir, componentName, dynamicWithBody, propFallbackVars, emittedWrapperVars);
|
|
2994
|
+
const memoPropsParamMap = new Map(ir.metadata.propsParams.map((p) => [p.name, p]));
|
|
2995
|
+
this.state.hoistedMemoLocals = new Map;
|
|
2996
|
+
const hoistNames = new Set;
|
|
2997
|
+
for (const memo of ir.metadata.memos) {
|
|
2998
|
+
for (const name of filterArmEarlierSiblingRefs(this.emitCtx, memo, ir.metadata.signals, ir.metadata.propsParams)) {
|
|
2999
|
+
hoistNames.add(name);
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
if (hoistNames.size > 0) {
|
|
3003
|
+
for (const memo of ir.metadata.memos) {
|
|
3004
|
+
if (!hoistNames.has(memo.name))
|
|
3005
|
+
continue;
|
|
3006
|
+
const goType = this.inferMemoType(memo, ir.metadata.signals, memoPropsParamMap);
|
|
3007
|
+
const value = computeMemoInitialValue(this.emitCtx, memo, ir.metadata.signals, ir.metadata.propsParams, propFallbackVars, goType);
|
|
3008
|
+
let localName = `memo${capitalizeFieldName(memo.name)}`;
|
|
3009
|
+
while (GO_KEYWORDS.has(localName))
|
|
3010
|
+
localName += "_";
|
|
3011
|
+
lines.push(` var ${localName} ${goType} = ${value}`);
|
|
3012
|
+
this.state.hoistedMemoLocals.set(memo.name, localName);
|
|
3013
|
+
}
|
|
3014
|
+
lines.push("");
|
|
3015
|
+
}
|
|
2848
3016
|
lines.push(` return ${propsTypeName}{`);
|
|
2849
3017
|
lines.push("\t\tScopeID: scopeID,");
|
|
2850
3018
|
lines.push("\t\tBfParent: in.BfParent,");
|
|
@@ -2918,11 +3086,15 @@ ${goFields.join(`
|
|
|
2918
3086
|
continue;
|
|
2919
3087
|
lines.push(` ${nested.name}s: ${varName},`);
|
|
2920
3088
|
}
|
|
2921
|
-
const memoPropsParamMap = new Map(ir.metadata.propsParams.map((p) => [p.name, p]));
|
|
2922
3089
|
for (const memo of ir.metadata.memos) {
|
|
2923
3090
|
const fieldName = capitalizeFieldName(memo.name);
|
|
2924
3091
|
if (propFieldNames.has(fieldName))
|
|
2925
3092
|
continue;
|
|
3093
|
+
const hoistedLocal = this.state.hoistedMemoLocals.get(memo.name);
|
|
3094
|
+
if (hoistedLocal) {
|
|
3095
|
+
lines.push(` ${fieldName}: ${hoistedLocal},`);
|
|
3096
|
+
continue;
|
|
3097
|
+
}
|
|
2926
3098
|
const goType = this.inferMemoType(memo, ir.metadata.signals, memoPropsParamMap);
|
|
2927
3099
|
const memoValue = computeMemoInitialValue(this.emitCtx, memo, ir.metadata.signals, ir.metadata.propsParams, propFallbackVars, goType);
|
|
2928
3100
|
lines.push(` ${fieldName}: ${memoValue},`);
|
|
@@ -3611,12 +3783,14 @@ ${goFields.join(`
|
|
|
3611
3783
|
}
|
|
3612
3784
|
const memo = memos.find((m) => m.name === getterName);
|
|
3613
3785
|
if (memo) {
|
|
3614
|
-
return computeMemoInitialValueOrNull(this.emitCtx, memo, signals, propsParams);
|
|
3786
|
+
return computeMemoInitialValueOrNull(this.emitCtx, memo, signals, propsParams, undefined, new Set([memo.name]));
|
|
3615
3787
|
}
|
|
3616
3788
|
}
|
|
3617
3789
|
return null;
|
|
3618
3790
|
}
|
|
3619
3791
|
inferMemoType(memo, signals, propsParamMap) {
|
|
3792
|
+
if (isListFilterMemo(memo))
|
|
3793
|
+
return "[]any";
|
|
3620
3794
|
if (memo.bodyIsTemplateLiteral)
|
|
3621
3795
|
return "string";
|
|
3622
3796
|
if (memo.computation.includes("*") || memo.computation.includes("/") || memo.computation.includes("+") || memo.computation.includes("-")) {
|
|
@@ -4138,7 +4312,7 @@ ${goFields.join(`
|
|
|
4138
4312
|
"some"
|
|
4139
4313
|
]);
|
|
4140
4314
|
higherOrderShapeOf(node) {
|
|
4141
|
-
const cb =
|
|
4315
|
+
const cb = asCallbackMethodCall3(node);
|
|
4142
4316
|
if (!cb)
|
|
4143
4317
|
return null;
|
|
4144
4318
|
if (!GoTemplateAdapter.PREDICATE_METHODS.has(cb.method))
|
|
@@ -4213,6 +4387,12 @@ ${goFields.join(`
|
|
|
4213
4387
|
return evalForm;
|
|
4214
4388
|
return this.pushCallbackBF101(method, true);
|
|
4215
4389
|
}
|
|
4390
|
+
if (method === "map") {
|
|
4391
|
+
const evalForm = emitMapEval(recv, body, params[0] ?? "_", emit);
|
|
4392
|
+
if (evalForm !== null)
|
|
4393
|
+
return evalForm;
|
|
4394
|
+
return this.pushCallbackBF101(method, true);
|
|
4395
|
+
}
|
|
4216
4396
|
return this.pushCallbackBF101(method, true);
|
|
4217
4397
|
}
|
|
4218
4398
|
arrayMethod(method, object, args, emit) {
|
|
@@ -4566,6 +4746,9 @@ ${goFields.join(`
|
|
|
4566
4746
|
if (expr.callee.kind === "identifier" && expr.args.length === 0) {
|
|
4567
4747
|
return `$.${capitalizeFieldName(expr.callee.name)}`;
|
|
4568
4748
|
}
|
|
4749
|
+
if (asCallbackMethodCall3(expr) !== null) {
|
|
4750
|
+
return this.refuseFilterExprNode(expr);
|
|
4751
|
+
}
|
|
4569
4752
|
const result = this.renderFilterExpr(expr.callee, param, localVarMap);
|
|
4570
4753
|
if (this.filterExprUnsupported)
|
|
4571
4754
|
return "false";
|
|
@@ -4630,21 +4813,23 @@ ${goFields.join(`
|
|
|
4630
4813
|
}
|
|
4631
4814
|
return `or (${left}) (${right})`;
|
|
4632
4815
|
}
|
|
4633
|
-
default:
|
|
4634
|
-
this.
|
|
4635
|
-
this.state.errors.push({
|
|
4636
|
-
code: "BF101",
|
|
4637
|
-
severity: "error",
|
|
4638
|
-
message: `Filter predicate contains an expression that cannot be lowered to a Go template action: ${exprToString(expr)}`,
|
|
4639
|
-
loc: this.makeLoc(),
|
|
4640
|
-
suggestion: {
|
|
4641
|
-
message: "Options:\n1. Use /* @client */ for client-side evaluation\n2. Rewrite the predicate to avoid nested higher-order methods (`.filter()` / `.map()` / etc. inside the predicate body)"
|
|
4642
|
-
}
|
|
4643
|
-
});
|
|
4644
|
-
return "false";
|
|
4645
|
-
}
|
|
4816
|
+
default:
|
|
4817
|
+
return this.refuseFilterExprNode(expr);
|
|
4646
4818
|
}
|
|
4647
4819
|
}
|
|
4820
|
+
refuseFilterExprNode(expr) {
|
|
4821
|
+
this.filterExprUnsupported = true;
|
|
4822
|
+
this.state.errors.push({
|
|
4823
|
+
code: "BF101",
|
|
4824
|
+
severity: "error",
|
|
4825
|
+
message: `Filter predicate contains an expression that cannot be lowered to a Go template action: ${exprToString(expr)}`,
|
|
4826
|
+
loc: this.makeLoc(),
|
|
4827
|
+
suggestion: {
|
|
4828
|
+
message: "Options:\n1. Use /* @client */ for client-side evaluation\n2. Rewrite the predicate to avoid nested higher-order methods (`.filter()` / `.map()` / etc. inside the predicate body)"
|
|
4829
|
+
}
|
|
4830
|
+
});
|
|
4831
|
+
return "false";
|
|
4832
|
+
}
|
|
4648
4833
|
isGoFunctionCall(expr) {
|
|
4649
4834
|
switch (expr.kind) {
|
|
4650
4835
|
case "binary":
|
|
@@ -4898,7 +5083,7 @@ ${goFields.join(`
|
|
|
4898
5083
|
});
|
|
4899
5084
|
return plain("false");
|
|
4900
5085
|
}
|
|
4901
|
-
if (
|
|
5086
|
+
if (asCallbackMethodCall3(expr)) {
|
|
4902
5087
|
const rendered = this.renderParsedExpr(expr);
|
|
4903
5088
|
const split = this.splitPreamble(rendered);
|
|
4904
5089
|
if (split)
|