svelte-shaker 0.0.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/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/index.cjs +1 -0
- package/dist/index.js +1 -0
- package/dist/scan.js +1 -0
- package/dist/types/src/analyze.d.ts +135 -0
- package/dist/types/src/css.d.ts +32 -0
- package/dist/types/src/dead.d.ts +71 -0
- package/dist/types/src/eval.d.ts +26 -0
- package/dist/types/src/index.d.ts +44 -0
- package/dist/types/src/ir.d.ts +75 -0
- package/dist/types/src/mono.d.ts +82 -0
- package/dist/types/src/parse.d.ts +83 -0
- package/dist/types/src/scan.d.ts +12 -0
- package/dist/types/src/transform.d.ts +45 -0
- package/dist/types/src/vite.d.ts +40 -0
- package/dist/vite.js +1 -0
- package/package.json +76 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Yuichiro Yamashita
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/baseballyama/svelte-shaker/main/assets/logo.png" alt="svelte-shaker" width="190" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">svelte-shaker</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">A <strong>sound, source-level tree-shaker for Svelte 5 (runes) components.</strong></p>
|
|
8
|
+
|
|
9
|
+
**▶ Try it in the browser: https://baseballyama.github.io/svelte-shaker/** — an
|
|
10
|
+
interactive playground that runs the engine entirely client-side (and is itself
|
|
11
|
+
built with rsvelte + dogfooded through svelte-shaker).
|
|
12
|
+
|
|
13
|
+
It runs in your app's production build, _before_ the Svelte compiler, and slims
|
|
14
|
+
each `.svelte` file by partially evaluating it against how the **whole app**
|
|
15
|
+
actually uses it: props that are never passed (or always passed the same value)
|
|
16
|
+
are folded to their constant, the dead `{#if}` arms behind them are deleted,
|
|
17
|
+
those props are dropped from the `$props()` signature, and the now-pointless
|
|
18
|
+
attributes are removed at every call site. The Svelte compiler then only sees the
|
|
19
|
+
code your app can actually reach.
|
|
20
|
+
|
|
21
|
+
It is **sound first**: it never changes what the user sees. When it cannot prove
|
|
22
|
+
a transform is safe, it leaves the code untouched (bails).
|
|
23
|
+
|
|
24
|
+
See [`docs/ARCHITECTURE.md`](https://github.com/baseballyama/svelte-shaker/blob/main/docs/ARCHITECTURE.md) for the full design.
|
|
25
|
+
|
|
26
|
+
## Why this exists (and why a JS bundler can't do it)
|
|
27
|
+
|
|
28
|
+
Design-system components carry lots of props (`Button` with
|
|
29
|
+
`variant / size / loading / icon / iconPosition / fullWidth / rounded / href …`),
|
|
30
|
+
but any one app uses only a few. The code behind the unused props — template
|
|
31
|
+
branches, class computation, reactive statements, imports, CSS — is effectively
|
|
32
|
+
dead for that app, yet it ships anyway.
|
|
33
|
+
|
|
34
|
+
It cannot be removed _after_ Svelte compiles, because Svelte emits **one generic
|
|
35
|
+
JS module per component**, shared by every caller. In that JS the prop values
|
|
36
|
+
flow through the runtime (`$.prop(...)`), so `loading` / `variant` are not static
|
|
37
|
+
JS constants — terser/esbuild/Rollup cannot fold `if (loading)` to `if (false)`,
|
|
38
|
+
and the single module has no whole-program information to know which props this
|
|
39
|
+
particular app never uses.
|
|
40
|
+
|
|
41
|
+
svelte-shaker works **one step earlier**, on the pre-compile Svelte source, where
|
|
42
|
+
the prop's value (its default, or the literal at the call site) is still visible
|
|
43
|
+
and the template structure is intact. It is essentially a **whole-program partial
|
|
44
|
+
evaluator + dead-code eliminator that understands Svelte**, driven by every call
|
|
45
|
+
site in the app.
|
|
46
|
+
|
|
47
|
+
### The CSS differentiator (what a bundler genuinely can't reach)
|
|
48
|
+
|
|
49
|
+
Given `class="btn btn-{variant}"` where the app only ever passes
|
|
50
|
+
`variant ∈ {primary, secondary}`, the class `btn-danger` can never exist at
|
|
51
|
+
runtime. But the class only appears as a runtime string, so:
|
|
52
|
+
|
|
53
|
+
- Svelte's own unused-CSS pruning keeps `.btn-danger` (it can't see inside the
|
|
54
|
+
interpolation), and
|
|
55
|
+
- Rollup/terser can't touch it either (the class isn't in the JS at all).
|
|
56
|
+
|
|
57
|
+
svelte-shaker computes the reachable value set of `variant`, proves the
|
|
58
|
+
`.btn-danger` / `.btn-ghost` rules can never match any element this component
|
|
59
|
+
renders, and **removes those `<style>` rules** — while keeping `.btn`,
|
|
60
|
+
`.btn-primary`, `.btn-secondary`. This is verified end-to-end in
|
|
61
|
+
`packages/svelte-shaker/tests/css.test.ts`.
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
pnpm add -D svelte-shaker
|
|
67
|
+
# or: npm i -D svelte-shaker / yarn add -D svelte-shaker
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Requires `svelte@^5`.
|
|
71
|
+
|
|
72
|
+
## Usage (Vite)
|
|
73
|
+
|
|
74
|
+
Add the plugin **before** `svelte()` so it hands already-slimmed source to the
|
|
75
|
+
Svelte compiler. It is **build-only by design** — dev is a pass-through (see
|
|
76
|
+
[Soundness](#soundness) / [Limitations](#limitations)).
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// vite.config.ts
|
|
80
|
+
import { defineConfig } from 'vite';
|
|
81
|
+
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
82
|
+
import { shaker } from 'svelte-shaker/vite';
|
|
83
|
+
|
|
84
|
+
export default defineConfig({
|
|
85
|
+
plugins: [
|
|
86
|
+
// `include` must cover EVERY call site in the app, or prop elimination
|
|
87
|
+
// would be unsound. Defaults to the Vite root.
|
|
88
|
+
shaker({ include: ['src'] }),
|
|
89
|
+
svelte(),
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
There is also a plain-Rollup plugin (`rollup-plugin-svelte-shaker`) for non-Vite
|
|
95
|
+
pipelines; the Vite plugin is preferred for apps.
|
|
96
|
+
|
|
97
|
+
### Options
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
shaker({
|
|
101
|
+
include: ['src'], // dirs (relative to root) holding every .svelte call site
|
|
102
|
+
level: 1, // 0 | 1 | 2 — default 1 (L0/L1/L1.5 always on). 2 = opt-in L2.
|
|
103
|
+
monomorphize: false, // L2 tuning; only consulted when level: 2.
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Opt into L2 per-call-site monomorphization:
|
|
107
|
+
shaker({ include: ['src'], level: 2, monomorphize: true });
|
|
108
|
+
shaker({ include: ['src'], level: 2, monomorphize: { maxVariants: 16 } });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## What it does
|
|
112
|
+
|
|
113
|
+
| Level | What it removes | Default |
|
|
114
|
+
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
|
|
115
|
+
| **L0** | Props no call site ever passes → fold to the default, drop from `$props()`, strip the attribute at call sites | on |
|
|
116
|
+
| **L1** | Props that collapse to one constant app-wide → fold + drop + strip every call site's attribute | on |
|
|
117
|
+
| **L1.5** | Value-set **narrowing**: with `variant ∈ {primary, secondary}`, delete provably-dead `{#if}`/`{:else if}` arms (prop stays in the signature) | on |
|
|
118
|
+
| **CSS** | `<style>` rules whose class can never be produced given the value sets — the bundler-can't differentiator | on |
|
|
119
|
+
| **L2** | Per-call-site monomorphization: specialize a component per prop shape (deduped by residual, capped by `maxVariants`) | **opt-in** |
|
|
120
|
+
|
|
121
|
+
Folding also reaches template ternaries (`{cond ? a : b}`) and class-string
|
|
122
|
+
interpolation when the condition/parts are provable constants.
|
|
123
|
+
|
|
124
|
+
## Soundness
|
|
125
|
+
|
|
126
|
+
The whole point is to **never change observable behavior**.
|
|
127
|
+
|
|
128
|
+
- **Differential-SSR verified.** Tests server-render the original and the shaken
|
|
129
|
+
component (comments stripped, whitespace normalized) and assert the HTML is
|
|
130
|
+
identical for every value the app actually passes
|
|
131
|
+
(`packages/svelte-shaker/tests/diff.ts`).
|
|
132
|
+
- **Conservative bail.** When a transform can't be _proven_ safe, the code is
|
|
133
|
+
left as-is. Whole-component bails: `<svelte:options accessors />` /
|
|
134
|
+
`customElement`, and any component that **escapes** as a value
|
|
135
|
+
(`<svelte:component this={X}>`, assigned/passed/stored), or is rendered through
|
|
136
|
+
a barrel/named import (its call sites aren't enumerable). Per-prop bails:
|
|
137
|
+
spread that could overwrite it, callee `...rest`, `bind:`, a name shadowed by
|
|
138
|
+
`{#each as}` / snippet params / `{#await then}` / `let:` / `{@const}`, or used
|
|
139
|
+
in `{@debug}`.
|
|
140
|
+
- **Side effects preserved.** A call-site attribute is only stripped if its value
|
|
141
|
+
has no side effects; a value's code is removed only when it is provably pure
|
|
142
|
+
and unused.
|
|
143
|
+
- **Whole-program fixpoint.** Call sites inside a folded-away `{#if}` don't count
|
|
144
|
+
toward a child's prop profile; analysis iterates to a fixpoint so cascades are
|
|
145
|
+
consistent with what the transform actually deletes.
|
|
146
|
+
|
|
147
|
+
## Limitations
|
|
148
|
+
|
|
149
|
+
- **Svelte 5 runes only** (`$props()` / `$derived` / `$effect`). Svelte 4
|
|
150
|
+
(`export let` / `$:` / `$$props`) is out of scope.
|
|
151
|
+
- **Needs `.svelte` source.** Libraries shipping compiled JS can't be shaken (the
|
|
152
|
+
source has to be visible — that's the whole premise). Distribute via
|
|
153
|
+
`svelte-package`. Anything it can't resolve is silently passed through.
|
|
154
|
+
- **Build only.** It runs in `vite build`, not in dev/HMR — whole-program
|
|
155
|
+
analysis is fundamentally incompatible with HMR's locality, and L1.5/CSS depend
|
|
156
|
+
on negative information ("this value never occurs") that a lazily-loaded dev
|
|
157
|
+
server can't guarantee. Dev is always a pass-through (and is unoptimized but
|
|
158
|
+
always correct). A `dev: 'coarse'` mode is a future opt-in.
|
|
159
|
+
- **`include` must cover the whole app.** A call site outside the scanned dirs is
|
|
160
|
+
invisible, so soundness requires every consumer of a prop to be in scope.
|
|
161
|
+
- **Partial-bail boundaries.** Spread/rest/`bind:`/shadowing limit how much can be
|
|
162
|
+
folded (by design — the engine errs toward keeping code). L2's `minSavings`,
|
|
163
|
+
and `exclude` / `unsafe` / `report` options, are reserved but not yet
|
|
164
|
+
implemented.
|
|
165
|
+
|
|
166
|
+
## Running the tests
|
|
167
|
+
|
|
168
|
+
```sh
|
|
169
|
+
pnpm --filter svelte-shaker test # vitest: eval / basic / shadow / probes2 / css / vite / mono
|
|
170
|
+
pnpm format:fix && pnpm all:check # type-check + lint + format
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Bench
|
|
174
|
+
|
|
175
|
+
`packages/svelte-shaker/tests/css.test.ts` builds a tiny app
|
|
176
|
+
(`App` passes `variant="primary"` and `variant="secondary"` to `Btn`, whose
|
|
177
|
+
`<style>` defines `.btn-{primary,secondary,danger,ghost}`) two ways:
|
|
178
|
+
|
|
179
|
+
- **control** (Svelte + Rollup, no shaker): keeps `.btn-danger` and `.btn-ghost`
|
|
180
|
+
in the emitted CSS — the toolchain cannot prove them dead.
|
|
181
|
+
- **shaken**: removes `.btn-danger` / `.btn-ghost`, keeps `.btn` /
|
|
182
|
+
`.btn-primary` / `.btn-secondary`, and the rendered HTML is identical for both
|
|
183
|
+
variants the app passes.
|
|
184
|
+
|
|
185
|
+
That's the headline result: the same source produces strictly smaller CSS with no
|
|
186
|
+
behavior change.
|
|
187
|
+
|
|
188
|
+
## Architecture & status
|
|
189
|
+
|
|
190
|
+
The engine is split into an environment-free **Engine** (Svelte-aware analysis +
|
|
191
|
+
transform) behind a stable IR, and a thin **Shell** (the Vite/Rollup plugin) that
|
|
192
|
+
owns file IO and module resolution — so the core can later be ported to Rust
|
|
193
|
+
(rsvelte / OXC). The current implementation status (what's done vs. remaining) is
|
|
194
|
+
tracked in [`docs/ARCHITECTURE.md` §11](https://github.com/baseballyama/svelte-shaker/blob/main/docs/ARCHITECTURE.md#11-実装状況implementation-status).
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e=require("svelte/compiler"),n=require("zimmerframe"),t=require("magic-string");function o(n,t){return e.parse(n,{modern:!0,filename:t})}function r(e,t,o){n.walk(e,t,o)}const s={known:!1};function a(e,n){if(!e)return s;switch(e.type){case"Literal":return{known:!0,value:e.value};case"Identifier":{const t=e.name??"";return"undefined"===t?{known:!0,value:void 0}:n.has(t)?{known:!0,value:n.get(t)}:s}case"UnaryExpression":{const t=a(e.argument,n);if(!t.known)return s;const o=t.value;switch(e.operator){case"!":return{known:!0,value:!o};case"-":return{known:!0,value:-o};case"+":return{known:!0,value:+o};case"typeof":return{known:!0,value:typeof o};case"void":return{known:!0,value:void 0};default:return s}}case"LogicalExpression":{const t=a(e.left,n);if(!t.known)return s;switch(e.operator){case"&&":return t.value?a(e.right,n):t;case"||":return t.value?t:a(e.right,n);case"??":return null===t.value||void 0===t.value?a(e.right,n):t;default:return s}}case"BinaryExpression":{const t=a(e.left,n),o=a(e.right,n);if(!t.known||!o.known)return s;const r=t.value,i=o.value;switch(e.operator){case"===":return{known:!0,value:r===i};case"!==":return{known:!0,value:r!==i};case"==":return{known:!0,value:r==i};case"!=":return{known:!0,value:r!=i};case"<":return{known:!0,value:r<i};case">":return{known:!0,value:r>i};case"<=":return{known:!0,value:r<=i};case">=":return{known:!0,value:r>=i};case"+":return{known:!0,value:r+i};case"-":return{known:!0,value:r-i};case"*":return{known:!0,value:r*i};case"/":return{known:!0,value:r/i};case"%":return{known:!0,value:r%i};default:return s}}default:return s}}function i(e,n,t){const o=a(e,n);if(o.known)return o;const r=c(e,n,t);return"unknown"===r?s:{known:!0,value:r}}function c(e,n,t){if(!e)return"unknown";switch(e.type){case"UnaryExpression":return"!"===e.operator?l(c(e.argument,n,t)):"unknown";case"LogicalExpression":{const s=c(e.left,n,t),a=()=>c(e.right,n,t);return"&&"===e.operator?!1!==s&&(o=s,r=a(),!1!==o&&!1!==r&&(!0===o&&!0===r||"unknown")):"||"===e.operator?!0===s||function(e,n){return!0===e||!0===n||(!1!==e||!1!==n)&&"unknown"}(s,a()):"unknown"}case"BinaryExpression":{const o=e.operator;if("==="===o||"=="===o||"!=="===o||"!="===o){const r="=="===o||"!="===o,s=function(e,n,t,o,r){const s=u(e,o),i=a(n,t);if(s&&i.known)return f(s,i.value,r);const c=u(n,o),l=a(e,t);return c&&l.known?f(c,l.value,r):l.known&&i.known?r?l.value==i.value:l.value===i.value:"unknown"}(e.left,e.right,n,t,r);return"!=="===o||"!="===o?l(s):s}return"unknown"}default:return"unknown"}var o,r}function u(e,n){return"Identifier"===e?.type&&e.name&&n.has(e.name)?n.get(e.name):null}function f(e,n,t){const o=t?e=>e==n:e=>e===n;return!!e.some(o)&&(!!e.every(o)||"unknown")}function l(e){return!0!==e&&(!1===e||"unknown")}function p(e,n,t){const{arms:o,elseFrag:r}=function(e){const n=[];let t,o=e;for(;o;){n.push({block:o,test:o.test,consequent:o.consequent});const e=o.alternate,r="Fragment"===e?.type&&1===e.nodes?.length&&"IfBlock"===e.nodes[0]?.type&&!0===e.nodes[0].elseif?e.nodes[0]:void 0;r?o=r:("Fragment"===e?.type&&(t=e),o=void 0)}return t?{arms:n,elseFrag:t}:{arms:n}}(e),s=[e.start,e.end],a=o.map((e=>i(e.test,n,t))),c=e=>e.known&&Boolean(e.value),u=e=>e.known&&!e.value;let f=!0;for(let e=0;e<o.length;e++){const n=a[e];if(c(n)&&f){const n=o[e].consequent;return{span:s,kept:n,removed:d(s,m(n)),recurse:!1}}u(n)||(f=!1)}const l=a.findIndex((e=>!u(e)));if(-1===l)return r?{span:s,kept:r,removed:d(s,m(r)),recurse:!1}:{span:s,kept:void 0,removed:[s],recurse:!1};if(0===l)return{span:s,kept:void 0,removed:v(o,a,l),recurse:!0};const p=o[l].block;return{span:s,kept:void 0,removed:[[s[0],p.start],...v(o,a,l)],recurse:!1,headerRewrite:{from:p.start,to:p.test.start,text:"{#if "}}}function d(e,n){if(!n)return[e];const t=[];return e[0]<n[0]&&t.push([e[0],n[0]]),n[1]<e[1]&&t.push([n[1],e[1]]),t}function m(e){const n=e?.nodes??[];return 0===n.length?null:[n[0].start,n[n.length-1].end]}function v(e,n,t){const o=[];for(let r=t+1;r<e.length;r++){const t=n[r];if(!t.known||t.value)continue;const s=e[r],a=e[r+1]?.block,i=a?a.start:h(s.consequent,s.block.end);o.push([s.block.start,i])}return o}function h(e,n){const t=e?.nodes??[];return t.length?t[t.length-1].end:n}function w(e,n){return n.some((([n,t])=>e.start>=n&&e.end<=t))}function g(e,n,t){if(0===n.size&&0===t.size)return[];const o=[];return r(e,null,{IfBlock(e,{next:r}){if(e.elseif||w(e,o))return;const s=p(e,n,t);for(const e of s.removed)o.push(e);s.recurse&&r()}}),o}const y=e=>e.endsWith(".svelte"),k=10,x="escapes as value (e.g. <svelte:component this={X}>)",S="rendered through a barrel/named import (call sites unobservable)";async function b(e,n,t){const o=await async function(e,n,t){const o=new Map,r=Array.isArray(e)?[...e]:[e],s=new Set(r);for(;r.length>0;){const e=r.shift(),a=await t(e),i=await D(e,a,n,t);o.set(e,i);for(const e of[...i.imports.values(),...i.barrelChildIds])s.has(e)||(s.add(e),r.push(e))}return o}(e,n,t),r=new Set;for(const e of o.values())for(const n of e.escapedComponents)r.add(n);for(const e of r){const n=o.get(e);n&&!n.bailReasons.includes(x)&&n.bailReasons.push(x)}const s=new Set;for(const e of o.values())for(const n of e.barrelChildIds)s.add(n);for(const e of s){const n=o.get(e);n&&!n.bailReasons.includes(S)&&n.bailReasons.push(S)}let a=M(o,I(o,new Map));for(let e=0;e<k;e++){const e=M(o,I(o,C(o,a)));if(E(a,e)){a=e;break}a=e}return{models:o,plans:a}}function I(e,n){const t=new Map,o=e=>{let n=t.get(e);return n||(n={sites:[]},t.set(e,n)),n};for(const t of e.values()){const e=n.get(t.id)??[];for(const n of t.childCalls)e.length>0&&w(n.node,e)||o(n.childId).sites.push(R(n.node))}return t}function M(e,n){const t=new Map;for(const o of e.values())t.set(o.id,T(o,n.get(o.id)));return t}function E(e,n){if(e.size!==n.size)return!1;for(const[t,o]of e){const e=n.get(t);if(!e)return!1;if(o.bail!==e.bail)return!1;if(!A(o.constFold,e.constFold))return!1;if(!P(o.narrow,e.narrow))return!1}return!0}function A(e,n){if(e.size!==n.size)return!1;for(const[t,o]of e)if(!n.has(t)||!Object.is(n.get(t),o))return!1;return!0}function P(e,n){if(e.size!==n.size)return!1;for(const[t,o]of e){const e=n.get(t);if(!e||o.length!==e.length)return!1;for(let n=0;n<o.length;n++)if(!Object.is(o[n],e[n]))return!1}return!0}function C(e,n){const t=new Map;for(const o of e.values()){const e=n.get(o.id);if(e.bail)continue;const r=g(o.ast.fragment,e.constFold,e.narrow);r.length>0&&t.set(o.id,r)}return t}async function D(e,n,t,s){const a=o(n,e),i=new Map,c=new Map,u=[];r(a.fragment,null,{SvelteOptions(e,{next:n}){for(const n of e.attributes??[])"Attribute"!==n.type||"accessors"!==n.name&&"customElement"!==n.name||u.push(`<svelte:options ${n.name}>`);n()}});let f,l,p=null,d=!1;const m=new Set,v=a.instance;if(v){for(const n of function*(e){const n=e.content;for(const e of n?.body??[]){if("ImportDeclaration"!==e.type)continue;const n=e.source?.value;if("string"==typeof n)for(const t of e.specifiers??[]){const e=t.local?.name;e&&("ImportDefaultSpecifier"===t.type?yield{value:n,local:e,imported:"default"}:"ImportNamespaceSpecifier"===t.type?yield{value:n,local:e,imported:"*"}:"ImportSpecifier"===t.type&&(yield{value:n,local:e,imported:q(t)??e}))}}}(v)){if(m.add(n.local),"default"===n.imported&&y(n.value)){const o=await t(n.value,e);o&&i.set(n.local,o);continue}const o=await W(n.value,n.imported,e,t,s);o&&c.set(n.local,o)}const n=function(e){const n=e.content;for(const e of n?.body??[])if("VariableDeclaration"===e.type)for(const n of e.declarations??[]){const t=n.init,o=n.id;if("CallExpression"===t?.type&&"Identifier"===t.callee?.type&&"$props"===t.callee.name&&"ObjectPattern"===o?.type)return{declaration:e,pattern:o,sharesStatement:(e.declarations?.length??1)>1}}return null}(v);if(n){f=n.declaration,l=n.pattern,n.sharesStatement&&u.push("$props() shares a multi-declarator statement"),p=[];for(const e of n.pattern.properties??[]){if("RestElement"===e.type){d=!0;continue}if("Property"!==e.type)continue;const n=e.key;if("Identifier"!==n?.type||!n.name)continue;const t=e.value,o="AssignmentPattern"===t?.type?t.right:void 0;p.push({name:n.name,property:e,defaultExpr:o})}}}const h=function(e,n){const t=[];return r(e.fragment,null,{Component(e,{next:o}){const r=e.name?n.get(e.name):void 0;r&&t.push({childId:r,node:e}),o()}}),t}(a,i),w=function(e,n){const t=new Set;return 0===n.size||r(e.fragment,null,{Component(e,{next:o}){const r=e.name?n.get(e.name):void 0;r&&t.add(r),o()}}),t}(a,c),{shadowedNames:g,debugNames:k}=function(e,n,t){const o=new Set,s=new Set;n&&r(n,null,{_(e,{next:n}){if("VariableDeclarator"!==e.type&&"FunctionDeclaration"!==e.type||e===t||"Identifier"!==e.id?.type||!e.id.name||o.add(e.id.name),"FunctionDeclaration"===e.type||"FunctionExpression"===e.type||"ArrowFunctionExpression"===e.type)for(const n of e.params??[])F(n,o);n()}});return r(e.fragment,null,{EachBlock(e,{next:n}){F(e.context,o),"string"==typeof e.index&&o.add(e.index),n()},SnippetBlock(e,{next:n}){"Identifier"===e.expression?.type&&e.expression.name&&o.add(e.expression.name);for(const n of e.parameters??[])F(n,o);n()},AwaitBlock(e,{next:n}){F(e.value,o),F(e.error,o),n()},LetDirective(e,{next:n}){e.name&&o.add(e.name),n()},ConstTag(e,{next:n}){for(const n of e.declaration?.declarations??[])F(n.id,o);n()},DebugTag(e,{next:n}){for(const n of e.identifiers??[])"Identifier"===n.type&&n.name&&s.add(n.name);n()}}),{shadowedNames:o,debugNames:s}}(a,v,f),x=function(e,n,t){const o=new Set,s=e=>{if(!e)return;const t=n.get(e);t&&o.add(t)};r(e.fragment,{parent:null},{_(e,{state:n,next:o}){"Identifier"===e.type&&e.name&&t.has(e.name)&&z(e,n.parent)&&s(e.name),o({parent:e})}}),e.instance&&r(e.instance,{parent:null},{_(e,{state:t,next:o}){"Identifier"===e.type&&e.name&&n.has(e.name)&&z(e,t.parent)&&!N(t.parent)&&s(e.name),o({parent:e})}});return o}(a,i,m);return{id:e,code:n,ast:a,imports:i,props:p,propsDeclaration:f,propsPattern:l,hasRestProp:d,childCalls:h,shadowedNames:g,debugNames:k,escapedComponents:x,barrelChildIds:w,bailReasons:u}}function F(e,n){if(e)switch(e.type){case"Identifier":return void(e.name&&n.add(e.name));case"ObjectPattern":for(const t of e.properties??[])"RestElement"===t.type?F(t.argument,n):"Property"===t.type&&F(t.value??t.key,n);return;case"ArrayPattern":for(const t of e.elements??[])F(t,n);return;case"AssignmentPattern":return void F(e.left,n);case"RestElement":return void F(e.argument,n);default:return}}function z(e,n){return!!n&&(!("MemberExpression"===n.type&&n.property===e&&!n.computed)&&(!("Property"===n.type&&n.key===e&&!n.computed&&!0!==n.shorthand)&&!N(n)))}function N(e){return null!=e&&("ImportSpecifier"===e.type||"ImportDefaultSpecifier"===e.type||"ImportNamespaceSpecifier"===e.type||"ExportSpecifier"===e.type)}function R(e){const n=e.attributes??[];let t=-1;for(let e=0;e<n.length;e++)"SpreadAttribute"===n[e].type&&(t=e);const o=new Map;for(let e=0;e<n.length;e++){const r=n[e],s=r.name;if("BindDirective"===r.type){s&&o.set(s,L(e,t));continue}if("Attribute"!==r.type||!s)continue;const a=$(r.value);o.set(s,a.known?{value:a.value,dynamic:!1,afterLastSpread:e>t}:L(e,t))}for(const r of function(e){const n=e.fragment?.nodes??[],t=[];let o=!1;for(const e of n)if("SnippetBlock"!==e.type){if("Comment"!==e.type){if("Text"===e.type){if(""===(e.data??e.raw??"").trim())continue}o=!0}}else"Identifier"===e.expression?.type&&e.expression.name&&t.push(e.expression.name);o&&t.push("children");return t}(e))o.set(r,L(n.length,t));return{hadSpread:t>=0,explicit:o}}function L(e,n){return{value:void 0,dynamic:!0,afterLastSpread:e>n}}function $(e){if(!0===e)return{known:!0,value:!0};if(null==e)return{known:!1};const n=Array.isArray(e)?e:[e];if(1===n.length){const e=n[0];return"Text"===e.type?{known:!0,value:e.data??e.raw??""}:"ExpressionTag"===e.type&&"Literal"===e.expression?.type?{known:!0,value:e.expression.value}:{known:!1}}let t="";for(const e of n){if("Text"!==e.type)return{known:!1};t+=e.data??e.raw??""}return{known:!0,value:t}}function O(e,n){return e.shadowedNames.has(n)||e.debugNames.has(n)}function T(e,n){const t={id:e.id,bail:!1,reasons:[],constFold:new Map,narrow:new Map,valueSets:new Map};if(e.bailReasons.length>0)return t.bail=!0,t.reasons.push(...e.bailReasons),t;if(!e.props||0===e.props.length)return t;const o=n?.sites??[];if(0===o.length)return t;for(const n of e.props){if(O(e,n.name))continue;const r=_(n,o);t.valueSets.set(n.name,r),r.top||r.dynamic||(1!==r.values.length?r.values.length>=2&&t.narrow.set(n.name,r.values):t.constFold.set(n.name,r.values[0]))}return t}function _(e,n){const t=[];let o=!1,r=!1;const s=e=>{t.some((n=>Object.is(n,e)))||t.push(e)};for(const t of n){const n=t.explicit.get(e.name);if(n?.afterLastSpread){n.dynamic?o=!0:s(n.value);continue}if(t.hadSpread){r=!0;continue}const a=B(e.defaultExpr);a.known?s(a.value):o=!0}return{values:t,dynamic:o,top:r}}function B(e){return e?"Literal"===e.type?{known:!0,value:e.value}:"Identifier"===e.type&&"undefined"===e.name?{known:!0,value:void 0}:{known:!1}:{known:!0,value:void 0}}function q(e){const n=e.imported;return"Identifier"===n?.type&&n.name?n.name:"Literal"===n?.type&&"string"==typeof n.value?n.value:void 0}function j(e){return"Identifier"===e?.type&&e.name?e.name:"Literal"===e?.type&&"string"==typeof e.value?e.value:void 0}const V=8;async function W(e,n,t,r,s,a=0){if(a>V)return null;const i=await r(e,t);if(!i)return null;if(y(e)||y(i))return"default"===n||"*"===n?i:null;let c;try{c=await s(i)}catch{return null}const u=function(e,n){try{const t=o(`<script module>\n${e}\n<\/script>`,n);return t.module?.content?.body??null}catch{return null}}(c,i);if(!u)return null;for(const e of u)if("ExportNamedDeclaration"===e.type&&e.source?.value){for(const t of e.specifiers??[])if(j(t.exported)===n)return W(String(e.source.value),j(t.local)??"default",i,r,s,a+1)}else if("ExportNamedDeclaration"!==e.type||e.source){if("ExportAllDeclaration"===e.type&&e.source?.value){const t=await W(String(e.source.value),n,i,r,s,a+1);if(t)return t}}else for(const t of e.specifiers??[]){if(j(t.exported)!==n)continue;const e=j(t.local);if(!e)continue;const o=U(u,e);return o?W(o.value,o.imported,i,r,s,a+1):null}return null}function U(e,n){for(const t of e){if("ImportDeclaration"!==t.type)continue;const e=t.source?.value;if("string"==typeof e)for(const o of t.specifiers??[])if(o.local?.name===n){if("ImportDefaultSpecifier"===o.type)return{value:e,imported:"default"};if("ImportNamespaceSpecifier"===o.type)return{value:e,imported:"*"};if("ImportSpecifier"===o.type)return{value:e,imported:q(o)??n}}}return null}const J=64;const X=Symbol("unbounded-class-source");function G(e,n,t){if(!0===e)return X;if(null==e)return new Set;const o=Array.isArray(e)?e:[e];let r=[""];for(const e of o){const o=H(e,n,t);if(o===X)return X;const s=[];for(const e of r)for(const n of o)if(s.push(e+n),s.length>J)return X;r=s}const s=new Set;for(const e of r)for(const n of e.split(/\s+/))n&&s.add(n);return s}function H(e,n,t){return"Text"===e.type?new Set([e.data??e.raw??""]):"ExpressionTag"===e.type?function(e,n,t){if(!e)return X;if("Identifier"===e.type&&e.name&&t.has(e.name)){const n=new Set;for(const o of t.get(e.name))n.add(K(o));return n}const o=a(e,n);return o.known?new Set([K(o.value)]):X}(e.expression,n,t):X}function K(e){return String(e)}function Q(e,n,t){const o=e.ast.css;if(!o||!o.children)return 0;const s=function(e,n){const t=new Set;let o=!1;const s=n.constFold,a=n.narrow;return r(e.ast.fragment,null,{_(e,{next:n}){if("RegularElement"===(r=e.type)||"SvelteElement"===r||"Component"===r||"SvelteComponent"===r||"SvelteSelf"===r){var r;for(const n of e.attributes??[]){if("SpreadAttribute"===n.type){o=!0;continue}if("ClassDirective"===n.type){n.name&&t.add(n.name);continue}if("Attribute"!==n.type||"class"!==n.name)continue;const e=G(n.value,s,a);if(e===X)o=!0;else for(const n of e)t.add(n)}n()}else n()}}),{classes:t,unbounded:o}}(e,n);if(s.unbounded)return 0;let a=0;for(const n of o.children)"Rule"===n.type&&Y(n,s.classes)&&(Z(e.code,n,o.children,t),a+=1);return a}function Y(e,n){if(function(e){let n=!1;return r(e,null,{_(e,{next:t}){"PseudoClassSelector"===e.type&&"global"===e.name&&(n=!0),t()}}),n}(e))return!1;const t=e.prelude?.children??[];return 0!==t.length&&t.every((e=>function(e,n){let t=!1;for(const o of e.children??[])for(const e of o.selectors??[])"ClassSelector"===e.type&&e.name&&!n.has(e.name)&&(t=!0);return t}(e,n)))}function Z(e,n,t,o){const r=t.indexOf(n),s=t[r-1];let a=n.start;const i=s?s.end:0;for(;a>i&&/\s/.test(e[a-1]);)a-=1;o.remove(a,n.end)}function ee(e,n){return te(e,ne(e,n))}function ne(e,n){const o=new Map,r=new Map;for(const s of e.values()){const e=new t(s.code);o.set(s.id,e);const a=n.get(s.id);r.set(s.id,a.bail?new Set:ie(s,a,e))}for(const n of e.values())de(n,r,o.get(n.id));return o}function te(e,n){const t={};for(const o of e.values())t[o.id]=n.get(o.id).toString();return t}function oe(e,n,t,o){const r=ne(e,n);return function(e,n,t,o){const r=new Map;for(const e of n){const n=r.get(e.owner);n?n.push(e):r.set(e.owner,[e])}for(const[n,s]of r){const r=e.get(n),a=o.get(n);if(!r||!a)continue;const i=new Map,c=[];let u=0;for(const e of s){const n=e.node.name??"Cmp";let o=i.get(e.variantId);void 0===o&&(o=`${n}__shaker_v${u++}`,i.set(e.variantId,o),c.push({local:o,spec:t(e.variantId)})),re(r.code,e.node,o,e.foldedProps,a)}c.length>0&&ae(r,c,a)}}(e,t,o,r),te(e,r)}function re(e,n,t,o,r){const s=n.name;if(!s)return;const a=n.start+1;e.slice(a,a+s.length)===s&&r.overwrite(a,a+s.length,t);const i=`</${s}`,c=e.lastIndexOf(i,n.end);if(c>=n.start){const e=c+2;r.overwrite(e,e+s.length,t)}for(const t of n.attributes??[])"Attribute"===t.type&&t.name&&o.has(t.name)&&se(e,t,r)}function se(e,n,t){let o=n.start;" "!==e[o-1]&&"\t"!==e[o-1]||(o-=1),t.remove(o,n.end)}function ae(e,n,t){const o=n.map((e=>` import ${e.local} from ${JSON.stringify(e.spec)};`)).join("\n"),r=e.ast.instance,s=r?.content?.body??[];if(r&&s.length>0){const e=s[s.length-1];t.appendLeft(e.end,`\n${o}`)}else r&&r.content?t.appendLeft(r.content.start,`\n${o}\n`):t.prepend(`<script>\n${o}\n<\/script>\n`)}function ie(e,n,t){return ce(e,n.constFold,n.narrow,n,t)}function ce(e,n,t,o,s){if(0===n.size&&0===t.size)return new Set;const i=e.code,c=[];!function(e,n,t,o,s,a){r(e,null,{IfBlock(e,{next:r}){if(e.elseif||w(e,a))return;const i=p(e,n,t);if(function(e,n,t,o){if(e.kept)return void o.overwrite(e.span[0],e.span[1],function(e,n,t){const o=e?.nodes??[];return 0===o.length?"":ue(o[0].start,o[o.length-1].end,o,n,t)}(e.kept,n,t));for(const[n,t]of e.removed)o.remove(n,t);if(e.headerRewrite){const{from:n,to:t,text:r}=e.headerRewrite;o.overwrite(n,t,r)}}(i,n,o,s),i.kept)a.push(i.span);else for(const e of i.removed)a.push(e);i.recurse&&r()}})}(e.ast.fragment,n,t,i,s,c),function(e,n,t,o,s){if(0===n.size)return;r(e,null,{ConditionalExpression(e,{next:r}){if(w(e,s))return;const i=a(e.test,n);if(!i.known)return void r();const c=i.value?e.consequent:e.alternate;c?(o.overwrite(e.start,e.end,ue(c.start,c.end,[c],n,t)),s.push([e.start,e.end])):r()}})}(e.ast.fragment,n,i,s,c);const u=function(e,n,t){const o=new Map,s=s=>{s&&r(s,{parent:null},{_(r,{state:s,next:a}){"Identifier"===r.type&&r.name&&n.has(r.name)&&!w(r,t)&&r!==e.propsPattern&&!fe(r,s.parent)&&(o.get(r.name)??function(e,n){const t=[];return e.set(n,t),t}(o,r.name)).push([r.start,r.end]),a({parent:r})}})};return s(e.ast.instance),s(e.ast.fragment),o}(e,n,c);for(const[e,t]of n)for(const n of u.get(e)??[])s.overwrite(n[0],n[1],ve(t));const f=new Set(n.keys());!function(e,n,t){if(!e.props||0===n.size)return;const o=e.props.filter((e=>!n.has(e.name)));if(0===o.length&&!e.hasRestProp&&e.propsDeclaration)return void function(e,n,t){let o=n.start;for(;o>0&&"\n"!==e[o-1];)o-=1;let r=n.end;for(;r<e.length&&"\n"!==e[r];)r+=1;const s=e.slice(o,n.start),a=e.slice(n.end,r);/^\s*$/.test(s)&&/^\s*;?\s*$/.test(a)?t.remove(o,r<e.length?r+1:r):t.remove(n.start,";"===e[n.end]?n.end+1:n.end)}(e.code,e.propsDeclaration,t);const r=e.propsPattern?.properties??[];for(const o of e.props)n.has(o.name)&&(le(r,o.property,t),e.propsPattern&&pe(e.propsPattern,o.name,t))}(e,f,s);return Q(e,{...o,constFold:n,narrow:t},s),f}function ue(e,n,t,o,s){if(0===o.size)return s.slice(e,n);const a=[];for(const e of t)r(e,{parent:null},{_(e,{state:n,next:t}){"Identifier"===e.type&&e.name&&o.has(e.name)&&!fe(e,n.parent)&&a.push({start:e.start,end:e.end,name:e.name}),t({parent:e})}});if(0===a.length)return s.slice(e,n);a.sort(((e,n)=>e.start-n.start));let i="",c=e;for(const e of a)i+=s.slice(c,e.start),i+=ve(o.get(e.name)),c=e.end;return i+=s.slice(c,n),i}function fe(e,n){return!!n&&("MemberExpression"===n.type&&n.property===e&&!n.computed||("Property"===n.type&&n.key===e&&!n.computed&&!0!==n.shorthand||("ImportSpecifier"===n.type||"ImportDefaultSpecifier"===n.type||"ImportNamespaceSpecifier"===n.type||"ExportSpecifier"===n.type)))}function le(e,n,t){const o=e.indexOf(n),r=e[o+1],s=e[o-1];r?t.remove(n.start,r.start):s?t.remove(s.end,n.end):t.remove(n.start,n.end)}function pe(e,n,t){const o=e.typeAnnotation?.typeAnnotation?.members??[],r=o.findIndex((e=>"Identifier"===e.key?.type&&e.key.name===n));if(-1===r)return;const s=o[r],a=o[r+1],i=o[r-1];a?t.remove(s.start,a.start):i?t.remove(i.end,s.end):t.remove(s.start,s.end)}function de(e,n,t){r(e.ast.fragment,null,{Component(o,{next:r}){const s=o.name?e.imports.get(o.name):void 0,a=s?n.get(s):void 0;if(a&&a.size>0)for(const n of o.attributes??[])"Attribute"===n.type&&n.name&&a.has(n.name)&&me(n.value)&&se(e.code,n,t);r()}})}function me(e){if(!0===e||null==e)return!0;return(Array.isArray(e)?e:[e]).every((e=>"Text"===e.type||"ExpressionTag"===e.type&&"Literal"===e.expression?.type))}function ve(e){return void 0===e?"undefined":JSON.stringify(e)}const he={enabled:!1,maxVariants:8,minSavings:0};function we(n,t,o=he,r=[]){const s=new Map,a=[];if(!o.enabled)return{variants:s,bindings:a};const i=C(n,t),c=new Map,u=new Set;for(const e of n.values()){const o=i.get(e.id)??[];for(const r of e.childCalls){if(o.length>0&&w(r.node,o))continue;const s=n.get(r.childId),a=t.get(r.childId);if(!s||!a)continue;if(a.bail||!s.props||0===s.props.length){u.add(r.childId);continue}const i=ke(r.node,s,a);if(0===i.size){u.add(r.childId);continue}const f=xe(s,a,i);if(f===be(s,a)){u.add(r.childId);continue}const l=c.get(r.childId);l?l.push({owner:e.id,node:r.node,shape:i,code:f}):c.set(r.childId,[{owner:e.id,node:r.node,shape:i,code:f}])}}const f=function(e,n){const t=new Map;for(const o of e.values()){const e=n.get(o.id);t.set(o.id,e.bail?o.code:be(o,e))}return t}(n,t),l=new Map;for(const e of n.values())l.set(e.id,ye(f.get(e.id),e));const p=new Set;for(const e of l.values())for(const n of e)p.add(n);const d=(Array.isArray(r)?r:[r]).filter((e=>n.has(e))).filter((e=>!p.has(e))),m=new Map,v=(n,t)=>{const o=m.get(t);if(void 0!==o)return o;let r;try{const{js:o}=e.compile(t,{generate:"client",dev:!1,filename:n});r=o.code.length}catch{r=null}return null!==r&&m.set(t,r),r},h=new Set;for(const e of c.keys())u.has(e)||h.add(e);for(const[e,t]of c){if(u.has(e))continue;if(t.some((n=>n.owner!==e&&h.has(n.owner))))continue;const r=new Map,i=[];let c=!1;for(const n of t){if(r.has(n.code))continue;if(i.length>=o.maxVariants){c=!0;break}const t=`${e}::v${i.length}`;r.set(n.code,t),i.push({id:t,code:n.code})}if(!c&&ge(e,i,n,f,l,d,v,o.minSavings)){for(const n of i){const o=t.find((e=>e.code===n.code));s.set(n.id,{id:n.id,childId:e,code:n.code,foldedProps:o.shape})}for(const n of t)a.push({owner:n.owner,childId:e,node:n.node,variantId:r.get(n.code),foldedProps:n.shape})}}return{variants:s,bindings:a}}function ge(e,n,t,o,r,s,a,i){const c=t.get(e),u=new Map;for(const e of n)u.set(e.id,ye(e.code,c));const f=new Set,l=[...s];for(;l.length>0;){const e=l.pop();if(!f.has(e)){f.add(e);for(const n of r.get(e)??[])l.push(n)}}let p=0;for(const e of f){const n=a(e,o.get(e));if(null===n)return!1;p+=n}const d=n.map((e=>e.id)),m=new Set,v=new Set,h=n=>n===e?{comps:[],vars:d}:{comps:[n],vars:[]},w=[],g=[];for(const e of s){const n=h(e);w.push(...n.comps),g.push(...n.vars)}for(;w.length>0||g.length>0;){if(w.length>0){const e=w.pop();if(m.has(e))continue;m.add(e);for(const n of r.get(e)??[]){const e=h(n);w.push(...e.comps),g.push(...e.vars)}continue}const e=g.pop();if(!v.has(e)){v.add(e);for(const n of u.get(e)??[]){const e=h(n);w.push(...e.comps),g.push(...e.vars)}}}let y=0;for(const e of m){const n=a(e,o.get(e));if(null===n)return!1;y+=n}for(const e of v){const t=n.find((n=>n.id===e)).code,o=a(e,t);if(null===o)return!1;y+=o}return y<p*(1-i)}function ye(e,n){let t;try{t=o(e,n.id)}catch{return[]}const s=[];return r(t.fragment,null,{Component(e,{next:t}){const o=e.name?n.imports.get(e.name):void 0;o&&s.push(o),t()}}),s}function ke(e,n,t){const o=R(e),r=new Map;for(const e of n.props??[])r.set(e.name,e);const s=new Map;for(const[e,a]of o.explicit)r.has(e)&&(t.constFold.has(e)||O(n,e)||!a.dynamic&&a.afterLastSpread&&s.set(e,a.value));return s}function xe(e,n,o){const r=new Map(n.constFold);for(const[e,n]of o)r.set(e,n);const s=new Map;for(const[e,t]of n.narrow)r.has(e)||s.set(e,t);const a=new t(e.code);return ce(e,r,s,n,a),a.toString()}const Se=new WeakMap;function be(e,n){const o=Se.get(e);if(void 0!==o)return o;const r=new t(e.code);ce(e,n.constFold,n.narrow,n,r);const s=r.toString();return Se.set(e,s),s}exports.DEFAULT_MONO_OPTIONS=he,exports.analyze=b,exports.monomorphize=we,exports.svelteShaker=async function(e,n,t){const{models:o,plans:r}=await b(e,n,t);return ee(o,r)},exports.svelteShakerWithMono=async function(e,n,t,o=he,r=(e=>e)){const{models:s,plans:a}=await b(e,n,t),i=we(s,a,o,e);return{files:0===i.bindings.length?ee(s,a):oe(s,a,i.bindings.map((e=>({owner:e.owner,node:e.node,variantId:e.variantId,foldedProps:e.foldedProps}))),r),mono:i}},exports.transformAll=ee,exports.transformAllWithMono=oe;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{parse as e,compile as n}from"svelte/compiler";import{walk as t}from"zimmerframe";import o from"magic-string";function r(n,t){return e(n,{modern:!0,filename:t})}function s(e,n,o){t(e,n,o)}const a={known:!1};function i(e,n){if(!e)return a;switch(e.type){case"Literal":return{known:!0,value:e.value};case"Identifier":{const t=e.name??"";return"undefined"===t?{known:!0,value:void 0}:n.has(t)?{known:!0,value:n.get(t)}:a}case"UnaryExpression":{const t=i(e.argument,n);if(!t.known)return a;const o=t.value;switch(e.operator){case"!":return{known:!0,value:!o};case"-":return{known:!0,value:-o};case"+":return{known:!0,value:+o};case"typeof":return{known:!0,value:typeof o};case"void":return{known:!0,value:void 0};default:return a}}case"LogicalExpression":{const t=i(e.left,n);if(!t.known)return a;switch(e.operator){case"&&":return t.value?i(e.right,n):t;case"||":return t.value?t:i(e.right,n);case"??":return null===t.value||void 0===t.value?i(e.right,n):t;default:return a}}case"BinaryExpression":{const t=i(e.left,n),o=i(e.right,n);if(!t.known||!o.known)return a;const r=t.value,s=o.value;switch(e.operator){case"===":return{known:!0,value:r===s};case"!==":return{known:!0,value:r!==s};case"==":return{known:!0,value:r==s};case"!=":return{known:!0,value:r!=s};case"<":return{known:!0,value:r<s};case">":return{known:!0,value:r>s};case"<=":return{known:!0,value:r<=s};case">=":return{known:!0,value:r>=s};case"+":return{known:!0,value:r+s};case"-":return{known:!0,value:r-s};case"*":return{known:!0,value:r*s};case"/":return{known:!0,value:r/s};case"%":return{known:!0,value:r%s};default:return a}}default:return a}}function c(e,n,t){const o=i(e,n);if(o.known)return o;const r=u(e,n,t);return"unknown"===r?a:{known:!0,value:r}}function u(e,n,t){if(!e)return"unknown";switch(e.type){case"UnaryExpression":return"!"===e.operator?p(u(e.argument,n,t)):"unknown";case"LogicalExpression":{const s=u(e.left,n,t),a=()=>u(e.right,n,t);return"&&"===e.operator?!1!==s&&(o=s,r=a(),!1!==o&&!1!==r&&(!0===o&&!0===r||"unknown")):"||"===e.operator?!0===s||function(e,n){return!0===e||!0===n||(!1!==e||!1!==n)&&"unknown"}(s,a()):"unknown"}case"BinaryExpression":{const o=e.operator;if("==="===o||"=="===o||"!=="===o||"!="===o){const r="=="===o||"!="===o,s=function(e,n,t,o,r){const s=f(e,o),a=i(n,t);if(s&&a.known)return l(s,a.value,r);const c=f(n,o),u=i(e,t);return c&&u.known?l(c,u.value,r):u.known&&a.known?r?u.value==a.value:u.value===a.value:"unknown"}(e.left,e.right,n,t,r);return"!=="===o||"!="===o?p(s):s}return"unknown"}default:return"unknown"}var o,r}function f(e,n){return"Identifier"===e?.type&&e.name&&n.has(e.name)?n.get(e.name):null}function l(e,n,t){const o=t?e=>e==n:e=>e===n;return!!e.some(o)&&(!!e.every(o)||"unknown")}function p(e){return!0!==e&&(!1===e||"unknown")}function d(e,n,t){const{arms:o,elseFrag:r}=function(e){const n=[];let t,o=e;for(;o;){n.push({block:o,test:o.test,consequent:o.consequent});const e=o.alternate,r="Fragment"===e?.type&&1===e.nodes?.length&&"IfBlock"===e.nodes[0]?.type&&!0===e.nodes[0].elseif?e.nodes[0]:void 0;r?o=r:("Fragment"===e?.type&&(t=e),o=void 0)}return t?{arms:n,elseFrag:t}:{arms:n}}(e),s=[e.start,e.end],a=o.map((e=>c(e.test,n,t))),i=e=>e.known&&Boolean(e.value),u=e=>e.known&&!e.value;let f=!0;for(let e=0;e<o.length;e++){const n=a[e];if(i(n)&&f){const n=o[e].consequent;return{span:s,kept:n,removed:m(s,v(n)),recurse:!1}}u(n)||(f=!1)}const l=a.findIndex((e=>!u(e)));if(-1===l)return r?{span:s,kept:r,removed:m(s,v(r)),recurse:!1}:{span:s,kept:void 0,removed:[s],recurse:!1};if(0===l)return{span:s,kept:void 0,removed:h(o,a,l),recurse:!0};const p=o[l].block;return{span:s,kept:void 0,removed:[[s[0],p.start],...h(o,a,l)],recurse:!1,headerRewrite:{from:p.start,to:p.test.start,text:"{#if "}}}function m(e,n){if(!n)return[e];const t=[];return e[0]<n[0]&&t.push([e[0],n[0]]),n[1]<e[1]&&t.push([n[1],e[1]]),t}function v(e){const n=e?.nodes??[];return 0===n.length?null:[n[0].start,n[n.length-1].end]}function h(e,n,t){const o=[];for(let r=t+1;r<e.length;r++){const t=n[r];if(!t.known||t.value)continue;const s=e[r],a=e[r+1]?.block,i=a?a.start:w(s.consequent,s.block.end);o.push([s.block.start,i])}return o}function w(e,n){const t=e?.nodes??[];return t.length?t[t.length-1].end:n}function g(e,n){return n.some((([n,t])=>e.start>=n&&e.end<=t))}function y(e,n,t){if(0===n.size&&0===t.size)return[];const o=[];return s(e,null,{IfBlock(e,{next:r}){if(e.elseif||g(e,o))return;const s=d(e,n,t);for(const e of s.removed)o.push(e);s.recurse&&r()}}),o}const k=e=>e.endsWith(".svelte"),x=10,b="escapes as value (e.g. <svelte:component this={X}>)",S="rendered through a barrel/named import (call sites unobservable)";async function I(e,n,t){const o=await async function(e,n,t){const o=new Map,r=Array.isArray(e)?[...e]:[e],s=new Set(r);for(;r.length>0;){const e=r.shift(),a=await t(e),i=await F(e,a,n,t);o.set(e,i);for(const e of[...i.imports.values(),...i.barrelChildIds])s.has(e)||(s.add(e),r.push(e))}return o}(e,n,t),r=new Set;for(const e of o.values())for(const n of e.escapedComponents)r.add(n);for(const e of r){const n=o.get(e);n&&!n.bailReasons.includes(b)&&n.bailReasons.push(b)}const s=new Set;for(const e of o.values())for(const n of e.barrelChildIds)s.add(n);for(const e of s){const n=o.get(e);n&&!n.bailReasons.includes(S)&&n.bailReasons.push(S)}let a=E(o,M(o,new Map));for(let e=0;e<x;e++){const e=E(o,M(o,D(o,a)));if(A(a,e)){a=e;break}a=e}return{models:o,plans:a}}function M(e,n){const t=new Map,o=e=>{let n=t.get(e);return n||(n={sites:[]},t.set(e,n)),n};for(const t of e.values()){const e=n.get(t.id)??[];for(const n of t.childCalls)e.length>0&&g(n.node,e)||o(n.childId).sites.push($(n.node))}return t}function E(e,n){const t=new Map;for(const o of e.values())t.set(o.id,T(o,n.get(o.id)));return t}function A(e,n){if(e.size!==n.size)return!1;for(const[t,o]of e){const e=n.get(t);if(!e)return!1;if(o.bail!==e.bail)return!1;if(!C(o.constFold,e.constFold))return!1;if(!P(o.narrow,e.narrow))return!1}return!0}function C(e,n){if(e.size!==n.size)return!1;for(const[t,o]of e)if(!n.has(t)||!Object.is(n.get(t),o))return!1;return!0}function P(e,n){if(e.size!==n.size)return!1;for(const[t,o]of e){const e=n.get(t);if(!e||o.length!==e.length)return!1;for(let n=0;n<o.length;n++)if(!Object.is(o[n],e[n]))return!1}return!0}function D(e,n){const t=new Map;for(const o of e.values()){const e=n.get(o.id);if(e.bail)continue;const r=y(o.ast.fragment,e.constFold,e.narrow);r.length>0&&t.set(o.id,r)}return t}async function F(e,n,t,o){const a=r(n,e),i=new Map,c=new Map,u=[];s(a.fragment,null,{SvelteOptions(e,{next:n}){for(const n of e.attributes??[])"Attribute"!==n.type||"accessors"!==n.name&&"customElement"!==n.name||u.push(`<svelte:options ${n.name}>`);n()}});let f,l,p=null,d=!1;const m=new Set,v=a.instance;if(v){for(const n of function*(e){const n=e.content;for(const e of n?.body??[]){if("ImportDeclaration"!==e.type)continue;const n=e.source?.value;if("string"==typeof n)for(const t of e.specifiers??[]){const e=t.local?.name;e&&("ImportDefaultSpecifier"===t.type?yield{value:n,local:e,imported:"default"}:"ImportNamespaceSpecifier"===t.type?yield{value:n,local:e,imported:"*"}:"ImportSpecifier"===t.type&&(yield{value:n,local:e,imported:q(t)??e}))}}}(v)){if(m.add(n.local),"default"===n.imported&&k(n.value)){const o=await t(n.value,e);o&&i.set(n.local,o);continue}const r=await U(n.value,n.imported,e,t,o);r&&c.set(n.local,r)}const n=function(e){const n=e.content;for(const e of n?.body??[])if("VariableDeclaration"===e.type)for(const n of e.declarations??[]){const t=n.init,o=n.id;if("CallExpression"===t?.type&&"Identifier"===t.callee?.type&&"$props"===t.callee.name&&"ObjectPattern"===o?.type)return{declaration:e,pattern:o,sharesStatement:(e.declarations?.length??1)>1}}return null}(v);if(n){f=n.declaration,l=n.pattern,n.sharesStatement&&u.push("$props() shares a multi-declarator statement"),p=[];for(const e of n.pattern.properties??[]){if("RestElement"===e.type){d=!0;continue}if("Property"!==e.type)continue;const n=e.key;if("Identifier"!==n?.type||!n.name)continue;const t=e.value,o="AssignmentPattern"===t?.type?t.right:void 0;p.push({name:n.name,property:e,defaultExpr:o})}}}const h=function(e,n){const t=[];return s(e.fragment,null,{Component(e,{next:o}){const r=e.name?n.get(e.name):void 0;r&&t.push({childId:r,node:e}),o()}}),t}(a,i),w=function(e,n){const t=new Set;return 0===n.size||s(e.fragment,null,{Component(e,{next:o}){const r=e.name?n.get(e.name):void 0;r&&t.add(r),o()}}),t}(a,c),{shadowedNames:g,debugNames:y}=function(e,n,t){const o=new Set,r=new Set;n&&s(n,null,{_(e,{next:n}){if("VariableDeclarator"!==e.type&&"FunctionDeclaration"!==e.type||e===t||"Identifier"!==e.id?.type||!e.id.name||o.add(e.id.name),"FunctionDeclaration"===e.type||"FunctionExpression"===e.type||"ArrowFunctionExpression"===e.type)for(const n of e.params??[])z(n,o);n()}});return s(e.fragment,null,{EachBlock(e,{next:n}){z(e.context,o),"string"==typeof e.index&&o.add(e.index),n()},SnippetBlock(e,{next:n}){"Identifier"===e.expression?.type&&e.expression.name&&o.add(e.expression.name);for(const n of e.parameters??[])z(n,o);n()},AwaitBlock(e,{next:n}){z(e.value,o),z(e.error,o),n()},LetDirective(e,{next:n}){e.name&&o.add(e.name),n()},ConstTag(e,{next:n}){for(const n of e.declaration?.declarations??[])z(n.id,o);n()},DebugTag(e,{next:n}){for(const n of e.identifiers??[])"Identifier"===n.type&&n.name&&r.add(n.name);n()}}),{shadowedNames:o,debugNames:r}}(a,v,f),x=function(e,n,t){const o=new Set,r=e=>{if(!e)return;const t=n.get(e);t&&o.add(t)};s(e.fragment,{parent:null},{_(e,{state:n,next:o}){"Identifier"===e.type&&e.name&&t.has(e.name)&&R(e,n.parent)&&r(e.name),o({parent:e})}}),e.instance&&s(e.instance,{parent:null},{_(e,{state:t,next:o}){"Identifier"===e.type&&e.name&&n.has(e.name)&&R(e,t.parent)&&!N(t.parent)&&r(e.name),o({parent:e})}});return o}(a,i,m);return{id:e,code:n,ast:a,imports:i,props:p,propsDeclaration:f,propsPattern:l,hasRestProp:d,childCalls:h,shadowedNames:g,debugNames:y,escapedComponents:x,barrelChildIds:w,bailReasons:u}}function z(e,n){if(e)switch(e.type){case"Identifier":return void(e.name&&n.add(e.name));case"ObjectPattern":for(const t of e.properties??[])"RestElement"===t.type?z(t.argument,n):"Property"===t.type&&z(t.value??t.key,n);return;case"ArrayPattern":for(const t of e.elements??[])z(t,n);return;case"AssignmentPattern":return void z(e.left,n);case"RestElement":return void z(e.argument,n);default:return}}function R(e,n){return!!n&&(!("MemberExpression"===n.type&&n.property===e&&!n.computed)&&(!("Property"===n.type&&n.key===e&&!n.computed&&!0!==n.shorthand)&&!N(n)))}function N(e){return null!=e&&("ImportSpecifier"===e.type||"ImportDefaultSpecifier"===e.type||"ImportNamespaceSpecifier"===e.type||"ExportSpecifier"===e.type)}function $(e){const n=e.attributes??[];let t=-1;for(let e=0;e<n.length;e++)"SpreadAttribute"===n[e].type&&(t=e);const o=new Map;for(let e=0;e<n.length;e++){const r=n[e],s=r.name;if("BindDirective"===r.type){s&&o.set(s,L(e,t));continue}if("Attribute"!==r.type||!s)continue;const a=B(r.value);o.set(s,a.known?{value:a.value,dynamic:!1,afterLastSpread:e>t}:L(e,t))}for(const r of function(e){const n=e.fragment?.nodes??[],t=[];let o=!1;for(const e of n)if("SnippetBlock"!==e.type){if("Comment"!==e.type){if("Text"===e.type){if(""===(e.data??e.raw??"").trim())continue}o=!0}}else"Identifier"===e.expression?.type&&e.expression.name&&t.push(e.expression.name);o&&t.push("children");return t}(e))o.set(r,L(n.length,t));return{hadSpread:t>=0,explicit:o}}function L(e,n){return{value:void 0,dynamic:!0,afterLastSpread:e>n}}function B(e){if(!0===e)return{known:!0,value:!0};if(null==e)return{known:!1};const n=Array.isArray(e)?e:[e];if(1===n.length){const e=n[0];return"Text"===e.type?{known:!0,value:e.data??e.raw??""}:"ExpressionTag"===e.type&&"Literal"===e.expression?.type?{known:!0,value:e.expression.value}:{known:!1}}let t="";for(const e of n){if("Text"!==e.type)return{known:!1};t+=e.data??e.raw??""}return{known:!0,value:t}}function O(e,n){return e.shadowedNames.has(n)||e.debugNames.has(n)}function T(e,n){const t={id:e.id,bail:!1,reasons:[],constFold:new Map,narrow:new Map,valueSets:new Map};if(e.bailReasons.length>0)return t.bail=!0,t.reasons.push(...e.bailReasons),t;if(!e.props||0===e.props.length)return t;const o=n?.sites??[];if(0===o.length)return t;for(const n of e.props){if(O(e,n.name))continue;const r=_(n,o);t.valueSets.set(n.name,r),r.top||r.dynamic||(1!==r.values.length?r.values.length>=2&&t.narrow.set(n.name,r.values):t.constFold.set(n.name,r.values[0]))}return t}function _(e,n){const t=[];let o=!1,r=!1;const s=e=>{t.some((n=>Object.is(n,e)))||t.push(e)};for(const t of n){const n=t.explicit.get(e.name);if(n?.afterLastSpread){n.dynamic?o=!0:s(n.value);continue}if(t.hadSpread){r=!0;continue}const a=j(e.defaultExpr);a.known?s(a.value):o=!0}return{values:t,dynamic:o,top:r}}function j(e){return e?"Literal"===e.type?{known:!0,value:e.value}:"Identifier"===e.type&&"undefined"===e.name?{known:!0,value:void 0}:{known:!1}:{known:!0,value:void 0}}function q(e){const n=e.imported;return"Identifier"===n?.type&&n.name?n.name:"Literal"===n?.type&&"string"==typeof n.value?n.value:void 0}function V(e){return"Identifier"===e?.type&&e.name?e.name:"Literal"===e?.type&&"string"==typeof e.value?e.value:void 0}const J=8;async function U(e,n,t,o,s,a=0){if(a>J)return null;const i=await o(e,t);if(!i)return null;if(k(e)||k(i))return"default"===n||"*"===n?i:null;let c;try{c=await s(i)}catch{return null}const u=function(e,n){try{const t=r(`<script module>\n${e}\n<\/script>`,n);return t.module?.content?.body??null}catch{return null}}(c,i);if(!u)return null;for(const e of u)if("ExportNamedDeclaration"===e.type&&e.source?.value){for(const t of e.specifiers??[])if(V(t.exported)===n)return U(String(e.source.value),V(t.local)??"default",i,o,s,a+1)}else if("ExportNamedDeclaration"!==e.type||e.source){if("ExportAllDeclaration"===e.type&&e.source?.value){const t=await U(String(e.source.value),n,i,o,s,a+1);if(t)return t}}else for(const t of e.specifiers??[]){if(V(t.exported)!==n)continue;const e=V(t.local);if(!e)continue;const r=W(u,e);return r?U(r.value,r.imported,i,o,s,a+1):null}return null}function W(e,n){for(const t of e){if("ImportDeclaration"!==t.type)continue;const e=t.source?.value;if("string"==typeof e)for(const o of t.specifiers??[])if(o.local?.name===n){if("ImportDefaultSpecifier"===o.type)return{value:e,imported:"default"};if("ImportNamespaceSpecifier"===o.type)return{value:e,imported:"*"};if("ImportSpecifier"===o.type)return{value:e,imported:q(o)??n}}}return null}const X=64;const G=Symbol("unbounded-class-source");function H(e,n,t){if(!0===e)return G;if(null==e)return new Set;const o=Array.isArray(e)?e:[e];let r=[""];for(const e of o){const o=K(e,n,t);if(o===G)return G;const s=[];for(const e of r)for(const n of o)if(s.push(e+n),s.length>X)return G;r=s}const s=new Set;for(const e of r)for(const n of e.split(/\s+/))n&&s.add(n);return s}function K(e,n,t){return"Text"===e.type?new Set([e.data??e.raw??""]):"ExpressionTag"===e.type?function(e,n,t){if(!e)return G;if("Identifier"===e.type&&e.name&&t.has(e.name)){const n=new Set;for(const o of t.get(e.name))n.add(Q(o));return n}const o=i(e,n);return o.known?new Set([Q(o.value)]):G}(e.expression,n,t):G}function Q(e){return String(e)}function Y(e,n,t){const o=e.ast.css;if(!o||!o.children)return 0;const r=function(e,n){const t=new Set;let o=!1;const r=n.constFold,a=n.narrow;return s(e.ast.fragment,null,{_(e,{next:n}){if("RegularElement"===(s=e.type)||"SvelteElement"===s||"Component"===s||"SvelteComponent"===s||"SvelteSelf"===s){var s;for(const n of e.attributes??[]){if("SpreadAttribute"===n.type){o=!0;continue}if("ClassDirective"===n.type){n.name&&t.add(n.name);continue}if("Attribute"!==n.type||"class"!==n.name)continue;const e=H(n.value,r,a);if(e===G)o=!0;else for(const n of e)t.add(n)}n()}else n()}}),{classes:t,unbounded:o}}(e,n);if(r.unbounded)return 0;let a=0;for(const n of o.children)"Rule"===n.type&&Z(n,r.classes)&&(ee(e.code,n,o.children,t),a+=1);return a}function Z(e,n){if(function(e){let n=!1;return s(e,null,{_(e,{next:t}){"PseudoClassSelector"===e.type&&"global"===e.name&&(n=!0),t()}}),n}(e))return!1;const t=e.prelude?.children??[];return 0!==t.length&&t.every((e=>function(e,n){let t=!1;for(const o of e.children??[])for(const e of o.selectors??[])"ClassSelector"===e.type&&e.name&&!n.has(e.name)&&(t=!0);return t}(e,n)))}function ee(e,n,t,o){const r=t.indexOf(n),s=t[r-1];let a=n.start;const i=s?s.end:0;for(;a>i&&/\s/.test(e[a-1]);)a-=1;o.remove(a,n.end)}function ne(e,n){return oe(e,te(e,n))}function te(e,n){const t=new Map,r=new Map;for(const s of e.values()){const e=new o(s.code);t.set(s.id,e);const a=n.get(s.id);r.set(s.id,a.bail?new Set:ce(s,a,e))}for(const n of e.values())me(n,r,t.get(n.id));return t}function oe(e,n){const t={};for(const o of e.values())t[o.id]=n.get(o.id).toString();return t}function re(e,n,t,o){const r=te(e,n);return function(e,n,t,o){const r=new Map;for(const e of n){const n=r.get(e.owner);n?n.push(e):r.set(e.owner,[e])}for(const[n,s]of r){const r=e.get(n),a=o.get(n);if(!r||!a)continue;const i=new Map,c=[];let u=0;for(const e of s){const n=e.node.name??"Cmp";let o=i.get(e.variantId);void 0===o&&(o=`${n}__shaker_v${u++}`,i.set(e.variantId,o),c.push({local:o,spec:t(e.variantId)})),se(r.code,e.node,o,e.foldedProps,a)}c.length>0&&ie(r,c,a)}}(e,t,o,r),oe(e,r)}function se(e,n,t,o,r){const s=n.name;if(!s)return;const a=n.start+1;e.slice(a,a+s.length)===s&&r.overwrite(a,a+s.length,t);const i=`</${s}`,c=e.lastIndexOf(i,n.end);if(c>=n.start){const e=c+2;r.overwrite(e,e+s.length,t)}for(const t of n.attributes??[])"Attribute"===t.type&&t.name&&o.has(t.name)&&ae(e,t,r)}function ae(e,n,t){let o=n.start;" "!==e[o-1]&&"\t"!==e[o-1]||(o-=1),t.remove(o,n.end)}function ie(e,n,t){const o=n.map((e=>` import ${e.local} from ${JSON.stringify(e.spec)};`)).join("\n"),r=e.ast.instance,s=r?.content?.body??[];if(r&&s.length>0){const e=s[s.length-1];t.appendLeft(e.end,`\n${o}`)}else r&&r.content?t.appendLeft(r.content.start,`\n${o}\n`):t.prepend(`<script>\n${o}\n<\/script>\n`)}function ce(e,n,t){return ue(e,n.constFold,n.narrow,n,t)}function ue(e,n,t,o,r){if(0===n.size&&0===t.size)return new Set;const a=e.code,c=[];!function(e,n,t,o,r,a){s(e,null,{IfBlock(e,{next:s}){if(e.elseif||g(e,a))return;const i=d(e,n,t);if(function(e,n,t,o){if(e.kept)return void o.overwrite(e.span[0],e.span[1],function(e,n,t){const o=e?.nodes??[];return 0===o.length?"":fe(o[0].start,o[o.length-1].end,o,n,t)}(e.kept,n,t));for(const[n,t]of e.removed)o.remove(n,t);if(e.headerRewrite){const{from:n,to:t,text:r}=e.headerRewrite;o.overwrite(n,t,r)}}(i,n,o,r),i.kept)a.push(i.span);else for(const e of i.removed)a.push(e);i.recurse&&s()}})}(e.ast.fragment,n,t,a,r,c),function(e,n,t,o,r){if(0===n.size)return;s(e,null,{ConditionalExpression(e,{next:s}){if(g(e,r))return;const a=i(e.test,n);if(!a.known)return void s();const c=a.value?e.consequent:e.alternate;c?(o.overwrite(e.start,e.end,fe(c.start,c.end,[c],n,t)),r.push([e.start,e.end])):s()}})}(e.ast.fragment,n,a,r,c);const u=function(e,n,t){const o=new Map,r=r=>{r&&s(r,{parent:null},{_(r,{state:s,next:a}){"Identifier"===r.type&&r.name&&n.has(r.name)&&!g(r,t)&&r!==e.propsPattern&&!le(r,s.parent)&&(o.get(r.name)??function(e,n){const t=[];return e.set(n,t),t}(o,r.name)).push([r.start,r.end]),a({parent:r})}})};return r(e.ast.instance),r(e.ast.fragment),o}(e,n,c);for(const[e,t]of n)for(const n of u.get(e)??[])r.overwrite(n[0],n[1],he(t));const f=new Set(n.keys());!function(e,n,t){if(!e.props||0===n.size)return;const o=e.props.filter((e=>!n.has(e.name)));if(0===o.length&&!e.hasRestProp&&e.propsDeclaration)return void function(e,n,t){let o=n.start;for(;o>0&&"\n"!==e[o-1];)o-=1;let r=n.end;for(;r<e.length&&"\n"!==e[r];)r+=1;const s=e.slice(o,n.start),a=e.slice(n.end,r);/^\s*$/.test(s)&&/^\s*;?\s*$/.test(a)?t.remove(o,r<e.length?r+1:r):t.remove(n.start,";"===e[n.end]?n.end+1:n.end)}(e.code,e.propsDeclaration,t);const r=e.propsPattern?.properties??[];for(const o of e.props)n.has(o.name)&&(pe(r,o.property,t),e.propsPattern&&de(e.propsPattern,o.name,t))}(e,f,r);return Y(e,{...o,constFold:n,narrow:t},r),f}function fe(e,n,t,o,r){if(0===o.size)return r.slice(e,n);const a=[];for(const e of t)s(e,{parent:null},{_(e,{state:n,next:t}){"Identifier"===e.type&&e.name&&o.has(e.name)&&!le(e,n.parent)&&a.push({start:e.start,end:e.end,name:e.name}),t({parent:e})}});if(0===a.length)return r.slice(e,n);a.sort(((e,n)=>e.start-n.start));let i="",c=e;for(const e of a)i+=r.slice(c,e.start),i+=he(o.get(e.name)),c=e.end;return i+=r.slice(c,n),i}function le(e,n){return!!n&&("MemberExpression"===n.type&&n.property===e&&!n.computed||("Property"===n.type&&n.key===e&&!n.computed&&!0!==n.shorthand||("ImportSpecifier"===n.type||"ImportDefaultSpecifier"===n.type||"ImportNamespaceSpecifier"===n.type||"ExportSpecifier"===n.type)))}function pe(e,n,t){const o=e.indexOf(n),r=e[o+1],s=e[o-1];r?t.remove(n.start,r.start):s?t.remove(s.end,n.end):t.remove(n.start,n.end)}function de(e,n,t){const o=e.typeAnnotation?.typeAnnotation?.members??[],r=o.findIndex((e=>"Identifier"===e.key?.type&&e.key.name===n));if(-1===r)return;const s=o[r],a=o[r+1],i=o[r-1];a?t.remove(s.start,a.start):i?t.remove(i.end,s.end):t.remove(s.start,s.end)}function me(e,n,t){s(e.ast.fragment,null,{Component(o,{next:r}){const s=o.name?e.imports.get(o.name):void 0,a=s?n.get(s):void 0;if(a&&a.size>0)for(const n of o.attributes??[])"Attribute"===n.type&&n.name&&a.has(n.name)&&ve(n.value)&&ae(e.code,n,t);r()}})}function ve(e){if(!0===e||null==e)return!0;return(Array.isArray(e)?e:[e]).every((e=>"Text"===e.type||"ExpressionTag"===e.type&&"Literal"===e.expression?.type))}function he(e){return void 0===e?"undefined":JSON.stringify(e)}const we={enabled:!1,maxVariants:8,minSavings:0};function ge(e,t,o=we,r=[]){const s=new Map,a=[];if(!o.enabled)return{variants:s,bindings:a};const i=D(e,t),c=new Map,u=new Set;for(const n of e.values()){const o=i.get(n.id)??[];for(const r of n.childCalls){if(o.length>0&&g(r.node,o))continue;const s=e.get(r.childId),a=t.get(r.childId);if(!s||!a)continue;if(a.bail||!s.props||0===s.props.length){u.add(r.childId);continue}const i=xe(r.node,s,a);if(0===i.size){u.add(r.childId);continue}const f=be(s,a,i);if(f===Ie(s,a)){u.add(r.childId);continue}const l=c.get(r.childId);l?l.push({owner:n.id,node:r.node,shape:i,code:f}):c.set(r.childId,[{owner:n.id,node:r.node,shape:i,code:f}])}}const f=function(e,n){const t=new Map;for(const o of e.values()){const e=n.get(o.id);t.set(o.id,e.bail?o.code:Ie(o,e))}return t}(e,t),l=new Map;for(const n of e.values())l.set(n.id,ke(f.get(n.id),n));const p=new Set;for(const e of l.values())for(const n of e)p.add(n);const d=(Array.isArray(r)?r:[r]).filter((n=>e.has(n))).filter((e=>!p.has(e))),m=new Map,v=(e,t)=>{const o=m.get(t);if(void 0!==o)return o;let r;try{const{js:o}=n(t,{generate:"client",dev:!1,filename:e});r=o.code.length}catch{r=null}return null!==r&&m.set(t,r),r},h=new Set;for(const e of c.keys())u.has(e)||h.add(e);for(const[n,t]of c){if(u.has(n))continue;if(t.some((e=>e.owner!==n&&h.has(e.owner))))continue;const r=new Map,i=[];let c=!1;for(const e of t){if(r.has(e.code))continue;if(i.length>=o.maxVariants){c=!0;break}const t=`${n}::v${i.length}`;r.set(e.code,t),i.push({id:t,code:e.code})}if(!c&&ye(n,i,e,f,l,d,v,o.minSavings)){for(const e of i){const o=t.find((n=>n.code===e.code));s.set(e.id,{id:e.id,childId:n,code:e.code,foldedProps:o.shape})}for(const e of t)a.push({owner:e.owner,childId:n,node:e.node,variantId:r.get(e.code),foldedProps:e.shape})}}return{variants:s,bindings:a}}function ye(e,n,t,o,r,s,a,i){const c=t.get(e),u=new Map;for(const e of n)u.set(e.id,ke(e.code,c));const f=new Set,l=[...s];for(;l.length>0;){const e=l.pop();if(!f.has(e)){f.add(e);for(const n of r.get(e)??[])l.push(n)}}let p=0;for(const e of f){const n=a(e,o.get(e));if(null===n)return!1;p+=n}const d=n.map((e=>e.id)),m=new Set,v=new Set,h=n=>n===e?{comps:[],vars:d}:{comps:[n],vars:[]},w=[],g=[];for(const e of s){const n=h(e);w.push(...n.comps),g.push(...n.vars)}for(;w.length>0||g.length>0;){if(w.length>0){const e=w.pop();if(m.has(e))continue;m.add(e);for(const n of r.get(e)??[]){const e=h(n);w.push(...e.comps),g.push(...e.vars)}continue}const e=g.pop();if(!v.has(e)){v.add(e);for(const n of u.get(e)??[]){const e=h(n);w.push(...e.comps),g.push(...e.vars)}}}let y=0;for(const e of m){const n=a(e,o.get(e));if(null===n)return!1;y+=n}for(const e of v){const t=n.find((n=>n.id===e)).code,o=a(e,t);if(null===o)return!1;y+=o}return y<p*(1-i)}function ke(e,n){let t;try{t=r(e,n.id)}catch{return[]}const o=[];return s(t.fragment,null,{Component(e,{next:t}){const r=e.name?n.imports.get(e.name):void 0;r&&o.push(r),t()}}),o}function xe(e,n,t){const o=$(e),r=new Map;for(const e of n.props??[])r.set(e.name,e);const s=new Map;for(const[e,a]of o.explicit)r.has(e)&&(t.constFold.has(e)||O(n,e)||!a.dynamic&&a.afterLastSpread&&s.set(e,a.value));return s}function be(e,n,t){const r=new Map(n.constFold);for(const[e,n]of t)r.set(e,n);const s=new Map;for(const[e,t]of n.narrow)r.has(e)||s.set(e,t);const a=new o(e.code);return ue(e,r,s,n,a),a.toString()}const Se=new WeakMap;function Ie(e,n){const t=Se.get(e);if(void 0!==t)return t;const r=new o(e.code);ue(e,n.constFold,n.narrow,n,r);const s=r.toString();return Se.set(e,s),s}async function Me(e,n,t){const{models:o,plans:r}=await I(e,n,t);return ne(o,r)}async function Ee(e,n,t,o=we,r=(e=>e)){const{models:s,plans:a}=await I(e,n,t),i=ge(s,a,o,e);return{files:0===i.bindings.length?ne(s,a):re(s,a,i.bindings.map((e=>({owner:e.owner,node:e.node,variantId:e.variantId,foldedProps:e.foldedProps}))),r),mono:i}}export{we as DEFAULT_MONO_OPTIONS,I as analyze,ge as monomorphize,Me as svelteShaker,Ee as svelteShakerWithMono,ne as transformAll,re as transformAllWithMono};
|
package/dist/scan.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as e from"node:fs";import*as t from"node:path";const n=(e,n)=>e.startsWith(".")?t.resolve(t.dirname(n),e):null,o=t=>e.readFileSync(t,"utf-8");function r(n){const o=[];let s;try{s=e.readdirSync(n,{withFileTypes:!0})}catch{return o}for(const e of s){if("node_modules"===e.name||e.name.startsWith("."))continue;const s=t.join(n,e.name);e.isDirectory()?o.push(...r(s)):e.isFile()&&e.name.endsWith(".svelte")&&o.push(s)}return o}export{r as collectSvelteFiles,o as fsReadFile,n as fsResolve};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { type AnyNode, type Root } from './parse';
|
|
2
|
+
import { type ComponentId, type ComponentPlan, type Literal } from './ir';
|
|
3
|
+
import { type Span } from './dead';
|
|
4
|
+
export type Resolve = (source: string, importer: ComponentId) => Promise<ComponentId | null> | ComponentId | null;
|
|
5
|
+
export type ReadFile = (id: ComponentId) => Promise<string> | string;
|
|
6
|
+
/** One declared prop in a `$props()` destructuring. */
|
|
7
|
+
export interface PropDecl {
|
|
8
|
+
name: string;
|
|
9
|
+
/** The `Property` node inside the `ObjectPattern` (for surgical removal). */
|
|
10
|
+
property: AnyNode;
|
|
11
|
+
/** Default value expression, if `name = <default>`. */
|
|
12
|
+
defaultExpr?: AnyNode | undefined;
|
|
13
|
+
}
|
|
14
|
+
/** Everything we learn from parsing one component, reused by the transform. */
|
|
15
|
+
export interface FileModel {
|
|
16
|
+
id: ComponentId;
|
|
17
|
+
code: string;
|
|
18
|
+
ast: Root;
|
|
19
|
+
/** local import name (`Sub`) -> resolved child component id. */
|
|
20
|
+
imports: Map<string, ComponentId>;
|
|
21
|
+
/** Declared props, or `null` if the component has no `$props()` pattern. */
|
|
22
|
+
props: PropDecl[] | null;
|
|
23
|
+
/** The `let { ... } = $props()` declaration + its pattern, for editing. */
|
|
24
|
+
propsDeclaration?: AnyNode | undefined;
|
|
25
|
+
propsPattern?: AnyNode | undefined;
|
|
26
|
+
hasRestProp: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Every `<Child .../>` instance THIS component renders, with the child it
|
|
29
|
+
* resolves to and the AST node (so the fixpoint can test whether the site
|
|
30
|
+
* falls inside a dead `{#if}` span of this component — docs §2.1).
|
|
31
|
+
*/
|
|
32
|
+
childCalls: ChildCall[];
|
|
33
|
+
/**
|
|
34
|
+
* Names this component binds OUTSIDE the `$props()` pattern — local `let` /
|
|
35
|
+
* `function` declarations in the instance script, and every template-scope
|
|
36
|
+
* binder (`{#each … as ctx, i}`, destructure patterns, `{#snippet name(p)}`,
|
|
37
|
+
* `{#await … then v}` / `{:catch e}`, `let:` directives). A declared prop
|
|
38
|
+
* whose name collides with any of these is a DIFFERENT entity inside that
|
|
39
|
+
* scope, so folding/substituting/dropping it would corrupt the binding (and
|
|
40
|
+
* often produce invalid Svelte). We therefore never fold such a prop.
|
|
41
|
+
*/
|
|
42
|
+
shadowedNames: Set<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Names that appear as a `{@debug …}` argument. Svelte requires those to be
|
|
45
|
+
* bare identifiers, so substituting a folded literal there is invalid and
|
|
46
|
+
* dropping the prop dangles the reference — we never fold a prop named here.
|
|
47
|
+
*/
|
|
48
|
+
debugNames: Set<string>;
|
|
49
|
+
/**
|
|
50
|
+
* Resolved ids of CHILD components this file leaks as a value (escape, docs
|
|
51
|
+
* §4.1) — e.g. `<svelte:component this={Child}>`. `analyze` unions these
|
|
52
|
+
* across the program and bails every escaped component completely, since its
|
|
53
|
+
* prop profile can no longer be observed from `<Child .../>` sites alone.
|
|
54
|
+
*/
|
|
55
|
+
escapedComponents: Set<ComponentId>;
|
|
56
|
+
/**
|
|
57
|
+
* Resolved ids of CHILD components this file renders through an import we do
|
|
58
|
+
* NOT treat as a direct `.svelte` default (a named/namespace import, or a
|
|
59
|
+
* `.js`/`.ts` barrel re-exporting a `.svelte` default). These `<Comp .../>`
|
|
60
|
+
* sites are invisible to {@link collectChildCalls} (whose attribution keys off
|
|
61
|
+
* the default-import `imports` map only), so the child's value set would omit
|
|
62
|
+
* them — folding/narrowing on a partial set is unsound (docs §4.2: every
|
|
63
|
+
* consumer must be enumerated). `analyze` unions these across the program and
|
|
64
|
+
* bails every such child completely, just like an escape.
|
|
65
|
+
*/
|
|
66
|
+
barrelChildIds: Set<ComponentId>;
|
|
67
|
+
/** Reasons this whole component must be left untouched. */
|
|
68
|
+
bailReasons: string[];
|
|
69
|
+
}
|
|
70
|
+
/** One `<Child .../>` instance rendered by a component. */
|
|
71
|
+
export interface ChildCall {
|
|
72
|
+
childId: ComponentId;
|
|
73
|
+
node: AnyNode;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* One value passed explicitly to a prop at one call site, after last-write-wins
|
|
77
|
+
* has been resolved (`{...obj}` aside). `dynamic` means the attribute had a
|
|
78
|
+
* non-literal value (`bind:`, dynamic expression): used, value not statically
|
|
79
|
+
* known. `afterLastSpread` records whether this explicit write happened after
|
|
80
|
+
* the site's last `{...spread}` — only then can a spread not silently override
|
|
81
|
+
* it (docs §4.1, "後勝ち順序で救う").
|
|
82
|
+
*/
|
|
83
|
+
export interface ExplicitProp {
|
|
84
|
+
value: Literal;
|
|
85
|
+
dynamic: boolean;
|
|
86
|
+
afterLastSpread: boolean;
|
|
87
|
+
}
|
|
88
|
+
/** How a child component is called at one `<Child .../>` site. */
|
|
89
|
+
export interface CallSite {
|
|
90
|
+
/** Did this site have at least one `{...spread}` attribute? */
|
|
91
|
+
hadSpread: boolean;
|
|
92
|
+
/** Last-write-wins explicit props at this site, keyed by prop name. */
|
|
93
|
+
explicit: Map<string, ExplicitProp>;
|
|
94
|
+
}
|
|
95
|
+
export interface AnalyzeResult {
|
|
96
|
+
models: Map<ComponentId, FileModel>;
|
|
97
|
+
plans: Map<ComponentId, ComponentPlan>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Crawl the component graph from `entries` and compute a plan per component,
|
|
101
|
+
* iterating to a whole-program fixpoint (docs §2.1).
|
|
102
|
+
*
|
|
103
|
+
* The crucial cascade: a `<Child/>` that lives inside a branch we fold away must
|
|
104
|
+
* NOT count toward the child's prop profile. Excluding it can shrink the
|
|
105
|
+
* child's value sets and enable more folding, which can fold away yet more
|
|
106
|
+
* branches. So we parse every component once, then alternate between
|
|
107
|
+
* (a) collecting call sites that are NOT inside a current dead span, and
|
|
108
|
+
* (b) recomputing plans (and hence dead spans) from that usage,
|
|
109
|
+
* until the plans stop changing.
|
|
110
|
+
*/
|
|
111
|
+
export declare function analyze(entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile): Promise<AnalyzeResult>;
|
|
112
|
+
/**
|
|
113
|
+
* Dead `{#if}` spans per component implied by `plans`, via the SAME shared
|
|
114
|
+
* predicate the transform uses ({@link computeDeadSpans}). A bailed component
|
|
115
|
+
* folds nothing, so it has no dead spans.
|
|
116
|
+
*/
|
|
117
|
+
export declare function deadSpansForPlans(models: Map<ComponentId, FileModel>, plans: Map<ComponentId, ComponentPlan>): Map<ComponentId, Span[]>;
|
|
118
|
+
/**
|
|
119
|
+
* Read one `<Child .../>` into a {@link CallSite}. Attributes are in source
|
|
120
|
+
* order, so we resolve last-write-wins (a later `a={…}` overrides an earlier
|
|
121
|
+
* one) and record, per prop, whether its winning write came *after* the last
|
|
122
|
+
* spread — the only case a spread cannot silently override it (docs §4.1).
|
|
123
|
+
*/
|
|
124
|
+
export declare function readCallSite(component: AnyNode): CallSite;
|
|
125
|
+
/** Decide what to fold for one component from its global usage. */
|
|
126
|
+
/**
|
|
127
|
+
* Whether a declared prop name is unsafe to fold/narrow/drop because it is also
|
|
128
|
+
* bound elsewhere: shadowed by a local `let`/`function` or a template binder
|
|
129
|
+
* (`{#each as}`, snippet params, `{#await then}`, `let:`, `{@const}`), or used as
|
|
130
|
+
* a `{@debug}` argument (Svelte forbids a literal there). In those scopes the
|
|
131
|
+
* name is a different entity, so folding it would corrupt the binding (often
|
|
132
|
+
* invalid Svelte). Both L1 planning ({@link buildPlan}) and L2 specialization
|
|
133
|
+
* (mono.ts) must honor this identically.
|
|
134
|
+
*/
|
|
135
|
+
export declare function isFoldBlockedName(model: FileModel, name: string): boolean;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type MagicString from 'magic-string';
|
|
2
|
+
import type { ComponentPlan } from './ir';
|
|
3
|
+
import type { FileModel } from './analyze';
|
|
4
|
+
/**
|
|
5
|
+
* The set of class names any element this component renders could carry.
|
|
6
|
+
* `unbounded` means at least one element has a class source we cannot enumerate
|
|
7
|
+
* (a non-foldable `class={x}`, or a spread that could carry `class`), so the
|
|
8
|
+
* "possible class set" is really "all class names" and NO rule may be removed.
|
|
9
|
+
*/
|
|
10
|
+
export interface PossibleClasses {
|
|
11
|
+
classes: Set<string>;
|
|
12
|
+
unbounded: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Compute the component's possible class set (docs §3, step 1). Sources:
|
|
16
|
+
* - static `class="a b"` -> the literal tokens,
|
|
17
|
+
* - `class:foo` directive -> `foo` is always possible (regardless of its cond),
|
|
18
|
+
* - `class="x-{expr}"` / `class={expr}` where every interpolated `expr` is
|
|
19
|
+
* foldable (constFold) or narrowable (narrow set) -> enumerate the tokens,
|
|
20
|
+
* - ANY unbounded source (`class={nonFoldable}`, or a `{...spread}` that could
|
|
21
|
+
* carry `class`) -> the whole set is unbounded.
|
|
22
|
+
*/
|
|
23
|
+
export declare function computePossibleClasses(model: FileModel, plan: ComponentPlan): PossibleClasses;
|
|
24
|
+
/**
|
|
25
|
+
* Remove provably-dead top-level `<style>` rules via MagicString span removal
|
|
26
|
+
* (docs §3, step 2/3). A rule is removed ONLY when the possible class set is
|
|
27
|
+
* bounded, the rule contains no `:global(...)` anywhere, and EVERY selector in
|
|
28
|
+
* the rule's selector list contains at least one class `.C` with `C` absent from
|
|
29
|
+
* the possible set (so no selector in the list can match any element this
|
|
30
|
+
* component can render). Anything else is KEPT. Returns the number removed.
|
|
31
|
+
*/
|
|
32
|
+
export declare function shakeCss(model: FileModel, plan: ComponentPlan, s: MagicString): number;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type AnyNode } from './parse';
|
|
2
|
+
import type { Literal } from './ir';
|
|
3
|
+
export type Span = [number, number];
|
|
4
|
+
/** One arm of an `{#if}` / `{:else if}` chain in source order. */
|
|
5
|
+
interface ChainArm {
|
|
6
|
+
block: AnyNode;
|
|
7
|
+
test: AnyNode | undefined;
|
|
8
|
+
consequent: AnyNode | undefined;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The outcome of folding one chain against the known environments.
|
|
12
|
+
*
|
|
13
|
+
* `removed` are source ranges that do NOT appear in the output (deleted branch
|
|
14
|
+
* markup, dead headers). `kept` is the single arm consequent that survives
|
|
15
|
+
* verbatim when the chain collapses to it (case (a)/(b1-else)), or `undefined`
|
|
16
|
+
* when the chain keeps its `{#if}` structure. `recurse` says whether the
|
|
17
|
+
* surviving subtree still contains live `{#if}` blocks that must be folded
|
|
18
|
+
* (true only when the original head arm is kept in place).
|
|
19
|
+
*
|
|
20
|
+
* `headerRewrite`, when present, promotes a surviving `{:else if …}` arm to a
|
|
21
|
+
* fresh `{#if …}` head: `[from, to]` is replaced by `text`. It carries no call
|
|
22
|
+
* site, so the dead-span view simply treats `[from, to]` as removed.
|
|
23
|
+
*/
|
|
24
|
+
export interface ChainDecision {
|
|
25
|
+
span: Span;
|
|
26
|
+
removed: Span[];
|
|
27
|
+
kept: AnyNode | undefined;
|
|
28
|
+
recurse: boolean;
|
|
29
|
+
headerRewrite?: {
|
|
30
|
+
from: number;
|
|
31
|
+
to: number;
|
|
32
|
+
text: string;
|
|
33
|
+
} | undefined;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Collect the arms of an `{#if}` / `{:else if}` chain in source order, plus the
|
|
37
|
+
* trailing `{:else}` fragment if present. Each arm carries its own IfBlock node
|
|
38
|
+
* (the head, or an `elseif` continuation).
|
|
39
|
+
*/
|
|
40
|
+
export declare function collectChain(top: AnyNode): {
|
|
41
|
+
arms: ChainArm[];
|
|
42
|
+
elseFrag?: AnyNode;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Decide how one if/else-if chain folds against `env` (constFold) and `setEnv`
|
|
46
|
+
* (value sets). Soundness: an arm is dropped only when its test is provably
|
|
47
|
+
* FALSE for every reachable value; the chain collapses to a consequent only when
|
|
48
|
+
* an arm is provably TRUE and every earlier arm is provably FALSE. Otherwise
|
|
49
|
+
* the head is kept and callers recurse for nested blocks.
|
|
50
|
+
*
|
|
51
|
+
* This is the single source of truth used by BOTH the transform and the
|
|
52
|
+
* analysis fixpoint, so they can never disagree on what folds.
|
|
53
|
+
*/
|
|
54
|
+
export declare function decideChain(top: AnyNode, env: Map<string, Literal>, setEnv: Map<string, Literal[]>): ChainDecision;
|
|
55
|
+
/** End offset of a consequent fragment (its last node), or a fallback. */
|
|
56
|
+
export declare function consequentEnd(fragment: AnyNode | undefined, fallback: number): number;
|
|
57
|
+
/** Is `node` fully contained in any of the given spans? */
|
|
58
|
+
export declare function inSpans(node: AnyNode, spans: Span[]): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Compute the source spans that genuinely vanish from a component's output when
|
|
61
|
+
* its plan is applied — i.e. the markup deleted by dead-`{#if}` folding. This
|
|
62
|
+
* is the SAME predicate the transform uses (both go through {@link decideChain}),
|
|
63
|
+
* so a call site is excluded from a child's prop profile iff the transform would
|
|
64
|
+
* actually delete it (docs §2.1 cascade).
|
|
65
|
+
*
|
|
66
|
+
* We mirror the transform's walk: a chain whose head we keep is recursed into
|
|
67
|
+
* (nested blocks can still fold); a chain we rewrote/collapsed is not, and its
|
|
68
|
+
* span is skipped on later visits via `inSpans`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function computeDeadSpans(fragment: AnyNode, env: Map<string, Literal>, setEnv: Map<string, Literal[]>): Span[];
|
|
71
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AnyNode } from './parse';
|
|
2
|
+
import type { Literal } from './ir';
|
|
3
|
+
export type EvalResult = {
|
|
4
|
+
known: true;
|
|
5
|
+
value: Literal;
|
|
6
|
+
} | {
|
|
7
|
+
known: false;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A deliberately tiny, total constant evaluator over an ESTree expression,
|
|
11
|
+
* given an environment of statically-known identifiers. It never throws and
|
|
12
|
+
* never guesses: anything it cannot prove is `{ known: false }`.
|
|
13
|
+
*
|
|
14
|
+
* This is the M0 stand-in for the abstract-interpretation engine described in
|
|
15
|
+
* docs/ARCHITECTURE.md §13 — same contract (sound over-approximation, falls to
|
|
16
|
+
* unknown on non-distributive ops), just without the interprocedural lattice.
|
|
17
|
+
*/
|
|
18
|
+
export declare function evaluate(node: AnyNode | null | undefined, env: Map<string, Literal>): EvalResult;
|
|
19
|
+
/**
|
|
20
|
+
* Sound set-aware predicate. `constEnv` holds props collapsed to a single
|
|
21
|
+
* literal (`constFold`); `setEnv` holds props whose reachable value set is known
|
|
22
|
+
* (`narrow`, >= 2 literals). Returns `{ known:true }` ONLY when the boolean is
|
|
23
|
+
* provable for the whole reachable set — a value reachable through the set keeps
|
|
24
|
+
* the branch. Anything outside the small supported fragment is `{ known:false }`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function evaluateWithSets(node: AnyNode | null | undefined, constEnv: Map<string, Literal>, setEnv: Map<string, Literal[]>): EvalResult;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type ReadFile, type Resolve } from './analyze';
|
|
2
|
+
import { type MonomorphizeOptions, type MonomorphizeResult } from './mono';
|
|
3
|
+
import type { ComponentId } from './ir';
|
|
4
|
+
export type { ComponentId } from './ir';
|
|
5
|
+
export type { Resolve, ReadFile } from './analyze';
|
|
6
|
+
export { analyze } from './analyze';
|
|
7
|
+
export { transformAll, transformAllWithMono } from './transform';
|
|
8
|
+
export { monomorphize, DEFAULT_MONO_OPTIONS, type MonomorphizeOptions, type MonomorphizeResult, type Variant, type CallSiteBinding, } from './mono';
|
|
9
|
+
/**
|
|
10
|
+
* Whole-program shake: crawl the component graph from `entry`, decide what to
|
|
11
|
+
* fold, and return the shaken source for every reachable `.svelte` file.
|
|
12
|
+
*
|
|
13
|
+
* `resolve` / `readFile` are injected so the engine stays environment-free —
|
|
14
|
+
* it has NO `node:*` imports, so it runs unchanged in the browser (the
|
|
15
|
+
* playground passes an in-memory file map). A Vite plugin passes `this.resolve`;
|
|
16
|
+
* Node callers use `fsResolve` / `fs.readFileSync` from `svelte-shaker/node`.
|
|
17
|
+
* See docs/ARCHITECTURE.md §5 — this is the Engine; the Shell owns resolution.
|
|
18
|
+
*/
|
|
19
|
+
export declare function svelteShaker(entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile): Promise<Record<ComponentId, string>>;
|
|
20
|
+
/** The full output of a shake including L2 specialization. */
|
|
21
|
+
export interface ShakeResult {
|
|
22
|
+
/**
|
|
23
|
+
* Whole-program output: shaken source per `.svelte` file. With L2 OFF this is
|
|
24
|
+
* byte-for-byte identical to {@link svelteShaker}; with L2 ON, owner files
|
|
25
|
+
* whose call sites were specialized have those sites rewritten to import a
|
|
26
|
+
* variant via `variantSpecifier(variantId)`.
|
|
27
|
+
*/
|
|
28
|
+
files: Record<ComponentId, string>;
|
|
29
|
+
/** L2 specialized variants + call-site bindings (empty when L2 is off). */
|
|
30
|
+
mono: MonomorphizeResult;
|
|
31
|
+
}
|
|
32
|
+
/** Build the module specifier a rewritten call site imports a variant from. */
|
|
33
|
+
export type VariantSpecifier = (variantId: string) => string;
|
|
34
|
+
/**
|
|
35
|
+
* Whole-program shake WITH optional L2 monomorphization (docs §3 "L2").
|
|
36
|
+
*
|
|
37
|
+
* `mono` carries the specialized variants (id -> residual source) and the call
|
|
38
|
+
* sites bound to them; `files` is the wired owner source. The Shell resolves
|
|
39
|
+
* `variantSpecifier(id)` to a virtual module whose source is
|
|
40
|
+
* `mono.variants.get(id)!.code`. With `mono.enabled` false (default) nothing is
|
|
41
|
+
* specialized and `files` equals the L0/L1/L1.5 output exactly — a strict
|
|
42
|
+
* superset of the default behavior, so existing consumers are unaffected.
|
|
43
|
+
*/
|
|
44
|
+
export declare function svelteShakerWithMono(entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile, mono?: MonomorphizeOptions, variantSpecifier?: VariantSpecifier): Promise<ShakeResult>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** Resolved absolute path of a `.svelte` file. */
|
|
2
|
+
export type ComponentId = string;
|
|
3
|
+
/** A statically-known literal value a prop can take. */
|
|
4
|
+
export type Literal = string | number | boolean | null | undefined;
|
|
5
|
+
/**
|
|
6
|
+
* The join, over every call site in the program, of the value passed to a
|
|
7
|
+
* single prop. See the lattice in docs/ARCHITECTURE.md §2.2.
|
|
8
|
+
*
|
|
9
|
+
* M0 only ever produces `const` (single literal across all sites) and `top`
|
|
10
|
+
* (something we cannot reason about — never fold). `multi` / `dynamic` are
|
|
11
|
+
* declared now so L1.5 narrowing can be added without reshaping callers.
|
|
12
|
+
*/
|
|
13
|
+
export type PropAbstraction = {
|
|
14
|
+
kind: 'bottom';
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'const';
|
|
17
|
+
value: Literal;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'multi';
|
|
20
|
+
values: Literal[];
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'dynamic';
|
|
23
|
+
} | {
|
|
24
|
+
kind: 'top';
|
|
25
|
+
reason: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The set of literal values one declared prop is seen to take across the whole
|
|
29
|
+
* program (default included for sites that omit it), plus the two ways it can
|
|
30
|
+
* escape the lattice. This is the value-set foundation later levels narrow on
|
|
31
|
+
* (docs §2.2 `multi`, §3 L1.5): `constFold` is just the `size === 1 && !dynamic
|
|
32
|
+
* && !top` projection of it. Kept on the plan as groundwork — no level yet
|
|
33
|
+
* consumes the multi-element / `dynamic` cases.
|
|
34
|
+
*/
|
|
35
|
+
export interface PropValueSet {
|
|
36
|
+
/** Distinct literals observed (dedup'd; `undefined`/`null` are distinct). */
|
|
37
|
+
values: Literal[];
|
|
38
|
+
/** A non-literal value was passed somewhere (used, value not statically known). */
|
|
39
|
+
dynamic: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* ⊤: a call-site spread may set this prop (docs §4.1 partial bail), so the
|
|
42
|
+
* value set is really "all values" and the prop must not be folded.
|
|
43
|
+
*/
|
|
44
|
+
top: boolean;
|
|
45
|
+
}
|
|
46
|
+
/** What the analysis decides to do to one component. */
|
|
47
|
+
export interface ComponentPlan {
|
|
48
|
+
id: ComponentId;
|
|
49
|
+
/** Whole-component bail (accessors / customElement / escape). */
|
|
50
|
+
bail: boolean;
|
|
51
|
+
reasons: string[];
|
|
52
|
+
/**
|
|
53
|
+
* L0/L1: props that collapse to a single constant. Under the "攻め"
|
|
54
|
+
* default (docs §12-2) these are folded in the body, dropped from the
|
|
55
|
+
* `$props()` signature, and their attributes are removed at every call site.
|
|
56
|
+
*/
|
|
57
|
+
constFold: Map<string, Literal>;
|
|
58
|
+
/**
|
|
59
|
+
* L1.5 value-set narrowing (docs §3): props whose reachable value set is a
|
|
60
|
+
* known set of >= 2 distinct literals (no `dynamic`/`top` contribution). We
|
|
61
|
+
* delete branches the prop can provably never reach (e.g. a `variant ===
|
|
62
|
+
* 'danger'` arm when `variant ∈ {'primary','secondary'}`), but — unlike
|
|
63
|
+
* `constFold` — the prop is still genuinely used/dynamic, so it is NOT
|
|
64
|
+
* substituted and NOT dropped from the `$props()` signature. Singletons stay
|
|
65
|
+
* in `constFold`; these two maps are disjoint.
|
|
66
|
+
*/
|
|
67
|
+
narrow: Map<string, Literal[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Per-declared-prop value-set foundation (see {@link PropValueSet}). Present
|
|
70
|
+
* for every declared prop the analysis reasoned about; `constFold` is its
|
|
71
|
+
* singleton projection and `narrow` is its multi-element projection.
|
|
72
|
+
*/
|
|
73
|
+
valueSets: Map<string, PropValueSet>;
|
|
74
|
+
}
|
|
75
|
+
export declare function emptyPlan(id: ComponentId): ComponentPlan;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type FileModel } from './analyze';
|
|
2
|
+
import { type AnyNode } from './parse';
|
|
3
|
+
import type { ComponentId, ComponentPlan, Literal } from './ir';
|
|
4
|
+
/** Tuning knobs for L2 (docs §8.1, §13.2). All have sound defaults. */
|
|
5
|
+
export interface MonomorphizeOptions {
|
|
6
|
+
/** Master switch. Default OFF — every existing behavior is unchanged. */
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Cap on distinct specialized variants generated per component (docs §13.2
|
|
10
|
+
* "maxVariants"). Dedup by residual means this counts *distinct residuals*,
|
|
11
|
+
* not call sites; if a child's distinct residuals exceed the cap it cannot be
|
|
12
|
+
* specialized all-sites, so it keeps the base entirely. Default 8.
|
|
13
|
+
*/
|
|
14
|
+
maxVariants: number;
|
|
15
|
+
/**
|
|
16
|
+
* Minimum FRACTION of the base-scenario module bytes the specialization must
|
|
17
|
+
* save before we apply it (docs §13.2 "measured net-win"). `0` (default) means
|
|
18
|
+
* specialize on ANY strict net reduction (`Sigma_spec < Sigma_base`); `0.15`
|
|
19
|
+
* would require a >=15% reduction. Higher is more conservative; it can never
|
|
20
|
+
* make L2 bloat, only decline more.
|
|
21
|
+
*/
|
|
22
|
+
minSavings: number;
|
|
23
|
+
}
|
|
24
|
+
export declare const DEFAULT_MONO_OPTIONS: MonomorphizeOptions;
|
|
25
|
+
/**
|
|
26
|
+
* One generated specialized module for a component shape.
|
|
27
|
+
*
|
|
28
|
+
* `key` is the dedup key — the residual SOURCE itself, so two shapes with
|
|
29
|
+
* identical residuals collapse to one variant (docs §13.2). `code` is the
|
|
30
|
+
* slimmed `.svelte` source; `foldedProps` records which props this variant
|
|
31
|
+
* folded to which literal (the call-site shape), so the Shell can strip those
|
|
32
|
+
* attributes from the rewritten call site.
|
|
33
|
+
*/
|
|
34
|
+
export interface Variant {
|
|
35
|
+
/** Stable id `<childId>::v<n>` — used as the virtual module specifier. */
|
|
36
|
+
id: string;
|
|
37
|
+
/** The component this is a specialization of. */
|
|
38
|
+
childId: ComponentId;
|
|
39
|
+
/** Slimmed `.svelte` source (the residual). */
|
|
40
|
+
code: string;
|
|
41
|
+
/** Props this variant froze to a literal (name -> value). */
|
|
42
|
+
foldedProps: Map<string, Literal>;
|
|
43
|
+
}
|
|
44
|
+
/** One call site that was assigned to a specialized variant. */
|
|
45
|
+
export interface CallSiteBinding {
|
|
46
|
+
/** The component that renders this `<Child .../>`. */
|
|
47
|
+
owner: ComponentId;
|
|
48
|
+
/** The component being called. */
|
|
49
|
+
childId: ComponentId;
|
|
50
|
+
/** The `<Child .../>` AST node (so the Shell can rewrite this exact site). */
|
|
51
|
+
node: AnyNode;
|
|
52
|
+
/** The variant this site resolves to. */
|
|
53
|
+
variantId: string;
|
|
54
|
+
/**
|
|
55
|
+
* The props THIS site froze to a literal. May differ from the resolved
|
|
56
|
+
* variant's `foldedProps` when two shapes dedup to one residual (a frozen prop
|
|
57
|
+
* that the residual ignores); the rewrite must strip THIS site's frozen attrs,
|
|
58
|
+
* so the binding carries its own shape rather than the dedup target's.
|
|
59
|
+
*/
|
|
60
|
+
foldedProps: Map<string, Literal>;
|
|
61
|
+
}
|
|
62
|
+
/** The complete L2 result the Shell consumes. */
|
|
63
|
+
export interface MonomorphizeResult {
|
|
64
|
+
/** Every generated variant, keyed by its id. */
|
|
65
|
+
variants: Map<string, Variant>;
|
|
66
|
+
/** Per (owner, node) the variant a call site was specialized to. */
|
|
67
|
+
bindings: CallSiteBinding[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Compute per-call-site specialized residuals and the dedup'd variant set under
|
|
71
|
+
* a MEASURED, never-bloat net-win gate (docs §3 L2, §13.2).
|
|
72
|
+
*
|
|
73
|
+
* Pure over `models`/`plans`: it reads the already-computed plans, finds the
|
|
74
|
+
* live call sites whose extra literal props fold, groups them per child, and —
|
|
75
|
+
* for each child — only specializes when (a) EVERY live site of that child gets
|
|
76
|
+
* a non-base residual (all-sites-or-nothing, so the base module becomes
|
|
77
|
+
* unreferenced) and (b) replacing the child with its variants strictly shrinks
|
|
78
|
+
* the whole-program module bytes reachable from `entries`. It never mutates the
|
|
79
|
+
* inputs and never touches the base transform, so with L2 off (or when no child
|
|
80
|
+
* passes the gate) the default whole-program output is byte-for-byte unchanged.
|
|
81
|
+
*/
|
|
82
|
+
export declare function monomorphize(models: Map<ComponentId, FileModel>, plans: Map<ComponentId, ComponentPlan>, options?: MonomorphizeOptions, entries?: ComponentId | ComponentId[]): MonomorphizeResult;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A deliberately loose view of the Svelte + ESTree AST: only the fields this
|
|
3
|
+
* engine reads, each optional. Avoiding an index signature keeps named access
|
|
4
|
+
* compatible with `noPropertyAccessFromIndexSignature` while still letting us
|
|
5
|
+
* walk an untyped tree. `value` is `unknown` because it means different things
|
|
6
|
+
* on `Literal` (a literal) vs `Attribute` (true | node | node[]).
|
|
7
|
+
*/
|
|
8
|
+
export interface AnyNode {
|
|
9
|
+
type: string;
|
|
10
|
+
start: number;
|
|
11
|
+
end: number;
|
|
12
|
+
test?: AnyNode | undefined;
|
|
13
|
+
consequent?: AnyNode | undefined;
|
|
14
|
+
alternate?: AnyNode | null | undefined;
|
|
15
|
+
expression?: AnyNode | undefined;
|
|
16
|
+
argument?: AnyNode | undefined;
|
|
17
|
+
left?: AnyNode | undefined;
|
|
18
|
+
right?: AnyNode | undefined;
|
|
19
|
+
callee?: AnyNode | undefined;
|
|
20
|
+
id?: AnyNode | undefined;
|
|
21
|
+
init?: AnyNode | undefined;
|
|
22
|
+
key?: AnyNode | undefined;
|
|
23
|
+
property?: AnyNode | undefined;
|
|
24
|
+
source?: AnyNode | undefined;
|
|
25
|
+
local?: AnyNode | undefined;
|
|
26
|
+
/** ImportSpecifier / ExportSpecifier exported-name slot. */
|
|
27
|
+
imported?: AnyNode | undefined;
|
|
28
|
+
exported?: AnyNode | undefined;
|
|
29
|
+
typeAnnotation?: AnyNode | undefined;
|
|
30
|
+
content?: AnyNode | undefined;
|
|
31
|
+
fragment?: AnyNode | null | undefined;
|
|
32
|
+
instance?: AnyNode | null | undefined;
|
|
33
|
+
module?: AnyNode | null | undefined;
|
|
34
|
+
css?: AnyNode | null | undefined;
|
|
35
|
+
context?: AnyNode | undefined;
|
|
36
|
+
error?: AnyNode | null | undefined;
|
|
37
|
+
then?: AnyNode | null | undefined;
|
|
38
|
+
catch?: AnyNode | null | undefined;
|
|
39
|
+
declaration?: AnyNode | undefined;
|
|
40
|
+
prelude?: AnyNode | undefined;
|
|
41
|
+
block?: AnyNode | null | undefined;
|
|
42
|
+
combinator?: AnyNode | null | undefined;
|
|
43
|
+
args?: AnyNode | null | undefined;
|
|
44
|
+
attributes?: AnyNode[] | undefined;
|
|
45
|
+
properties?: AnyNode[] | undefined;
|
|
46
|
+
members?: AnyNode[] | undefined;
|
|
47
|
+
body?: AnyNode[] | undefined;
|
|
48
|
+
declarations?: AnyNode[] | undefined;
|
|
49
|
+
specifiers?: AnyNode[] | undefined;
|
|
50
|
+
nodes?: AnyNode[] | undefined;
|
|
51
|
+
/** SnippetBlock parameters; ArrayPattern elements; DebugTag identifiers. */
|
|
52
|
+
parameters?: AnyNode[] | undefined;
|
|
53
|
+
/** Function / arrow ESTree parameters (`function f(a, b)`, `(a) => …`). */
|
|
54
|
+
params?: AnyNode[] | undefined;
|
|
55
|
+
elements?: (AnyNode | null)[] | undefined;
|
|
56
|
+
identifiers?: AnyNode[] | undefined;
|
|
57
|
+
/** CSS StyleSheet/SelectorList/ComplexSelector children, Block children. */
|
|
58
|
+
children?: AnyNode[] | undefined;
|
|
59
|
+
/** RelativeSelector simple selectors (ClassSelector, TypeSelector, …). */
|
|
60
|
+
selectors?: AnyNode[] | undefined;
|
|
61
|
+
name?: string | undefined;
|
|
62
|
+
/** EachBlock loop index variable name (a bare string, e.g. `i`). */
|
|
63
|
+
index?: string | undefined;
|
|
64
|
+
operator?: string | undefined;
|
|
65
|
+
raw?: string | undefined;
|
|
66
|
+
data?: string | undefined;
|
|
67
|
+
computed?: boolean | undefined;
|
|
68
|
+
shorthand?: boolean | undefined;
|
|
69
|
+
elseif?: boolean | undefined;
|
|
70
|
+
value?: unknown;
|
|
71
|
+
}
|
|
72
|
+
export interface Root extends AnyNode {
|
|
73
|
+
fragment: AnyNode;
|
|
74
|
+
}
|
|
75
|
+
export declare function parseSvelte(code: string, filename: string): Root;
|
|
76
|
+
export interface WalkCtx<S> {
|
|
77
|
+
state: S;
|
|
78
|
+
next: (state?: S) => void;
|
|
79
|
+
stop: () => void;
|
|
80
|
+
}
|
|
81
|
+
export type Visitors<S> = Record<string, (node: AnyNode, ctx: WalkCtx<S>) => void>;
|
|
82
|
+
/** `zimmerframe.walk`, narrowed to our loose node type. */
|
|
83
|
+
export declare function walk<S>(root: AnyNode, state: S, visitors: Visitors<S>): void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ComponentId } from './ir';
|
|
2
|
+
import type { Resolve, ReadFile } from './analyze';
|
|
3
|
+
/** Default filesystem resolver: resolve `source` relative to its importer. */
|
|
4
|
+
export declare const fsResolve: Resolve;
|
|
5
|
+
/** Default filesystem reader. */
|
|
6
|
+
export declare const fsReadFile: ReadFile;
|
|
7
|
+
/**
|
|
8
|
+
* Recursively collect every `.svelte` file under `dir` (skipping `node_modules`
|
|
9
|
+
* and dot-directories). A Shell helper, kept out of the env-free engine core
|
|
10
|
+
* (docs/ARCHITECTURE.md §5): plugins use it to seed the whole-program crawl.
|
|
11
|
+
*/
|
|
12
|
+
export declare function collectSvelteFiles(dir: string): ComponentId[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import MagicString from 'magic-string';
|
|
2
|
+
import { type AnyNode } from './parse';
|
|
3
|
+
import type { ComponentId, ComponentPlan, Literal } from './ir';
|
|
4
|
+
import type { FileModel } from './analyze';
|
|
5
|
+
/**
|
|
6
|
+
* Apply every plan to every component and return the shaken source per file.
|
|
7
|
+
*
|
|
8
|
+
* Two phases over a shared set of MagicStrings so that a parent's call-site
|
|
9
|
+
* attributes are removed using each child's *actually dropped* props (not just
|
|
10
|
+
* what the plan proposed): a prop only leaves the public signature when every
|
|
11
|
+
* reference to it could be folded or substituted away.
|
|
12
|
+
*/
|
|
13
|
+
export declare function transformAll(models: Map<ComponentId, FileModel>, plans: Map<ComponentId, ComponentPlan>): Record<ComponentId, string>;
|
|
14
|
+
/**
|
|
15
|
+
* Like {@link transformAll}, but additionally rewrites the L2-bound call sites in
|
|
16
|
+
* each owner to import a specialized variant from a virtual module. The base
|
|
17
|
+
* phases are unchanged (so files with no binding are byte-identical to
|
|
18
|
+
* {@link transformAll}); phase 3 only edits regions phase 2 never touches — a
|
|
19
|
+
* bound `<Child …>` tag's NAME, and the frozen-prop attributes (which are
|
|
20
|
+
* disjoint from the dropped-prop attributes phase 2 removes, because a frozen
|
|
21
|
+
* prop is by construction NOT in the child's app-wide `constFold`).
|
|
22
|
+
*
|
|
23
|
+
* `variantImport(variantId)` maps a variant id to the module specifier the
|
|
24
|
+
* rewritten `import` should reference (the Shell supplies the virtual id).
|
|
25
|
+
*/
|
|
26
|
+
export declare function transformAllWithMono(models: Map<ComponentId, FileModel>, plans: Map<ComponentId, ComponentPlan>, bindings: MonoBinding[], variantImport: (variantId: string) => string): Record<ComponentId, string>;
|
|
27
|
+
/** Minimal binding shape the rewrite needs (matches `mono.ts` CallSiteBinding). */
|
|
28
|
+
export interface MonoBinding {
|
|
29
|
+
owner: ComponentId;
|
|
30
|
+
node: AnyNode;
|
|
31
|
+
variantId: string;
|
|
32
|
+
/** Props the variant froze — their attributes are removed from the site. */
|
|
33
|
+
foldedProps: Map<string, Literal>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Slim one component's body against the given fold (`env`) and narrow (`setEnv`)
|
|
37
|
+
* environments, editing `s` in place, and return the set of props that left the
|
|
38
|
+
* `$props()` signature. Factored out of {@link transformBody} so L2
|
|
39
|
+
* monomorphization (see `mono.ts`) can re-run the SAME pipeline with an augmented
|
|
40
|
+
* `env` (a call site's extra literal props) on a fresh MagicString — guaranteeing
|
|
41
|
+
* a specialized residual is produced by exactly the audited L0/L1/L1.5 machinery,
|
|
42
|
+
* never a parallel code path. `cssPlan` carries the value sets CSS removal reads
|
|
43
|
+
* (its `constFold`/`narrow` are overridden by `env`/`setEnv` before use).
|
|
44
|
+
*/
|
|
45
|
+
export declare function shakeBody(model: FileModel, env: Map<string, Literal>, setEnv: Map<string, Literal[]>, cssPlan: ComponentPlan, s: MagicString): Set<string>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
import { type MonomorphizeOptions } from './mono';
|
|
3
|
+
export interface ShakerOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Directories (relative to the Vite root) to scan for `.svelte` components.
|
|
6
|
+
* Defaults to the Vite root itself. Every `.svelte` file found is treated as
|
|
7
|
+
* a call-site source, so the union of these dirs must contain the whole app
|
|
8
|
+
* for prop elimination to be sound (docs/ARCHITECTURE.md §4.2).
|
|
9
|
+
*/
|
|
10
|
+
include?: string[];
|
|
11
|
+
/**
|
|
12
|
+
* Optimization level (docs §3). L0/L1/L1.5 are always on (`level >= 1`); only
|
|
13
|
+
* `level: 2` additionally enables L2 per-call-site monomorphization, which is
|
|
14
|
+
* OPT-IN. Default `1` — behavior with the level unset is unchanged.
|
|
15
|
+
*/
|
|
16
|
+
level?: 0 | 1 | 2;
|
|
17
|
+
/**
|
|
18
|
+
* L2 monomorphization tuning (docs §13.2). Only consulted when `level: 2`.
|
|
19
|
+
* `true` enables it with defaults; an object overrides `maxVariants`. Defaults
|
|
20
|
+
* to OFF.
|
|
21
|
+
*/
|
|
22
|
+
monomorphize?: boolean | Partial<Omit<MonomorphizeOptions, 'enabled'>>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Source-level Svelte tree-shaking as a Vite plugin (docs/ARCHITECTURE.md §6).
|
|
26
|
+
*
|
|
27
|
+
* Build-only by design: `apply: 'build'` makes dev a pass-through, because the
|
|
28
|
+
* whole-program analysis is incompatible with HMR's locality (§6.2).
|
|
29
|
+
* `enforce: 'pre'` runs us before `@sveltejs/vite-plugin-svelte`, so we hand it
|
|
30
|
+
* already-slimmed `.svelte` source and stay decoupled from Svelte's codegen.
|
|
31
|
+
*
|
|
32
|
+
* L2 wiring (opt-in, `level: 2`): a specialized variant is exposed as a request
|
|
33
|
+
* for the ORIGINAL child file with a `?shaker_variant=<id>` query. Keeping the
|
|
34
|
+
* real `.svelte` path means the variant's own relative imports (`./Icon.svelte`)
|
|
35
|
+
* resolve exactly as they would in the unspecialized child, and vite-plugin-svelte
|
|
36
|
+
* still compiles it as a normal component; our `transform` swaps in the variant
|
|
37
|
+
* residual for that id. Two call sites with byte-identical residuals share one
|
|
38
|
+
* variant id (dedup), so they share one compiled module.
|
|
39
|
+
*/
|
|
40
|
+
export declare function shaker(options?: ShakerOptions): Plugin;
|
package/dist/vite.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as e from"node:fs";import*as n from"node:path";import{parse as t,compile as o}from"svelte/compiler";import{walk as r}from"zimmerframe";import s from"magic-string";function a(e,n){return t(e,{modern:!0,filename:n})}function i(e,n,t){r(e,n,t)}const c={known:!1};function u(e,n){if(!e)return c;switch(e.type){case"Literal":return{known:!0,value:e.value};case"Identifier":{const t=e.name??"";return"undefined"===t?{known:!0,value:void 0}:n.has(t)?{known:!0,value:n.get(t)}:c}case"UnaryExpression":{const t=u(e.argument,n);if(!t.known)return c;const o=t.value;switch(e.operator){case"!":return{known:!0,value:!o};case"-":return{known:!0,value:-o};case"+":return{known:!0,value:+o};case"typeof":return{known:!0,value:typeof o};case"void":return{known:!0,value:void 0};default:return c}}case"LogicalExpression":{const t=u(e.left,n);if(!t.known)return c;switch(e.operator){case"&&":return t.value?u(e.right,n):t;case"||":return t.value?t:u(e.right,n);case"??":return null===t.value||void 0===t.value?u(e.right,n):t;default:return c}}case"BinaryExpression":{const t=u(e.left,n),o=u(e.right,n);if(!t.known||!o.known)return c;const r=t.value,s=o.value;switch(e.operator){case"===":return{known:!0,value:r===s};case"!==":return{known:!0,value:r!==s};case"==":return{known:!0,value:r==s};case"!=":return{known:!0,value:r!=s};case"<":return{known:!0,value:r<s};case">":return{known:!0,value:r>s};case"<=":return{known:!0,value:r<=s};case">=":return{known:!0,value:r>=s};case"+":return{known:!0,value:r+s};case"-":return{known:!0,value:r-s};case"*":return{known:!0,value:r*s};case"/":return{known:!0,value:r/s};case"%":return{known:!0,value:r%s};default:return c}}default:return c}}function l(e,n,t){const o=u(e,n);if(o.known)return o;const r=f(e,n,t);return"unknown"===r?c:{known:!0,value:r}}function f(e,n,t){if(!e)return"unknown";switch(e.type){case"UnaryExpression":return"!"===e.operator?m(f(e.argument,n,t)):"unknown";case"LogicalExpression":{const s=f(e.left,n,t),a=()=>f(e.right,n,t);return"&&"===e.operator?!1!==s&&(o=s,r=a(),!1!==o&&!1!==r&&(!0===o&&!0===r||"unknown")):"||"===e.operator?!0===s||function(e,n){return!0===e||!0===n||(!1!==e||!1!==n)&&"unknown"}(s,a()):"unknown"}case"BinaryExpression":{const o=e.operator;if("==="===o||"=="===o||"!=="===o||"!="===o){const r="=="===o||"!="===o,s=function(e,n,t,o,r){const s=p(e,o),a=u(n,t);if(s&&a.known)return d(s,a.value,r);const i=p(n,o),c=u(e,t);return i&&c.known?d(i,c.value,r):c.known&&a.known?r?c.value==a.value:c.value===a.value:"unknown"}(e.left,e.right,n,t,r);return"!=="===o||"!="===o?m(s):s}return"unknown"}default:return"unknown"}var o,r}function p(e,n){return"Identifier"===e?.type&&e.name&&n.has(e.name)?n.get(e.name):null}function d(e,n,t){const o=t?e=>e==n:e=>e===n;return!!e.some(o)&&(!!e.every(o)||"unknown")}function m(e){return!0!==e&&(!1===e||"unknown")}function v(e,n,t){const{arms:o,elseFrag:r}=function(e){const n=[];let t,o=e;for(;o;){n.push({block:o,test:o.test,consequent:o.consequent});const e=o.alternate,r="Fragment"===e?.type&&1===e.nodes?.length&&"IfBlock"===e.nodes[0]?.type&&!0===e.nodes[0].elseif?e.nodes[0]:void 0;r?o=r:("Fragment"===e?.type&&(t=e),o=void 0)}return t?{arms:n,elseFrag:t}:{arms:n}}(e),s=[e.start,e.end],a=o.map((e=>l(e.test,n,t))),i=e=>e.known&&Boolean(e.value),c=e=>e.known&&!e.value;let u=!0;for(let e=0;e<o.length;e++){const n=a[e];if(i(n)&&u){const n=o[e].consequent;return{span:s,kept:n,removed:h(s,w(n)),recurse:!1}}c(n)||(u=!1)}const f=a.findIndex((e=>!c(e)));if(-1===f)return r?{span:s,kept:r,removed:h(s,w(r)),recurse:!1}:{span:s,kept:void 0,removed:[s],recurse:!1};if(0===f)return{span:s,kept:void 0,removed:y(o,a,f),recurse:!0};const p=o[f].block;return{span:s,kept:void 0,removed:[[s[0],p.start],...y(o,a,f)],recurse:!1,headerRewrite:{from:p.start,to:p.test.start,text:"{#if "}}}function h(e,n){if(!n)return[e];const t=[];return e[0]<n[0]&&t.push([e[0],n[0]]),n[1]<e[1]&&t.push([n[1],e[1]]),t}function w(e){const n=e?.nodes??[];return 0===n.length?null:[n[0].start,n[n.length-1].end]}function y(e,n,t){const o=[];for(let r=t+1;r<e.length;r++){const t=n[r];if(!t.known||t.value)continue;const s=e[r],a=e[r+1]?.block,i=a?a.start:g(s.consequent,s.block.end);o.push([s.block.start,i])}return o}function g(e,n){const t=e?.nodes??[];return t.length?t[t.length-1].end:n}function k(e,n){return n.some((([n,t])=>e.start>=n&&e.end<=t))}function x(e,n,t){if(0===n.size&&0===t.size)return[];const o=[];return i(e,null,{IfBlock(e,{next:r}){if(e.elseif||k(e,o))return;const s=v(e,n,t);for(const e of s.removed)o.push(e);s.recurse&&r()}}),o}const b=e=>e.endsWith(".svelte"),S=10,I="escapes as value (e.g. <svelte:component this={X}>)",M="rendered through a barrel/named import (call sites unobservable)";async function E(e,n,t){const o=await async function(e,n,t){const o=new Map,r=Array.isArray(e)?[...e]:[e],s=new Set(r);for(;r.length>0;){const e=r.shift(),a=await t(e),i=await z(e,a,n,t);o.set(e,i);for(const e of[...i.imports.values(),...i.barrelChildIds])s.has(e)||(s.add(e),r.push(e))}return o}(e,n,t),r=new Set;for(const e of o.values())for(const n of e.escapedComponents)r.add(n);for(const e of r){const n=o.get(e);n&&!n.bailReasons.includes(I)&&n.bailReasons.push(I)}const s=new Set;for(const e of o.values())for(const n of e.barrelChildIds)s.add(n);for(const e of s){const n=o.get(e);n&&!n.bailReasons.includes(M)&&n.bailReasons.push(M)}let a=$(o,A(o,new Map));for(let e=0;e<S;e++){const e=$(o,A(o,D(o,a)));if(C(a,e)){a=e;break}a=e}return{models:o,plans:a}}function A(e,n){const t=new Map,o=e=>{let n=t.get(e);return n||(n={sites:[]},t.set(e,n)),n};for(const t of e.values()){const e=n.get(t.id)??[];for(const n of t.childCalls)e.length>0&&k(n.node,e)||o(n.childId).sites.push(O(n.node))}return t}function $(e,n){const t=new Map;for(const o of e.values())t.set(o.id,j(o,n.get(o.id)));return t}function C(e,n){if(e.size!==n.size)return!1;for(const[t,o]of e){const e=n.get(t);if(!e)return!1;if(o.bail!==e.bail)return!1;if(!F(o.constFold,e.constFold))return!1;if(!P(o.narrow,e.narrow))return!1}return!0}function F(e,n){if(e.size!==n.size)return!1;for(const[t,o]of e)if(!n.has(t)||!Object.is(n.get(t),o))return!1;return!0}function P(e,n){if(e.size!==n.size)return!1;for(const[t,o]of e){const e=n.get(t);if(!e||o.length!==e.length)return!1;for(let n=0;n<o.length;n++)if(!Object.is(o[n],e[n]))return!1}return!0}function D(e,n){const t=new Map;for(const o of e.values()){const e=n.get(o.id);if(e.bail)continue;const r=x(o.ast.fragment,e.constFold,e.narrow);r.length>0&&t.set(o.id,r)}return t}async function z(e,n,t,o){const r=a(n,e),s=new Map,c=new Map,u=[];i(r.fragment,null,{SvelteOptions(e,{next:n}){for(const n of e.attributes??[])"Attribute"!==n.type||"accessors"!==n.name&&"customElement"!==n.name||u.push(`<svelte:options ${n.name}>`);n()}});let l,f,p=null,d=!1;const m=new Set,v=r.instance;if(v){for(const n of function*(e){const n=e.content;for(const e of n?.body??[]){if("ImportDeclaration"!==e.type)continue;const n=e.source?.value;if("string"==typeof n)for(const t of e.specifiers??[]){const e=t.local?.name;e&&("ImportDefaultSpecifier"===t.type?yield{value:n,local:e,imported:"default"}:"ImportNamespaceSpecifier"===t.type?yield{value:n,local:e,imported:"*"}:"ImportSpecifier"===t.type&&(yield{value:n,local:e,imported:V(t)??e}))}}}(v)){if(m.add(n.local),"default"===n.imported&&b(n.value)){const o=await t(n.value,e);o&&s.set(n.local,o);continue}const r=await X(n.value,n.imported,e,t,o);r&&c.set(n.local,r)}const n=function(e){const n=e.content;for(const e of n?.body??[])if("VariableDeclaration"===e.type)for(const n of e.declarations??[]){const t=n.init,o=n.id;if("CallExpression"===t?.type&&"Identifier"===t.callee?.type&&"$props"===t.callee.name&&"ObjectPattern"===o?.type)return{declaration:e,pattern:o,sharesStatement:(e.declarations?.length??1)>1}}return null}(v);if(n){l=n.declaration,f=n.pattern,n.sharesStatement&&u.push("$props() shares a multi-declarator statement"),p=[];for(const e of n.pattern.properties??[]){if("RestElement"===e.type){d=!0;continue}if("Property"!==e.type)continue;const n=e.key;if("Identifier"!==n?.type||!n.name)continue;const t=e.value,o="AssignmentPattern"===t?.type?t.right:void 0;p.push({name:n.name,property:e,defaultExpr:o})}}}const h=function(e,n){const t=[];return i(e.fragment,null,{Component(e,{next:o}){const r=e.name?n.get(e.name):void 0;r&&t.push({childId:r,node:e}),o()}}),t}(r,s),w=function(e,n){const t=new Set;return 0===n.size||i(e.fragment,null,{Component(e,{next:o}){const r=e.name?n.get(e.name):void 0;r&&t.add(r),o()}}),t}(r,c),{shadowedNames:y,debugNames:g}=function(e,n,t){const o=new Set,r=new Set;n&&i(n,null,{_(e,{next:n}){if("VariableDeclarator"!==e.type&&"FunctionDeclaration"!==e.type||e===t||"Identifier"!==e.id?.type||!e.id.name||o.add(e.id.name),"FunctionDeclaration"===e.type||"FunctionExpression"===e.type||"ArrowFunctionExpression"===e.type)for(const n of e.params??[])R(n,o);n()}});return i(e.fragment,null,{EachBlock(e,{next:n}){R(e.context,o),"string"==typeof e.index&&o.add(e.index),n()},SnippetBlock(e,{next:n}){"Identifier"===e.expression?.type&&e.expression.name&&o.add(e.expression.name);for(const n of e.parameters??[])R(n,o);n()},AwaitBlock(e,{next:n}){R(e.value,o),R(e.error,o),n()},LetDirective(e,{next:n}){e.name&&o.add(e.name),n()},ConstTag(e,{next:n}){for(const n of e.declaration?.declarations??[])R(n.id,o);n()},DebugTag(e,{next:n}){for(const n of e.identifiers??[])"Identifier"===n.type&&n.name&&r.add(n.name);n()}}),{shadowedNames:o,debugNames:r}}(r,v,l),k=function(e,n,t){const o=new Set,r=e=>{if(!e)return;const t=n.get(e);t&&o.add(t)};i(e.fragment,{parent:null},{_(e,{state:n,next:o}){"Identifier"===e.type&&e.name&&t.has(e.name)&&N(e,n.parent)&&r(e.name),o({parent:e})}}),e.instance&&i(e.instance,{parent:null},{_(e,{state:t,next:o}){"Identifier"===e.type&&e.name&&n.has(e.name)&&N(e,t.parent)&&!L(t.parent)&&r(e.name),o({parent:e})}});return o}(r,s,m);return{id:e,code:n,ast:r,imports:s,props:p,propsDeclaration:l,propsPattern:f,hasRestProp:d,childCalls:h,shadowedNames:y,debugNames:g,escapedComponents:k,barrelChildIds:w,bailReasons:u}}function R(e,n){if(e)switch(e.type){case"Identifier":return void(e.name&&n.add(e.name));case"ObjectPattern":for(const t of e.properties??[])"RestElement"===t.type?R(t.argument,n):"Property"===t.type&&R(t.value??t.key,n);return;case"ArrayPattern":for(const t of e.elements??[])R(t,n);return;case"AssignmentPattern":return void R(e.left,n);case"RestElement":return void R(e.argument,n);default:return}}function N(e,n){return!!n&&(!("MemberExpression"===n.type&&n.property===e&&!n.computed)&&(!("Property"===n.type&&n.key===e&&!n.computed&&!0!==n.shorthand)&&!L(n)))}function L(e){return null!=e&&("ImportSpecifier"===e.type||"ImportDefaultSpecifier"===e.type||"ImportNamespaceSpecifier"===e.type||"ExportSpecifier"===e.type)}function O(e){const n=e.attributes??[];let t=-1;for(let e=0;e<n.length;e++)"SpreadAttribute"===n[e].type&&(t=e);const o=new Map;for(let e=0;e<n.length;e++){const r=n[e],s=r.name;if("BindDirective"===r.type){s&&o.set(s,_(e,t));continue}if("Attribute"!==r.type||!s)continue;const a=B(r.value);o.set(s,a.known?{value:a.value,dynamic:!1,afterLastSpread:e>t}:_(e,t))}for(const r of function(e){const n=e.fragment?.nodes??[],t=[];let o=!1;for(const e of n)if("SnippetBlock"!==e.type){if("Comment"!==e.type){if("Text"===e.type){if(""===(e.data??e.raw??"").trim())continue}o=!0}}else"Identifier"===e.expression?.type&&e.expression.name&&t.push(e.expression.name);o&&t.push("children");return t}(e))o.set(r,_(n.length,t));return{hadSpread:t>=0,explicit:o}}function _(e,n){return{value:void 0,dynamic:!0,afterLastSpread:e>n}}function B(e){if(!0===e)return{known:!0,value:!0};if(null==e)return{known:!1};const n=Array.isArray(e)?e:[e];if(1===n.length){const e=n[0];return"Text"===e.type?{known:!0,value:e.data??e.raw??""}:"ExpressionTag"===e.type&&"Literal"===e.expression?.type?{known:!0,value:e.expression.value}:{known:!1}}let t="";for(const e of n){if("Text"!==e.type)return{known:!1};t+=e.data??e.raw??""}return{known:!0,value:t}}function T(e,n){return e.shadowedNames.has(n)||e.debugNames.has(n)}function j(e,n){const t={id:e.id,bail:!1,reasons:[],constFold:new Map,narrow:new Map,valueSets:new Map};if(e.bailReasons.length>0)return t.bail=!0,t.reasons.push(...e.bailReasons),t;if(!e.props||0===e.props.length)return t;const o=n?.sites??[];if(0===o.length)return t;for(const n of e.props){if(T(e,n.name))continue;const r=W(n,o);t.valueSets.set(n.name,r),r.top||r.dynamic||(1!==r.values.length?r.values.length>=2&&t.narrow.set(n.name,r.values):t.constFold.set(n.name,r.values[0]))}return t}function W(e,n){const t=[];let o=!1,r=!1;const s=e=>{t.some((n=>Object.is(n,e)))||t.push(e)};for(const t of n){const n=t.explicit.get(e.name);if(n?.afterLastSpread){n.dynamic?o=!0:s(n.value);continue}if(t.hadSpread){r=!0;continue}const a=q(e.defaultExpr);a.known?s(a.value):o=!0}return{values:t,dynamic:o,top:r}}function q(e){return e?"Literal"===e.type?{known:!0,value:e.value}:"Identifier"===e.type&&"undefined"===e.name?{known:!0,value:void 0}:{known:!1}:{known:!0,value:void 0}}function V(e){const n=e.imported;return"Identifier"===n?.type&&n.name?n.name:"Literal"===n?.type&&"string"==typeof n.value?n.value:void 0}function J(e){return"Identifier"===e?.type&&e.name?e.name:"Literal"===e?.type&&"string"==typeof e.value?e.value:void 0}const U=8;async function X(e,n,t,o,r,s=0){if(s>U)return null;const i=await o(e,t);if(!i)return null;if(b(e)||b(i))return"default"===n||"*"===n?i:null;let c;try{c=await r(i)}catch{return null}const u=function(e,n){try{const t=a(`<script module>\n${e}\n<\/script>`,n);return t.module?.content?.body??null}catch{return null}}(c,i);if(!u)return null;for(const e of u)if("ExportNamedDeclaration"===e.type&&e.source?.value){for(const t of e.specifiers??[])if(J(t.exported)===n)return X(String(e.source.value),J(t.local)??"default",i,o,r,s+1)}else if("ExportNamedDeclaration"!==e.type||e.source){if("ExportAllDeclaration"===e.type&&e.source?.value){const t=await X(String(e.source.value),n,i,o,r,s+1);if(t)return t}}else for(const t of e.specifiers??[]){if(J(t.exported)!==n)continue;const e=J(t.local);if(!e)continue;const a=G(u,e);return a?X(a.value,a.imported,i,o,r,s+1):null}return null}function G(e,n){for(const t of e){if("ImportDeclaration"!==t.type)continue;const e=t.source?.value;if("string"==typeof e)for(const o of t.specifiers??[])if(o.local?.name===n){if("ImportDefaultSpecifier"===o.type)return{value:e,imported:"default"};if("ImportNamespaceSpecifier"===o.type)return{value:e,imported:"*"};if("ImportSpecifier"===o.type)return{value:e,imported:V(o)??n}}}return null}const H=64;const K=Symbol("unbounded-class-source");function Q(e,n,t){if(!0===e)return K;if(null==e)return new Set;const o=Array.isArray(e)?e:[e];let r=[""];for(const e of o){const o=Y(e,n,t);if(o===K)return K;const s=[];for(const e of r)for(const n of o)if(s.push(e+n),s.length>H)return K;r=s}const s=new Set;for(const e of r)for(const n of e.split(/\s+/))n&&s.add(n);return s}function Y(e,n,t){return"Text"===e.type?new Set([e.data??e.raw??""]):"ExpressionTag"===e.type?function(e,n,t){if(!e)return K;if("Identifier"===e.type&&e.name&&t.has(e.name)){const n=new Set;for(const o of t.get(e.name))n.add(Z(o));return n}const o=u(e,n);return o.known?new Set([Z(o.value)]):K}(e.expression,n,t):K}function Z(e){return String(e)}function ee(e,n,t){const o=e.ast.css;if(!o||!o.children)return 0;const r=function(e,n){const t=new Set;let o=!1;const r=n.constFold,s=n.narrow;return i(e.ast.fragment,null,{_(e,{next:n}){if("RegularElement"===(a=e.type)||"SvelteElement"===a||"Component"===a||"SvelteComponent"===a||"SvelteSelf"===a){var a;for(const n of e.attributes??[]){if("SpreadAttribute"===n.type){o=!0;continue}if("ClassDirective"===n.type){n.name&&t.add(n.name);continue}if("Attribute"!==n.type||"class"!==n.name)continue;const e=Q(n.value,r,s);if(e===K)o=!0;else for(const n of e)t.add(n)}n()}else n()}}),{classes:t,unbounded:o}}(e,n);if(r.unbounded)return 0;let s=0;for(const n of o.children)"Rule"===n.type&&ne(n,r.classes)&&(te(e.code,n,o.children,t),s+=1);return s}function ne(e,n){if(function(e){let n=!1;return i(e,null,{_(e,{next:t}){"PseudoClassSelector"===e.type&&"global"===e.name&&(n=!0),t()}}),n}(e))return!1;const t=e.prelude?.children??[];return 0!==t.length&&t.every((e=>function(e,n){let t=!1;for(const o of e.children??[])for(const e of o.selectors??[])"ClassSelector"===e.type&&e.name&&!n.has(e.name)&&(t=!0);return t}(e,n)))}function te(e,n,t,o){const r=t.indexOf(n),s=t[r-1];let a=n.start;const i=s?s.end:0;for(;a>i&&/\s/.test(e[a-1]);)a-=1;o.remove(a,n.end)}function oe(e,n){return se(e,re(e,n))}function re(e,n){const t=new Map,o=new Map;for(const r of e.values()){const e=new s(r.code);t.set(r.id,e);const a=n.get(r.id);o.set(r.id,a.bail?new Set:le(r,a,e))}for(const n of e.values())he(n,o,t.get(n.id));return t}function se(e,n){const t={};for(const o of e.values())t[o.id]=n.get(o.id).toString();return t}function ae(e,n,t,o){const r=re(e,n);return function(e,n,t,o){const r=new Map;for(const e of n){const n=r.get(e.owner);n?n.push(e):r.set(e.owner,[e])}for(const[n,s]of r){const r=e.get(n),a=o.get(n);if(!r||!a)continue;const i=new Map,c=[];let u=0;for(const e of s){const n=e.node.name??"Cmp";let o=i.get(e.variantId);void 0===o&&(o=`${n}__shaker_v${u++}`,i.set(e.variantId,o),c.push({local:o,spec:t(e.variantId)})),ie(r.code,e.node,o,e.foldedProps,a)}c.length>0&&ue(r,c,a)}}(e,t,o,r),se(e,r)}function ie(e,n,t,o,r){const s=n.name;if(!s)return;const a=n.start+1;e.slice(a,a+s.length)===s&&r.overwrite(a,a+s.length,t);const i=`</${s}`,c=e.lastIndexOf(i,n.end);if(c>=n.start){const e=c+2;r.overwrite(e,e+s.length,t)}for(const t of n.attributes??[])"Attribute"===t.type&&t.name&&o.has(t.name)&&ce(e,t,r)}function ce(e,n,t){let o=n.start;" "!==e[o-1]&&"\t"!==e[o-1]||(o-=1),t.remove(o,n.end)}function ue(e,n,t){const o=n.map((e=>` import ${e.local} from ${JSON.stringify(e.spec)};`)).join("\n"),r=e.ast.instance,s=r?.content?.body??[];if(r&&s.length>0){const e=s[s.length-1];t.appendLeft(e.end,`\n${o}`)}else r&&r.content?t.appendLeft(r.content.start,`\n${o}\n`):t.prepend(`<script>\n${o}\n<\/script>\n`)}function le(e,n,t){return fe(e,n.constFold,n.narrow,n,t)}function fe(e,n,t,o,r){if(0===n.size&&0===t.size)return new Set;const s=e.code,a=[];!function(e,n,t,o,r,s){i(e,null,{IfBlock(e,{next:a}){if(e.elseif||k(e,s))return;const i=v(e,n,t);if(function(e,n,t,o){if(e.kept)return void o.overwrite(e.span[0],e.span[1],function(e,n,t){const o=e?.nodes??[];return 0===o.length?"":pe(o[0].start,o[o.length-1].end,o,n,t)}(e.kept,n,t));for(const[n,t]of e.removed)o.remove(n,t);if(e.headerRewrite){const{from:n,to:t,text:r}=e.headerRewrite;o.overwrite(n,t,r)}}(i,n,o,r),i.kept)s.push(i.span);else for(const e of i.removed)s.push(e);i.recurse&&a()}})}(e.ast.fragment,n,t,s,r,a),function(e,n,t,o,r){if(0===n.size)return;i(e,null,{ConditionalExpression(e,{next:s}){if(k(e,r))return;const a=u(e.test,n);if(!a.known)return void s();const i=a.value?e.consequent:e.alternate;i?(o.overwrite(e.start,e.end,pe(i.start,i.end,[i],n,t)),r.push([e.start,e.end])):s()}})}(e.ast.fragment,n,s,r,a);const c=function(e,n,t){const o=new Map,r=r=>{r&&i(r,{parent:null},{_(r,{state:s,next:a}){"Identifier"===r.type&&r.name&&n.has(r.name)&&!k(r,t)&&r!==e.propsPattern&&!de(r,s.parent)&&(o.get(r.name)??function(e,n){const t=[];return e.set(n,t),t}(o,r.name)).push([r.start,r.end]),a({parent:r})}})};return r(e.ast.instance),r(e.ast.fragment),o}(e,n,a);for(const[e,t]of n)for(const n of c.get(e)??[])r.overwrite(n[0],n[1],ye(t));const l=new Set(n.keys());!function(e,n,t){if(!e.props||0===n.size)return;const o=e.props.filter((e=>!n.has(e.name)));if(0===o.length&&!e.hasRestProp&&e.propsDeclaration)return void function(e,n,t){let o=n.start;for(;o>0&&"\n"!==e[o-1];)o-=1;let r=n.end;for(;r<e.length&&"\n"!==e[r];)r+=1;const s=e.slice(o,n.start),a=e.slice(n.end,r);/^\s*$/.test(s)&&/^\s*;?\s*$/.test(a)?t.remove(o,r<e.length?r+1:r):t.remove(n.start,";"===e[n.end]?n.end+1:n.end)}(e.code,e.propsDeclaration,t);const r=e.propsPattern?.properties??[];for(const o of e.props)n.has(o.name)&&(me(r,o.property,t),e.propsPattern&&ve(e.propsPattern,o.name,t))}(e,l,r);return ee(e,{...o,constFold:n,narrow:t},r),l}function pe(e,n,t,o,r){if(0===o.size)return r.slice(e,n);const s=[];for(const e of t)i(e,{parent:null},{_(e,{state:n,next:t}){"Identifier"===e.type&&e.name&&o.has(e.name)&&!de(e,n.parent)&&s.push({start:e.start,end:e.end,name:e.name}),t({parent:e})}});if(0===s.length)return r.slice(e,n);s.sort(((e,n)=>e.start-n.start));let a="",c=e;for(const e of s)a+=r.slice(c,e.start),a+=ye(o.get(e.name)),c=e.end;return a+=r.slice(c,n),a}function de(e,n){return!!n&&("MemberExpression"===n.type&&n.property===e&&!n.computed||("Property"===n.type&&n.key===e&&!n.computed&&!0!==n.shorthand||("ImportSpecifier"===n.type||"ImportDefaultSpecifier"===n.type||"ImportNamespaceSpecifier"===n.type||"ExportSpecifier"===n.type)))}function me(e,n,t){const o=e.indexOf(n),r=e[o+1],s=e[o-1];r?t.remove(n.start,r.start):s?t.remove(s.end,n.end):t.remove(n.start,n.end)}function ve(e,n,t){const o=e.typeAnnotation?.typeAnnotation?.members??[],r=o.findIndex((e=>"Identifier"===e.key?.type&&e.key.name===n));if(-1===r)return;const s=o[r],a=o[r+1],i=o[r-1];a?t.remove(s.start,a.start):i?t.remove(i.end,s.end):t.remove(s.start,s.end)}function he(e,n,t){i(e.ast.fragment,null,{Component(o,{next:r}){const s=o.name?e.imports.get(o.name):void 0,a=s?n.get(s):void 0;if(a&&a.size>0)for(const n of o.attributes??[])"Attribute"===n.type&&n.name&&a.has(n.name)&&we(n.value)&&ce(e.code,n,t);r()}})}function we(e){if(!0===e||null==e)return!0;return(Array.isArray(e)?e:[e]).every((e=>"Text"===e.type||"ExpressionTag"===e.type&&"Literal"===e.expression?.type))}function ye(e){return void 0===e?"undefined":JSON.stringify(e)}const ge={enabled:!1,maxVariants:8,minSavings:0};function ke(e,n,t=ge,r=[]){const s=new Map,a=[];if(!t.enabled)return{variants:s,bindings:a};const i=D(e,n),c=new Map,u=new Set;for(const t of e.values()){const o=i.get(t.id)??[];for(const r of t.childCalls){if(o.length>0&&k(r.node,o))continue;const s=e.get(r.childId),a=n.get(r.childId);if(!s||!a)continue;if(a.bail||!s.props||0===s.props.length){u.add(r.childId);continue}const i=Se(r.node,s,a);if(0===i.size){u.add(r.childId);continue}const l=Ie(s,a,i);if(l===Ee(s,a)){u.add(r.childId);continue}const f=c.get(r.childId);f?f.push({owner:t.id,node:r.node,shape:i,code:l}):c.set(r.childId,[{owner:t.id,node:r.node,shape:i,code:l}])}}const l=function(e,n){const t=new Map;for(const o of e.values()){const e=n.get(o.id);t.set(o.id,e.bail?o.code:Ee(o,e))}return t}(e,n),f=new Map;for(const n of e.values())f.set(n.id,be(l.get(n.id),n));const p=new Set;for(const e of f.values())for(const n of e)p.add(n);const d=(Array.isArray(r)?r:[r]).filter((n=>e.has(n))).filter((e=>!p.has(e))),m=new Map,v=(e,n)=>{const t=m.get(n);if(void 0!==t)return t;let r;try{const{js:t}=o(n,{generate:"client",dev:!1,filename:e});r=t.code.length}catch{r=null}return null!==r&&m.set(n,r),r},h=new Set;for(const e of c.keys())u.has(e)||h.add(e);for(const[n,o]of c){if(u.has(n))continue;if(o.some((e=>e.owner!==n&&h.has(e.owner))))continue;const r=new Map,i=[];let c=!1;for(const e of o){if(r.has(e.code))continue;if(i.length>=t.maxVariants){c=!0;break}const o=`${n}::v${i.length}`;r.set(e.code,o),i.push({id:o,code:e.code})}if(!c&&xe(n,i,e,l,f,d,v,t.minSavings)){for(const e of i){const t=o.find((n=>n.code===e.code));s.set(e.id,{id:e.id,childId:n,code:e.code,foldedProps:t.shape})}for(const e of o)a.push({owner:e.owner,childId:n,node:e.node,variantId:r.get(e.code),foldedProps:e.shape})}}return{variants:s,bindings:a}}function xe(e,n,t,o,r,s,a,i){const c=t.get(e),u=new Map;for(const e of n)u.set(e.id,be(e.code,c));const l=new Set,f=[...s];for(;f.length>0;){const e=f.pop();if(!l.has(e)){l.add(e);for(const n of r.get(e)??[])f.push(n)}}let p=0;for(const e of l){const n=a(e,o.get(e));if(null===n)return!1;p+=n}const d=n.map((e=>e.id)),m=new Set,v=new Set,h=n=>n===e?{comps:[],vars:d}:{comps:[n],vars:[]},w=[],y=[];for(const e of s){const n=h(e);w.push(...n.comps),y.push(...n.vars)}for(;w.length>0||y.length>0;){if(w.length>0){const e=w.pop();if(m.has(e))continue;m.add(e);for(const n of r.get(e)??[]){const e=h(n);w.push(...e.comps),y.push(...e.vars)}continue}const e=y.pop();if(!v.has(e)){v.add(e);for(const n of u.get(e)??[]){const e=h(n);w.push(...e.comps),y.push(...e.vars)}}}let g=0;for(const e of m){const n=a(e,o.get(e));if(null===n)return!1;g+=n}for(const e of v){const t=n.find((n=>n.id===e)).code,o=a(e,t);if(null===o)return!1;g+=o}return g<p*(1-i)}function be(e,n){let t;try{t=a(e,n.id)}catch{return[]}const o=[];return i(t.fragment,null,{Component(e,{next:t}){const r=e.name?n.imports.get(e.name):void 0;r&&o.push(r),t()}}),o}function Se(e,n,t){const o=O(e),r=new Map;for(const e of n.props??[])r.set(e.name,e);const s=new Map;for(const[e,a]of o.explicit)r.has(e)&&(t.constFold.has(e)||T(n,e)||!a.dynamic&&a.afterLastSpread&&s.set(e,a.value));return s}function Ie(e,n,t){const o=new Map(n.constFold);for(const[e,n]of t)o.set(e,n);const r=new Map;for(const[e,t]of n.narrow)o.has(e)||r.set(e,t);const a=new s(e.code);return fe(e,o,r,n,a),a.toString()}const Me=new WeakMap;function Ee(e,n){const t=Me.get(e);if(void 0!==t)return t;const o=new s(e.code);fe(e,n.constFold,n.narrow,n,o);const r=o.toString();return Me.set(e,r),r}const Ae=(e,t)=>e.startsWith(".")?n.resolve(n.dirname(t),e):null;function $e(t){const o=[];let r;try{r=e.readdirSync(t,{withFileTypes:!0})}catch{return o}for(const e of r){if("node_modules"===e.name||e.name.startsWith("."))continue;const r=n.join(t,e.name);e.isDirectory()?o.push(...$e(r)):e.isFile()&&e.name.endsWith(".svelte")&&o.push(r)}return o}const Ce="shaker_variant";function Fe(t={}){const o=function(e){if(2!==e.level||!e.monomorphize)return ge;const n="object"==typeof e.monomorphize?e.monomorphize:{};return{...ge,enabled:!0,...n}}(t);let r={},s=new Map,a=process.cwd();const i=e=>{const n=e.lastIndexOf("::v"),t=e.slice(0,n),o=e.slice(n+3);return`${t}?${Ce}=${o}`};return{name:"vite-plugin-svelte-shaker",enforce:"pre",apply:"build",configResolved(e){a=e.root},async buildStart(){const c=(t.include??["."]).map((e=>n.resolve(a,e))).flatMap($e);if(0===c.length)return r={},void(s=new Map);const u=n=>e.readFileSync(n,"utf-8");if(!o.enabled)return r=await async function(e,n,t){const{models:o,plans:r}=await E(e,n,t);return oe(o,r)}(c,Ae,u),void(s=new Map);const l=await async function(e,n,t,o=ge,r=(e=>e)){const{models:s,plans:a}=await E(e,n,t),i=ke(s,a,o,e);return{files:0===i.bindings.length?oe(s,a):ae(s,a,i.bindings.map((e=>({owner:e.owner,node:e.node,variantId:e.variantId,foldedProps:e.foldedProps}))),r),mono:i}}(c,Ae,u,o,i);r=l.files,s=new Map;for(const e of l.mono.variants.values())s.set(i(e.id),e.code)},resolveId(e,t){if(!e.includes(`${Ce}=`))return null;const[o,r]=e.split("?");if(!o||!o.endsWith(".svelte"))return null;const s=t?n.dirname(t.split("?")[0]):a;return`${o.startsWith(".")?n.resolve(s,o):o}?${r}`},load:e=>e.includes(`${Ce}=`)?s.get(e)??null:null,transform(e,n){if(n.includes(`${Ce}=`))return null;if(n.includes("svelte&type="))return null;const t=n.split("?")[0];if(!t.endsWith(".svelte"))return null;const o=r[t];return null==o||o===e?null:o}}}export{Fe as shaker};
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "svelte-shaker",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Tree shaking for Svelte components",
|
|
5
|
+
"author": "baseballyama",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"svelte",
|
|
9
|
+
"tree-shaking",
|
|
10
|
+
"tree-shaker",
|
|
11
|
+
"vite-plugin",
|
|
12
|
+
"rollup-plugin",
|
|
13
|
+
"dead-code-elimination"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/baseballyama/svelte-shaker#readme",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/baseballyama/svelte-shaker.git",
|
|
19
|
+
"directory": "packages/svelte-shaker"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/baseballyama/svelte-shaker/issues"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/types/src/index.d.ts",
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/*.js",
|
|
29
|
+
"dist/*.cjs",
|
|
30
|
+
"dist/types/src"
|
|
31
|
+
],
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/types/src/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js",
|
|
39
|
+
"require": "./dist/index.cjs"
|
|
40
|
+
},
|
|
41
|
+
"./vite": {
|
|
42
|
+
"types": "./dist/types/src/vite.d.ts",
|
|
43
|
+
"import": "./dist/vite.js"
|
|
44
|
+
},
|
|
45
|
+
"./node": {
|
|
46
|
+
"types": "./dist/types/src/scan.d.ts",
|
|
47
|
+
"import": "./dist/scan.js"
|
|
48
|
+
},
|
|
49
|
+
"./package.json": "./package.json"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "rollup -c",
|
|
53
|
+
"prepack": "rollup -c",
|
|
54
|
+
"dev": "rollup -cw",
|
|
55
|
+
"test:watch": "vitest",
|
|
56
|
+
"test": "vitest run"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"magic-string": "0.30.10",
|
|
60
|
+
"zimmerframe": "1.1.2"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"svelte": "^5"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@rollup/plugin-commonjs": "25.0.8",
|
|
67
|
+
"@rollup/plugin-node-resolve": "15.2.3",
|
|
68
|
+
"@rollup/plugin-terser": "0.4.4",
|
|
69
|
+
"@rollup/plugin-typescript": "11.1.6",
|
|
70
|
+
"@sveltejs/vite-plugin-svelte": "^4",
|
|
71
|
+
"rollup": "4.18.0",
|
|
72
|
+
"svelte": "^5",
|
|
73
|
+
"tslib": "2.6.2",
|
|
74
|
+
"vite": "^5"
|
|
75
|
+
}
|
|
76
|
+
}
|