svelte-shaker 0.18.0 → 0.18.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/crawl-dRCzCERA.d.ts +43 -0
- package/dist/{dev-only.d.ts → dev-only-vMKGdavE.d.ts} +7 -4
- package/dist/index.d.ts +175 -23
- package/dist/index.js +1 -75
- package/dist/ir-BDouNmBm.d.ts +225 -0
- package/dist/mono-gDUeXt7W.d.ts +82 -0
- package/dist/parse-NbmYG3ZM.js +2 -0
- package/dist/scan-Ch9et3Ei.js +1 -0
- package/dist/scan.d.ts +89 -10
- package/dist/scan.js +1 -48
- package/dist/src-5VY_edrh.js +4 -0
- package/dist/vite.d.ts +184 -167
- package/dist/vite.js +4 -591
- package/package.json +7 -4
- package/dist/analyze.d.ts +0 -117
- package/dist/analyze.js +0 -496
- package/dist/call-site.d.ts +0 -76
- package/dist/call-site.js +0 -300
- package/dist/crawl.d.ts +0 -67
- package/dist/crawl.js +0 -305
- package/dist/css.d.ts +0 -45
- package/dist/css.js +0 -307
- package/dist/dead.d.ts +0 -71
- package/dist/dead.js +0 -280
- package/dist/dev-only.js +0 -43
- package/dist/drop-props.d.ts +0 -3
- package/dist/drop-props.js +0 -109
- package/dist/engine.d.ts +0 -81
- package/dist/engine.js +0 -121
- package/dist/escape-scan.d.ts +0 -66
- package/dist/escape-scan.js +0 -187
- package/dist/eval.d.ts +0 -63
- package/dist/eval.js +0 -288
- package/dist/exclude.d.ts +0 -25
- package/dist/exclude.js +0 -29
- package/dist/ir.d.ts +0 -148
- package/dist/ir.js +0 -31
- package/dist/model.d.ts +0 -144
- package/dist/model.js +0 -800
- package/dist/mono-rewrite.d.ts +0 -24
- package/dist/mono-rewrite.js +0 -116
- package/dist/mono.d.ts +0 -95
- package/dist/mono.js +0 -558
- package/dist/native-engine.d.ts +0 -59
- package/dist/native-engine.js +0 -140
- package/dist/parse.d.ts +0 -167
- package/dist/parse.js +0 -135
- package/dist/reverse.d.ts +0 -53
- package/dist/reverse.js +0 -139
- package/dist/revert-cascade.d.ts +0 -34
- package/dist/revert-cascade.js +0 -95
- package/dist/rsvelte-parse.d.ts +0 -25
- package/dist/rsvelte-parse.js +0 -78
- package/dist/substitute.d.ts +0 -57
- package/dist/substitute.js +0 -232
- package/dist/transform.d.ts +0 -57
- package/dist/transform.js +0 -492
- package/dist/unread.d.ts +0 -17
- package/dist/unread.js +0 -155
- package/dist/walk-dir.d.ts +0 -10
- package/dist/walk-dir.js +0 -39
- package/dist/whitespace.d.ts +0 -41
- package/dist/whitespace.js +0 -154
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { d as ParseCache, n as ComponentId, t as AnalyzeInput, u as Parse } from "./ir-BDouNmBm.js";
|
|
2
|
+
//#region src/crawl.d.ts
|
|
3
|
+
type Resolve = (source: string, importer: ComponentId) => Promise<ComponentId | null> | ComponentId | null;
|
|
4
|
+
type ReadFile = (id: ComponentId) => Promise<string> | string;
|
|
5
|
+
interface ImportInfo {
|
|
6
|
+
value: string;
|
|
7
|
+
local: string;
|
|
8
|
+
/** `default` for a default import, the exported name for a named import, or
|
|
9
|
+
* `*` for a namespace import. */
|
|
10
|
+
imported: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The three per-file facts the crawl needs to resolve edges — import specifiers,
|
|
14
|
+
* rendered `<Local>` tag names, and `<ns.X>` member tag names. `null` when the file
|
|
15
|
+
* has no instance script (nothing to attribute), matching the crawl's skip.
|
|
16
|
+
*/
|
|
17
|
+
interface CrawlFacts {
|
|
18
|
+
imports: ImportInfo[];
|
|
19
|
+
renderedTags: Set<string>;
|
|
20
|
+
memberTags: Set<string>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Source of {@link CrawlFacts} for one `(id, code)`. The default is a JS parse +
|
|
24
|
+
* extraction ({@link jsCrawlFacts}); the native engine passes a provider backed by
|
|
25
|
+
* `ShakeSession` facts, so the crawl resolves the SAME edges without the JS parse.
|
|
26
|
+
*/
|
|
27
|
+
type FactsProvider = (id: ComponentId, code: string) => CrawlFacts | null;
|
|
28
|
+
/**
|
|
29
|
+
* The Shell-side resolution + IO layer (docs/RUST-MIGRATION.md §2.1): BFS-crawl
|
|
30
|
+
* the component graph from `entries`, resolving every import edge and reading
|
|
31
|
+
* every reachable `.svelte` file up front, into a batched {@link AnalyzeInput}.
|
|
32
|
+
*
|
|
33
|
+
* This is the half that STAYS in JS — it owns `this.resolve` / file IO for Vite
|
|
34
|
+
* ecosystem compat (docs ARCHITECTURE §5/§9) — so the engine ({@link
|
|
35
|
+
* analyzeInput}) consumes its output with no callback across the boundary. The
|
|
36
|
+
* traversal mirrors the old crawl exactly: direct default-`.svelte` children and
|
|
37
|
+
* the barrel children a file actually RENDERS are followed (an unrendered barrel
|
|
38
|
+
* import is never crawled — its `<Comp/>` site cannot exist, so it cannot taint a
|
|
39
|
+
* value set), keeping the produced model set — and thus the output — identical.
|
|
40
|
+
*/
|
|
41
|
+
declare function buildAnalyzeInput(entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile, parseCache?: ParseCache, parse?: Parse, escaped?: ComponentId[], factsProvider?: FactsProvider): Promise<AnalyzeInput>;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { Resolve as n, buildAnalyzeInput as r, ReadFile as t };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { n as ComponentId } from "./ir-BDouNmBm.js";
|
|
2
|
+
//#region src/dev-only.d.ts
|
|
2
3
|
/**
|
|
3
4
|
* Files treated as DEV-ONLY by default: colocated tests, mocks, and Storybook
|
|
4
5
|
* stories — files that never ship, so their call sites must not count toward the
|
|
@@ -7,9 +8,9 @@ import type { ComponentId } from './ir.js';
|
|
|
7
8
|
* not for narrowing app coverage, and the shipped-file-matching failure mode).
|
|
8
9
|
* Passing `devOnly` REPLACES this list; `devOnly: []` counts every file.
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
declare const DEFAULT_DEV_ONLY: readonly string[];
|
|
11
12
|
/** Predicate over an ABSOLUTE path: `true` when the file is dev-only (discounted by a scan). */
|
|
12
|
-
|
|
13
|
+
type DevOnlyFilter = (file: ComponentId) => boolean;
|
|
13
14
|
/**
|
|
14
15
|
* Compile dev-only `patterns` into a {@link DevOnlyFilter}. Each candidate path is
|
|
15
16
|
* matched (with `picomatch`) as its `base`-relative, posix-normalized form, so the
|
|
@@ -20,4 +21,6 @@ export type DevOnlyFilter = (file: ComponentId) => boolean;
|
|
|
20
21
|
* Defaults to {@link DEFAULT_DEV_ONLY}; an empty `patterns` array matches nothing, so
|
|
21
22
|
* `devOnly: []` counts every file (the pre-`devOnly` behavior).
|
|
22
23
|
*/
|
|
23
|
-
|
|
24
|
+
declare function compileDevOnly(base: string, patterns?: readonly string[]): DevOnlyFilter;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { DevOnlyFilter as n, compileDevOnly as r, DEFAULT_DEV_ONLY as t };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,163 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { a as EditResult, c as ResolvedEdge, f as Root, i as EdgeKind, l as AnyNode, n as ComponentId, o as InputFile, r as ComponentPlan, s as Literal, t as AnalyzeInput, u as Parse } from "./ir-BDouNmBm.js";
|
|
2
|
+
import { n as Resolve, r as buildAnalyzeInput, t as ReadFile } from "./crawl-dRCzCERA.js";
|
|
3
|
+
import { i as OwnSize, n as MonomorphizeOptions, r as MonomorphizeResult, t as DEFAULT_MONO_OPTIONS } from "./mono-gDUeXt7W.js";
|
|
4
|
+
//#region src/model.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The set of input names a child component can ever OBSERVE at runtime (reverse
|
|
7
|
+
* analysis). In runes there is no `$$props`/`$$restProps`, so a
|
|
8
|
+
* component reads an input only through its `$props()` destructure:
|
|
9
|
+
* - `{ kind: 'names' }` — a clean, rest-free ObjectPattern `$props()` (or no
|
|
10
|
+
* `$props()` at all, giving the empty set): the child can observe EXACTLY
|
|
11
|
+
* these declared external names, so a call-site input NOT in the set can
|
|
12
|
+
* never be seen and is safe to drop;
|
|
13
|
+
* - `{ kind: 'all' }` — anything we cannot pin down: a `...rest` (captures
|
|
14
|
+
* undeclared inputs), an Identifier/Array binding (`let p = $props()`),
|
|
15
|
+
* more than one `$props()` call, `$props()` outside a `let <pat> = …`
|
|
16
|
+
* declarator, or a component that observes slotted content outside `$props()`
|
|
17
|
+
* — a legacy `<slot>` element or a `$$slots` read (both legal in runes mode).
|
|
18
|
+
* Then any input might be observed, so nothing is dropped.
|
|
19
|
+
*/
|
|
20
|
+
type ReachableInputs = {
|
|
21
|
+
kind: "all";
|
|
22
|
+
} | {
|
|
23
|
+
kind: "names";
|
|
24
|
+
names: Set<string>;
|
|
25
|
+
};
|
|
26
|
+
/** One declared prop in a `$props()` destructuring. */
|
|
27
|
+
interface PropDecl {
|
|
28
|
+
/** The EXTERNAL prop name — the destructure KEY (`prop` in `prop: alias`).
|
|
29
|
+
* Call sites pass this name, so value sets / dropping key off it. */
|
|
30
|
+
name: string;
|
|
31
|
+
/**
|
|
32
|
+
* The LOCAL binding name the entry introduces in the body — the destructure
|
|
33
|
+
* VALUE (`alias` in `prop: alias`, or the bare name for a shorthand `prop`),
|
|
34
|
+
* or `null` when the entry binds a NESTED pattern (`prop: { x }`) rather than a
|
|
35
|
+
* single identifier. Body and template references use THIS name, not {@link
|
|
36
|
+
* name}, so folding/substitution must look props up by it (`prop` and its alias
|
|
37
|
+
* `alias` can even be different entities — e.g. a same-named import). A `null`
|
|
38
|
+
* local is never foldable: there is no single identifier to substitute or drop.
|
|
39
|
+
*/
|
|
40
|
+
local: string | null;
|
|
41
|
+
/** The `Property` node inside the `ObjectPattern` (for surgical removal). */
|
|
42
|
+
property: AnyNode;
|
|
43
|
+
/** Default value expression, if `name = <default>`. */
|
|
44
|
+
defaultExpr?: AnyNode | undefined;
|
|
45
|
+
}
|
|
46
|
+
/** Everything we learn from parsing one component, reused by the transform. */
|
|
47
|
+
interface FileModel {
|
|
48
|
+
id: ComponentId;
|
|
49
|
+
code: string;
|
|
50
|
+
ast: Root;
|
|
51
|
+
/**
|
|
52
|
+
* Tag name a call site renders -> resolved child component id. Holds every
|
|
53
|
+
* attributable edge into this file: a bare local for a direct `.svelte`
|
|
54
|
+
* default or a simple barrel/named import (`Sub`), and a dotted member for a
|
|
55
|
+
* namespace render (`ns.Sub`). {@link collectChildCalls} keys `<Tag .../>`
|
|
56
|
+
* sites off this map, so every kind feeds the child's value set.
|
|
57
|
+
*/
|
|
58
|
+
imports: Map<string, ComponentId>;
|
|
59
|
+
/** Declared props, or `null` if the component has no `$props()` pattern. */
|
|
60
|
+
props: PropDecl[] | null;
|
|
61
|
+
/** The `let { ... } = $props()` declaration + its pattern, for editing. */
|
|
62
|
+
propsDeclaration?: AnyNode | undefined;
|
|
63
|
+
propsPattern?: AnyNode | undefined;
|
|
64
|
+
hasRestProp: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* The inputs this component can observe. Drives the reverse pass:
|
|
67
|
+
* a call site of THIS component may drop an input outside {@link
|
|
68
|
+
* ReachableInputs}. Computed syntactically from the `$props()` shape.
|
|
69
|
+
*/
|
|
70
|
+
reachableInputs: ReachableInputs;
|
|
71
|
+
/**
|
|
72
|
+
* EXTERNAL names of props this component DECLARES but never READS (unread
|
|
73
|
+
* declared props): destructured out of a clean `$props()` yet with zero
|
|
74
|
+
* value-position reference to their local binding anywhere in the instance
|
|
75
|
+
* script or template. Such a
|
|
76
|
+
* prop is invisible to the child, so its call-site attribute is dead and — when
|
|
77
|
+
* safe — the declaration can be dropped. Source-only (independent of the call
|
|
78
|
+
* sites), so it is computed ONCE here, never inside the fixpoint; the transform
|
|
79
|
+
* gates its use on the component's plan not being bailed.
|
|
80
|
+
*/
|
|
81
|
+
unreadDeclaredProps: Set<string>;
|
|
82
|
+
/**
|
|
83
|
+
* Every `<Child .../>` instance THIS component renders, with the child it
|
|
84
|
+
* resolves to and the AST node (so the fixpoint can test whether the site
|
|
85
|
+
* falls inside a dead `{#if}` span of this component — docs §2.1).
|
|
86
|
+
*/
|
|
87
|
+
childCalls: ChildCall[];
|
|
88
|
+
/**
|
|
89
|
+
* Names this component binds OUTSIDE the `$props()` pattern — local `let` /
|
|
90
|
+
* `function` declarations in the instance script, and every template-scope
|
|
91
|
+
* binder (`{#each … as ctx, i}`, destructure patterns, `{#snippet name(p)}`,
|
|
92
|
+
* `{#await … then v}` / `{:catch e}`, `let:` directives). A declared prop
|
|
93
|
+
* whose name collides with any of these is a DIFFERENT entity inside that
|
|
94
|
+
* scope, so folding/substituting/dropping it would corrupt the binding (and
|
|
95
|
+
* often produce invalid Svelte). We therefore never fold such a prop.
|
|
96
|
+
*/
|
|
97
|
+
shadowedNames: Set<string>;
|
|
98
|
+
/**
|
|
99
|
+
* Names that appear as a `{@debug …}` argument. Svelte requires those to be
|
|
100
|
+
* bare identifiers, so substituting a folded literal there is invalid and
|
|
101
|
+
* dropping the prop dangles the reference — we never fold a prop named here.
|
|
102
|
+
*/
|
|
103
|
+
debugNames: Set<string>;
|
|
104
|
+
/**
|
|
105
|
+
* Names the component WRITES TO — reassigns (`p = …`, `p += …`), mutates with
|
|
106
|
+
* `++`/`--`, destructure-assigns (`({ p } = obj)`), or two-way `bind:`s. A
|
|
107
|
+
* written prop is not a constant even when every call site passes the same
|
|
108
|
+
* literal: the write changes it at runtime, so folding it would substitute the
|
|
109
|
+
* literal into the write's target (`"a" = …`, `0++`, `bind:value={"a"}`) —
|
|
110
|
+
* invalid Svelte — and, more importantly, silently change what renders after
|
|
111
|
+
* the write. We never fold such a prop, exactly like a shadowed one.
|
|
112
|
+
*/
|
|
113
|
+
writtenNames: Set<string>;
|
|
114
|
+
/**
|
|
115
|
+
* Owner-local bindings that are provably a single primitive CONSTANT, keyed by
|
|
116
|
+
* the LOCAL name a forwarded call-site expression references (docs §13.1
|
|
117
|
+
* interprocedural pass-through). Merged into the owner's fold env so that
|
|
118
|
+
* `<Child {count}/>` — where `count` is an unmutated `let count = $state(0)` or
|
|
119
|
+
* a `const count = 0` — folds in the child exactly as a call-site literal would,
|
|
120
|
+
* feeding BOTH constant fold and value-set narrowing. A static property of the
|
|
121
|
+
* source (independent of the fixpoint's plans), so it is computed ONCE here.
|
|
122
|
+
* See {@link computeScriptConstEnv} for the (conservative) admission rules.
|
|
123
|
+
*/
|
|
124
|
+
scriptConstEnv: ReadonlyMap<string, Literal>;
|
|
125
|
+
/**
|
|
126
|
+
* Resolved ids of CHILD components this file leaks as a value (escape, docs
|
|
127
|
+
* §4.1) — e.g. `<svelte:component this={Child}>`. `analyze` unions these
|
|
128
|
+
* across the program and bails every escaped component completely, since its
|
|
129
|
+
* prop profile can no longer be observed from `<Child .../>` sites alone.
|
|
130
|
+
*/
|
|
131
|
+
escapedComponents: Set<ComponentId>;
|
|
132
|
+
/** Reasons this whole component must be left untouched. */
|
|
133
|
+
bailReasons: string[];
|
|
134
|
+
}
|
|
135
|
+
/** One `<Child .../>` instance rendered by a component. */
|
|
136
|
+
interface ChildCall {
|
|
137
|
+
childId: ComponentId;
|
|
138
|
+
node: AnyNode;
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/analyze.d.ts
|
|
142
|
+
interface AnalyzeResult {
|
|
143
|
+
models: Map<ComponentId, FileModel>;
|
|
144
|
+
plans: Map<ComponentId, ComponentPlan>;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Crawl the component graph from `entries` and compute a plan per component,
|
|
148
|
+
* iterating to a whole-program fixpoint (docs §2.1).
|
|
149
|
+
*
|
|
150
|
+
* The crucial cascade: a `<Child/>` that lives inside a branch we fold away must
|
|
151
|
+
* NOT count toward the child's prop profile. Excluding it can shrink the
|
|
152
|
+
* child's value sets and enable more folding, which can fold away yet more
|
|
153
|
+
* branches. So we parse every component once, then alternate between
|
|
154
|
+
* (a) collecting call sites that are NOT inside a current dead span, and
|
|
155
|
+
* (b) recomputing plans (and hence dead spans) from that usage,
|
|
156
|
+
* until the plans stop changing.
|
|
157
|
+
*/
|
|
158
|
+
declare function analyze(entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile, escaped?: ComponentId[]): Promise<AnalyzeResult>;
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/index.d.ts
|
|
11
161
|
/**
|
|
12
162
|
* Whole-program shake: crawl the component graph from `entry`, decide what to
|
|
13
163
|
* fold, and return the shaken source for every reachable `.svelte` file.
|
|
@@ -18,21 +168,21 @@ export { DEFAULT_MONO_OPTIONS, type MonomorphizeOptions, type OwnSize } from './
|
|
|
18
168
|
* Node callers use `fsResolve` / `fs.readFileSync` from `svelte-shaker/node`.
|
|
19
169
|
* See docs/ARCHITECTURE.md §5 — this is the Engine; the Shell owns resolution.
|
|
20
170
|
*/
|
|
21
|
-
|
|
171
|
+
declare function svelteShaker(entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile, parse?: Parse, escaped?: ComponentId[]): Promise<Record<ComponentId, string>>;
|
|
22
172
|
/** The full output of a shake including monomorphization specialization. */
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
173
|
+
interface ShakeResult {
|
|
174
|
+
/**
|
|
175
|
+
* Whole-program output: shaken source per `.svelte` file. With monomorphization OFF this is
|
|
176
|
+
* byte-for-byte identical to {@link svelteShaker}; with monomorphization ON, owner files
|
|
177
|
+
* whose call sites were specialized have those sites rewritten to import a
|
|
178
|
+
* variant via `variantSpecifier(variantId)`.
|
|
179
|
+
*/
|
|
180
|
+
files: Record<ComponentId, string>;
|
|
181
|
+
/** monomorphization specialized variants + call-site bindings (empty when monomorphization is off). */
|
|
182
|
+
mono: MonomorphizeResult;
|
|
33
183
|
}
|
|
34
184
|
/** Build the module specifier a rewritten call site imports a variant from. */
|
|
35
|
-
|
|
185
|
+
type VariantSpecifier = (variantId: string) => string;
|
|
36
186
|
/**
|
|
37
187
|
* Whole-program shake WITH optional monomorphization (docs §3 "monomorphization").
|
|
38
188
|
*
|
|
@@ -43,4 +193,6 @@ export type VariantSpecifier = (variantId: string) => string;
|
|
|
43
193
|
* specialized and `files` equals the unused-prop fold / constant fold / value-set narrowing output exactly — a strict
|
|
44
194
|
* superset of the default behavior, so existing consumers are unaffected.
|
|
45
195
|
*/
|
|
46
|
-
|
|
196
|
+
declare function svelteShakerWithMono(entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile, mono?: MonomorphizeOptions, variantSpecifier?: VariantSpecifier, parse?: Parse, escaped?: ComponentId[], ownSize?: OwnSize): Promise<ShakeResult>;
|
|
197
|
+
//#endregion
|
|
198
|
+
export { type AnalyzeInput, type ComponentId, DEFAULT_MONO_OPTIONS, type EdgeKind, type EditResult, type InputFile, type MonomorphizeOptions, type OwnSize, type Parse, type ReadFile, type Resolve, type ResolvedEdge, type Root, ShakeResult, VariantSpecifier, analyze, buildAnalyzeInput, svelteShaker, svelteShakerWithMono };
|
package/dist/index.js
CHANGED
|
@@ -1,75 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { buildAnalyzeInput } from './crawl.js';
|
|
3
|
-
import {} from './parse.js';
|
|
4
|
-
import { transformAll } from './transform.js';
|
|
5
|
-
import { transformAllWithMono } from './mono-rewrite.js';
|
|
6
|
-
import { monomorphize, DEFAULT_MONO_OPTIONS, } from './mono.js';
|
|
7
|
-
import { shakeWithRevertCascade } from './revert-cascade.js';
|
|
8
|
-
export { analyze } from './analyze.js';
|
|
9
|
-
export { buildAnalyzeInput } from './crawl.js';
|
|
10
|
-
export { DEFAULT_MONO_OPTIONS } from './mono.js';
|
|
11
|
-
/**
|
|
12
|
-
* Whole-program shake: crawl the component graph from `entry`, decide what to
|
|
13
|
-
* fold, and return the shaken source for every reachable `.svelte` file.
|
|
14
|
-
*
|
|
15
|
-
* `resolve` / `readFile` are injected so the engine stays environment-free —
|
|
16
|
-
* it has NO `node:*` imports, so it runs unchanged in the browser (the
|
|
17
|
-
* playground passes an in-memory file map). A Vite plugin passes `this.resolve`;
|
|
18
|
-
* Node callers use `fsResolve` / `fs.readFileSync` from `svelte-shaker/node`.
|
|
19
|
-
* See docs/ARCHITECTURE.md §5 — this is the Engine; the Shell owns resolution.
|
|
20
|
-
*/
|
|
21
|
-
export async function svelteShaker(entries, resolve, readFile, parse, escaped = []) {
|
|
22
|
-
const { models, plans } = await analyzeWith(entries, resolve, readFile, parse, escaped);
|
|
23
|
-
return shakeWithRevertCascade(models, plans, (p) => transformAll(models, p));
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Crawl + analyze with an optional non-default parser ({@link Parse}). When
|
|
27
|
-
* `parse` is given (the Vite plugin's `parser: 'rsvelte'` path), each file is
|
|
28
|
-
* parsed ONCE into a shared cache during the crawl and that cache is reused by the
|
|
29
|
-
* analysis — so the alternate parser drives the whole engine with no second parse.
|
|
30
|
-
* When omitted, this is exactly `analyze(entries, resolve, readFile)` (the default
|
|
31
|
-
* svelte/compiler path, byte-for-byte unchanged).
|
|
32
|
-
*/
|
|
33
|
-
async function analyzeWith(entries, resolve, readFile, parse, escaped = []) {
|
|
34
|
-
if (!parse)
|
|
35
|
-
return analyze(entries, resolve, readFile, escaped);
|
|
36
|
-
const cache = new Map();
|
|
37
|
-
const input = await buildAnalyzeInput(entries, resolve, readFile, cache, parse, escaped);
|
|
38
|
-
return analyzeInput(input, cache);
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Whole-program shake WITH optional monomorphization (docs §3 "monomorphization").
|
|
42
|
-
*
|
|
43
|
-
* `mono` carries the specialized variants (id -> residual source) and the call
|
|
44
|
-
* sites bound to them; `files` is the wired owner source. The Shell resolves
|
|
45
|
-
* `variantSpecifier(id)` to a virtual module whose source is
|
|
46
|
-
* `mono.variants.get(id)!.code`. With `mono.enabled` false (default) nothing is
|
|
47
|
-
* specialized and `files` equals the unused-prop fold / constant fold / value-set narrowing output exactly — a strict
|
|
48
|
-
* superset of the default behavior, so existing consumers are unaffected.
|
|
49
|
-
*/
|
|
50
|
-
export async function svelteShakerWithMono(entries, resolve, readFile, mono = DEFAULT_MONO_OPTIONS, variantSpecifier = (id) => id, parse, escaped = [], ownSize) {
|
|
51
|
-
const { models, plans } = await analyzeWith(entries, resolve, readFile, parse, escaped);
|
|
52
|
-
// The cascade may re-run the transform with force-bailed plans, so recompute
|
|
53
|
-
// monomorphization inside it: a bailed component must not be specialized either.
|
|
54
|
-
// `lastResult` captures the mono result of the final (converged or no-op) pass.
|
|
55
|
-
// On the no-op fallback the emitted owner files are the untouched originals —
|
|
56
|
-
// they import no variant — so any variants `lastResult` still lists are simply
|
|
57
|
-
// never requested.
|
|
58
|
-
let lastResult;
|
|
59
|
-
const files = shakeWithRevertCascade(models, plans, (p) => {
|
|
60
|
-
// Thread the shake entries through so the net-win gate can compute module
|
|
61
|
-
// reachability from them (docs §3 monomorphization, §13.2).
|
|
62
|
-
lastResult = monomorphize(models, p, mono, entries, ownSize);
|
|
63
|
-
// With no bindings the wired pass and the base pass are identical, so reuse
|
|
64
|
-
// the plain transform to keep the default path byte-for-byte unchanged.
|
|
65
|
-
return lastResult.bindings.length === 0
|
|
66
|
-
? transformAll(models, p)
|
|
67
|
-
: transformAllWithMono(models, p, lastResult.bindings.map((b) => ({
|
|
68
|
-
owner: b.owner,
|
|
69
|
-
node: b.node,
|
|
70
|
-
variantId: b.variantId,
|
|
71
|
-
foldedProps: b.foldedProps,
|
|
72
|
-
})), variantSpecifier);
|
|
73
|
-
});
|
|
74
|
-
return { files, mono: lastResult };
|
|
75
|
-
}
|
|
1
|
+
import{c as e,i as t,n,o as r,t as i}from"./src-5VY_edrh.js";export{t as DEFAULT_MONO_OPTIONS,r as analyze,e as buildAnalyzeInput,i as svelteShaker,n as svelteShakerWithMono};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
//#region src/parse.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A deliberately loose view of the Svelte + ESTree AST: only the fields this
|
|
4
|
+
* engine reads, each optional. Avoiding an index signature keeps named access
|
|
5
|
+
* compatible with `noPropertyAccessFromIndexSignature` while still letting us
|
|
6
|
+
* walk an untyped tree. `value` is `unknown` because it means different things
|
|
7
|
+
* on `Literal` (a literal) vs `Attribute` (true | node | node[]).
|
|
8
|
+
*/
|
|
9
|
+
interface AnyNode {
|
|
10
|
+
type: string;
|
|
11
|
+
start: number;
|
|
12
|
+
end: number;
|
|
13
|
+
test?: AnyNode | undefined;
|
|
14
|
+
consequent?: AnyNode | undefined;
|
|
15
|
+
alternate?: AnyNode | null | undefined;
|
|
16
|
+
expression?: AnyNode | undefined;
|
|
17
|
+
argument?: AnyNode | undefined;
|
|
18
|
+
left?: AnyNode | undefined;
|
|
19
|
+
right?: AnyNode | undefined;
|
|
20
|
+
callee?: AnyNode | undefined;
|
|
21
|
+
id?: AnyNode | undefined;
|
|
22
|
+
init?: AnyNode | undefined;
|
|
23
|
+
key?: AnyNode | undefined;
|
|
24
|
+
property?: AnyNode | undefined;
|
|
25
|
+
/** MemberExpression object (`$state` in `$state.raw`). */
|
|
26
|
+
object?: AnyNode | undefined;
|
|
27
|
+
source?: AnyNode | undefined;
|
|
28
|
+
local?: AnyNode | undefined;
|
|
29
|
+
/** ImportSpecifier / ExportSpecifier exported-name slot. */
|
|
30
|
+
imported?: AnyNode | undefined;
|
|
31
|
+
exported?: AnyNode | undefined;
|
|
32
|
+
typeAnnotation?: AnyNode | undefined;
|
|
33
|
+
content?: AnyNode | undefined;
|
|
34
|
+
fragment?: AnyNode | null | undefined;
|
|
35
|
+
instance?: AnyNode | null | undefined;
|
|
36
|
+
module?: AnyNode | null | undefined;
|
|
37
|
+
css?: AnyNode | null | undefined;
|
|
38
|
+
context?: AnyNode | undefined;
|
|
39
|
+
error?: AnyNode | null | undefined;
|
|
40
|
+
then?: AnyNode | null | undefined;
|
|
41
|
+
catch?: AnyNode | null | undefined;
|
|
42
|
+
declaration?: AnyNode | undefined;
|
|
43
|
+
prelude?: AnyNode | undefined;
|
|
44
|
+
block?: AnyNode | null | undefined;
|
|
45
|
+
combinator?: AnyNode | null | undefined;
|
|
46
|
+
args?: AnyNode | null | undefined;
|
|
47
|
+
attributes?: AnyNode[] | undefined;
|
|
48
|
+
properties?: AnyNode[] | undefined;
|
|
49
|
+
members?: AnyNode[] | undefined;
|
|
50
|
+
body?: AnyNode[] | undefined;
|
|
51
|
+
declarations?: AnyNode[] | undefined;
|
|
52
|
+
/** CallExpression / NewExpression arguments (`0` in `$state(0)`). */
|
|
53
|
+
arguments?: (AnyNode | null)[] | undefined;
|
|
54
|
+
specifiers?: AnyNode[] | undefined;
|
|
55
|
+
nodes?: AnyNode[] | undefined;
|
|
56
|
+
/** SnippetBlock parameters; ArrayPattern elements; DebugTag identifiers. */
|
|
57
|
+
parameters?: AnyNode[] | undefined;
|
|
58
|
+
/** Function / arrow ESTree parameters (`function f(a, b)`, `(a) => …`). */
|
|
59
|
+
params?: AnyNode[] | undefined;
|
|
60
|
+
elements?: (AnyNode | null)[] | undefined;
|
|
61
|
+
identifiers?: AnyNode[] | undefined;
|
|
62
|
+
/** CSS StyleSheet/SelectorList/ComplexSelector children, Block children. */
|
|
63
|
+
children?: AnyNode[] | undefined;
|
|
64
|
+
/** RelativeSelector simple selectors (ClassSelector, TypeSelector, …). */
|
|
65
|
+
selectors?: AnyNode[] | undefined;
|
|
66
|
+
name?: string | undefined;
|
|
67
|
+
/** EachBlock loop index variable name (a bare string, e.g. `i`). */
|
|
68
|
+
index?: string | undefined;
|
|
69
|
+
operator?: string | undefined;
|
|
70
|
+
raw?: string | undefined;
|
|
71
|
+
data?: string | undefined;
|
|
72
|
+
computed?: boolean | undefined;
|
|
73
|
+
shorthand?: boolean | undefined;
|
|
74
|
+
elseif?: boolean | undefined;
|
|
75
|
+
/** ObjectExpression `Property` accessor kind (`init` / `get` / `set`). */
|
|
76
|
+
kind?: string | undefined;
|
|
77
|
+
/** ObjectExpression `Property` shorthand-method flag (`{ m() {} }`). */
|
|
78
|
+
method?: boolean | undefined;
|
|
79
|
+
/** ESTree `Literal` discriminators, present only on a RegExp (`/x/g`) or
|
|
80
|
+
* BigInt (`1n`) literal — neither of which carries a foldable `value`. */
|
|
81
|
+
regex?: {
|
|
82
|
+
pattern: string;
|
|
83
|
+
flags: string;
|
|
84
|
+
} | undefined;
|
|
85
|
+
bigint?: string | undefined;
|
|
86
|
+
value?: unknown;
|
|
87
|
+
}
|
|
88
|
+
interface Root extends AnyNode {
|
|
89
|
+
fragment: AnyNode;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Content-keyed parse cache: a hit returns the IDENTICAL AST for unchanged
|
|
93
|
+
* source, so the dev engine re-parses only the files that actually changed
|
|
94
|
+
* (docs/RUST-MIGRATION.md §2.2 — `parse(id)` is the cached input query, the
|
|
95
|
+
* dominant cost an edit avoids). Keyed by content, so a stale entry can never
|
|
96
|
+
* return an AST whose byte offsets disagree with the source: a code mismatch
|
|
97
|
+
* forces a re-parse.
|
|
98
|
+
*/
|
|
99
|
+
type ParseCache = Map<string, {
|
|
100
|
+
code: string;
|
|
101
|
+
ast: Root;
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* A `.svelte` -> modern-AST parser, swappable for the default {@link parseSvelte}
|
|
105
|
+
* (svelte/compiler). The engine reads only the AST it returns, so any parser that
|
|
106
|
+
* emits svelte/compiler's modern shape (UTF-16 `start`/`end`) can drive it — the
|
|
107
|
+
* Vite plugin's `parser: 'rsvelte'` option supplies rsvelte's parser (the
|
|
108
|
+
* `@rsvelte/compiler` WASM build) here (docs/RUST-MIGRATION.md §6).
|
|
109
|
+
*/
|
|
110
|
+
type Parse = (code: string, filename: string) => Root;
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/ir.d.ts
|
|
113
|
+
/** Resolved absolute path of a `.svelte` file. */
|
|
114
|
+
type ComponentId = string;
|
|
115
|
+
/** A statically-known literal value a prop can take. */
|
|
116
|
+
type Literal = string | number | boolean | null | undefined;
|
|
117
|
+
/**
|
|
118
|
+
* How an imported local name binds to a child `.svelte` component. All three
|
|
119
|
+
* kinds are attributable — the `local` they carry is the exact tag name a call
|
|
120
|
+
* site renders (`Child` for the first two, the dotted `ns.Child` for the third),
|
|
121
|
+
* so {@link AnalyzeInput} drives the child's value set off every one of them.
|
|
122
|
+
*/
|
|
123
|
+
type EdgeKind = "default-svelte" | "barrel" | "namespace";
|
|
124
|
+
/** One reachable `.svelte` source the engine will model. */
|
|
125
|
+
interface InputFile {
|
|
126
|
+
id: ComponentId;
|
|
127
|
+
code: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* One resolved import edge: in `from`, the tag name `local` renders the child
|
|
131
|
+
* `.svelte` `to`. `local` is the literal tag a call site uses — a bare name for
|
|
132
|
+
* `default-svelte`/`barrel` (`<Child/>`) or a dotted member for `namespace`
|
|
133
|
+
* (`<ns.Child/>`) — so the engine attributes `<local .../>` sites by name lookup.
|
|
134
|
+
*/
|
|
135
|
+
interface ResolvedEdge {
|
|
136
|
+
from: ComponentId;
|
|
137
|
+
local: string;
|
|
138
|
+
to: ComponentId;
|
|
139
|
+
kind: EdgeKind;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The fully-resolved, batched input to the engine (docs §2.1). `files` is every
|
|
143
|
+
* reachable `.svelte` (barrel `.js`/`.ts` are consumed during resolution and do
|
|
144
|
+
* not appear here); `edges` are already resolved to absolute ids; `entries` is
|
|
145
|
+
* the call-site-completeness set (the Shell's FS scan) and the monomorphization net-win roots.
|
|
146
|
+
*/
|
|
147
|
+
interface AnalyzeInput {
|
|
148
|
+
files: InputFile[];
|
|
149
|
+
edges: ResolvedEdge[];
|
|
150
|
+
entries: ComponentId[];
|
|
151
|
+
/**
|
|
152
|
+
* Components with at least one consumer OUTSIDE the analyzed `.svelte` graph —
|
|
153
|
+
* a call site in a `.ts`/`.js` module (`mount(Comp, …)`, a lazy `import()`), or
|
|
154
|
+
* a user-declared `preserve` (docs/ARCHITECTURE.md §4.2). The Shell computes
|
|
155
|
+
* this set (its FS scan cannot parse `.ts` call sites); the engine unions it
|
|
156
|
+
* into the same whole-component escape bail auto-detected escapes use, so these
|
|
157
|
+
* components are never folded and never reported as never-passed — while their
|
|
158
|
+
* OWN call sites still count toward their children. Omitted/`[]` means every
|
|
159
|
+
* consumer is inside the crawled `.svelte` graph, keeping the output
|
|
160
|
+
* byte-for-byte unchanged.
|
|
161
|
+
*/
|
|
162
|
+
escaped?: ComponentId[];
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The delta the dev engine returns after applying file changes (docs §2.1, the
|
|
166
|
+
* `vite dev` incremental path). `changed` maps each component whose SLIMMED
|
|
167
|
+
* OUTPUT changed to its new source — a SUPERSET of the edited files, because a
|
|
168
|
+
* call-site edit can change a child's residual without the child being touched
|
|
169
|
+
* (the HMR module-graph divergence the Shell must widen for). `removed` lists
|
|
170
|
+
* components no longer in the program (deleted or now unreachable).
|
|
171
|
+
*/
|
|
172
|
+
interface EditResult {
|
|
173
|
+
changed: Record<ComponentId, string>;
|
|
174
|
+
removed: ComponentId[];
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The set of literal values one declared prop is seen to take across the whole
|
|
178
|
+
* program (default included for sites that omit it), plus the two ways it can
|
|
179
|
+
* escape the lattice. This is the value-set foundation later levels narrow on
|
|
180
|
+
* (docs §2.2 `multi`, §3 value-set narrowing): `constFold` is just the `size === 1 && !dynamic
|
|
181
|
+
* && !top` projection of it. Kept on the plan as groundwork — no level yet
|
|
182
|
+
* consumes the multi-element / `dynamic` cases.
|
|
183
|
+
*/
|
|
184
|
+
interface PropValueSet {
|
|
185
|
+
/** Distinct literals observed (dedup'd; `undefined`/`null` are distinct). */
|
|
186
|
+
values: Literal[];
|
|
187
|
+
/** A non-literal value was passed somewhere (used, value not statically known). */
|
|
188
|
+
dynamic: boolean;
|
|
189
|
+
/**
|
|
190
|
+
* ⊤: a call-site spread may set this prop (docs §4.1 partial bail), so the
|
|
191
|
+
* value set is really "all values" and the prop must not be folded.
|
|
192
|
+
*/
|
|
193
|
+
top: boolean;
|
|
194
|
+
}
|
|
195
|
+
/** What the analysis decides to do to one component. */
|
|
196
|
+
interface ComponentPlan {
|
|
197
|
+
id: ComponentId;
|
|
198
|
+
/** Whole-component bail (accessors / customElement / escape). */
|
|
199
|
+
bail: boolean;
|
|
200
|
+
reasons: string[];
|
|
201
|
+
/**
|
|
202
|
+
* unused-prop fold / constant fold: props that collapse to a single constant. Under the "攻め"
|
|
203
|
+
* default (docs §12-2) these are folded in the body, dropped from the
|
|
204
|
+
* `$props()` signature, and their attributes are removed at every call site.
|
|
205
|
+
*/
|
|
206
|
+
constFold: Map<string, Literal>;
|
|
207
|
+
/**
|
|
208
|
+
* value-set narrowing (docs §3): props whose reachable value set is a
|
|
209
|
+
* known set of >= 2 distinct literals (no `dynamic`/`top` contribution). We
|
|
210
|
+
* delete branches the prop can provably never reach (e.g. a `variant ===
|
|
211
|
+
* 'danger'` arm when `variant ∈ {'primary','secondary'}`), but — unlike
|
|
212
|
+
* `constFold` — the prop is still genuinely used/dynamic, so it is NOT
|
|
213
|
+
* substituted and NOT dropped from the `$props()` signature. Singletons stay
|
|
214
|
+
* in `constFold`; these two maps are disjoint.
|
|
215
|
+
*/
|
|
216
|
+
narrow: Map<string, Literal[]>;
|
|
217
|
+
/**
|
|
218
|
+
* Per-declared-prop value-set foundation (see {@link PropValueSet}). Present
|
|
219
|
+
* for every declared prop the analysis reasoned about; `constFold` is its
|
|
220
|
+
* singleton projection and `narrow` is its multi-element projection.
|
|
221
|
+
*/
|
|
222
|
+
valueSets: Map<string, PropValueSet>;
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
export { EditResult as a, ResolvedEdge as c, ParseCache as d, Root as f, EdgeKind as i, AnyNode as l, ComponentId as n, InputFile as o, ComponentPlan as r, Literal as s, AnalyzeInput as t, Parse as u };
|