svelte-shaker 0.8.0 → 0.9.0
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/README.md +5 -5
- package/dist/svelte_shaker_engine.js +74 -0
- package/dist/svelte_shaker_engine_bg.wasm +0 -0
- package/dist/vite.d.ts +11 -13
- package/dist/vite.js +19 -16
- package/dist/wasm-engine.d.ts +24 -1
- package/dist/wasm-engine.js +55 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -110,11 +110,11 @@ shaker({
|
|
|
110
110
|
shaker({ include: ['src'], level: 1 }); // L2 off (L0/L1/L1.5 only)
|
|
111
111
|
shaker({ include: ['src'], monomorphize: { maxVariants: 16 } }); // raise the variant cap
|
|
112
112
|
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
shaker({ include: ['src'],
|
|
113
|
+
// Engine: the native Rust (WASM) engine runs the whole shake INCLUDING L2 (it
|
|
114
|
+
// calls back to JS only for the net-win gate's compiled-size proxy) and is
|
|
115
|
+
// differentially tested to be byte-identical to the JS engine, so it only changes
|
|
116
|
+
// speed. It is the default ('auto'); force it (or the JS engine) explicitly with:
|
|
117
|
+
shaker({ include: ['src'], engine: 'rust' }); // or engine: 'js'
|
|
118
118
|
|
|
119
119
|
// Opt into the faster rsvelte parser (~1.46x full build, ~2.2x parse).
|
|
120
120
|
// Requires the optional peer `@rsvelte/vite-plugin-svelte-native` (install it
|
|
@@ -73,9 +73,56 @@ function shake_program(input_json) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
exports.shake_program = shake_program;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Whole-program shake WITH L2 monomorphization. `input` is the same shape as
|
|
79
|
+
* `shake_program`; `options_json` is `{enabled, maxVariants, minSavings}`;
|
|
80
|
+
* `own_size(source) -> number | null` is the per-module compiled-byte proxy the
|
|
81
|
+
* net-win gate uses (the JS side runs svelte/compiler, so decisions match the TS
|
|
82
|
+
* engine). Returns `{ files: {id: code}, variants: {specifier: code} }`.
|
|
83
|
+
* @param {string} input_json
|
|
84
|
+
* @param {string} options_json
|
|
85
|
+
* @param {Function} own_size
|
|
86
|
+
* @returns {string}
|
|
87
|
+
*/
|
|
88
|
+
function shake_program_with_mono(input_json, options_json, own_size) {
|
|
89
|
+
let deferred3_0;
|
|
90
|
+
let deferred3_1;
|
|
91
|
+
try {
|
|
92
|
+
const ptr0 = passStringToWasm0(input_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
93
|
+
const len0 = WASM_VECTOR_LEN;
|
|
94
|
+
const ptr1 = passStringToWasm0(options_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
95
|
+
const len1 = WASM_VECTOR_LEN;
|
|
96
|
+
const ret = wasm.shake_program_with_mono(ptr0, len0, ptr1, len1, own_size);
|
|
97
|
+
deferred3_0 = ret[0];
|
|
98
|
+
deferred3_1 = ret[1];
|
|
99
|
+
return getStringFromWasm0(ret[0], ret[1]);
|
|
100
|
+
} finally {
|
|
101
|
+
wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.shake_program_with_mono = shake_program_with_mono;
|
|
76
105
|
function __wbg_get_imports() {
|
|
77
106
|
const import0 = {
|
|
78
107
|
__proto__: null,
|
|
108
|
+
__wbg___wbindgen_number_get_9bb1761122181af2: function(arg0, arg1) {
|
|
109
|
+
const obj = arg1;
|
|
110
|
+
const ret = typeof(obj) === 'number' ? obj : undefined;
|
|
111
|
+
getDataViewMemory0().setFloat64(arg0 + 8 * 1, isLikeNone(ret) ? 0 : ret, true);
|
|
112
|
+
getDataViewMemory0().setInt32(arg0 + 4 * 0, !isLikeNone(ret), true);
|
|
113
|
+
},
|
|
114
|
+
__wbg___wbindgen_throw_1506f2235d1bdba0: function(arg0, arg1) {
|
|
115
|
+
throw new Error(getStringFromWasm0(arg0, arg1));
|
|
116
|
+
},
|
|
117
|
+
__wbg_call_40e4174f169eaca7: function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
|
118
|
+
const ret = arg0.call(arg1, arg2, arg3);
|
|
119
|
+
return ret;
|
|
120
|
+
}, arguments); },
|
|
121
|
+
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
|
122
|
+
// Cast intrinsic for `Ref(String) -> Externref`.
|
|
123
|
+
const ret = getStringFromWasm0(arg0, arg1);
|
|
124
|
+
return ret;
|
|
125
|
+
},
|
|
79
126
|
__wbindgen_init_externref_table: function() {
|
|
80
127
|
const table = wasm.__wbindgen_externrefs;
|
|
81
128
|
const offset = table.grow(4);
|
|
@@ -92,6 +139,20 @@ function __wbg_get_imports() {
|
|
|
92
139
|
};
|
|
93
140
|
}
|
|
94
141
|
|
|
142
|
+
function addToExternrefTable0(obj) {
|
|
143
|
+
const idx = wasm.__externref_table_alloc();
|
|
144
|
+
wasm.__wbindgen_externrefs.set(idx, obj);
|
|
145
|
+
return idx;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let cachedDataViewMemory0 = null;
|
|
149
|
+
function getDataViewMemory0() {
|
|
150
|
+
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
|
|
151
|
+
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
|
|
152
|
+
}
|
|
153
|
+
return cachedDataViewMemory0;
|
|
154
|
+
}
|
|
155
|
+
|
|
95
156
|
function getStringFromWasm0(ptr, len) {
|
|
96
157
|
return decodeText(ptr >>> 0, len);
|
|
97
158
|
}
|
|
@@ -104,6 +165,19 @@ function getUint8ArrayMemory0() {
|
|
|
104
165
|
return cachedUint8ArrayMemory0;
|
|
105
166
|
}
|
|
106
167
|
|
|
168
|
+
function handleError(f, args) {
|
|
169
|
+
try {
|
|
170
|
+
return f.apply(this, args);
|
|
171
|
+
} catch (e) {
|
|
172
|
+
const idx = addToExternrefTable0(e);
|
|
173
|
+
wasm.__wbindgen_exn_store(idx);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function isLikeNone(x) {
|
|
178
|
+
return x === undefined || x === null;
|
|
179
|
+
}
|
|
180
|
+
|
|
107
181
|
function passStringToWasm0(arg, malloc, realloc) {
|
|
108
182
|
if (realloc === undefined) {
|
|
109
183
|
const buf = cachedTextEncoder.encode(arg);
|
|
Binary file
|
package/dist/vite.d.ts
CHANGED
|
@@ -14,8 +14,7 @@ export interface ShakerOptions {
|
|
|
14
14
|
* additionally enables L2 per-call-site monomorphization. **Default `2`** — L2
|
|
15
15
|
* is ON by default because it is bail-safe and never bloats (the measured
|
|
16
16
|
* net-win gate, docs §3 L2). Set `level: 1` (or `0`) to turn L2 OFF, e.g. to
|
|
17
|
-
* trade a little compression for faster builds
|
|
18
|
-
* engine} (which implements L0/L1/L1.5 only).
|
|
17
|
+
* trade a little compression for faster builds.
|
|
19
18
|
*/
|
|
20
19
|
level?: 0 | 1 | 2;
|
|
21
20
|
/**
|
|
@@ -28,17 +27,16 @@ export interface ShakerOptions {
|
|
|
28
27
|
*/
|
|
29
28
|
monomorphize?: boolean | Partial<Omit<MonomorphizeOptions, 'enabled'>>;
|
|
30
29
|
/**
|
|
31
|
-
* Which engine runs the
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* the JS engine, so the choice only affects speed, never what is shaken.
|
|
30
|
+
* Which engine runs the whole shake (analysis + transform, INCLUDING L2).
|
|
31
|
+
* Default `'auto'`. The native Rust (WASM) engine implements every level — for
|
|
32
|
+
* L2 it calls back into JS only for the per-module compiled-size proxy the
|
|
33
|
+
* net-win gate needs — so it is the default fast path:
|
|
34
|
+
* - `'auto'` — use the native Rust engine when it can be loaded; otherwise fall
|
|
35
|
+
* back to the JS engine.
|
|
36
|
+
* - `'rust'` — force the Rust engine; throws if the WASM module can't be loaded.
|
|
37
|
+
* - `'js'` — force the JS engine.
|
|
38
|
+
* Both engines are differentially tested to produce byte-identical output, so the
|
|
39
|
+
* choice only affects speed, never what is shaken.
|
|
42
40
|
*/
|
|
43
41
|
engine?: 'auto' | 'js' | 'rust';
|
|
44
42
|
/**
|
package/dist/vite.js
CHANGED
|
@@ -6,7 +6,7 @@ import { DevShaker } from './engine.js';
|
|
|
6
6
|
import { collectSvelteFiles, fsResolve } from './scan.js';
|
|
7
7
|
import { DEFAULT_MONO_OPTIONS } from './mono.js';
|
|
8
8
|
import { tryLoadRsvelteParser } from './rsvelte-parse.js';
|
|
9
|
-
import { svelteShakerWasm, tryLoadWasmEngine } from './wasm-engine.js';
|
|
9
|
+
import { svelteShakerWasm, svelteShakerWasmWithMono, tryLoadWasmEngine } from './wasm-engine.js';
|
|
10
10
|
/** kB with two decimals, the unit Vite itself uses in its build size report. */
|
|
11
11
|
function formatKB(bytes) {
|
|
12
12
|
return `${(bytes / 1024).toFixed(2)} kB`;
|
|
@@ -269,41 +269,44 @@ export function shaker(options = {}) {
|
|
|
269
269
|
return null;
|
|
270
270
|
return resolved.id.split('?')[0];
|
|
271
271
|
};
|
|
272
|
-
// Decide the engine.
|
|
273
|
-
//
|
|
274
|
-
//
|
|
272
|
+
// Decide the engine. The native Rust engine now implements every level
|
|
273
|
+
// INCLUDING L2 (it calls back to JS only for the compiled-size proxy), so it
|
|
274
|
+
// is the default: `'auto'` uses it whenever it can be loaded and falls back
|
|
275
|
+
// to JS otherwise, `'rust'` forces it (throwing if it can't load), `'js'`
|
|
276
|
+
// forces the JS engine. Both engines produce byte-identical output.
|
|
275
277
|
const engineChoice = options.engine ?? 'auto';
|
|
276
|
-
let effectiveMono = mono;
|
|
277
278
|
let wasm = null;
|
|
278
279
|
if (engineChoice === 'rust') {
|
|
279
|
-
if (mono.enabled) {
|
|
280
|
-
log('engine: "rust" runs L0/L1/L1.5 only — L2 monomorphization (JS-only) is skipped');
|
|
281
|
-
effectiveMono = DEFAULT_MONO_OPTIONS;
|
|
282
|
-
}
|
|
283
280
|
wasm = tryLoadWasmEngine();
|
|
284
281
|
if (!wasm)
|
|
285
282
|
throw new Error('[vite-plugin-svelte-shaker] engine: "rust" was requested but the WASM engine ' +
|
|
286
283
|
'could not be loaded. Remove the option (or use engine: "js") to use the JS engine.');
|
|
287
284
|
}
|
|
288
|
-
else if (engineChoice === 'auto'
|
|
289
|
-
// Auto: prefer the native engine on the L2-off path; fall back to JS silently.
|
|
285
|
+
else if (engineChoice === 'auto') {
|
|
290
286
|
wasm = tryLoadWasmEngine();
|
|
291
287
|
}
|
|
292
288
|
if (wasm) {
|
|
293
|
-
// Native Rust
|
|
294
|
-
|
|
295
|
-
|
|
289
|
+
// Native Rust engine — byte-identical to the JS engine, including L2.
|
|
290
|
+
if (mono.enabled) {
|
|
291
|
+
const result = await svelteShakerWasmWithMono(wasm, entries, resolve, read, mono, getParse());
|
|
292
|
+
shaken = result.files;
|
|
293
|
+
variantSources = result.variants;
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
shaken = await svelteShakerWasm(wasm, entries, resolve, read, getParse());
|
|
297
|
+
variantSources = new Map();
|
|
298
|
+
}
|
|
296
299
|
reportSizes(shaken, read, root, options.verbose === true, log);
|
|
297
300
|
return;
|
|
298
301
|
}
|
|
299
|
-
if (!
|
|
302
|
+
if (!mono.enabled) {
|
|
300
303
|
// JS engine, L2 off: byte-for-byte the L0/L1/L1.5 output.
|
|
301
304
|
shaken = await svelteShaker(entries, resolve, read, getParse());
|
|
302
305
|
variantSources = new Map();
|
|
303
306
|
reportSizes(shaken, read, root, options.verbose === true, log);
|
|
304
307
|
return;
|
|
305
308
|
}
|
|
306
|
-
const result = await svelteShakerWithMono(entries, resolve, read,
|
|
309
|
+
const result = await svelteShakerWithMono(entries, resolve, read, mono, variantSpecifier, getParse());
|
|
307
310
|
shaken = result.files;
|
|
308
311
|
variantSources = new Map();
|
|
309
312
|
for (const v of result.mono.variants.values())
|
package/dist/wasm-engine.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { type ReadFile, type Resolve } from './analyze.js';
|
|
2
2
|
import { type Parse } from './parse.js';
|
|
3
|
+
import { type MonomorphizeOptions } from './mono.js';
|
|
3
4
|
import type { ComponentId } from './ir.js';
|
|
4
|
-
/** The subset of the WASM exports the plugin uses (docs/RUST-MIGRATION.md M5). */
|
|
5
|
+
/** The subset of the WASM exports the plugin uses (docs/RUST-MIGRATION.md M5+). */
|
|
5
6
|
interface WasmEngine {
|
|
6
7
|
/** Whole-program L0/L1/L1.5 shake: input JSON in, `{id: shakenCode}` JSON out. */
|
|
7
8
|
shake_program: (inputJson: string) => string;
|
|
9
|
+
/**
|
|
10
|
+
* Whole-program shake WITH L2 monomorphization. `ownSize(id, source)` is the
|
|
11
|
+
* per-module compiled-byte proxy the net-win gate calls back into JS for (the
|
|
12
|
+
* Svelte compiler has no in-WASM equivalent); returns
|
|
13
|
+
* `{ files: {id: code}, variants: {specifier: code} }` JSON.
|
|
14
|
+
*/
|
|
15
|
+
shake_program_with_mono: (inputJson: string, optionsJson: string, ownSize: (id: string, source: string) => number | null) => string;
|
|
8
16
|
}
|
|
9
17
|
/**
|
|
10
18
|
* Load the native Rust (WASM) engine, or `null` if it can't be loaded (no built
|
|
@@ -30,4 +38,19 @@ export declare function tryLoadWasmEngine(): WasmEngine | null;
|
|
|
30
38
|
* the build.
|
|
31
39
|
*/
|
|
32
40
|
export declare function svelteShakerWasm(engine: WasmEngine, entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile, parse?: Parse): Promise<Record<ComponentId, string>>;
|
|
41
|
+
/** The output of a Rust L2 shake: the wired owner files + the variant residuals
|
|
42
|
+
* keyed by their request specifier (what the Shell's `load` hook serves). */
|
|
43
|
+
export interface WasmMonoResult {
|
|
44
|
+
files: Record<ComponentId, string>;
|
|
45
|
+
variants: Map<string, string>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Whole-program shake WITH L2 monomorphization, run entirely in the native Rust
|
|
49
|
+
* engine — the counterpart of {@link svelteShakerWithMono}. The crawl/resolution
|
|
50
|
+
* stays in JS; the Rust engine does the analysis, the L2 graph/gate, and the
|
|
51
|
+
* call-site rewrite, calling back into JS only for {@link ownSize} (the Svelte
|
|
52
|
+
* compiler). Feeding it the same compiler the JS engine uses makes the result
|
|
53
|
+
* byte-identical (pinned by the differential `wasm-mono` test).
|
|
54
|
+
*/
|
|
55
|
+
export declare function svelteShakerWasmWithMono(engine: WasmEngine, entries: ComponentId | ComponentId[], resolve: Resolve, readFile: ReadFile, mono: MonomorphizeOptions, parse?: Parse): Promise<WasmMonoResult>;
|
|
33
56
|
export {};
|
package/dist/wasm-engine.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
+
import { compile } from 'svelte/compiler';
|
|
2
3
|
import { buildAnalyzeInput } from './analyze.js';
|
|
3
4
|
import { parseCached, parseSvelte } from './parse.js';
|
|
5
|
+
import {} from './mono.js';
|
|
4
6
|
// NODE-ONLY: loads the native Rust (WASM) engine and drives it from the Vite
|
|
5
7
|
// plugin. Imported only by `vite.ts` (a Node entry), never by the environment-free
|
|
6
8
|
// engine (`index.ts`/`analyze.ts`), so the browser playground build stays clean.
|
|
@@ -19,8 +21,12 @@ export function tryLoadWasmEngine() {
|
|
|
19
21
|
try {
|
|
20
22
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
21
23
|
const mod = require(spec);
|
|
22
|
-
if (typeof mod.shake_program === 'function'
|
|
23
|
-
|
|
24
|
+
if (typeof mod.shake_program === 'function' &&
|
|
25
|
+
typeof mod.shake_program_with_mono === 'function')
|
|
26
|
+
return {
|
|
27
|
+
shake_program: mod.shake_program.bind(mod),
|
|
28
|
+
shake_program_with_mono: mod.shake_program_with_mono.bind(mod),
|
|
29
|
+
};
|
|
24
30
|
}
|
|
25
31
|
catch {
|
|
26
32
|
// Try the next location.
|
|
@@ -28,6 +34,16 @@ export function tryLoadWasmEngine() {
|
|
|
28
34
|
}
|
|
29
35
|
return null;
|
|
30
36
|
}
|
|
37
|
+
/** The compiled-byte size proxy the L2 net-win gate uses — the same call
|
|
38
|
+
* `mono.ts` makes, so the Rust gate decides byte-for-byte like the JS engine. */
|
|
39
|
+
function ownSize(id, source) {
|
|
40
|
+
try {
|
|
41
|
+
return compile(source, { generate: 'client', dev: false, filename: id }).js.code.length;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
31
47
|
/**
|
|
32
48
|
* Whole-program shake via the native Rust engine — the L0/L1/L1.5 counterpart of
|
|
33
49
|
* {@link svelteShaker} (L2 lives only in the JS engine). The crawl/resolution
|
|
@@ -54,7 +70,43 @@ export async function svelteShakerWasm(engine, entries, resolve, readFile, parse
|
|
|
54
70
|
entries: input.entries,
|
|
55
71
|
};
|
|
56
72
|
const out = JSON.parse(engine.shake_program(JSON.stringify(programInput)));
|
|
57
|
-
|
|
73
|
+
revertUnparseable(out, input.files);
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Whole-program shake WITH L2 monomorphization, run entirely in the native Rust
|
|
78
|
+
* engine — the counterpart of {@link svelteShakerWithMono}. The crawl/resolution
|
|
79
|
+
* stays in JS; the Rust engine does the analysis, the L2 graph/gate, and the
|
|
80
|
+
* call-site rewrite, calling back into JS only for {@link ownSize} (the Svelte
|
|
81
|
+
* compiler). Feeding it the same compiler the JS engine uses makes the result
|
|
82
|
+
* byte-identical (pinned by the differential `wasm-mono` test).
|
|
83
|
+
*/
|
|
84
|
+
export async function svelteShakerWasmWithMono(engine, entries, resolve, readFile, mono, parse) {
|
|
85
|
+
const cache = new Map();
|
|
86
|
+
const input = await buildAnalyzeInput(entries, resolve, readFile, cache, parse);
|
|
87
|
+
const programInput = {
|
|
88
|
+
files: input.files.map((f) => ({
|
|
89
|
+
id: f.id,
|
|
90
|
+
ast: parseCached(f.id, f.code, cache, parse),
|
|
91
|
+
code: f.code,
|
|
92
|
+
})),
|
|
93
|
+
edges: input.edges,
|
|
94
|
+
entries: input.entries,
|
|
95
|
+
};
|
|
96
|
+
const options = JSON.stringify({
|
|
97
|
+
enabled: mono.enabled,
|
|
98
|
+
maxVariants: mono.maxVariants,
|
|
99
|
+
minSavings: mono.minSavings,
|
|
100
|
+
});
|
|
101
|
+
const result = JSON.parse(engine.shake_program_with_mono(JSON.stringify(programInput), options, ownSize));
|
|
102
|
+
revertUnparseable(result.files, input.files);
|
|
103
|
+
return { files: result.files, variants: new Map(Object.entries(result.variants)) };
|
|
104
|
+
}
|
|
105
|
+
/** Self-check: revert any emitted file that no longer parses to its original — a
|
|
106
|
+
* sound "did not shake this component", mirroring the JS engine's last line of
|
|
107
|
+
* defense ({@link svelteShaker}'s `revertUnparseable`). */
|
|
108
|
+
function revertUnparseable(out, files) {
|
|
109
|
+
for (const file of files) {
|
|
58
110
|
const code = out[file.id];
|
|
59
111
|
if (code === undefined || code === file.code)
|
|
60
112
|
continue;
|
|
@@ -65,5 +117,4 @@ export async function svelteShakerWasm(engine, entries, resolve, readFile, parse
|
|
|
65
117
|
out[file.id] = file.code;
|
|
66
118
|
}
|
|
67
119
|
}
|
|
68
|
-
return out;
|
|
69
120
|
}
|