@rsvelte/compiler 0.3.0 → 0.4.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 CHANGED
@@ -1,39 +1,52 @@
1
1
  # rsvelte
2
2
 
3
- > **⚠️ Early Stage Project** — This project can compile a wide range of Svelte components and is fully passing the official compiler test suite, but it is still in an early phase of development. APIs, output, and behavior may change without notice. Not yet recommended for production use.
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
4
 
5
- A Rust port of the official Svelte 5 compiler. Targets **100% test compatibility** with `svelte/compiler` and is designed to slot into the [OXC](https://oxc.rs/) JavaScript/TypeScript toolchain.
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.
6
21
 
7
22
  ## Packages
8
23
 
9
- rsvelte ships drop-in replacements for the main pieces of the Svelte toolchain. All packages are published under the `@rsvelte` scope on npm.
24
+ All packages ship under the `@rsvelte` scope on npm.
10
25
 
11
26
  | Package | Drop-in for | Status |
12
27
  |---|---|---|
13
- | [`@rsvelte/compiler`](npm/compiler) | [`svelte/compiler`](https://svelte.dev/docs/svelte-compiler) | ✅ 100% test compat ([details](#compatibility)) |
28
+ | [`@rsvelte/compiler`](npm/compiler) | [`svelte/compiler`](https://svelte.dev/docs/svelte-compiler) (wasm) | ✅ 100% test compat ([details](#compatibility)) |
14
29
  | [`@rsvelte/svelte2tsx`](npm/svelte2tsx) | [`svelte2tsx`](https://github.com/sveltejs/language-tools/tree/master/packages/svelte2tsx) | ✅ 245 / 245 fixtures |
15
- | [`@rsvelte/svelte-check`](npm/svelte-check) | [`svelte-check`](https://github.com/sveltejs/language-tools/tree/master/packages/svelte-check) CLI | 🟡 In progress (walker + overlay + tsgo backend) |
16
- | [`@rsvelte/vite-plugin-svelte`](https://github.com/baseballyama/vite-plugin-svelte/tree/rsvelte) | [`@sveltejs/vite-plugin-svelte`](https://github.com/sveltejs/vite-plugin-svelte) | 🟡 Fork that swaps in the Rust compiler |
17
- | [`@rsvelte/vite-plugin-svelte-native`](npm/vite-plugin-svelte-native) | — | NAPI bindings consumed by the Vite plugin |
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 |
18
33
 
19
- See [`docs/ecosystem-implementation-plan.md`](docs/ecosystem-implementation-plan.md) for the full ecosystem port plan.
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).
20
35
 
21
- ## Quick Start
36
+ ## Quick start
22
37
 
23
- ### Node.js
38
+ ### Use as `svelte/compiler` (wasm)
24
39
 
25
40
  ```bash
26
41
  npm install @rsvelte/compiler
27
42
  ```
28
43
 
29
- Use it as a drop-in replacement for `svelte/compiler`:
30
-
31
44
  ```js
32
- import { compile, compileModule, parse } from '@rsvelte/compiler';
45
+ import { compile, compileModule, parse, VERSION } from '@rsvelte/compiler';
33
46
 
34
47
  const result = compile('<h1>Hello, {name}!</h1>', {
35
- generate: 'client', // or 'server'
36
- filename: 'App.svelte'
48
+ generate: 'client', // or 'server'
49
+ filename: 'App.svelte',
37
50
  });
38
51
 
39
52
  console.log(result.js.code);
@@ -41,18 +54,22 @@ console.log(result.css?.code);
41
54
 
42
55
  // Compile a Svelte module (.svelte.js / .svelte.ts)
43
56
  const moduleResult = compileModule('export const count = $state(0);', {
44
- filename: 'counter.svelte.js'
57
+ filename: 'counter.svelte.js',
45
58
  });
46
59
 
47
- // Parse into AST
60
+ // Parse to AST
48
61
  const ast = parse('<h1>Hello</h1>', { modern: true });
62
+
63
+ console.log(VERSION); // upstream Svelte version this build targets
49
64
  ```
50
65
 
51
- The API matches the official [`svelte/compiler`](https://svelte.dev/docs/svelte-compiler) — `compile`, `compileModule`, `parse`, and `VERSION` are all available.
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`.
52
69
 
53
- ### Vite
70
+ ### Use with Vite
54
71
 
55
- Use [`@rsvelte/vite-plugin-svelte`](https://github.com/baseballyama/vite-plugin-svelte/tree/rsvelte) — a fork of `@sveltejs/vite-plugin-svelte` that swaps in the Rust compiler:
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.
56
73
 
57
74
  ```bash
58
75
  npm install -D @rsvelte/vite-plugin-svelte
@@ -64,19 +81,19 @@ import { svelte } from '@rsvelte/vite-plugin-svelte';
64
81
  import { defineConfig } from 'vite';
65
82
 
66
83
  export default defineConfig({
67
- plugins: [svelte()]
84
+ plugins: [svelte()],
68
85
  });
69
86
  ```
70
87
 
71
- ### SvelteKit
88
+ ### Use with SvelteKit
72
89
 
73
- SvelteKit imports `@sveltejs/vite-plugin-svelte` internally. Use pnpm `overrides` to redirect it to the rsvelte fork:
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:
74
91
 
75
92
  ```bash
76
93
  pnpm add -D @rsvelte/vite-plugin-svelte
77
94
  ```
78
95
 
79
- ```json
96
+ ```jsonc
80
97
  // package.json
81
98
  {
82
99
  "pnpm": {
@@ -87,42 +104,118 @@ pnpm add -D @rsvelte/vite-plugin-svelte
87
104
  }
88
105
  ```
89
106
 
90
- Then run `pnpm install`. No changes to `vite.config.js` or `svelte.config.js` are needed.
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
+ ```
91
137
 
92
- ### Rust
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
+ ```
93
160
 
94
161
  ```rust
95
- use rsvelte::{compile, CompileOptions};
162
+ use svelte_compiler_rust::{compile, CompileOptions};
96
163
 
97
164
  let source = r#"<h1>Hello, {name}!</h1>"#;
98
165
  let result = compile(source, CompileOptions::default()).unwrap();
99
166
  println!("{}", result.js.code);
100
167
  ```
101
168
 
102
- ## Highlights
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 / …
103
172
 
104
- - **3,341 / 3,341 in-scope tests passing** — every in-scope category of the official Svelte 5 compiler test suite at 100%
105
- - **2.1x faster single-threaded, 15.8x faster multi-threaded** vs the official JS compiler
106
- - **Drop-in replacement** — N-API bindings for seamless use with existing tools (Vite, SvelteKit, …)
107
- - **WASM build** — runs in the browser (used by the docs playground)
108
- - **Ecosystem port underway** — `svelte2tsx` already at 100%; `svelte-check` and `vite-plugin-svelte` shim in progress
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
+ ```bash
176
+ cargo build -p rsvelte_capi --release
177
+ # → target/release/librsvelte_capi.{dylib,so,a}, rsvelte_capi.dll
178
+ # → crates/rsvelte_capi/include/rsvelte.h (regenerated via cbindgen)
179
+ ```
180
+
181
+ 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.
182
+
183
+ ## Compiler option compatibility
184
+
185
+ 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**.
186
+
187
+ If your build relies on any of these, the value won't take effect. Use the workarounds below.
188
+
189
+ | Option | Behaviour in rsvelte (JS surface) | Workaround |
190
+ |---|---|---|
191
+ | `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. |
192
+ | `warningFilter(warning) => boolean` | Ignored. All compiler warnings are returned unfiltered. | Filter `result.warnings` yourself after compilation. |
193
+
194
+ 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).
195
+
196
+ 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.
109
197
 
110
198
  ## Performance
111
199
 
112
- Compile benchmark across 3,654 real Svelte files (average of 3 runs):
200
+ Per-task benchmark across 3,637 real `.svelte` files, 10 iterations (3 warmup), against the official `svelte/compiler`:
113
201
 
114
- | Runner | Time | Throughput | Speedup |
115
- |---|---:|---:|---:|
116
- | **JavaScript (`svelte/compiler`)** | 689 ms | 5,304 files/sec | 1.0× |
117
- | **Rust (single-threaded)** | 333 ms | 10,986 files/sec | **2.1×** |
118
- | **Rust (multi-threaded)** | 44 ms | 83,797 files/sec | **15.8×** |
202
+ | Task | JS (`svelte/compiler`) | Rust (single-threaded) | Rust (multi-threaded) | Multi vs JS |
203
+ |---|---:|---:|---:|---:|
204
+ | **Full pipeline** — parse / analyze / codegen | 864.8 ms | 381.1 ms | 50.1 ms | **17.3×** |
205
+ | **Parser only** — phase 1, isolated | 187.5 ms | 8.7 ms | 1.9 ms | **99.5×** |
206
+ | **`svelte2tsx`** — `.svelte` → `.tsx` generation | 306.1 ms | 115.3 ms | 16.0 ms | **19.1×** |
207
+ | **`svelte-check`** — CLI, 500-file workspace | 2,088.0 ms | 46.9 ms | 13.8 ms | **151.5×** |
119
208
 
120
- > Apple M1 Max · 3,654 files · average of 3 runs. Reproduce locally with `./scripts/bench.sh`.
209
+ > 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).
121
210
 
122
- A single-threaded **100× speedup** over the JS compiler is one of this project's explicit goals — current numbers are a snapshot, not a ceiling.
211
+ 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.
123
212
 
124
213
  ## Compatibility
125
214
 
215
+ <!-- svelte-target-version -->
216
+ **Targeting Svelte `v5.52.0`** ([`cbf4e246fc0d`](https://github.com/sveltejs/svelte/commit/cbf4e246fc0d)) — automatically maintained by `pnpm run update-docs`.
217
+ <!-- /svelte-target-version -->
218
+
126
219
  Current compatibility with the official Svelte compiler test suite:
127
220
 
128
221
  | Test Suite | Pass | Total | Status | Notes |
@@ -149,11 +242,11 @@ Re-run `pnpm run test-and-update` to refresh these numbers.
149
242
 
150
243
  ## Goals
151
244
 
152
- 1. **100% test compatibility** with the official `svelte/compiler` test suite
153
- 2. **100× single-threaded speedup** over the JS compiler via Rust + OXC
154
- 3. **Drop-in replacement** — identical output, N-API bindings, no toolchain changes required
155
- 4. **Ecosystem port** — pluggable into `svelte-check`, `vite-plugin-svelte`, and the wider Svelte tooling chain (see [`docs/ecosystem-implementation-plan.md`](docs/ecosystem-implementation-plan.md))
156
- 5. **OXC integration** — serve as the foundation for Svelte support in OXC's linter, formatter, and bundler ecosystem
245
+ 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.
246
+ 2. **100% test compatibility** with the official `svelte/compiler` test suite — keeps the Rust port provably equivalent to upstream while OXC integration lands.
247
+ 3. **100× single-threaded speedup** over the JS compiler via Rust + OXC.
248
+ 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.
249
+ 5. **Ecosystem port** — see [`docs/ecosystem-implementation-plan.md`](docs/ecosystem-implementation-plan.md) for the multi-wave plan.
157
250
 
158
251
  ## Architecture
159
252
 
@@ -168,11 +261,11 @@ src/compiler/phases/
168
261
 
169
262
  Key design decisions:
170
263
 
171
- - Memory-efficient AST (u32 positions, `compact_str`)
172
- - JavaScript parsing / codegen via OXC
173
- - Direct AST passing between phases — no re-parsing
174
- - Parallel processing with `rayon`
175
- - No backward-compat shims for internal APIs — refactor freely
264
+ - JavaScript parsing, semantic analysis, and codegen all run on OXC — the same crates `oxlint` / `oxfmt` use, so the OXC integration target stays cheap.
265
+ - Memory-efficient AST (u32 positions, `compact_str`, `bumpalo`-arena allocation on hot paths).
266
+ - Direct AST passing between phases — no re-parsing.
267
+ - Parallel processing with `rayon`.
268
+ - No backward-compat shims for internal APIs — refactor freely.
176
269
 
177
270
  ## Development
178
271
 
@@ -233,6 +326,10 @@ Tests an error-mode option not yet wired through rsvelte's diagnostic pipeline.
233
326
 
234
327
  Two svelte2tsx fixtures shaped around `expected.error.json` (error-path assertions) are skipped pending a structured error-fixture runner.
235
328
 
329
+ ### Function-valued compiler options (JS surface)
330
+
331
+ See [Compiler option compatibility](#compiler-option-compatibility). The Rust API is unaffected.
332
+
236
333
  ## License
237
334
 
238
335
  MIT
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@rsvelte/compiler",
3
3
  "type": "module",
4
4
  "description": "A high-performance Rust implementation of the Svelte compiler",
5
- "version": "0.3.0",
5
+ "version": "0.4.0",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
Binary file