svelte-shaker 0.17.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.
Files changed (52) hide show
  1. package/README.md +24 -34
  2. package/dist/crawl-dRCzCERA.d.ts +43 -0
  3. package/dist/{dev-only.d.ts → dev-only-vMKGdavE.d.ts} +7 -4
  4. package/dist/index.d.ts +175 -25
  5. package/dist/index.js +1 -74
  6. package/dist/ir-BDouNmBm.d.ts +225 -0
  7. package/dist/mono-gDUeXt7W.d.ts +82 -0
  8. package/dist/parse-NbmYG3ZM.js +2 -0
  9. package/dist/scan-Ch9et3Ei.js +1 -0
  10. package/dist/scan.d.ts +89 -10
  11. package/dist/scan.js +1 -69
  12. package/dist/src-5VY_edrh.js +4 -0
  13. package/dist/vite.d.ts +184 -177
  14. package/dist/vite.js +4 -630
  15. package/package.json +7 -5
  16. package/dist/analyze.d.ts +0 -367
  17. package/dist/analyze.js +0 -1981
  18. package/dist/css.d.ts +0 -45
  19. package/dist/css.js +0 -307
  20. package/dist/dead.d.ts +0 -71
  21. package/dist/dead.js +0 -280
  22. package/dist/dev-only.js +0 -43
  23. package/dist/engine.d.ts +0 -81
  24. package/dist/engine.js +0 -120
  25. package/dist/escape-scan.d.ts +0 -66
  26. package/dist/escape-scan.js +0 -197
  27. package/dist/eval.d.ts +0 -63
  28. package/dist/eval.js +0 -288
  29. package/dist/exclude.d.ts +0 -25
  30. package/dist/exclude.js +0 -29
  31. package/dist/ir.d.ts +0 -148
  32. package/dist/ir.js +0 -31
  33. package/dist/mono.d.ts +0 -95
  34. package/dist/mono.js +0 -508
  35. package/dist/native-engine.d.ts +0 -59
  36. package/dist/native-engine.js +0 -141
  37. package/dist/parse.d.ts +0 -152
  38. package/dist/parse.js +0 -106
  39. package/dist/reverse.d.ts +0 -48
  40. package/dist/reverse.js +0 -137
  41. package/dist/revert-cascade.d.ts +0 -34
  42. package/dist/revert-cascade.js +0 -95
  43. package/dist/rsvelte-parse.d.ts +0 -25
  44. package/dist/rsvelte-parse.js +0 -79
  45. package/dist/svelte_shaker_engine.js +0 -265
  46. package/dist/svelte_shaker_engine_bg.wasm +0 -0
  47. package/dist/transform.d.ts +0 -70
  48. package/dist/transform.js +0 -1064
  49. package/dist/unread.d.ts +0 -17
  50. package/dist/unread.js +0 -155
  51. package/dist/wasm-engine.d.ts +0 -56
  52. package/dist/wasm-engine.js +0 -97
package/README.md CHANGED
@@ -49,19 +49,17 @@ npm i -D svelte-shaker # requires svelte@^5
49
49
 
50
50
  Nothing else to install. The plugin picks its engine automatically (see
51
51
  [Options](#options)): if a **native (napi) Rust** binary loads it runs there —
52
- parsing with rsvelte in process, fastest, no size ceiling; otherwise a small/medium
53
- app runs on the **WASM Rust engine** (parsing with **rsvelte** from
54
- `@rsvelte/compiler`, a bundled WASM dependency no peer, no platform-specific
55
- binary) and a large app on the **JS engine** with **svelte/compiler**. `engine` and
56
- `parser` let you pin the choice.
52
+ parsing with rsvelte in process, by far the fastest; otherwise the shake runs on the
53
+ **JS engine** with **svelte/compiler**, which needs no prebuilt binary and always
54
+ works. Both shake **byte-identically**, so the fallback costs only build time.
55
+ `engine` and `parser` let you pin the choice.
57
56
 
58
57
  ## Usage (Vite)
59
58
 
60
59
  Add the plugin **before** `svelte()`. By default it runs only in `vite build` —
61
60
  dev/HMR is a pass-through (opt into dev shaking with the `dev` option, see
62
61
  [Options](#options)). The engine is chosen automatically (native Rust if a binary
63
- loads, else WASM Rust for small/medium or JS for large), and every path falls back
64
- cleanly (see [Options](#options)).
62
+ loads, else JS), and every path falls back cleanly (see [Options](#options)).
65
63
 
66
64
  ```ts
67
65
  // vite.config.ts
@@ -87,7 +85,7 @@ through your plugin's `resolveId`/`load` hooks; the unused-prop fold / constant
87
85
  fold / value-set narrowing shake only needs the `transform` swap. The
88
86
  environment-free engine and the in-browser playground parse with svelte/compiler
89
87
  — the Vite plugin's rsvelte selection is a plugin concern (it loads a Node-only
90
- WASM module); the engine takes an optional `parse` argument if you want to swap it.
88
+ module); the engine takes an optional `parse` argument if you want to swap it.
91
89
 
92
90
  ### Options
93
91
 
@@ -107,9 +105,9 @@ shaker({
107
105
  verbose: false, // true = per-file size breakdown after the build
108
106
 
109
107
  // Engine is auto-selected; set these only to pin.
110
- engine: 'auto', // 'auto' (native Rust if it loads, else WASM if <=~300, else JS) | 'js' | 'rust'
111
- parser: undefined, // JS/WASM parse only (native always uses in-process rsvelte);
112
- // default follows the engine (WASM->rsvelte, JS->svelte); set 'rsvelte' | 'svelte' to pin
108
+ engine: 'auto', // 'auto' (native Rust if it loads, else JS) | 'js' | 'rust'
109
+ parser: undefined, // JS engine only (native always uses in-process rsvelte);
110
+ // defaults to svelte/compiler; set 'rsvelte' to pin rsvelte instead
113
111
 
114
112
  dev: false, // default off: dev is a pass-through. 'incremental' (re-parse only
115
113
  // changed files) | 'coarse' (re-analyze everything) opts in; never monomorphizes
@@ -120,33 +118,25 @@ That list is exhaustive: any other key **fails the build**, naming the key and t
120
118
  options that do exist. A typo would otherwise be ignored — and a misspelled
121
119
  `preserve` ships the component you meant to protect, over-shaken.
122
120
 
123
- - **`engine`** — which engine runs the shake. There are two Rust engines (the same
124
- analysis behind two frontends) plus the JS engine. The **native (napi)** engine
125
- parses with rsvelte **in process** and keeps the ASTs Rust-side, so no
126
- whole-program AST crosses a boundary fastest, no size ceiling but it ships as
127
- a per-platform prebuilt binary that may not exist for every install. The **WASM**
128
- engine is the same Rust engine, but the whole-program AST must cross the JS↔WASM
129
- boundary as JSON (tens of MB past a few hundred components), so it only wins for
130
- small/medium apps; the **JS** engine needs no boundary crossing, so it wins for a
131
- large app when the native binary isn't available. **`'auto'`** (default) uses the
132
- native engine if a binary loads (no size gate), else the WASM engine for a
133
- small/medium app or the JS engine for a large one. **`'rust'`** forces a Rust
134
- engine — native if it loads, else WASM (throwing if `@rsvelte/compiler` can't load
135
- for the WASM fallback); **`'js'`** forces the JS engine. All three are
121
+ - **`engine`** — which engine runs the shake. There are two. The **native (napi
122
+ Rust)** engine parses with rsvelte **in process** and keeps the ASTs Rust-side, so
123
+ no whole-program AST crosses a boundary by far the fastest but it ships as a
124
+ per-platform prebuilt binary that may not exist for every install. The **JS**
125
+ engine needs no prebuilt binary and always works. **`'auto'`** (default) uses the
126
+ native engine if a binary loads, else the JS engine. **`'rust'`** forces the native
127
+ engine, throwing if it can't be loaded; **`'js'`** forces the JS engine. Both are
136
128
  differentially tested to shake **byte-identically**, so this is **speed-only** — it
137
129
  never changes what ships.
138
- - **`parser`** — how the **JS / WASM** engines parse `.svelte`. It does **not** apply
139
- to the native engine, which always parses with rsvelte in process. The default
140
- **follows the engine**: **rsvelte** on the WASM engine (its AST crosses into Rust
141
- directly), **svelte/compiler** on the JS engine (where rsvelte's parse is ~2× slower
142
- with no downstream benefit). The choice is **soundness-neutral** the engine reads
143
- only UTF-16 `start`/`end`, so both parsers are differentially tested to produce
130
+ - **`parser`** — how the **JS** engine parses `.svelte`. It does **not** apply to the
131
+ native engine, which always parses with rsvelte in process. It defaults to
132
+ **svelte/compiler**, because on the JS engine rsvelte's parse is ~2× slower with no
133
+ downstream benefit. The choice is **soundness-neutral** the engine reads only
134
+ UTF-16 `start`/`end`, so both parsers are differentially tested to produce
144
135
  **byte-identical** output, never changing what renders. `parser: 'svelte'` also
145
136
  forces the native engine **off** (it can't honor svelte/compiler), so the shake uses
146
- a JS/WASM engine with svelte/compiler. When rsvelte is the resolved JS-side parser
147
- (`parser: 'rsvelte'`, or the WASM engine's default) and `@rsvelte/compiler` can't
148
- load, the plugin **throws** rather than silently swapping (so the same source can't
149
- shake differently on another machine); `parser: 'svelte'` is the explicit opt-out.
137
+ the JS engine. With an explicit `parser: 'rsvelte'` that `@rsvelte/compiler` can't
138
+ satisfy, the plugin **throws** rather than silently swapping (so the same source
139
+ can't shake differently on another machine); `parser: 'svelte'` is the opt-out.
150
140
  - **`monomorphize`** — the one shaking knob, **on** by default. A measured
151
141
  net-win gate only specializes a component when that strictly shrinks the whole
152
142
  program, so monomorphization **never bloats**: whatever the knobs are set to,
@@ -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 type { ComponentId } from './ir.js';
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
- export declare const DEFAULT_DEV_ONLY: readonly string[];
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
- export type DevOnlyFilter = (file: ComponentId) => boolean;
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
- export declare function compileDevOnly(base: string, patterns?: readonly string[]): DevOnlyFilter;
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,15 +1,163 @@
1
- import { type ReadFile, type Resolve } from './analyze.js';
2
- import { type Parse } from './parse.js';
3
- import { type MonomorphizeOptions, type MonomorphizeResult, type OwnSize } from './mono.js';
4
- import type { ComponentId } from './ir.js';
5
- export type { ComponentId, AnalyzeInput, InputFile, ResolvedEdge, EdgeKind, EditResult, } from './ir.js';
6
- export type { Resolve, ReadFile, ResolveSync, ReadFileSync } from './analyze.js';
7
- export type { Parse, Root } from './parse.js';
8
- export { analyze, analyzeInput, buildAnalyzeInput, buildAnalyzeInputSync, deadSpansForPlans, findNeverPassedProps, } from './analyze.js';
9
- export type { UnpassedProp } from './analyze.js';
10
- export { DevShaker, type DevMode, type DevShakerChange } from './engine.js';
11
- export { transformAll, transformAllWithMono } from './transform.js';
12
- export { monomorphize, DEFAULT_MONO_OPTIONS, type MonomorphizeOptions, type MonomorphizeResult, type OwnSize, type Variant, type CallSiteBinding, } from './mono.js';
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
13
161
  /**
14
162
  * Whole-program shake: crawl the component graph from `entry`, decide what to
15
163
  * fold, and return the shaken source for every reachable `.svelte` file.
@@ -20,21 +168,21 @@ export { monomorphize, DEFAULT_MONO_OPTIONS, type MonomorphizeOptions, type Mono
20
168
  * Node callers use `fsResolve` / `fs.readFileSync` from `svelte-shaker/node`.
21
169
  * See docs/ARCHITECTURE.md §5 — this is the Engine; the Shell owns resolution.
22
170
  */
23
- export declare function svelteShaker(entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile, parse?: Parse, escaped?: ComponentId[]): Promise<Record<ComponentId, string>>;
171
+ declare function svelteShaker(entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile, parse?: Parse, escaped?: ComponentId[]): Promise<Record<ComponentId, string>>;
24
172
  /** The full output of a shake including monomorphization specialization. */
25
- export interface ShakeResult {
26
- /**
27
- * Whole-program output: shaken source per `.svelte` file. With monomorphization OFF this is
28
- * byte-for-byte identical to {@link svelteShaker}; with monomorphization ON, owner files
29
- * whose call sites were specialized have those sites rewritten to import a
30
- * variant via `variantSpecifier(variantId)`.
31
- */
32
- files: Record<ComponentId, string>;
33
- /** monomorphization specialized variants + call-site bindings (empty when monomorphization is off). */
34
- mono: MonomorphizeResult;
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;
35
183
  }
36
184
  /** Build the module specifier a rewritten call site imports a variant from. */
37
- export type VariantSpecifier = (variantId: string) => string;
185
+ type VariantSpecifier = (variantId: string) => string;
38
186
  /**
39
187
  * Whole-program shake WITH optional monomorphization (docs §3 "monomorphization").
40
188
  *
@@ -45,4 +193,6 @@ export type VariantSpecifier = (variantId: string) => string;
45
193
  * specialized and `files` equals the unused-prop fold / constant fold / value-set narrowing output exactly — a strict
46
194
  * superset of the default behavior, so existing consumers are unaffected.
47
195
  */
48
- export declare function svelteShakerWithMono(entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile, mono?: MonomorphizeOptions, variantSpecifier?: VariantSpecifier, parse?: Parse, escaped?: ComponentId[], ownSize?: OwnSize): Promise<ShakeResult>;
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,74 +1 @@
1
- import { analyze, analyzeInput, buildAnalyzeInput, } from './analyze.js';
2
- import {} from './parse.js';
3
- import { transformAll, transformAllWithMono } from './transform.js';
4
- import { monomorphize, DEFAULT_MONO_OPTIONS, } from './mono.js';
5
- import { shakeWithRevertCascade } from './revert-cascade.js';
6
- export { analyze, analyzeInput, buildAnalyzeInput, buildAnalyzeInputSync, deadSpansForPlans, findNeverPassedProps, } from './analyze.js';
7
- export { DevShaker } from './engine.js';
8
- export { transformAll, transformAllWithMono } from './transform.js';
9
- export { monomorphize, DEFAULT_MONO_OPTIONS, } from './mono.js';
10
- /**
11
- * Whole-program shake: crawl the component graph from `entry`, decide what to
12
- * fold, and return the shaken source for every reachable `.svelte` file.
13
- *
14
- * `resolve` / `readFile` are injected so the engine stays environment-free —
15
- * it has NO `node:*` imports, so it runs unchanged in the browser (the
16
- * playground passes an in-memory file map). A Vite plugin passes `this.resolve`;
17
- * Node callers use `fsResolve` / `fs.readFileSync` from `svelte-shaker/node`.
18
- * See docs/ARCHITECTURE.md §5 — this is the Engine; the Shell owns resolution.
19
- */
20
- export async function svelteShaker(entries, resolve, readFile, parse, escaped = []) {
21
- const { models, plans } = await analyzeWith(entries, resolve, readFile, parse, escaped);
22
- return shakeWithRevertCascade(models, plans, (p) => transformAll(models, p));
23
- }
24
- /**
25
- * Crawl + analyze with an optional non-default parser ({@link Parse}). When
26
- * `parse` is given (the Vite plugin's `parser: 'rsvelte'` path), each file is
27
- * parsed ONCE into a shared cache during the crawl and that cache is reused by the
28
- * analysis — so the alternate parser drives the whole engine with no second parse.
29
- * When omitted, this is exactly `analyze(entries, resolve, readFile)` (the default
30
- * svelte/compiler path, byte-for-byte unchanged).
31
- */
32
- async function analyzeWith(entries, resolve, readFile, parse, escaped = []) {
33
- if (!parse)
34
- return analyze(entries, resolve, readFile, escaped);
35
- const cache = new Map();
36
- const input = await buildAnalyzeInput(entries, resolve, readFile, cache, parse, escaped);
37
- return analyzeInput(input, cache);
38
- }
39
- /**
40
- * Whole-program shake WITH optional monomorphization (docs §3 "monomorphization").
41
- *
42
- * `mono` carries the specialized variants (id -> residual source) and the call
43
- * sites bound to them; `files` is the wired owner source. The Shell resolves
44
- * `variantSpecifier(id)` to a virtual module whose source is
45
- * `mono.variants.get(id)!.code`. With `mono.enabled` false (default) nothing is
46
- * specialized and `files` equals the unused-prop fold / constant fold / value-set narrowing output exactly — a strict
47
- * superset of the default behavior, so existing consumers are unaffected.
48
- */
49
- export async function svelteShakerWithMono(entries, resolve, readFile, mono = DEFAULT_MONO_OPTIONS, variantSpecifier = (id) => id, parse, escaped = [], ownSize) {
50
- const { models, plans } = await analyzeWith(entries, resolve, readFile, parse, escaped);
51
- // The cascade may re-run the transform with force-bailed plans, so recompute
52
- // monomorphization inside it: a bailed component must not be specialized either.
53
- // `lastResult` captures the mono result of the final (converged or no-op) pass.
54
- // On the no-op fallback the emitted owner files are the untouched originals —
55
- // they import no variant — so any variants `lastResult` still lists are simply
56
- // never requested.
57
- let lastResult;
58
- const files = shakeWithRevertCascade(models, plans, (p) => {
59
- // Thread the shake entries through so the net-win gate can compute module
60
- // reachability from them (docs §3 monomorphization, §13.2).
61
- lastResult = monomorphize(models, p, mono, entries, ownSize);
62
- // With no bindings the wired pass and the base pass are identical, so reuse
63
- // the plain transform to keep the default path byte-for-byte unchanged.
64
- return lastResult.bindings.length === 0
65
- ? transformAll(models, p)
66
- : transformAllWithMono(models, p, lastResult.bindings.map((b) => ({
67
- owner: b.owner,
68
- node: b.node,
69
- variantId: b.variantId,
70
- foldedProps: b.foldedProps,
71
- })), variantSpecifier);
72
- });
73
- return { files, mono: lastResult };
74
- }
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};