Skip to content

Commit 581d1a5

Browse files
committed
Docs
1 parent 26e9f5a commit 581d1a5

File tree

3 files changed

+57
-0
lines changed

3 files changed

+57
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export default {
22
"getting-started": "Getting started",
33
cheatsheet: "Cheatsheet",
4+
compatibility: "Compatibility",
45
};
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Compatibility for wasm plugin
2+
3+
In the past, SWC Wasm plugins were not compatible between `@swc/core` versions. This meant that developers using Wasm plugins often had to update them with each new release of @swc/core to ensure compatibility, and the Wasm plugins maintainers should also update their `swc_core` Rust dependencies. This created friction, added maintenance overhead, and could disrupt development workflows.
4+
5+
However, starting from @swc/core v1.15.0, our Wasm plugins are now compatible between `@swc/core` versions to some extent.
6+
7+
This document explains from the implementation perspective why Wasm plugins were not compatible before, and how you can make your plugins compatible in the new version.
8+
9+
## Background
10+
11+
[WebAssembly (Wasm)](https://webassembly.org/) is designed as a portable compilation target for programming languages, enabling execution within a memory-safe, sandboxed environment. The Wasm artifacts compiled by the SWC Wasm plugin are loaded and executed by runtimes such as Wasmer or Wasmtime within `@swc/core`.
12+
13+
Wasm plugins cannot directly manipulate the Rust AST data structures within the `@swc/core` host environment. Instead, when invoking a Wasm plugin, the host environment serializes the entire AST into a byte array with the [rkyv](https://rkyv.org/) serialization and transmits it to the Wasm runtime. Prior to executing the Wasm plugin, the wasm plugin deserializes the byte array back into a Rust AST and performs the transformation. And then, the wasm plugin serializes the transformed AST back into a byte array and sends it back to the host environment.
14+
15+
![Overview](./compatibility/overview.png)
16+
17+
**However, a critical issue arises here: the serialization logic and the deserialization are compiled into both the host (`@swc/core`) and the Wasm plugin.** This implies that whenever **`@swc/core` modifies the AST** and releases a new version, the older logic in old Wasm plugins becomes incompatible and ceases to function correctly.
18+
19+
## What SWC has done
20+
21+
There are two primary changes that SWC has implemented to address this issue:
22+
23+
1. **Replacing the serialization scheme [rkyv](https://rkyv.org/) with the self-describing serialization scheme [cbor4ii](https://github.com/quininer/cbor4ii).** The rkyv scheme typically requires the ABI layout of serialization and deserialization to match exactly. It is highly sensitive to changes in the memory layout of data structures, and any modification to a field can cause compatibility problems. In contrast, cbor4ii is a self-describing serialization/deserialization scheme that records the memory layout of fields as metadata within the byte array. Although this approach sacrifices some performance and data compactness, it enables the serialization and deserialization process to be aware of changes in the data structure.
24+
25+
2. **Making enums extensible.** For each AST enum data type, SWC has added an `Unknown` variant to accommodate differences in data between different versions of `@swc/core` and Wasm plugins. This allows data introduced in newer versions of `@swc/core`, which older versions of the plugin cannot recognize—to, be preserved across serialization and deserialization.
26+
27+
To clarify, based on the above technical principles, SWC does not resolve all compatibility issues, such as those caused by field deletions or changes in field types. In practice, these cases are relatively rare since changes to the AST data structures primarily arise from support for new ECMAScript standards. Therefore, we continue to minimize the frequency of breaking changes in Wasm plugins through CI checks and version release strategies.
28+
29+
## Make your plugin compatible
30+
31+
It's very simple to enable your Wasm plugin to benefit from the aforementioned changes.
32+
33+
1. Make sure your Wasm plugin depends on `swc_core >= 47`.
34+
2. Enable `swc_ast_unknown` cfg in your `.cargo/config.toml` file.
35+
36+
```toml
37+
[target.'cfg(target_arch = "wasm32")']
38+
rustflags = [
39+
"--cfg=swc_ast_unknown"
40+
]
41+
```
42+
43+
3. For each pattern-match code with ast enum, add a match arm for the `Unknown` variant, and just `panic!()` it. For example:
44+
45+
```rs
46+
match imported {
47+
ModuleExportName::Ident(v) => v.sym == exported.name,
48+
ModuleExportName::Str(v) => {
49+
v.value.as_str() == Some(exported.name.as_str())
50+
}
51+
#[cfg(swc_ast_unknown)]
52+
_ => panic!("unknown node")
53+
}
54+
```
55+
56+
Why is it the `panic!()` here? What's the difference between the old version and the new version with `panic!()`? Consider a scenario where ECMAScript introduces a new type of AST node. With the older version of SWC, the Wasm plugin would fail immediately during the deserialization, regardless of whether the actual code or the parsed AST contains the newly introduced node type. In contrast, with the newer version of SWC, the Wasm plugin is able to work correctly on ASTs that do not contain the new node type.
123 KB
Loading

0 commit comments

Comments
 (0)