Skip to content

Replace wasmer with wasmtime for SWC plugin WASM engine#91533

Merged
lukesandberg merged 3 commits into
canaryfrom
wasmer_to_wasmtime
Apr 9, 2026
Merged

Replace wasmer with wasmtime for SWC plugin WASM engine#91533
lukesandberg merged 3 commits into
canaryfrom
wasmer_to_wasmtime

Conversation

@lukesandberg
Copy link
Copy Markdown
Contributor

@lukesandberg lukesandberg commented Mar 17, 2026

What?

Replace wasmer with wasmtime as the WASM runtime for SWC plugins.

Why?

  • Wasmer has been a source of build complexity due to conflicting feature flags (wasmer/default vs wasmer/js-default) and large binary size
  • Wasmtime is actively maintained by the Bytecode Alliance with better performance and smaller footprint
  • The upstream SWC project already provides swc_plugin_backend_wasmtime on crates.io, so we can use it directly instead of maintaining our own implementation

How?

  • Use the upstream swc_plugin_backend_wasmtime v9.0.0 crate from crates.io instead of a custom local implementation
  • Replace swc_core/plugin_transform_host_native (which pulls in wasmer via __plugin_transform_env_native) with granular features: __plugin_transform_host + __plugin_transform_host_schema_v1
  • Provide WasmtimeRuntime explicitly in both the NAPI bindings and turbopack plugin paths
  • Remove all direct wasmtime/wasmtime-wasi workspace dependencies — the upstream crate manages its own wasmtime version
  • Replace wasmer-related release profile size optimizations with equivalent wasmtime ones (swc_plugin_backend_wasmtime, swc_plugin_runner, wasmtime, wasmtime-internal-cranelift)
  • Pin vergen = "=9.0.6" to avoid a vergen-lib version conflict with vergen-gitcl 1.0.8

Binary size impact (darwin-arm64, compared against current canary)

Metric Canary Wasmtime (this PR) Delta
Unstripped dylib 133.1 MB 128.2 MB -5.0 MB (-3.7%)
Stripped 91.2 MB 86.9 MB -4.3 MB (-4.8%)
Stripped + gzip (npm) 30.9 MB 29.4 MB -1.5 MB (-5.0%)

@nextjs-bot nextjs-bot added created-by: Turbopack team PRs by the Turbopack team. Turbopack Related to Turbopack with Next.js. labels Mar 17, 2026
Copy link
Copy Markdown
Contributor Author

lukesandberg commented Mar 17, 2026

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Mar 17, 2026

Merging this PR will not alter performance

✅ 17 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing wasmer_to_wasmtime (b82c568) with canary (9cf9a63)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Comment thread crates/swc-plugin-backend-wasmtime/src/lib.rs Outdated
@sack1580-oss
Copy link
Copy Markdown

85hu5WTLEudeW1exQBcXch4HMYk422wo7nJxej6AwdJC

@lukesandberg lukesandberg force-pushed the update_swc_to_support_plugins branch from 3a26fe4 to 48d399c Compare March 27, 2026 18:32
@lukesandberg lukesandberg force-pushed the update_swc_to_support_plugins branch from 48d399c to dc7f06b Compare March 27, 2026 20:09
Comment thread crates/swc-plugin-backend-wasmtime/Cargo.toml Outdated
@lukesandberg lukesandberg force-pushed the wasmer_to_wasmtime branch 2 times, most recently from 556ef7f to 76f0037 Compare March 31, 2026 07:37
@lukesandberg lukesandberg force-pushed the update_swc_to_support_plugins branch from dc7f06b to 74a9431 Compare March 31, 2026 07:37
@nextjs-bot
Copy link
Copy Markdown
Contributor

nextjs-bot commented Mar 31, 2026

Stats from current PR

✅ No significant changes detected

📊 All Metrics
📖 Metrics Glossary

Dev Server Metrics:

  • Listen = TCP port starts accepting connections
  • First Request = HTTP server returns successful response
  • Cold = Fresh build (no cache)
  • Warm = With cached build artifacts

Build Metrics:

  • Fresh = Clean build (no .next directory)
  • Cached = With existing .next directory

Change Thresholds:

  • Time: Changes < 50ms AND < 10%, OR < 2% are insignificant
  • Size: Changes < 1KB AND < 1% are insignificant
  • All other changes are flagged to catch regressions

⚡ Dev Server

Metric Canary PR Change Trend
Cold (Listen) 455ms 456ms ▁█▅▅▅
Cold (Ready in log) 444ms 444ms ▁▂▅▇▄
Cold (First Request) 1.089s 1.125s ▆▁▁▂▂
Warm (Listen) 456ms 457ms █▁██▁
Warm (Ready in log) 445ms 444ms ▂▂▇█▁
Warm (First Request) 349ms 347ms ▃▄▇█▄
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 455ms 455ms ▁▁▅▁▅
Cold (Ready in log) 436ms 436ms ▁▃▆▂▂
Cold (First Request) 1.869s 1.864s ▇▇▇▆▁
Warm (Listen) 456ms 456ms ▁▁▁▁▁
Warm (Ready in log) 436ms 436ms ▁▂▅▁▂
Warm (First Request) 1.861s 1.877s ▆▇█▆▁

⚡ Production Builds

Metric Canary PR Change Trend
Fresh Build 3.851s 3.911s ▇▆██▁
Cached Build 3.956s 3.934s ▄███▄
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 14.395s 14.520s ▁▂▅▂▂
Cached Build 14.537s 14.583s ▁▂▇▃▄
node_modules Size 492 MB 492 MB █████
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles
Canary PR Change
0-xemccipzit0.js gzip 13 kB N/A -
00-sfwc7hh0nr.js gzip 12.9 kB N/A -
0cz1d0mv5g_q7.js gzip 39.4 kB 39.4 kB
0f46ogy4oqhbg.js gzip 155 B N/A -
0ksxp7o1hx8pf.js gzip 167 B N/A -
0z6zj7qe8s-s2.js gzip 157 B N/A -
10pt4w_1muouh.js gzip 155 B N/A -
13_2qj6sfhcpr.js gzip 8.51 kB N/A -
13q15tdry7-jw.js gzip 9.81 kB N/A -
16jdy7mb2hpzo.js gzip 2.28 kB N/A -
16lhqjoqbznyg.js gzip 220 B 220 B
17c6iioxxyq_7.js gzip 8.51 kB N/A -
1c5i9886ljzvw.js gzip 152 B N/A -
1dckul_xzwsx_.js gzip 155 B N/A -
1e9hak60wi8_q.js gzip 10.1 kB N/A -
1elt1qium-r2m.css gzip 115 B 115 B
1f8lc2681z647.js gzip 154 B N/A -
1lti_nqcijcmi.js gzip 225 B N/A -
1m9l9vnf18-38.js gzip 49 kB N/A -
1zq04q8id1dsq.js gzip 8.59 kB N/A -
246le60fytek6.js gzip 1.46 kB N/A -
25c1ukc-mir52.js gzip 70.8 kB N/A -
2ipc3se0d9mja.js gzip 7.61 kB N/A -
2rm1ibbmjhlgi.js gzip 8.57 kB N/A -
2yj-ir7tizolc.js gzip 157 B N/A -
2zm7mepei8xku.js gzip 158 B N/A -
30ls8fwpkrnuy.js gzip 155 B N/A -
30z650ayitjz3.js gzip 5.67 kB N/A -
33ur7c3w36-m-.js gzip 8.56 kB N/A -
35rz-np9njhmv.js gzip 158 B N/A -
36acwz6drq2cy.js gzip 154 B N/A -
377ai6ro_mien.js gzip 65.5 kB N/A -
396buwq-nlhir.js gzip 8.59 kB N/A -
39mk1fjm58e-4.js gzip 8.62 kB N/A -
3cq10epinkxrc.js gzip 450 B N/A -
3t2qn5usd1f-e.js gzip 156 B N/A -
3wc1tgfurjhi-.js gzip 9.23 kB N/A -
3ze9s70gat6n_.js gzip 8.56 kB N/A -
41obdnb4lqdgs.js gzip 13.3 kB N/A -
454bom347xpxj.js gzip 13.8 kB N/A -
457x5n-k0jr1x.js gzip 10.4 kB N/A -
turbopack-00..186v.js gzip 4.17 kB N/A -
turbopack-0e..0dyk.js gzip 4.19 kB N/A -
turbopack-0l..avon.js gzip 4.17 kB N/A -
turbopack-0n..lai4.js gzip 4.17 kB N/A -
turbopack-16..zovp.js gzip 4.17 kB N/A -
turbopack-1a..polh.js gzip 4.17 kB N/A -
turbopack-1s..l6v_.js gzip 4.17 kB N/A -
turbopack-1v..cs5b.js gzip 4.17 kB N/A -
turbopack-2-..z_z1.js gzip 4.17 kB N/A -
turbopack-25..h1ve.js gzip 4.17 kB N/A -
turbopack-28..pcbs.js gzip 4.17 kB N/A -
turbopack-2z..hyi3.js gzip 4.17 kB N/A -
turbopack-3t..arfg.js gzip 4.16 kB N/A -
turbopack-44..b1wm.js gzip 4.18 kB N/A -
0_1u_xrpzaeaj.js gzip N/A 8.52 kB -
0-ua_-urjvdtw.js gzip N/A 8.56 kB -
001tqi6o0z1t9.js gzip N/A 156 B -
05_r_-_rf4w-n.js gzip N/A 7.61 kB -
0eihfygkvyao-.js gzip N/A 1.46 kB -
0g_88ua4o_jp-.js gzip N/A 9.24 kB -
0kvc43dagz7f6.js gzip N/A 157 B -
0pk3a0imvjxyg.js gzip N/A 156 B -
0ua91j3aes80c.js gzip N/A 8.58 kB -
0zwsxw6xkvw9p.js gzip N/A 8.62 kB -
1_fyx0hi94qc-.js gzip N/A 49 kB -
10mvvt3xn1_3j.js gzip N/A 8.59 kB -
13v7vkq3viabh.js gzip N/A 70.8 kB -
13wh3pltnc-77.js gzip N/A 156 B -
1904znfimgi15.js gzip N/A 156 B -
1b75ishu64v5s.js gzip N/A 13 kB -
1b83ah3nflxjf.js gzip N/A 8.52 kB -
1brt7q153-qnw.js gzip N/A 65.5 kB -
1f2et7pys_lyt.js gzip N/A 157 B -
1fd23spooi5r7.js gzip N/A 225 B -
1nw99o32asytf.js gzip N/A 450 B -
1owct1x5874t1.js gzip N/A 157 B -
1uboyp1_w1n83.js gzip N/A 161 B -
1x-xn_y5xlhug.js gzip N/A 151 B -
1y6qa6xp0i1nz.js gzip N/A 13.3 kB -
25hjq5q0t8w_3.js gzip N/A 168 B -
2kxdvc3gr7nt9.js gzip N/A 8.59 kB -
2qn63kv8i9bty.js gzip N/A 155 B -
2sgg_sxyixu_p.js gzip N/A 13.8 kB -
2sk4gp5rmalb0.js gzip N/A 10.1 kB -
2u87ln5_zfir_.js gzip N/A 5.67 kB -
2w3dswowq0syc.js gzip N/A 162 B -
33602db0fe2xi.js gzip N/A 9.81 kB -
3j3snr-ce7e0q.js gzip N/A 10.4 kB -
3ousyyvaw2mdb.js gzip N/A 156 B -
3yby446qbgls0.js gzip N/A 8.56 kB -
3yypm2pwzx0mq.js gzip N/A 12.9 kB -
42lado0_6oegq.js gzip N/A 2.28 kB -
turbopack-08..x59c.js gzip N/A 4.18 kB -
turbopack-0d..8f4o.js gzip N/A 4.18 kB -
turbopack-0f..c2p2.js gzip N/A 4.16 kB -
turbopack-0g..u8-b.js gzip N/A 4.19 kB -
turbopack-0q.._3ql.js gzip N/A 4.18 kB -
turbopack-1n..l6b0.js gzip N/A 4.18 kB -
turbopack-1s..rst2.js gzip N/A 4.18 kB -
turbopack-2n..9un6.js gzip N/A 4.18 kB -
turbopack-2r..9e9u.js gzip N/A 4.18 kB -
turbopack-2w.._-oe.js gzip N/A 4.18 kB -
turbopack-3-..hz1i.js gzip N/A 4.18 kB -
turbopack-34..c6vu.js gzip N/A 4.18 kB -
turbopack-37..pbkw.js gzip N/A 4.18 kB -
turbopack-40..gi5p.js gzip N/A 4.18 kB -
Total 464 kB 465 kB ⚠️ +135 B

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 714 B 719 B
Total 714 B 719 B ⚠️ +5 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 435 B 434 B
Total 435 B 434 B ✅ -1 B

📦 Webpack

Client

Main Bundles
Canary PR Change
1011-HASH.js gzip 5.58 kB N/A -
2168.HASH.js gzip 169 B N/A -
2225-HASH.js gzip 4.64 kB N/A -
61a8f394-HASH.js gzip 62.8 kB N/A -
850-HASH.js gzip 60.6 kB N/A -
framework-HASH.js gzip 59.7 kB 59.7 kB
main-app-HASH.js gzip 257 B 251 B 🟢 6 B (-2%)
main-HASH.js gzip 39.3 kB 39.6 kB
webpack-HASH.js gzip 1.68 kB 1.68 kB
36c7d9a6-HASH.js gzip N/A 62.8 kB -
3967-HASH.js gzip N/A 4.63 kB -
5025-HASH.js gzip N/A 5.58 kB -
634-HASH.js gzip N/A 60.9 kB -
7586.HASH.js gzip N/A 170 B -
Total 235 kB 235 kB ⚠️ +607 B
Polyfills
Canary PR Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Total 39.4 kB 39.4 kB
Pages
Canary PR Change
_app-HASH.js gzip 194 B 194 B
_error-HASH.js gzip 182 B 181 B
css-HASH.js gzip 334 B 333 B
dynamic-HASH.js gzip 1.8 kB 1.81 kB
edge-ssr-HASH.js gzip 255 B 254 B
head-HASH.js gzip 352 B 352 B
hooks-HASH.js gzip 384 B 384 B
image-HASH.js gzip 580 B 581 B
index-HASH.js gzip 259 B 259 B
link-HASH.js gzip 2.52 kB 2.52 kB
routerDirect..HASH.js gzip 320 B 317 B
script-HASH.js gzip 386 B 386 B
withRouter-HASH.js gzip 315 B 315 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 7.98 kB 7.99 kB ⚠️ +4 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 125 kB 126 kB
page.js gzip 272 kB 273 kB
Total 398 kB 399 kB ⚠️ +1.1 kB
Middleware
Canary PR Change
middleware-b..fest.js gzip 614 B 622 B 🔴 +8 B (+1%)
middleware-r..fest.js gzip 156 B 156 B
middleware.js gzip 44.4 kB 44.3 kB
edge-runtime..pack.js gzip 842 B 842 B
Total 46 kB 45.9 kB ✅ -56 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 719 B 718 B
Total 719 B 718 B ✅ -1 B
Build Cache
Canary PR Change
0.pack gzip 4.38 MB 4.38 MB
index.pack gzip 115 kB 114 kB 🟢 1.41 kB (-1%)
index.pack.old gzip 115 kB 114 kB 🟢 1.42 kB (-1%)
Total 4.61 MB 4.61 MB ✅ -5.38 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 345 kB 345 kB
app-page-exp..prod.js gzip 191 kB 191 kB
app-page-tur...dev.js gzip 345 kB 345 kB
app-page-tur..prod.js gzip 191 kB 191 kB
app-page-tur...dev.js gzip 341 kB 341 kB
app-page-tur..prod.js gzip 189 kB 189 kB
app-page.run...dev.js gzip 342 kB 342 kB
app-page.run..prod.js gzip 189 kB 189 kB
app-route-ex...dev.js gzip 76.9 kB 76.9 kB
app-route-ex..prod.js gzip 52.5 kB 52.5 kB
app-route-tu...dev.js gzip 77 kB 77 kB
app-route-tu..prod.js gzip 52.5 kB 52.5 kB
app-route-tu...dev.js gzip 76.6 kB 76.6 kB
app-route-tu..prod.js gzip 52.2 kB 52.2 kB
app-route.ru...dev.js gzip 76.5 kB 76.5 kB
app-route.ru..prod.js gzip 52.2 kB 52.2 kB
dist_client_...dev.js gzip 324 B 324 B
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 318 B 318 B
dist_client_...dev.js gzip 317 B 317 B
pages-api-tu...dev.js gzip 43.9 kB 43.9 kB
pages-api-tu..prod.js gzip 33.5 kB 33.5 kB
pages-api.ru...dev.js gzip 43.8 kB 43.8 kB
pages-api.ru..prod.js gzip 33.4 kB 33.4 kB
pages-turbo....dev.js gzip 53.3 kB 53.3 kB
pages-turbo...prod.js gzip 39.1 kB 39.1 kB
pages.runtim...dev.js gzip 53.2 kB 53.2 kB
pages.runtim..prod.js gzip 39 kB 39 kB
server.runti..prod.js gzip 62.8 kB 62.8 kB
Total 3.05 MB 3.05 MB ✅ -1 B
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/b82c5688991a4cf673a67f8551469913c426d5b4/next

@mischnic mischnic changed the base branch from update_swc_to_support_plugins to graphite-base/91533 April 8, 2026 08:28
@lukesandberg lukesandberg force-pushed the graphite-base/91533 branch from 74a9431 to 1d8e326 Compare April 8, 2026 17:33
@lukesandberg lukesandberg changed the base branch from graphite-base/91533 to canary April 8, 2026 17:33
@nextjs-bot
Copy link
Copy Markdown
Contributor

nextjs-bot commented Apr 8, 2026

Tests Passed

@lukesandberg lukesandberg marked this pull request as ready for review April 8, 2026 18:31
@lukesandberg lukesandberg assigned lukesandberg and bgw and unassigned lukesandberg Apr 8, 2026
Comment thread Cargo.toml Outdated
Swap out swc_plugin_backend_wasmer (wasmer 6.1.0-rc) with a new
swc_plugin_backend_wasmtime crate to reduce native binary size.

- Create crates/swc-plugin-backend-wasmtime implementing the
  swc_plugin_runner Runtime/Instance/Caller traits via wasmtime
- Update workspace Cargo.toml: add wasmtime deps, remove wasmer deps
  and release profiles
- Wire WasmtimeRuntime into both code paths:
  - Turbopack: turbopack-ecmascript-plugins
  - Webpack/SWC NAPI: next-napi-bindings transform.rs
- Remove swc_core/plugin_backend_wasmer feature flag usage
- Integration points designed for future swappability (e.g. Node NAPI
  WASM engine)

Fix compilation errors in wasmtime backend implementation

- Remove unused import (std::sync::Arc)
- Use raw pointer approach for Caller trait implementation to work
  around Rust lifetime constraints with wasmtime::Caller parameter
- Implement runtime::Caller trait methods using unsafe pointer access,
  which is safe because the pointer lifetime is controlled within
  the host function closure

The key challenge was that wasmtime's closure signature provides a
Caller parameter with a specific lifetime that doesn't satisfy the
trait object requirements for runtime::Caller. The solution uses
unsafe pointer casting with careful lifetime management to bridge
the gap between wasmtime's API and swc_plugin_runner's trait interface.

Remove wasmer from compilation graph

Replace swc_core/plugin_transform_host_native with granular internal features
(__plugin_transform_host + __plugin_transform_host_schema_v1) to bypass
__plugin_transform_env_native, which is the only feature path that pulls in
wasmer via swc -> swc_plugin_backend_wasmer.

Since we provide WasmtimeRuntime explicitly, we don't need swc's env-native
backend wiring. This removes wasmer and all its sub-crates from cargo tree
entirely, eliminating unnecessary compilation time.
@lukesandberg lukesandberg merged commit a24462d into canary Apr 9, 2026
172 of 173 checks passed
Copy link
Copy Markdown
Contributor Author

Merge activity

@lukesandberg lukesandberg deleted the wasmer_to_wasmtime branch April 9, 2026 16:34
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Apr 24, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

created-by: Turbopack team PRs by the Turbopack team. locked Turbopack Related to Turbopack with Next.js.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants