@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.
@@ -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>): string | null;
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":"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"}
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;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAGvD;;;;;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,CA8BT;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAS/F"}
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
- searchParamsLocalNames,
384
- prepareLoweringMatchers
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 getterCallName = (e) => e.kind === "call" && e.callee.kind === "identifier" && e.args.length === 0 ? e.callee.name : null;
1773
- const propsMemberName = (e) => e.kind === "member" && !e.computed && e.object.kind === "identifier" && e.object.name === "props" ? e.property : null;
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.searchParamsLocals = searchParamsLocalNames(ir.metadata);
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 = asCallbackMethodCall(node);
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.filterExprUnsupported = true;
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 (asCallbackMethodCall(expr)) {
5086
+ if (asCallbackMethodCall3(expr)) {
4902
5087
  const rendered = this.renderParsedExpr(expr);
4903
5088
  const split = this.splitPreamble(rendered);
4904
5089
  if (split)