@rsvelte/compiler 0.5.1 → 0.6.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/package.json
CHANGED
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
"name": "@rsvelte/compiler",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"description": "A high-performance Rust implementation of the Svelte compiler",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.6.1",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "git+https://github.com/baseballyama/rsvelte.git"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
12
|
+
"rsvelte_core_bg.wasm",
|
|
13
|
+
"rsvelte_core.js",
|
|
14
|
+
"rsvelte_core.d.ts"
|
|
15
15
|
],
|
|
16
|
-
"main": "
|
|
16
|
+
"main": "rsvelte_core.js",
|
|
17
17
|
"homepage": "https://github.com/baseballyama/rsvelte#readme",
|
|
18
|
-
"types": "
|
|
18
|
+
"types": "rsvelte_core.d.ts",
|
|
19
19
|
"sideEffects": [
|
|
20
20
|
"./snippets/*"
|
|
21
21
|
],
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
/* @ts-self-types="./
|
|
1
|
+
/* @ts-self-types="./rsvelte_core.d.ts" */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Result of compiling a Svelte component.
|
|
5
5
|
*/
|
|
6
6
|
export class CompileResultWasm {
|
|
7
7
|
static __wrap(ptr) {
|
|
8
|
-
ptr = ptr >>> 0;
|
|
9
8
|
const obj = Object.create(CompileResultWasm.prototype);
|
|
10
9
|
obj.__wbg_ptr = ptr;
|
|
11
10
|
CompileResultWasmFinalization.register(obj, obj.__wbg_ptr, obj);
|
|
@@ -78,7 +77,6 @@ if (Symbol.dispose) CompileResultWasm.prototype[Symbol.dispose] = CompileResultW
|
|
|
78
77
|
*/
|
|
79
78
|
export class ParseResultWasm {
|
|
80
79
|
static __wrap(ptr) {
|
|
81
|
-
ptr = ptr >>> 0;
|
|
82
80
|
const obj = Object.create(ParseResultWasm.prototype);
|
|
83
81
|
obj.__wbg_ptr = ptr;
|
|
84
82
|
ParseResultWasmFinalization.register(obj, obj.__wbg_ptr, obj);
|
|
@@ -222,14 +220,13 @@ export function version() {
|
|
|
222
220
|
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
|
|
223
221
|
}
|
|
224
222
|
}
|
|
225
|
-
|
|
226
223
|
function __wbg_get_imports() {
|
|
227
224
|
const import0 = {
|
|
228
225
|
__proto__: null,
|
|
229
|
-
|
|
226
|
+
__wbg___wbindgen_throw_1506f2235d1bdba0: function(arg0, arg1) {
|
|
230
227
|
throw new Error(getStringFromWasm0(arg0, arg1));
|
|
231
228
|
},
|
|
232
|
-
|
|
229
|
+
__wbg_error_a6fa202b58aa1cd3: function(arg0, arg1) {
|
|
233
230
|
let deferred0_0;
|
|
234
231
|
let deferred0_1;
|
|
235
232
|
try {
|
|
@@ -240,11 +237,11 @@ function __wbg_get_imports() {
|
|
|
240
237
|
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
|
|
241
238
|
}
|
|
242
239
|
},
|
|
243
|
-
|
|
240
|
+
__wbg_new_227d7c05414eb861: function() {
|
|
244
241
|
const ret = new Error();
|
|
245
242
|
return ret;
|
|
246
243
|
},
|
|
247
|
-
|
|
244
|
+
__wbg_stack_3b0d974bbf31e44f: function(arg0, arg1) {
|
|
248
245
|
const ret = arg1.stack;
|
|
249
246
|
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
250
247
|
const len1 = WASM_VECTOR_LEN;
|
|
@@ -263,16 +260,16 @@ function __wbg_get_imports() {
|
|
|
263
260
|
};
|
|
264
261
|
return {
|
|
265
262
|
__proto__: null,
|
|
266
|
-
"./
|
|
263
|
+
"./rsvelte_core_bg.js": import0,
|
|
267
264
|
};
|
|
268
265
|
}
|
|
269
266
|
|
|
270
267
|
const CompileResultWasmFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
271
268
|
? { register: () => {}, unregister: () => {} }
|
|
272
|
-
: new FinalizationRegistry(ptr => wasm.__wbg_compileresultwasm_free(ptr
|
|
269
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_compileresultwasm_free(ptr, 1));
|
|
273
270
|
const ParseResultWasmFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
274
271
|
? { register: () => {}, unregister: () => {} }
|
|
275
|
-
: new FinalizationRegistry(ptr => wasm.__wbg_parseresultwasm_free(ptr
|
|
272
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_parseresultwasm_free(ptr, 1));
|
|
276
273
|
|
|
277
274
|
let cachedDataViewMemory0 = null;
|
|
278
275
|
function getDataViewMemory0() {
|
|
@@ -283,8 +280,7 @@ function getDataViewMemory0() {
|
|
|
283
280
|
}
|
|
284
281
|
|
|
285
282
|
function getStringFromWasm0(ptr, len) {
|
|
286
|
-
|
|
287
|
-
return decodeText(ptr, len);
|
|
283
|
+
return decodeText(ptr >>> 0, len);
|
|
288
284
|
}
|
|
289
285
|
|
|
290
286
|
let cachedUint8ArrayMemory0 = null;
|
|
@@ -361,8 +357,9 @@ if (!('encodeInto' in cachedTextEncoder)) {
|
|
|
361
357
|
|
|
362
358
|
let WASM_VECTOR_LEN = 0;
|
|
363
359
|
|
|
364
|
-
let wasmModule, wasm;
|
|
360
|
+
let wasmModule, wasmInstance, wasm;
|
|
365
361
|
function __wbg_finalize_init(instance, module) {
|
|
362
|
+
wasmInstance = instance;
|
|
366
363
|
wasm = instance.exports;
|
|
367
364
|
wasmModule = module;
|
|
368
365
|
cachedDataViewMemory0 = null;
|
|
@@ -439,7 +436,7 @@ async function __wbg_init(module_or_path) {
|
|
|
439
436
|
}
|
|
440
437
|
|
|
441
438
|
if (module_or_path === undefined) {
|
|
442
|
-
module_or_path = new URL('
|
|
439
|
+
module_or_path = new URL('rsvelte_core_bg.wasm', import.meta.url);
|
|
443
440
|
}
|
|
444
441
|
const imports = __wbg_get_imports();
|
|
445
442
|
|
|
Binary file
|
package/README.md
DELETED
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
# rsvelte
|
|
2
|
-
|
|
3
|
-
> **⚠️ Early Stage Project** — rsvelte already passes the official Svelte 5 compiler test suite end-to-end, but it's still pre-1.0. APIs, output, and behaviour may change without notice. Use it in production at your own risk.
|
|
4
|
-
|
|
5
|
-
**A Rust port of the official Svelte 5 compiler, built to slot natively into the [OXC](https://oxc.rs/) ecosystem.**
|
|
6
|
-
|
|
7
|
-
## Why rsvelte exists
|
|
8
|
-
|
|
9
|
-
The end goal isn't "another Svelte compiler" — it's making Svelte a first-class citizen of OXC's Rust-native JavaScript/TypeScript toolchain.
|
|
10
|
-
|
|
11
|
-
Today, the native JS toolchain that has grown up around OXC — `oxlint`, `oxfmt`, [Rolldown](https://rolldown.rs/), and [`tsgo`](https://github.com/microsoft/typescript-go) (wired into `oxlint` via [`tsgolint`](https://github.com/oxc-project/tsgolint)) — can only see `.js` / `.ts` / `.jsx` / `.tsx` files. `.svelte` files are invisible to them because parsing Svelte requires running the JavaScript-based Svelte compiler, which native tools can't and won't link against. The result: Svelte developers don't get the order-of-magnitude speed-ups that the rest of the JS ecosystem is starting to take for granted.
|
|
12
|
-
|
|
13
|
-
rsvelte fixes that at the source. By porting the compiler — **and** the surrounding ecosystem hot paths (`svelte2tsx`, `svelte-check`, `vite-plugin-svelte`) — to Rust on top of OXC's own parser, codegen, and semantic stack, rsvelte gives OXC a Svelte surface it can call into directly. Once upstreamed, that surface unlocks:
|
|
14
|
-
|
|
15
|
-
- **`oxlint`** — lint `<script>` blocks and Svelte-specific patterns at OXC speed (a Rust path forward for `eslint-plugin-svelte`).
|
|
16
|
-
- **`oxfmt`** — format `.svelte` files alongside the rest of the project (a Rust path forward for `prettier-plugin-svelte`).
|
|
17
|
-
- **Rolldown** — native bundling of Svelte projects through OXC's parser stack, without a JS-side compiler hop.
|
|
18
|
-
- **`tsgo` + `tsgolint`** — type-checking and type-aware linting over `.svelte` files. Already wired into `@rsvelte/svelte-check` today as the correctness bridge.
|
|
19
|
-
|
|
20
|
-
Until we get there, the drop-in replacement story — `@rsvelte/compiler`, `@rsvelte/svelte-check`, `@rsvelte/vite-plugin-svelte` — lets you use rsvelte today and acts as the correctness bridge that proves the Rust port is byte-identical to upstream Svelte.
|
|
21
|
-
|
|
22
|
-
## Packages
|
|
23
|
-
|
|
24
|
-
All packages ship under the `@rsvelte` scope on npm.
|
|
25
|
-
|
|
26
|
-
| Package | Drop-in for | Status |
|
|
27
|
-
|---|---|---|
|
|
28
|
-
| [`@rsvelte/compiler`](npm/compiler) | [`svelte/compiler`](https://svelte.dev/docs/svelte-compiler) (wasm) | ✅ 100% test compat ([details](#compatibility)) |
|
|
29
|
-
| [`@rsvelte/svelte2tsx`](npm/svelte2tsx) | [`svelte2tsx`](https://github.com/sveltejs/language-tools/tree/master/packages/svelte2tsx) | ✅ 245 / 245 fixtures |
|
|
30
|
-
| [`@rsvelte/svelte-check`](npm/svelte-check) | [`svelte-check`](https://github.com/sveltejs/language-tools/tree/master/packages/svelte-check) CLI | ✅ v1.0 — walker + overlay + tsgo backend + incremental + watch |
|
|
31
|
-
| [`@rsvelte/vite-plugin-svelte`](https://github.com/baseballyama/vite-plugin-svelte/tree/rsvelte) | [`@sveltejs/vite-plugin-svelte`](https://github.com/sveltejs/vite-plugin-svelte) | ✅ v1.0 — fork that routes through the NAPI compiler |
|
|
32
|
-
| [`@rsvelte/vite-plugin-svelte-native`](npm/vite-plugin-svelte-native) | — | NAPI bindings the Vite plugin and other Node tools consume |
|
|
33
|
-
|
|
34
|
-
See [`docs/ecosystem-implementation-plan.md`](docs/ecosystem-implementation-plan.md) for the full ecosystem port plan, including which upstream tools are intentionally **out of scope** (and where they're being routed instead — usually back to OXC).
|
|
35
|
-
|
|
36
|
-
## Quick start
|
|
37
|
-
|
|
38
|
-
### Use as `svelte/compiler` (wasm)
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npm install @rsvelte/compiler
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
```js
|
|
45
|
-
import { compile, compileModule, parse, VERSION } from '@rsvelte/compiler';
|
|
46
|
-
|
|
47
|
-
const result = compile('<h1>Hello, {name}!</h1>', {
|
|
48
|
-
generate: 'client', // or 'server'
|
|
49
|
-
filename: 'App.svelte',
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
console.log(result.js.code);
|
|
53
|
-
console.log(result.css?.code);
|
|
54
|
-
|
|
55
|
-
// Compile a Svelte module (.svelte.js / .svelte.ts)
|
|
56
|
-
const moduleResult = compileModule('export const count = $state(0);', {
|
|
57
|
-
filename: 'counter.svelte.js',
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Parse to AST
|
|
61
|
-
const ast = parse('<h1>Hello</h1>', { modern: true });
|
|
62
|
-
|
|
63
|
-
console.log(VERSION); // upstream Svelte version this build targets
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
The public surface mirrors [`svelte/compiler`](https://svelte.dev/docs/svelte-compiler) — `compile`, `compileModule`, `parse`, and `VERSION` are all available. Output is byte-identical to the official compiler on every in-scope fixture (see [Compatibility](#compatibility)).
|
|
67
|
-
|
|
68
|
-
> **Heads-up:** a few function-valued options can't cross the wasm / NAPI boundary. See [Compiler option compatibility](#compiler-option-compatibility) before passing `cssHash` or `warningFilter`.
|
|
69
|
-
|
|
70
|
-
### Use with Vite
|
|
71
|
-
|
|
72
|
-
[`@rsvelte/vite-plugin-svelte`](https://github.com/baseballyama/vite-plugin-svelte/tree/rsvelte) is a fork of `@sveltejs/vite-plugin-svelte` that swaps in the rsvelte compiler. The public API matches upstream exactly — your `vite.config.js` doesn't need to change.
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
npm install -D @rsvelte/vite-plugin-svelte
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
```js
|
|
79
|
-
// vite.config.js
|
|
80
|
-
import { svelte } from '@rsvelte/vite-plugin-svelte';
|
|
81
|
-
import { defineConfig } from 'vite';
|
|
82
|
-
|
|
83
|
-
export default defineConfig({
|
|
84
|
-
plugins: [svelte()],
|
|
85
|
-
});
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### Use with SvelteKit
|
|
89
|
-
|
|
90
|
-
SvelteKit pulls in `@sveltejs/vite-plugin-svelte` internally, so the cleanest swap is a package-manager override that redirects the upstream plugin to the rsvelte fork. With pnpm:
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
pnpm add -D @rsvelte/vite-plugin-svelte
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
```jsonc
|
|
97
|
-
// package.json
|
|
98
|
-
{
|
|
99
|
-
"pnpm": {
|
|
100
|
-
"overrides": {
|
|
101
|
-
"@sveltejs/vite-plugin-svelte": "npm:@rsvelte/vite-plugin-svelte@^0.1.0"
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Then `pnpm install`. No changes to `vite.config.js` or `svelte.config.js` are needed. (npm and yarn ship equivalent `overrides` / `resolutions` fields if you prefer those.)
|
|
108
|
-
|
|
109
|
-
### Type-check with `svelte-check`
|
|
110
|
-
|
|
111
|
-
`@rsvelte/svelte-check` is a drop-in CLI replacement for `svelte-check`, backed by a Rust walker plus a tsgo overlay for `<script lang="ts">` diagnostics.
|
|
112
|
-
|
|
113
|
-
```bash
|
|
114
|
-
npm install -D @rsvelte/svelte-check
|
|
115
|
-
npx svelte-check
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
Common flags:
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
npx svelte-check --workspace . # type-check the current workspace
|
|
122
|
-
npx svelte-check --tsgo # run tsgo against the .svelte overlay (recommended)
|
|
123
|
-
npx svelte-check --watch # re-check on file changes
|
|
124
|
-
npx svelte-check --incremental # reuse cached overlay between runs
|
|
125
|
-
npx svelte-check --output machine # JSON-friendly output for CI
|
|
126
|
-
npx svelte-check --fail-on-warnings # treat warnings as errors
|
|
127
|
-
npx svelte-check --compiler-warnings "css-unused-selector:ignore"
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
See `npx svelte-check --help` for the full list. The CLI flag set is a superset of upstream's — every upstream flag works, plus a few rsvelte-specific ones (`--tsgo`, `--emit-overlay`).
|
|
131
|
-
|
|
132
|
-
### Convert `.svelte` to `.tsx` (`svelte2tsx`)
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
npm install @rsvelte/svelte2tsx
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
```js
|
|
139
|
-
import { svelte2tsx } from '@rsvelte/svelte2tsx';
|
|
140
|
-
|
|
141
|
-
const result = await svelte2tsx('<h1>Hello, {name}!</h1>', {
|
|
142
|
-
filename: 'App.svelte',
|
|
143
|
-
isTsFile: true,
|
|
144
|
-
mode: 'ts', // or 'dts' to emit a declaration file
|
|
145
|
-
version: '5',
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
console.log(result.code); // the synthesised .tsx
|
|
149
|
-
console.log(result.exportedNames); // { props, all }
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
Useful if you're building your own language tooling on top of the same surface `svelte-check`, the Svelte language server, and `tsc` all rely on.
|
|
153
|
-
|
|
154
|
-
### Embed in a Rust crate
|
|
155
|
-
|
|
156
|
-
```toml
|
|
157
|
-
[dependencies]
|
|
158
|
-
svelte-compiler-rust = { git = "https://github.com/baseballyama/rsvelte" }
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
```rust
|
|
162
|
-
use svelte_compiler_rust::{compile, CompileOptions};
|
|
163
|
-
|
|
164
|
-
let source = r#"<h1>Hello, {name}!</h1>"#;
|
|
165
|
-
let result = compile(source, CompileOptions::default()).unwrap();
|
|
166
|
-
println!("{}", result.js.code);
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
The Rust API is the same surface OXC will eventually wire `oxlint` / `oxfmt` into. Unlike the JS surface, the Rust `CompileOptions` honours **every** field — including `css_hash` and `warning_filter` as real Rust closures.
|
|
170
|
-
|
|
171
|
-
### Call from C / Go / PHP / Ruby / Zig / Java / …
|
|
172
|
-
|
|
173
|
-
A `cdylib` exposing a stable C ABI ships in [`crates/rsvelte_capi`](crates/rsvelte_capi). One shared library + one cbindgen-generated header (`rsvelte.h`) lets any language with a C FFI drive the same compiler — UTF-8 JSON in, UTF-8 JSON out, no per-language schema generation.
|
|
174
|
-
|
|
175
|
-
**Download prebuilt binaries** from [GitHub Releases](https://github.com/baseballyama/rsvelte/releases) under the `capi-vX.Y.Z` tag scheme (`darwin-{arm64,x64}`, `linux-{x64,arm64}-gnu`, `win32-x64-msvc`; each archive ships the dylib + static archive + `rsvelte.h` + checksums):
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
VERSION=0.1.1 TRIPLE=darwin-arm64
|
|
179
|
-
curl -L "https://github.com/baseballyama/rsvelte/releases/download/capi-v${VERSION}/rsvelte_capi-${VERSION}-${TRIPLE}.tar.gz" | tar -xz
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
Or build from source:
|
|
183
|
-
|
|
184
|
-
```bash
|
|
185
|
-
cargo build -p rsvelte_capi --release
|
|
186
|
-
# → target/release/librsvelte_capi.{dylib,so,a}, rsvelte_capi.dll
|
|
187
|
-
# → crates/rsvelte_capi/include/rsvelte.h (regenerated via cbindgen)
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Ready-to-run smoke tests are shipped — and run in CI on every PR — for **C, Go, Python, Ruby, Zig, PHP, and Java (JDK 22+ FFM)**. Drift in the generated header or any `CompileOption` deserializer is caught by 35 cargo integration tests + a `RSVELTE_CAPI_CHECK_HEADER=1` build guard. See [`crates/rsvelte_capi/README.md`](crates/rsvelte_capi/README.md) for the full API, JSON envelope shape, memory ownership rules, and the per-language quick-start table.
|
|
191
|
-
|
|
192
|
-
## Compiler option compatibility
|
|
193
|
-
|
|
194
|
-
The JS-facing surfaces (`@rsvelte/compiler` wasm bundle, `@rsvelte/vite-plugin-svelte-native` NAPI bindings) accept the full `svelte/compiler#CompileOptions` shape, but **function-valued** options can't currently cross the language boundary. The Rust core has no way to call back into JavaScript, so callback-shaped fields are accepted (so the TypeScript types stay drop-in compatible with upstream Svelte) and then **silently ignored**.
|
|
195
|
-
|
|
196
|
-
If your build relies on any of these, the value won't take effect. Use the workarounds below.
|
|
197
|
-
|
|
198
|
-
| Option | Behaviour in rsvelte (JS surface) | Workaround |
|
|
199
|
-
|---|---|---|
|
|
200
|
-
| `cssHash({ hash, name, filename, css }) => string` | Ignored. CSS scope classes fall back to the default `svelte-<base36hash>` scheme — identical to upstream Svelte's default `cssHash`. | Pre-compute the hash on the JS side and pass it as `cssHashOverride: '<hash>'` — an rsvelte-specific extension that injects a deterministic string. |
|
|
201
|
-
| `warningFilter(warning) => boolean` | Ignored. All compiler warnings are returned unfiltered. | Filter `result.warnings` yourself after compilation. |
|
|
202
|
-
|
|
203
|
-
Everything else (`generate`, `css`, `dev`, `hmr`, `sourcemap`, `runes`, `compatibility`, `experimental.async`, `preserveComments`, `preserveWhitespace`, `customElement`, `accessors`, `namespace`, `immutable`, `modernAst`, `discloseVersion`, `outputFilename`, `cssOutputFilename`, …) matches upstream exactly. The full list of accepted fields is mirrored in [`npm/vite-plugin-svelte-native/index.d.ts`](npm/vite-plugin-svelte-native/index.d.ts).
|
|
204
|
-
|
|
205
|
-
The Rust API (`svelte_compiler_rust::compile`) has no such restriction — `css_hash: Option<CssHashFn>` and `warning_filter: Option<WarningFilterFn>` work as real `Arc<dyn Fn>` closures.
|
|
206
|
-
|
|
207
|
-
## Performance
|
|
208
|
-
|
|
209
|
-
Per-task benchmark across 3,637 real `.svelte` files, 10 iterations (3 warmup), against the official `svelte/compiler`:
|
|
210
|
-
|
|
211
|
-
| Task | JS (`svelte/compiler`) | Rust (single-threaded) | Rust (multi-threaded) | Multi vs JS |
|
|
212
|
-
|---|---:|---:|---:|---:|
|
|
213
|
-
| **Full pipeline** — parse / analyze / codegen | 864.8 ms | 381.1 ms | 50.1 ms | **17.3×** |
|
|
214
|
-
| **Parser only** — phase 1, isolated | 187.5 ms | 8.7 ms | 1.9 ms | **99.5×** |
|
|
215
|
-
| **`svelte2tsx`** — `.svelte` → `.tsx` generation | 306.1 ms | 115.3 ms | 16.0 ms | **19.1×** |
|
|
216
|
-
| **`svelte-check`** — CLI, 500-file workspace | 2,088.0 ms | 46.9 ms | 13.8 ms | **151.5×** |
|
|
217
|
-
|
|
218
|
-
> Apple M1 Pro · 10-core arm64 · 3,637 `.svelte` files · 10 iterations (3 warmup). Recorded 2026-05-24 at commit `da6b3c8`. Live numbers, charts, and reproduction steps live on the [benchmark page](https://baseballyama.github.io/rsvelte/benchmark) (or run `node scripts/run-benchmark.mjs > docs/static/benchmark-results.json` locally).
|
|
219
|
-
|
|
220
|
-
A single-threaded **100× speedup** over the JS compiler is one of this project's explicit goals — the parser is already at multi-threaded `99.5×` and `svelte-check` at `151.5×`, but the full pipeline is still climbing. Current numbers are a snapshot, not a ceiling.
|
|
221
|
-
|
|
222
|
-
## Compatibility
|
|
223
|
-
|
|
224
|
-
<!-- svelte-target-version -->
|
|
225
|
-
**Targeting Svelte `v5.55.9`** ([`b65a3f3fc5e1`](https://github.com/sveltejs/svelte/commit/b65a3f3fc5e1)) — automatically maintained by `pnpm run update-docs`.
|
|
226
|
-
<!-- /svelte-target-version -->
|
|
227
|
-
|
|
228
|
-
Current compatibility with the official Svelte compiler test suite:
|
|
229
|
-
|
|
230
|
-
| Test Suite | Pass | Total | Status | Notes |
|
|
231
|
-
|---|---:|---:|---|---|
|
|
232
|
-
| Parser Modern | 22 | 22 | 100% | |
|
|
233
|
-
| Parser Legacy | 82 | 83 | 100% | 1 skipped (acorn vs OXC comment attachment) |
|
|
234
|
-
| Compiler Snapshot | 28 | 28 | 100% | |
|
|
235
|
-
| CSS | 179 | 179 | 100% | |
|
|
236
|
-
| Validator | 324 | 325 | 100% | 1 skipped (`error-mode-warn`) |
|
|
237
|
-
| Compiler Errors | 144 | 144 | 100% | |
|
|
238
|
-
| Runtime Runes | 865 | 865 | 100% | |
|
|
239
|
-
| Runtime Legacy | 1,202 | 1,202 | 100% | |
|
|
240
|
-
| Runtime Browser | 31 | 31 | 100% | |
|
|
241
|
-
| Hydration | 78 | 78 | 100% | |
|
|
242
|
-
| SSR | 82 | 82 | 100% | |
|
|
243
|
-
| Preprocess | 19 | 19 | 100% | |
|
|
244
|
-
| Print | 40 | 40 | 100% | |
|
|
245
|
-
| svelte2tsx | 245 | 245 | 100% | 2 skipped (`expected.error.json` error fixtures) |
|
|
246
|
-
| **Total (in-scope)** | **3,341** | **3,341** | **100%** | |
|
|
247
|
-
| Migrate | 0 | 76 | — | **Out of scope** — rsvelte is a Svelte 5 compiler port, not a 4→5 migrator |
|
|
248
|
-
| Sourcemaps | 0 | 0 | — | No fixtures yet |
|
|
249
|
-
|
|
250
|
-
Re-run `pnpm run test-and-update` to refresh these numbers.
|
|
251
|
-
|
|
252
|
-
## Goals
|
|
253
|
-
|
|
254
|
-
1. **OXC ecosystem integration** — be the Svelte surface that `oxlint`, `oxfmt`, Rolldown, and `tsgo` (via `tsgolint`) all link against. This is the project's reason for existing; everything else is a step toward it.
|
|
255
|
-
2. **100% test compatibility** with the official `svelte/compiler` test suite — keeps the Rust port provably equivalent to upstream while OXC integration lands.
|
|
256
|
-
3. **100× single-threaded speedup** over the JS compiler via Rust + OXC.
|
|
257
|
-
4. **Drop-in replacements** for the ecosystem hot paths (`svelte/compiler`, `svelte-check`, `vite-plugin-svelte`, `svelte2tsx`) so you can adopt rsvelte today without touching the rest of your build.
|
|
258
|
-
5. **Ecosystem port** — see [`docs/ecosystem-implementation-plan.md`](docs/ecosystem-implementation-plan.md) for the multi-wave plan.
|
|
259
|
-
|
|
260
|
-
## Architecture
|
|
261
|
-
|
|
262
|
-
The directory structure mirrors `submodules/svelte/packages/svelte/src/compiler/`:
|
|
263
|
-
|
|
264
|
-
```
|
|
265
|
-
src/compiler/phases/
|
|
266
|
-
├── 1_parse/ # Parsing (Svelte syntax → AST)
|
|
267
|
-
├── 2_analyze/ # Analysis (scope tree, bindings, rune detection)
|
|
268
|
-
└── 3_transform/ # Code generation (AST → JS/CSS, client + SSR)
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
Key design decisions:
|
|
272
|
-
|
|
273
|
-
- JavaScript parsing, semantic analysis, and codegen all run on OXC — the same crates `oxlint` / `oxfmt` use, so the OXC integration target stays cheap.
|
|
274
|
-
- Memory-efficient AST (u32 positions, `compact_str`, `bumpalo`-arena allocation on hot paths).
|
|
275
|
-
- Direct AST passing between phases — no re-parsing.
|
|
276
|
-
- Parallel processing with `rayon`.
|
|
277
|
-
- No backward-compat shims for internal APIs — refactor freely.
|
|
278
|
-
|
|
279
|
-
## Development
|
|
280
|
-
|
|
281
|
-
### Setup
|
|
282
|
-
|
|
283
|
-
```bash
|
|
284
|
-
git submodule update --init --recursive
|
|
285
|
-
git config core.hooksPath .githooks
|
|
286
|
-
pnpm install
|
|
287
|
-
pnpm run generate-fixtures # required before running tests
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### Build & test
|
|
291
|
-
|
|
292
|
-
```bash
|
|
293
|
-
cargo build
|
|
294
|
-
cargo test # all tests
|
|
295
|
-
cargo test --release # recommended for full runs
|
|
296
|
-
cargo test --test parser_fixtures -- --nocapture # single suite
|
|
297
|
-
pnpm run compatibility-report # generate compatibility JSON
|
|
298
|
-
pnpm run test-and-update # refresh report + docs
|
|
299
|
-
./scripts/bench.sh # JS vs Rust benchmark
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
The pre-commit hook (`.githooks/pre-commit`) runs `cargo fmt` and `cargo clippy` automatically.
|
|
303
|
-
|
|
304
|
-
### Docker (optional)
|
|
305
|
-
|
|
306
|
-
A `Dockerfile` and `docker-compose.yml` provide a reproducible toolchain (Rust nightly + Node 22 + pnpm):
|
|
307
|
-
|
|
308
|
-
```bash
|
|
309
|
-
docker compose up -d
|
|
310
|
-
docker compose exec dev bash
|
|
311
|
-
docker compose exec dev cargo test
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
VS Code Dev Containers ("Reopen in Container") also works.
|
|
315
|
-
|
|
316
|
-
### Upgrading Svelte
|
|
317
|
-
|
|
318
|
-
```bash
|
|
319
|
-
./scripts/upgrade-svelte.sh 5.52.0
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
Updates the Svelte submodule, rebuilds, regenerates fixtures, and refreshes the compatibility report.
|
|
323
|
-
|
|
324
|
-
## Known incompatibilities
|
|
325
|
-
|
|
326
|
-
### Parser Legacy: `javascript-comments` (1 test skipped)
|
|
327
|
-
|
|
328
|
-
The official compiler uses acorn and attaches comments to AST nodes as `leadingComments` / `trailingComments`. rsvelte uses OXC, where comments are provided as a separate list. This only affects the legacy AST format (Svelte 4 compatibility mode) and does **not** impact compiled output or runtime behavior.
|
|
329
|
-
|
|
330
|
-
### Validator: `error-mode-warn` (1 test skipped)
|
|
331
|
-
|
|
332
|
-
Tests an error-mode option not yet wired through rsvelte's diagnostic pipeline. Compiled output is unaffected.
|
|
333
|
-
|
|
334
|
-
### svelte2tsx: 2 error-fixture skips
|
|
335
|
-
|
|
336
|
-
Two svelte2tsx fixtures shaped around `expected.error.json` (error-path assertions) are skipped pending a structured error-fixture runner.
|
|
337
|
-
|
|
338
|
-
### Function-valued compiler options (JS surface)
|
|
339
|
-
|
|
340
|
-
See [Compiler option compatibility](#compiler-option-compatibility). The Rust API is unaffected.
|
|
341
|
-
|
|
342
|
-
## License
|
|
343
|
-
|
|
344
|
-
MIT
|
|
File without changes
|