0. 背景
swc 是一個用 Rust 寫的高性能 TypeScript / JavaScript 轉(zhuǎn)譯器吹埠,類似于 babel苍姜。
對比 babel漆枚,swc 有至少 10 倍以上的性能優(yōu)勢,
Performance comparison of swc and babel,
對比 babel 的 plugin 體系蝗茁,swc 支持的特性還有待增強娜汁,
Comparison with babel
下文總結(jié)了一下我對 swc 項目的初步認識。
1. Rust 項目
Rust 是一個無 GC(垃圾回收) 的高性能編程語言斧抱。
關(guān)于這門語言本身常拓,就不再詳述了。
下面簡單羅列下辉浦,Rust 語言跟 swc 相關(guān)的知識點弄抬。
(1)rustup 和 cargo
rustup,Rust 語言的安裝器宪郊,安裝了才可用
rustup 會自動安裝 cargo
命令行工具掂恕。
(2)初始化
cargo
是 Rust 語言的:腳手架工具 + 構(gòu)建工具 + 包管理器。
$ cargo new hello-rust # 腳手架
生成的目錄結(jié)構(gòu)如下弛槐,
hello-rust
├── .gitignore
├── Cargo.toml
└── src
└── main.rs
其中懊亡,Cargo.toml 中包含了 Rust 項目相關(guān)的配置信息,
[package]
name = "hello-rust"
version = "0.1.0"
authors = ["..."]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[dependencies]
用于配置外部依賴丐黄,包管理平臺 crate.io
src/main.js
中是 Rust 代碼斋配,
fn main() {
println!("Hello, world!");
}
(3)構(gòu)建和運行
$ cargo build
$ cargo run
(4)一些知識點
Rust 項目編譯單元稱為 crate,一個 crate 可以是一個 binary 或 library。
對比其他編程語言艰争,是指編譯產(chǎn)物為可執(zhí)行文件(binary)坏瞄,還是代碼庫(library)。
詳細可參考這里:
The Rust Programming Language: Packages and Crates
Rust 的部署單元稱為 package甩卓,一個 package 可以由一個或多個 crate 組成鸠匀。
cargo
創(chuàng)建的每個 package 都有一個 Cargo.toml
文件。
package 可包含 crate 的數(shù)量:>=1
package 可包含 binary crate 的數(shù)量:>=0
package 可包含 library crate 的數(shù)量:0, 1
下面是一些目錄約定:
- cargo 默認將
src/main.rs
作為 binary crate 的入口 - cargo 默認將
src/lib.rs
作為 library crate 的入口 - 如果需要構(gòu)建出多個 binary crate逾柿,則分為多個文件寫到
src/bin/
目錄中
2. neon
swc/package.json 中的 scripts.build
配置如下缀棍,
{
...,
"scripts": {
...,
"build": "tsc -d && neon build --release"
},
...
}
表明 swc v1.2.9 使用了 neon 進行打包。
neon 是一個為 Node.js 打包 Rust 擴展的工具机错。
我們按官方的 README 來簡單試用下爬范。
(1)腳手架
$ npm install -g neon-cli
$ neon new my-project
會生成這些文件:
my-project
├── .gitignore
├── README.md
├── lib
│ └── index.js
├── native
│ ├── Cargo.toml
│ ├── build.rs
│ └── src
│ └── lib.rs
└── package.json
其中 lib/index.js
是 Node.js 代碼,
var addon = require('../native'); // 引用 Node.js 擴展
console.log(addon.hello());
native/src/lib.rs
是一個 Rust 庫弱匪,將打包為 Node.js 擴展青瀑,
use neon::prelude::*;
fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
Ok(cx.string("hello node"))
}
register_module!(mut cx, {
cx.export_function("hello", hello)
});
(2)打包
package.json 中配置了 scripts.install
為 neon build
,
{
...,
"dependencies": {
"neon-cli": "^0.4.0"
},
"scripts": {
"install": "neon build"
}
}
因此萧诫,只要我們安裝依賴斥难,就自動進行打包了(安裝依賴后自動執(zhí)行 npm run install
),
$ npm i
(3)構(gòu)建結(jié)果
native/
目錄下會多了很多 rust 構(gòu)建產(chǎn)物帘饶,
my-project
├── .gitignore
├── README.md
├── lib
│ └── index.js
├── native
│ ├── Cargo.lock <- 新增
│ ├── Cargo.toml
│ ├── artifacts.json <- 新增
│ ├── build.rs
│ ├── index.node <- 新增(Node.js 擴展)
│ ├── src
│ │ └── lib.rs
│ └── target <- 新增
│ ├── .rustc_info.json
│ └── debug
├── package-lock.json
└── package.json
其中哑诊,native/index.node
就是打包好的 Node.js 擴展了。
3. Node.js 中調(diào)用 swc
有了 neon 之后及刻,在 Node.js 中調(diào)用 swc 就變得簡單多了镀裤,
$ npm i -D @swc/core @swc/cli
只需要像安裝其他 npm 模塊一樣安裝 swc 就行了。
具體可參考 swc 使用文檔 缴饭,這里就不再詳述了淹禾。
4. Rust 中直接調(diào)用 swc crate
為了了解 swc 內(nèi)部原理,我們需要 debug 進 swc 源碼中茴扁。
下面我們來看一下,怎么做到這些汪疮。
(1)新建一個 debug-swc 項目
$ cargo new debug-swc
$ cd debug-swc
目錄結(jié)構(gòu)如下峭火,
debug-swc
├── .gitignore
├── Cargo.toml
└── src
└── main.rs
(2)添加依賴
Cargo.toml
文件,[dependencies]
配置項下面添加如下內(nèi)容智嚷,
swc_ecma_parser = "0.31.2"
swc_common = "0.7.0"
swc_ecma_ast = "0.26.0"
swc_atoms = "0.2.2"
(3)新增 test.js
文件
根目錄下的這個文件卖丸,用于 debug swc 的解析之用,內(nèi)容如下盏道,
const i = 1;
(4)編寫 src/main.rs
官方例子 Crate: swc_ecma_parser 幾乎不用做任何修改稍浆。
為了能展示 swc 解析 JavaScript 代碼的結(jié)果,
我增加了一個 get_identifier_name
函數(shù),用于獲取 test.js
代碼中的變量名 i
衅枫。
fn get_identifier_name(module: &Module) -> Result<&JsWord, ()> {
for module_item in &module.body {
if let ModuleItem::Stmt(Stmt::Decl(Decl::Var(var))) = module_item {
for decl in &var.decls {
if let Pat::Ident(identifier) = &decl.name {
return Ok(&identifier.sym);
}
}
}
}
Err(())
}
src/main.rs
的完整源碼可以看這里:github: debug-swc/src/main.rs
(4)來看一下執(zhí)行結(jié)果
$ cargo run
執(zhí)行 cargo run
會先進行 cargo build
嫁艇,最終結(jié)果如下,
我們經(jīng)過解析 AST 從 test.js 源碼中拿到了變量名
i
弦撩。
下面我們來看看調(diào)試步咪。
(5)添加 VSCode 調(diào)試配置 .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "debug swc",
"type": "lldb",
"request": "launch",
"program": "${workspaceRoot}/target/debug/debug-swc",
"args": [],
"cwd": "${workspaceRoot}",
}
]
}
注:
在成功啟動調(diào)試之前,遇到了兩個問題:
- VSCode 安裝 Rust Extension 時益楼,提示找不到 rustup猾漫。
這是因為 rustup 安裝時,將環(huán)境變量寫到了 ~/.bash_profile
中感凤。
export PATH="$HOME/.cargo/bin:$PATH"
而 ~/.bash_profile
中的設置悯周,是在電腦重啟后,而不是終端重啟后生效陪竿。
所以即使在終端執(zhí)行了 source ~/.bash_profile
禽翼,終端里生效了,
VSCode 仍然無法讀到 rustup萨惑。
解決方案是重啟電腦就好了捐康。
VSCode Rust Extension 討論區(qū)相關(guān)的 issue 如下:rustup not available
- 要完成調(diào)試,還需要安裝 VSCode CodeLLDB Extension 可是一直下載不了庸蔼。
此時解总,我們可以到 github vscode-lldb release 中下載安裝包:vscode-lldb v1.5.3 codelldb-x86_64-darwin.vsix
然后在 VSCode 中選擇從 .vsix 文件安裝擴展就行了。
(6)啟動調(diào)試
我們在編譯完成之后姐仅,在 src/main.rs
中打個斷點花枫,按 F5
啟動調(diào)試。
其中 module
就是 parser.parse_module()
之后得到的 AST 了掏膏。
同樣我們可以在 let module = parser\n
這一行打個斷點劳翰,
然后單步調(diào)試到 swc-ecma-parser 里面,位于 github: swc v1.2.10/ecmascript/parser/src/parser/mod.rs#L122馒疹,
5. 總結(jié)
本文介紹了我對 swc 項目的初步認識佳簸。
其中涉及到了以下幾方面的內(nèi)容:
- Rust:rustup、cargo颖变、crate
- neon:Node.js addon
- swc-ecma-parser
- VSCode debug rust
參考
swc
swc v1.2.9
Performance comparison of swc and babel
Comparison with babel
Rust
rustup
crate.io
The Rust Programming Language: Packages and Crates
neon
Crate: swc_ecma_parser
rustup not available
VSCode: Rust Extension
VSCode: CodeLLDB Extension
github: debug-swc