@barefootjs/jsx 0.16.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/adapters/env-signal.d.ts +73 -15
- package/dist/adapters/env-signal.d.ts.map +1 -1
- package/dist/adapters/jsx-adapter.d.ts.map +1 -1
- package/dist/adapters/parsed-expr-emitter.d.ts +7 -6
- package/dist/adapters/parsed-expr-emitter.d.ts.map +1 -1
- package/dist/analyzer-context.d.ts +29 -1
- package/dist/analyzer-context.d.ts.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/builtin-lowering-plugins.d.ts +34 -0
- package/dist/builtin-lowering-plugins.d.ts.map +1 -0
- package/dist/compiler.d.ts.map +1 -1
- package/dist/expression-parser.d.ts +264 -163
- package/dist/expression-parser.d.ts.map +1 -1
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7839 -7019
- package/dist/ir-to-client-js/csr-substitute.d.ts.map +1 -1
- package/dist/ir-to-client-js/plan/build-declaration-emit.d.ts.map +1 -1
- package/dist/ir-to-client-js/plan/declaration-emit.d.ts +9 -0
- package/dist/ir-to-client-js/plan/declaration-emit.d.ts.map +1 -1
- package/dist/jsx-to-ir.d.ts.map +1 -1
- package/dist/lowering-registry.d.ts +122 -0
- package/dist/lowering-registry.d.ts.map +1 -0
- package/dist/query-href-lowering.d.ts +63 -0
- package/dist/query-href-lowering.d.ts.map +1 -0
- package/dist/ssr-defaults.d.ts.map +1 -1
- package/dist/ssr-seed-plan.d.ts +84 -0
- package/dist/ssr-seed-plan.d.ts.map +1 -0
- package/dist/types.d.ts +180 -11
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +68 -3
- package/src/__tests__/analyzer.test.ts +53 -0
- package/src/__tests__/expression-parser.test.ts +714 -392
- package/src/__tests__/free-identifiers.test.ts +55 -0
- package/src/__tests__/ir-reduce-op.test.ts +18 -21
- package/src/__tests__/ir-sort-comparator.test.ts +19 -20
- package/src/__tests__/lowering-registry.test.ts +141 -0
- package/src/__tests__/materialize-getter-calls.test.ts +58 -0
- package/src/__tests__/primitive-resolver-alias.test.ts +23 -0
- package/src/__tests__/query-href-recognition.test.ts +58 -0
- package/src/__tests__/serialize-parsed-expr.test.ts +223 -0
- package/src/__tests__/ssr-seed-plan.test.ts +212 -0
- package/src/__tests__/unsupported-expression.test.ts +98 -4
- package/src/adapters/env-signal.ts +108 -21
- package/src/adapters/jsx-adapter.ts +17 -0
- package/src/adapters/parsed-expr-emitter.ts +39 -41
- package/src/analyzer-context.ts +72 -27
- package/src/analyzer.ts +226 -9
- package/src/builtin-lowering-plugins.ts +54 -0
- package/src/compiler.ts +6 -1
- package/src/expression-parser.ts +1375 -929
- package/src/index.ts +31 -3
- package/src/ir-to-client-js/csr-substitute.ts +5 -0
- package/src/ir-to-client-js/plan/build-declaration-emit.ts +16 -0
- package/src/ir-to-client-js/plan/declaration-emit.ts +9 -0
- package/src/ir-to-client-js/stringify/declaration-emit.ts +11 -0
- package/src/jsx-to-ir.ts +182 -43
- package/src/lowering-registry.ts +160 -0
- package/src/query-href-lowering.ts +147 -0
- package/src/ssr-defaults.ts +5 -1
- package/src/ssr-seed-plan.ts +146 -0
- package/src/types.ts +182 -12
- package/src/__tests__/flatmap-support.test.ts +0 -218
- package/src/__tests__/reduce-op.test.ts +0 -201
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csr-substitute.d.ts","sourceRoot":"","sources":["../../src/ir-to-client-js/csr-substitute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEvD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACrC;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC,CAAA;AAEtE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,YAAY,CAAA;IAC3B,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAA;IACnB,qFAAqF;IACrF,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACrC;AAED,MAAM,WAAW,MAAM;IACrB;;;;;OAKG;IACH,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAC3C,4EAA4E;IAC5E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACV;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,CAmB7D;AAmMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGtF;AAiBD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,GAC7B,MAAM,
|
|
1
|
+
{"version":3,"file":"csr-substitute.d.ts","sourceRoot":"","sources":["../../src/ir-to-client-js/csr-substitute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEvD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACrC;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC,CAAA;AAEtE;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,YAAY,CAAA;IAC3B,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAA;IACnB,qFAAqF;IACrF,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACrC;AAED,MAAM,WAAW,MAAM;IACrB;;;;;OAKG;IACH,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAC3C,4EAA4E;IAC5E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACV;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,CAmB7D;AAmMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGtF;AAiBD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,GAC7B,MAAM,CAsBR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-declaration-emit.d.ts","sourceRoot":"","sources":["../../../src/ir-to-client-js/plan/build-declaration-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"build-declaration-emit.d.ts","sourceRoot":"","sources":["../../../src/ir-to-client-js/plan/build-declaration-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAG3D,OAAO,KAAK,EAEV,mBAAmB,EAEpB,MAAM,uBAAuB,CAAA;AAE9B;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,EAAE,WAAW,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;IAC7D,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;CAC3C;AAED,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,eAAe,EACpB,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,GAC7C,sBAAsB,CAMxB;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,sBAAsB,GAC9B,mBAAmB,CAkCrB"}
|
|
@@ -52,6 +52,15 @@ export interface SignalEmitPlan {
|
|
|
52
52
|
branchCondition?: string;
|
|
53
53
|
/** Profile-mode IR-aligned id, appended as the `createSignal` 2nd arg (#1690). */
|
|
54
54
|
bfId?: string;
|
|
55
|
+
/**
|
|
56
|
+
* When set, the full initializer expression to emit verbatim instead of
|
|
57
|
+
* `createSignal(<initialValueExpr>)`. Env signals (#2057) emit their own
|
|
58
|
+
* factory call — e.g. `createSearchParams()` — with no baked initial value,
|
|
59
|
+
* profile id, controlled effect, or branch condition (the tuple is a stable
|
|
60
|
+
* request-scoped view, not stored state). When present the stringifier emits
|
|
61
|
+
* `const [<getter>, <setter>] = <initializerOverride>` and nothing else.
|
|
62
|
+
*/
|
|
63
|
+
initializerOverride?: string;
|
|
55
64
|
}
|
|
56
65
|
export interface ControlledSignalEffectPlan {
|
|
57
66
|
/** Receiver setter — must equal the parent signal's `setter`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"declaration-emit.d.ts","sourceRoot":"","sources":["../../../src/ir-to-client-js/plan/declaration-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAA;IAChB,qDAAqD;IACrD,OAAO,EAAE,OAAO,GAAG,KAAK,GAAG,KAAK,CAAA;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,+EAA+E;IAC/E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAA;IACd,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,CAAA;IACxB;;;OAGG;IACH,gBAAgB,EAAE,0BAA0B,GAAG,IAAI,CAAA;IACnD;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"declaration-emit.d.ts","sourceRoot":"","sources":["../../../src/ir-to-client-js/plan/declaration-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAA;IAChB,qDAAqD;IACrD,OAAO,EAAE,OAAO,GAAG,KAAK,GAAG,KAAK,CAAA;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,+EAA+E;IAC/E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAA;IACd,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,CAAA;IACxB;;;OAGG;IACH,gBAAgB,EAAE,0BAA0B,GAAG,IAAI,CAAA;IACnD;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAA;IACpB,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,qDAAqD;IACrD,eAAe,EAAE,MAAM,CAAA;IACvB,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAA;IACjB,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAA;IACZ,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,MAAM,mBAAmB,GAC3B,gBAAgB,GAChB,cAAc,GACd,YAAY,GACZ,gBAAgB,CAAA"}
|
package/dist/jsx-to-ir.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsx-to-ir.d.ts","sourceRoot":"","sources":["../src/jsx-to-ir.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,KAAK,MAAM,EAyBZ,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,KAAK,eAAe,
|
|
1
|
+
{"version":3,"file":"jsx-to-ir.d.ts","sourceRoot":"","sources":["../src/jsx-to-ir.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,KAAK,MAAM,EAyBZ,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,KAAK,eAAe,EAA0E,MAAM,uBAAuB,CAAA;AAqnBpI,wBAAgB,OAAO,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAIhE"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Call-lowering plugin registry (#2057).
|
|
3
|
+
*
|
|
4
|
+
* The compiler core carries no bespoke per-API recognition branches. Instead, a
|
|
5
|
+
* lowering plugin *recognises* a call — by the import it comes from and its
|
|
6
|
+
* argument shape — and returns a **backend-neutral `LoweringNode`**. Each adapter
|
|
7
|
+
* renders that node in its own template syntax. This is a deliberate two-layer
|
|
8
|
+
* split:
|
|
9
|
+
*
|
|
10
|
+
* - **Layer 1 (this module + plugins):** adapter-agnostic. A plugin matches a
|
|
11
|
+
* call to a neutral node and never mentions Go/Perl/… syntax.
|
|
12
|
+
* - **Layer 2 (adapters):** plugin-agnostic. Each adapter has ONE renderer per
|
|
13
|
+
* node kind, so SSR/CSR parity is enforced once, not per plugin.
|
|
14
|
+
*
|
|
15
|
+
* Everything flows through this one seam — including first-party APIs. A
|
|
16
|
+
* userland package registers its plugin via {@link registerLoweringPlugin}, and
|
|
17
|
+
* a built-in like `queryHref` is registered by the compiler *as a default
|
|
18
|
+
* plugin* (see `builtin-lowering-plugins.ts`), not as an adapter branch. So an
|
|
19
|
+
* adapter can't tell a shipped API from a third-party one: both are just entries
|
|
20
|
+
* in this registry. That uniformity is the point — there is no "special" path to
|
|
21
|
+
* keep in sync.
|
|
22
|
+
*
|
|
23
|
+
* This is NOT the "output-rewriting hook" CLAUDE.md forbids: a plugin returns a
|
|
24
|
+
* structured IR node, never a rewritten output string, so the compiler's output
|
|
25
|
+
* stays determined by the compiler, not by whatever munges the emitted text.
|
|
26
|
+
*/
|
|
27
|
+
import type { ParsedExpr } from './expression-parser.ts';
|
|
28
|
+
import type { IRMetadata } from './types.ts';
|
|
29
|
+
/**
|
|
30
|
+
* A backend-neutral include triple for a {@link LoweringNode} `guard-list`.
|
|
31
|
+
* `guard` is the conditional test of a `key: cond ? v : <omit>` include, or null
|
|
32
|
+
* for a plain `key: v` (included purely on value-truthiness). An adapter renders
|
|
33
|
+
* the guard to decide inclusion; the *runtime helper* then applies the emptiness
|
|
34
|
+
* / array-append rules to the value. (Structurally identical to the query-href
|
|
35
|
+
* lowering's own triple, which the `queryHref` plugin passes through unchanged.)
|
|
36
|
+
*/
|
|
37
|
+
export interface LoweringTriple {
|
|
38
|
+
guard: ParsedExpr | null;
|
|
39
|
+
key: string;
|
|
40
|
+
value: ParsedExpr;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A backend-neutral lowering result. Adapters render each variant in their own
|
|
44
|
+
* template language; the shapes carry everything a renderer needs and nothing
|
|
45
|
+
* adapter-specific.
|
|
46
|
+
*/
|
|
47
|
+
export type LoweringNode =
|
|
48
|
+
/**
|
|
49
|
+
* A guard/key/value include list lowered to a query helper — the shape of
|
|
50
|
+
* `queryHref(base, { … })`. `helper` is the logical helper id (`'query'`),
|
|
51
|
+
* which each adapter maps to its own runtime helper (`bf_query` in go,
|
|
52
|
+
* `bf->query` in mojo, `$bf.query` in xslate). Each triple's `guard` controls
|
|
53
|
+
* inclusion; the runtime helper then applies the non-empty / array-append
|
|
54
|
+
* rules to the value (so an included-but-empty value is dropped and array
|
|
55
|
+
* members are appended), matching the client `queryHref` exactly. Adapters
|
|
56
|
+
* MUST switch on `helper` — a `guard-list` is not implicitly `query`.
|
|
57
|
+
*/
|
|
58
|
+
{
|
|
59
|
+
kind: 'guard-list';
|
|
60
|
+
helper: string;
|
|
61
|
+
base: ParsedExpr;
|
|
62
|
+
triples: LoweringTriple[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A plain helper call `helper(...args)` — the general escape hatch for a pure
|
|
66
|
+
* builder that lowers to a single runtime-helper invocation. Unused today;
|
|
67
|
+
* present so the neutral vocabulary isn't single-purpose.
|
|
68
|
+
*/
|
|
69
|
+
| {
|
|
70
|
+
kind: 'helper-call';
|
|
71
|
+
helper: string;
|
|
72
|
+
args: readonly ParsedExpr[];
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* A matcher bound to one component's metadata: given a parsed call's callee +
|
|
76
|
+
* args, returns a neutral node or null to decline. Produced by a plugin's
|
|
77
|
+
* {@link LoweringPlugin.prepare} so the per-component import-name resolution runs
|
|
78
|
+
* once (at adapter init), not on every emit.
|
|
79
|
+
*/
|
|
80
|
+
export type LoweringMatcher = (callee: ParsedExpr, args: readonly ParsedExpr[]) => LoweringNode | null;
|
|
81
|
+
/**
|
|
82
|
+
* A lowering plugin. `prepare` resolves the local names its import is bound
|
|
83
|
+
* under in this component and returns a bound {@link LoweringMatcher}, or null
|
|
84
|
+
* when the component doesn't use it (so the adapter skips it entirely). A
|
|
85
|
+
* plugin never emits adapter syntax — only neutral nodes.
|
|
86
|
+
*/
|
|
87
|
+
export interface LoweringPlugin {
|
|
88
|
+
/** Stable id, for dedup/diagnostics (e.g. `'my-pkg-url'`). */
|
|
89
|
+
name: string;
|
|
90
|
+
prepare(metadata: IRMetadata): LoweringMatcher | null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Register a lowering plugin. Idempotent by `name` — re-registering the same
|
|
94
|
+
* name replaces the prior plugin, so a double side-effect import can't stack
|
|
95
|
+
* duplicates. First-party packages call this at module load.
|
|
96
|
+
*/
|
|
97
|
+
export declare function registerLoweringPlugin(plugin: LoweringPlugin): void;
|
|
98
|
+
/** The registered plugins, in registration order (a copy — mutating the result
|
|
99
|
+
* can't reorder or corrupt the registry). */
|
|
100
|
+
export declare function getLoweringPlugins(): readonly LoweringPlugin[];
|
|
101
|
+
/**
|
|
102
|
+
* Bind every registered plugin to a component's metadata, returning the matchers
|
|
103
|
+
* that are active for it (import present). Adapters call this once at init and
|
|
104
|
+
* store the result, then try each matcher on the calls they lower — replacing a
|
|
105
|
+
* hardcoded per-API recognizer.
|
|
106
|
+
*/
|
|
107
|
+
export declare function prepareLoweringMatchers(metadata: IRMetadata): LoweringMatcher[];
|
|
108
|
+
/**
|
|
109
|
+
* Convenience one-shot match against all registered plugins for a given
|
|
110
|
+
* metadata. Prefer {@link prepareLoweringMatchers} on a hot path (it resolves
|
|
111
|
+
* import names once); this re-resolves per call and is meant for tests / cold
|
|
112
|
+
* call sites.
|
|
113
|
+
*/
|
|
114
|
+
export declare function matchLoweringCall(callee: ParsedExpr, args: readonly ParsedExpr[], metadata: IRMetadata): LoweringNode | null;
|
|
115
|
+
/**
|
|
116
|
+
* Test-only: replace the registry contents wholesale. The double-underscore
|
|
117
|
+
* prefix marks it as an internal seam — tests use it to restore global state in
|
|
118
|
+
* `afterEach` so a sample plugin can't leak into other suites. Never call from
|
|
119
|
+
* production code.
|
|
120
|
+
*/
|
|
121
|
+
export declare function __resetLoweringPluginsForTest(next?: readonly LoweringPlugin[]): void;
|
|
122
|
+
//# sourceMappingURL=lowering-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lowering-registry.d.ts","sourceRoot":"","sources":["../src/lowering-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,UAAU,CAAA;CAClB;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY;AACtB;;;;;;;;;GASG;AACD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,cAAc,EAAE,CAAA;CAAE;AACrF;;;;GAIG;GACD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,UAAU,EAAE,CAAA;CAAE,CAAA;AAExE;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,CAC5B,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,SAAS,UAAU,EAAE,KACxB,YAAY,GAAG,IAAI,CAAA;AAExB;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,QAAQ,EAAE,UAAU,GAAG,eAAe,GAAG,IAAI,CAAA;CACtD;AAID;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAInE;AAED;8CAC8C;AAC9C,wBAAgB,kBAAkB,IAAI,SAAS,cAAc,EAAE,CAE9D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,UAAU,GAAG,eAAe,EAAE,CAO/E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,QAAQ,EAAE,UAAU,GACnB,YAAY,GAAG,IAAI,CAMrB;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,GAAE,SAAS,cAAc,EAAO,GAAG,IAAI,CAGxF"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend-neutral destructuring of a recognised `queryHref(base, { … })` call
|
|
3
|
+
* (#2042) into a base expression plus include triples, shared by the SSR
|
|
4
|
+
* adapters' query lowering.
|
|
5
|
+
*
|
|
6
|
+
* `queryHref` is the pure functional URL-query builder (the counterpart to
|
|
7
|
+
* `searchParams()`); its call + object literal are already structured IR, so an
|
|
8
|
+
* adapter lowers it to its query helper without any block-body recognition or
|
|
9
|
+
* re-parse. This module only does the structural match — turning the object
|
|
10
|
+
* literal's properties into `{ guard, key, value }` triples — leaving each
|
|
11
|
+
* adapter to format the include condition and the helper call in its own
|
|
12
|
+
* template language.
|
|
13
|
+
*
|
|
14
|
+
* Inclusion is truthy-omit over string values (matching the client `queryHref`'s
|
|
15
|
+
* `if (value)`): a plain `key: v` is included iff `v` is a non-empty string
|
|
16
|
+
* (`guard: null`); a conditional `key: cond ? a : <undefined|null|''>` is
|
|
17
|
+
* included iff `cond` AND `a` is non-empty (`guard: cond`, `value: a`).
|
|
18
|
+
*/
|
|
19
|
+
import type { ParsedExpr } from './expression-parser.ts';
|
|
20
|
+
export interface QueryHrefTriple {
|
|
21
|
+
/**
|
|
22
|
+
* The conditional test of a `key: cond ? a : <omit>` include, or null for a
|
|
23
|
+
* plain `key: v` (which is included purely on value-truthiness). An adapter
|
|
24
|
+
* combines this with the value's non-emptiness to form the include condition.
|
|
25
|
+
*/
|
|
26
|
+
guard: ParsedExpr | null;
|
|
27
|
+
/** The literal search-param key. */
|
|
28
|
+
key: string;
|
|
29
|
+
/** The value expression (the consequent for a conditional include). */
|
|
30
|
+
value: ParsedExpr;
|
|
31
|
+
}
|
|
32
|
+
export interface QueryHrefCall {
|
|
33
|
+
base: ParsedExpr;
|
|
34
|
+
triples: QueryHrefTriple[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Match a `queryHref(base, { … })` call from its callee + args, returning the
|
|
38
|
+
* base and include triples, or null when it isn't a `queryHref` call with a
|
|
39
|
+
* plain object-literal second argument (→ the adapter falls back to its generic
|
|
40
|
+
* lowering). `localNames` are the bindings `queryHref` is imported under (from
|
|
41
|
+
* `queryHrefLocalNames`).
|
|
42
|
+
*/
|
|
43
|
+
export declare function matchQueryHrefCall(callee: ParsedExpr, args: readonly ParsedExpr[], localNames: ReadonlySet<string>): QueryHrefCall | null;
|
|
44
|
+
/**
|
|
45
|
+
* Format a {@link QueryHrefCall} as the flat argument list for a guard-list
|
|
46
|
+
* query helper (`bf->query(base, guard, key, value, …)` in Mojo / `$bf.query(…)`
|
|
47
|
+
* in Xslate — the two adapters whose helper does the non-empty check itself).
|
|
48
|
+
* Each triple contributes a guard (`'1'` for a plain include, or the lowered
|
|
49
|
+
* condition for a conditional one), the key as a string literal, and the value —
|
|
50
|
+
* all lowered through the adapter's `emit`. The caller wraps the result in its
|
|
51
|
+
* own `<helper>(…)` call. (The go-template adapter folds the non-empty check
|
|
52
|
+
* into the include condition itself, so it formats its own form instead.)
|
|
53
|
+
*
|
|
54
|
+
* A conditional guard that is NOT already boolean-shaped (a bare value, a member
|
|
55
|
+
* access, `&&`/`||`) is JS *string* truthiness — `'0'` is a truthy string in JS
|
|
56
|
+
* but false under Perl's `unless`. To keep SSR byte-identical to the client (and
|
|
57
|
+
* to the go adapter, whose `lowerUrlGuard` does the same), such a guard is
|
|
58
|
+
* normalised to a `guard !== ''` test, emitted against a string literal so each
|
|
59
|
+
* adapter renders string `ne`, not numeric `!=`. Comparisons / `!x` / boolean
|
|
60
|
+
* literals already yield a real boolean and pass through unchanged.
|
|
61
|
+
*/
|
|
62
|
+
export declare function queryHrefArgs(q: QueryHrefCall, emit: (e: ParsedExpr) => string): string[];
|
|
63
|
+
//# sourceMappingURL=query-href-lowering.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-href-lowering.d.ts","sourceRoot":"","sources":["../src/query-href-lowering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACxB,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,uEAAuE;IACvE,KAAK,EAAE,UAAU,CAAA;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,EAAE,eAAe,EAAE,CAAA;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,GAC9B,aAAa,GAAG,IAAI,CAkBtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,MAAM,GAAG,MAAM,EAAE,CAoBzF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-defaults.d.ts","sourceRoot":"","sources":["../src/ssr-defaults.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,KAAK,EAAE,OAAO,CAAA;IACd;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAkBD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"ssr-defaults.d.ts","sourceRoot":"","sources":["../src/ssr-defaults.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,KAAK,EAAE,OAAO,CAAA;IACd;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAkBD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,CAuG/F"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend-neutral SSR seed plan — which signals/memos an adapter may seed
|
|
3
|
+
* in-template at SSR time, and from what scope.
|
|
4
|
+
*
|
|
5
|
+
* Design principle: the IR/analyzer side ANALYZES and attaches structured
|
|
6
|
+
* information; adapters only EMIT. The "is this binding derivable from names
|
|
7
|
+
* already in template scope" decision used to live (triplicated) in the
|
|
8
|
+
* template adapters' seed paths; this module computes it once, on the IR, so
|
|
9
|
+
* every adapter consumes the same plan and only supplies its own syntax.
|
|
10
|
+
*
|
|
11
|
+
* Ordering / acyclicity guarantee: `steps` lists the component's signals
|
|
12
|
+
* first, then its memos, each group in declaration order (matching
|
|
13
|
+
* `IRMetadata.signals` / `IRMetadata.memos`). A binding's name only enters
|
|
14
|
+
* the scope set AFTER its own step is decided, so a `derived` step's `frees`
|
|
15
|
+
* can only name `baseScope` entries or EARLIER steps — self- and
|
|
16
|
+
* forward-references are rejected by construction, and a consumer emitting
|
|
17
|
+
* the steps top-to-bottom never reads an undeclared local.
|
|
18
|
+
*
|
|
19
|
+
* Module-scope pure-string consts count as in-scope (they are part of
|
|
20
|
+
* `baseScope`) because every adapter compile-time-inlines them to their
|
|
21
|
+
* literal value — `collectModuleStringConsts` is the shared source of that
|
|
22
|
+
* set — so a reference to one is never a template-variable read.
|
|
23
|
+
*
|
|
24
|
+
* A `derived` step with EMPTY `frees` is a constant expression (e.g.
|
|
25
|
+
* `createSignal('b')`): the plan still classifies it as derived because the
|
|
26
|
+
* expression is fully analyzable; emit-side constant-skipping (adapters keep
|
|
27
|
+
* their existing ssr-defaults seeding for such inits) is an adapter concern,
|
|
28
|
+
* not a plan concern. Likewise the plan makes no backend-specific choices —
|
|
29
|
+
* no target-variable checks, no self-shadowing rules, no per-backend shape
|
|
30
|
+
* catalogs — those stay in the adapters.
|
|
31
|
+
*/
|
|
32
|
+
import { type EnvSignalReader } from './adapters/env-signal.ts';
|
|
33
|
+
import { type ParsedExpr } from './expression-parser.ts';
|
|
34
|
+
import type { IRMetadata } from './types.ts';
|
|
35
|
+
/**
|
|
36
|
+
* One binding in component declaration order (signals first, then memos —
|
|
37
|
+
* matching `IRMetadata` order and the adapters' iteration).
|
|
38
|
+
*
|
|
39
|
+
* - `env-reader`: an env signal whose `envReader` key resolves in the shared
|
|
40
|
+
* registry (`envSignalReaderFor`). The runtime provides the per-request
|
|
41
|
+
* reader, so there is nothing to seed; the name still enters scope so a
|
|
42
|
+
* later derived step may reference it. An `envReader` key UNKNOWN to the
|
|
43
|
+
* registry falls through to the normal derived/opaque rules instead.
|
|
44
|
+
* - `derived`: the binding's value expression is a supported shape whose free
|
|
45
|
+
* identifiers are all in scope at this point (baseScope + earlier steps) —
|
|
46
|
+
* an adapter may seed it in-template by lowering `parsed`/`expr`.
|
|
47
|
+
* - `opaque`: not seedable this way (empty init, unsupported shape,
|
|
48
|
+
* unanalyzable free set, out-of-scope reference, or a block-bodied memo).
|
|
49
|
+
* The name still enters scope for later steps; adapters keep their static
|
|
50
|
+
* ssr-defaults seeding for it.
|
|
51
|
+
*/
|
|
52
|
+
export type SsrSeedStep = {
|
|
53
|
+
kind: 'env-reader';
|
|
54
|
+
name: string;
|
|
55
|
+
reader: EnvSignalReader;
|
|
56
|
+
} | {
|
|
57
|
+
kind: 'derived';
|
|
58
|
+
name: string;
|
|
59
|
+
origin: 'signal' | 'memo';
|
|
60
|
+
expr: string;
|
|
61
|
+
parsed: ParsedExpr;
|
|
62
|
+
frees: string[];
|
|
63
|
+
} | {
|
|
64
|
+
kind: 'opaque';
|
|
65
|
+
name: string;
|
|
66
|
+
origin: 'signal' | 'memo';
|
|
67
|
+
};
|
|
68
|
+
export interface SsrSeedPlan {
|
|
69
|
+
/**
|
|
70
|
+
* Names in scope before any step: props params, the props-object name
|
|
71
|
+
* (when the component takes an undestructured props object), and module
|
|
72
|
+
* pure-string consts (compile-time inlined by every adapter).
|
|
73
|
+
*/
|
|
74
|
+
baseScope: string[];
|
|
75
|
+
steps: SsrSeedStep[];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Compute the component's SSR seed plan from its metadata. See the module
|
|
79
|
+
* doc for the contract. Memo steps are gated to EXPRESSION-BODIED memos
|
|
80
|
+
* (`extractArrowBodyExpression` returns the body): a block-bodied memo is
|
|
81
|
+
* `opaque` even when the analyzer folded it to a `parsed` expression.
|
|
82
|
+
*/
|
|
83
|
+
export declare function computeSsrSeedPlan(metadata: IRMetadata): SsrSeedPlan;
|
|
84
|
+
//# sourceMappingURL=ssr-seed-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr-seed-plan.d.ts","sourceRoot":"","sources":["../src/ssr-seed-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAA;AACnF,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,GAC/G;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAA;CAAE,CAAA;AAE/D,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,KAAK,EAAE,WAAW,EAAE,CAAA;CACrB;AA0BD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,UAAU,GAAG,WAAW,CAwCpE"}
|
package/dist/types.d.ts
CHANGED
|
@@ -3,7 +3,25 @@
|
|
|
3
3
|
*
|
|
4
4
|
* JSX-independent intermediate representation for multi-backend support.
|
|
5
5
|
*/
|
|
6
|
-
import type { ParsedExpr, ParsedStatement
|
|
6
|
+
import type { ParsedExpr, ParsedStatement } from './expression-parser.ts';
|
|
7
|
+
import type { SsrSeedPlan } from './ssr-seed-plan.ts';
|
|
8
|
+
/**
|
|
9
|
+
* Loop-hoisted sort comparator for the `.sort().map()` / `.toSorted().map()`
|
|
10
|
+
* pattern (#2018 P5). Carries the generic comparator `arrow` (params + body)
|
|
11
|
+
* that the SSR adapter serializes to the runtime evaluator (eval-first) or, for
|
|
12
|
+
* a `localeCompare` comparator the evaluator can't model, recovers a structured
|
|
13
|
+
* comparator from via `sortComparatorFromArrow`. The `paramA` / `paramB` / `raw`
|
|
14
|
+
* fields round-trip the comparator to native JS for the client / CSR path
|
|
15
|
+
* (`(paramA, paramB) => raw`), so the client is untouched.
|
|
16
|
+
*/
|
|
17
|
+
export type IRLoopSort = {
|
|
18
|
+
arrow: Extract<ParsedExpr, {
|
|
19
|
+
kind: 'arrow';
|
|
20
|
+
}>;
|
|
21
|
+
paramA: string;
|
|
22
|
+
paramB: string;
|
|
23
|
+
raw: string;
|
|
24
|
+
};
|
|
7
25
|
export interface Position {
|
|
8
26
|
line: number;
|
|
9
27
|
column: number;
|
|
@@ -201,6 +219,20 @@ export interface IRExpression {
|
|
|
201
219
|
expr: string;
|
|
202
220
|
/** Pre-transformed expr with destructured prop refs rewritten to _p.xxx (for client JS templates). */
|
|
203
221
|
templateExpr?: string;
|
|
222
|
+
/**
|
|
223
|
+
* Structured parse of `expr` (`parseExpression(expr.trim())`), attached once
|
|
224
|
+
* during IR construction so SSR adapters emit from the tree instead of each
|
|
225
|
+
* re-parsing the string at emit time (and so a multi-adapter build parses it
|
|
226
|
+
* once, not per adapter). Plain serializable data.
|
|
227
|
+
*
|
|
228
|
+
* OPTIONAL by design — consumers MUST fall back to parsing `expr` when it is
|
|
229
|
+
* missing. It is absent for an empty/whitespace `expr`, and may also be
|
|
230
|
+
* absent for a node the IR-build walk doesn't reach (the walk is best-effort;
|
|
231
|
+
* under-coverage is a missed optimization, never a behavioural change). When
|
|
232
|
+
* present, an unparsable expression is a `{ kind: 'unsupported' }` node (the
|
|
233
|
+
* adapter's own support gate handles it).
|
|
234
|
+
*/
|
|
235
|
+
parsed?: ParsedExpr;
|
|
204
236
|
typeInfo: TypeInfo | null;
|
|
205
237
|
reactive: boolean;
|
|
206
238
|
slotId: string | null;
|
|
@@ -224,6 +256,13 @@ export interface IRConditional {
|
|
|
224
256
|
condition: string;
|
|
225
257
|
/** Pre-transformed condition with destructured prop refs rewritten to _p.xxx. */
|
|
226
258
|
templateCondition?: string;
|
|
259
|
+
/**
|
|
260
|
+
* Structured parse of `condition` (`parseExpression(condition.trim())`),
|
|
261
|
+
* attached during IR construction so adapters lower the condition from the
|
|
262
|
+
* tree instead of re-parsing the string. Optional/best-effort — see
|
|
263
|
+
* `IRExpression.parsed`; consumers fall back to parsing `condition`.
|
|
264
|
+
*/
|
|
265
|
+
parsedCondition?: ParsedExpr;
|
|
227
266
|
conditionType: TypeInfo | null;
|
|
228
267
|
reactive: boolean;
|
|
229
268
|
whenTrue: IRNode;
|
|
@@ -275,6 +314,14 @@ export interface IRLoop {
|
|
|
275
314
|
*/
|
|
276
315
|
method?: 'flatMap';
|
|
277
316
|
array: string;
|
|
317
|
+
/**
|
|
318
|
+
* Structured parse of `array` (`parseExpression(array.trim())`), attached
|
|
319
|
+
* during IR construction so adapters lower the loop's array from the tree
|
|
320
|
+
* instead of re-parsing the string (e.g. the Go adapter's scalar-literal
|
|
321
|
+
* loop typing). Optional/best-effort — mirrors `IRExpression.parsed`;
|
|
322
|
+
* consumers fall back to parsing `array`.
|
|
323
|
+
*/
|
|
324
|
+
arrayParsed?: ParsedExpr;
|
|
278
325
|
/** Pre-transformed array expr with destructured prop refs rewritten to _p.xxx. */
|
|
279
326
|
templateArray?: string;
|
|
280
327
|
arrayType: TypeInfo | null;
|
|
@@ -336,14 +383,16 @@ export interface IRLoop {
|
|
|
336
383
|
* When present, the loop renders with an if-condition wrapping each iteration.
|
|
337
384
|
* Example: todos.filter(t => !t.done).map(...) stores { param: 't', predicate: ParsedExpr, raw: '!t.done' }
|
|
338
385
|
*
|
|
339
|
-
*
|
|
386
|
+
* Block-body filters like
|
|
340
387
|
* filter(t => { const f = filter(); if (f === 'active') return !t.done; return true })
|
|
341
|
-
*
|
|
388
|
+
* are normalized to a single boolean `predicate` expression at IR-build time
|
|
389
|
+
* (#2040, `foldBlockToExpr` + `predicateTernaryToLogical` in `jsx-to-ir`), so
|
|
390
|
+
* adapters only ever see the unified expression form — there is no separate
|
|
391
|
+
* block-statement shape to lower.
|
|
342
392
|
*/
|
|
343
393
|
filterPredicate?: {
|
|
344
394
|
param: string;
|
|
345
395
|
predicate?: ParsedExpr;
|
|
346
|
-
blockBody?: ParsedStatement[];
|
|
347
396
|
raw: string;
|
|
348
397
|
};
|
|
349
398
|
/**
|
|
@@ -351,14 +400,14 @@ export interface IRLoop {
|
|
|
351
400
|
* When present, the loop array is sorted before iteration.
|
|
352
401
|
* Example: todos.sort((a, b) => a.priority - b.priority).map(...)
|
|
353
402
|
*
|
|
354
|
-
* The
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
403
|
+
* The {@link IRLoopSort} struct carries the generic comparator `arrow`
|
|
404
|
+
* (params + body) the SSR adapter serializes to the runtime evaluator
|
|
405
|
+
* (eval-first; `sortComparatorFromArrow` fallback for `localeCompare`), plus
|
|
406
|
+
* the param names + raw body for the client JS round-trip. Lifted off the
|
|
407
|
+
* `.sort()` callback during `jsx-to-ir.ts` chain detection — see
|
|
408
|
+
* `extractSortComparator`. (#2018 P5)
|
|
360
409
|
*/
|
|
361
|
-
sortComparator?:
|
|
410
|
+
sortComparator?: IRLoopSort;
|
|
362
411
|
/**
|
|
363
412
|
* When both filter and sort are chained, indicates the order of operations.
|
|
364
413
|
* 'filter-sort': filter first, then sort (e.g., filter().sort().map())
|
|
@@ -595,6 +644,13 @@ export interface IRIfStatement {
|
|
|
595
644
|
condition: string;
|
|
596
645
|
/** Pre-transformed condition with destructured prop refs rewritten to _p.xxx. */
|
|
597
646
|
templateCondition?: string;
|
|
647
|
+
/**
|
|
648
|
+
* Structured parse of `condition` (`parseExpression(condition.trim())`),
|
|
649
|
+
* attached during IR construction so adapters lower the condition from the
|
|
650
|
+
* tree instead of re-parsing the string. Optional/best-effort — see
|
|
651
|
+
* `IRExpression.parsed`; consumers fall back to parsing `condition`.
|
|
652
|
+
*/
|
|
653
|
+
parsedCondition?: ParsedExpr;
|
|
598
654
|
/** The JSX return in the then branch */
|
|
599
655
|
consequent: IRNode;
|
|
600
656
|
/** The else branch: either another IRIfStatement (else if) or IRNode (final else) */
|
|
@@ -686,6 +742,14 @@ export interface ExpressionAttr {
|
|
|
686
742
|
/** `expr` with destructured prop refs rewritten to `_p.xxx`, for SSR
|
|
687
743
|
* template inlining. Absent when no rewrite was needed. */
|
|
688
744
|
templateExpr?: string;
|
|
745
|
+
/**
|
|
746
|
+
* Structured parse of `expr` (`parseExpression(expr.trim())`), attached
|
|
747
|
+
* during IR construction so adapters lower the attribute value from the tree
|
|
748
|
+
* instead of re-parsing the string (often several times per attribute).
|
|
749
|
+
* Optional/best-effort — see `IRExpression.parsed`; consumers fall back to
|
|
750
|
+
* parsing `expr`.
|
|
751
|
+
*/
|
|
752
|
+
parsed?: ParsedExpr;
|
|
689
753
|
/** Set when the producer peeled an `expr || undefined` boolean-presence
|
|
690
754
|
* pattern; adapters fold this back into `(expr) || undefined` at emit. */
|
|
691
755
|
presenceOrUndefined?: boolean;
|
|
@@ -716,6 +780,16 @@ export interface SpreadAttr {
|
|
|
716
780
|
kind: 'spread';
|
|
717
781
|
expr: string;
|
|
718
782
|
templateExpr?: string;
|
|
783
|
+
/**
|
|
784
|
+
* Structured parse of `expr` (`parseExpression(expr.trim())`), attached
|
|
785
|
+
* during IR construction so adapters lower the spread bag from the tree
|
|
786
|
+
* instead of re-parsing the string with `ts.createSourceFile`. Optional /
|
|
787
|
+
* best-effort — mirrors `ExpressionAttr.parsed`: it may be absent (a node the
|
|
788
|
+
* attach walk misses, or an empty `expr`), and parsing may yield
|
|
789
|
+
* `{ kind: 'unsupported' }`, which adapters treat as unlowerable and handle
|
|
790
|
+
* via their existing non-conditional spread paths (or BF101).
|
|
791
|
+
*/
|
|
792
|
+
parsed?: ParsedExpr;
|
|
719
793
|
/**
|
|
720
794
|
* Component-scoped, stable slot ID assigned at IR-build time for
|
|
721
795
|
* adapters that need to plumb the spread bag through a structured
|
|
@@ -837,6 +911,14 @@ export interface SignalInfo {
|
|
|
837
911
|
getter: string;
|
|
838
912
|
setter: string | null;
|
|
839
913
|
initialValue: string;
|
|
914
|
+
/**
|
|
915
|
+
* `initialValue` parsed into a structured tree (Roadmap A). Attached
|
|
916
|
+
* best-effort by the analyzer so adapters can lower a literal initial value
|
|
917
|
+
* (e.g. `useState(['a', 'b'])`) from structure instead of re-parsing the
|
|
918
|
+
* string with `ts.createSourceFile`. Absent when the shape isn't supported;
|
|
919
|
+
* consumers fall back to parsing `initialValue`.
|
|
920
|
+
*/
|
|
921
|
+
parsed?: ParsedExpr;
|
|
840
922
|
/** Initial value with TypeScript type annotations preserved, for .tsx output */
|
|
841
923
|
typedInitialValue?: string;
|
|
842
924
|
type: TypeInfo;
|
|
@@ -873,12 +955,78 @@ export interface SignalInfo {
|
|
|
873
955
|
isModule?: boolean;
|
|
874
956
|
/** When true, the declaration carries an `export` keyword. */
|
|
875
957
|
isExported?: boolean;
|
|
958
|
+
/**
|
|
959
|
+
* Request-scoped environment-signal key when this signal was produced by an
|
|
960
|
+
* env-signal factory (`createSearchParams()` → `'search'`), rather than by
|
|
961
|
+
* `createSignal`. Set structurally by the analyzer (#2057) — the getter is a
|
|
962
|
+
* normal reactive getter (so it lands in the fold purity oracle for free, no
|
|
963
|
+
* name allow-list), but its *value* is a request-scoped reader with methods
|
|
964
|
+
* (`.get(key)`), which adapters lower to their per-request reader object
|
|
965
|
+
* instead of a plain template field. This flag is how adapters recognise an
|
|
966
|
+
* env signal from structure instead of matching the import name.
|
|
967
|
+
*/
|
|
968
|
+
envReader?: string;
|
|
969
|
+
/**
|
|
970
|
+
* For an env signal (`envReader` set), the exact callee text of its factory
|
|
971
|
+
* call as written — `'createSearchParams'`, an alias (`'csp'` for
|
|
972
|
+
* `import { createSearchParams as csp }`), or a namespace access
|
|
973
|
+
* (`'bf.createSearchParams'`). Backends that re-emit the declaration (client
|
|
974
|
+
* JS, JSX/Hono SSR) emit `<envFactory>()` so the call resolves to the binding
|
|
975
|
+
* actually in scope, not a hardcoded canonical name (#2057).
|
|
976
|
+
*/
|
|
977
|
+
envFactory?: string;
|
|
876
978
|
}
|
|
877
979
|
export interface MemoInfo {
|
|
878
980
|
name: string;
|
|
879
981
|
computation: string;
|
|
880
982
|
/** Computation with TypeScript type annotations preserved, for .tsx output */
|
|
881
983
|
typedComputation?: string;
|
|
984
|
+
/**
|
|
985
|
+
* Structured parse of the memo's BODY as a single value expression, computed
|
|
986
|
+
* once at analysis time. Lets adapters pattern-match the memo's shape on the
|
|
987
|
+
* structured tree instead of re-parsing `computation` with their own AST walks
|
|
988
|
+
* / regexes.
|
|
989
|
+
*
|
|
990
|
+
* Set for an expression-bodied arrow (`() => <body>`) whose body
|
|
991
|
+
* `parseExpression` supports, AND — since #2040 — for a block-bodied memo
|
|
992
|
+
* (`() => { … }`) whose statements `foldBlockToExpr` can normalize to one
|
|
993
|
+
* expression (`let`-inline + value `if` / early `return` → ternary). A block
|
|
994
|
+
* the fold refuses (imperative residue) or a shape `parseExpression` can't
|
|
995
|
+
* represent leaves `parsed` undefined, so consumers must still fall back to
|
|
996
|
+
* `parsedBlock` / `computation` when it's missing. NOTE: a present `parsed`
|
|
997
|
+
* therefore no longer implies an expression-bodied arrow.
|
|
998
|
+
*/
|
|
999
|
+
parsed?: ParsedExpr;
|
|
1000
|
+
/**
|
|
1001
|
+
* Whether the memo's effective body is a template literal (`() => `…`` or a
|
|
1002
|
+
* block body whose first `return` is one), classified once at analysis time
|
|
1003
|
+
* from the real arrow AST. Lets the Go adapter pick the `string` field type
|
|
1004
|
+
* without re-parsing `computation` with `ts.createSourceFile`. A template
|
|
1005
|
+
* literal — including a no-substitution `` `plain` `` — folds to a plain
|
|
1006
|
+
* string `ParsedExpr` literal, losing the backtick distinction, so this is a
|
|
1007
|
+
* dedicated flag rather than a `parsed.kind` check.
|
|
1008
|
+
*/
|
|
1009
|
+
bodyIsTemplateLiteral?: boolean;
|
|
1010
|
+
/**
|
|
1011
|
+
* A block-bodied memo's statements, parsed best-effort (tolerant: a statement
|
|
1012
|
+
* the parser can't represent is omitted). Lets the Go adapter pattern-match
|
|
1013
|
+
* block-body memo shapes — e.g. the `const k = getter(); if (!k) return CONST`
|
|
1014
|
+
* guard — on the structured statements instead of re-parsing `computation`
|
|
1015
|
+
* with `ts.createSourceFile`. Absent for expression-bodied memos (those carry
|
|
1016
|
+
* `parsed` instead) and when the arrow has no block body.
|
|
1017
|
+
*/
|
|
1018
|
+
parsedBlock?: ParsedStatement[];
|
|
1019
|
+
/**
|
|
1020
|
+
* Whether {@link parsedBlock} represents EVERY statement of the block (true)
|
|
1021
|
+
* or the tolerant parser omitted at least one it couldn't represent (false).
|
|
1022
|
+
* A consumer that must reason about the whole block — e.g. one that bails on
|
|
1023
|
+
* any statement it doesn't recognise (the template-literal memo lowering) —
|
|
1024
|
+
* reads this and falls back when it's `false`, since omitted statements are
|
|
1025
|
+
* otherwise invisible. Consumers that scan for a recognised prefix and ignore
|
|
1026
|
+
* the rest (the guard-and-return-const lowering) can disregard it. Only set
|
|
1027
|
+
* when `parsedBlock` is.
|
|
1028
|
+
*/
|
|
1029
|
+
parsedBlockComplete?: boolean;
|
|
882
1030
|
type: TypeInfo;
|
|
883
1031
|
deps: string[];
|
|
884
1032
|
loc: SourceLocation;
|
|
@@ -1069,6 +1217,17 @@ export interface FunctionInfo {
|
|
|
1069
1217
|
export interface ConstantInfo {
|
|
1070
1218
|
name: string;
|
|
1071
1219
|
value?: string;
|
|
1220
|
+
/**
|
|
1221
|
+
* `value` parsed into a structured tree (Roadmap A). Attached best-effort by
|
|
1222
|
+
* the analyzer (parsed from the parenthesised value so a bare object literal
|
|
1223
|
+
* — which TS reads as a block at statement position — resolves to an
|
|
1224
|
+
* `object-literal` rather than failing). Lets adapters lower a constant value
|
|
1225
|
+
* (e.g. a module-scope record's `{ … }`) from structure instead of
|
|
1226
|
+
* re-parsing the string with `ts.createSourceFile`. Absent when the constant
|
|
1227
|
+
* has no `value` string (e.g. an inlined-JSX const) or when the analyzer
|
|
1228
|
+
* couldn't structure it (best-effort — consumers fall back to the string).
|
|
1229
|
+
*/
|
|
1230
|
+
parsed?: ParsedExpr;
|
|
1072
1231
|
/** Value with TypeScript type annotations preserved, for .tsx output */
|
|
1073
1232
|
typedValue?: string;
|
|
1074
1233
|
valueBranches?: string[];
|
|
@@ -1152,6 +1311,16 @@ export interface IRMetadata {
|
|
|
1152
1311
|
* resolves the compiled module rather than the source `.tsx`.
|
|
1153
1312
|
*/
|
|
1154
1313
|
clientSignalImportSources?: Set<string>;
|
|
1314
|
+
/**
|
|
1315
|
+
* Backend-neutral SSR seed plan: per-binding derived/opaque/env-reader
|
|
1316
|
+
* classification in declaration order, plus the base scope, computed by
|
|
1317
|
+
* `computeSsrSeedPlan` from this metadata. Attached by `buildMetadata` and
|
|
1318
|
+
* serialized into IR JSON like the rest of the metadata; template adapters
|
|
1319
|
+
* consume it instead of re-deriving scope/derivability themselves (their
|
|
1320
|
+
* target-syntax choices — lowering, self-shadow rules, constant-emit
|
|
1321
|
+
* guards — stay adapter-side).
|
|
1322
|
+
*/
|
|
1323
|
+
ssrSeedPlan?: SsrSeedPlan;
|
|
1155
1324
|
}
|
|
1156
1325
|
/**
|
|
1157
1326
|
* Where a reference appears in the emitted client JS. The emitter's rule
|