[Rust] 初步認識 swc

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.installneon 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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末生均,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子腥刹,更是在濱河造成了極大的恐慌马胧,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衔峰,死亡現(xiàn)場離奇詭異佩脊,居然都是意外死亡蛙粘,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門威彰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來出牧,“玉大人,你說我怎么就攤上這事抱冷〈蘖校” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵旺遮,是天一觀的道長赵讯。 經(jīng)常有香客問我,道長耿眉,這世上最難降的妖魔是什么边翼? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮鸣剪,結(jié)果婚禮上组底,老公的妹妹穿的比我還像新娘。我一直安慰自己筐骇,他們只是感情好债鸡,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铛纬,像睡著了一般厌均。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上告唆,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天棺弊,我揣著相機與錄音,去河邊找鬼擒悬。 笑死模她,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的懂牧。 我是一名探鬼主播侈净,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼僧凤!你這毒婦竟也來了用狱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤拼弃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后摇展,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吻氧,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盯孙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鲁森。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖振惰,靈堂內(nèi)的尸體忽然破棺而出歌溉,到底是詐尸還是另有隱情,我是刑警寧澤骑晶,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布痛垛,位于F島的核電站,受9級特大地震影響桶蛔,放射性物質(zhì)發(fā)生泄漏匙头。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一仔雷、第九天 我趴在偏房一處隱蔽的房頂上張望蹂析。 院中可真熱鬧,春花似錦碟婆、人聲如沸电抚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝙叛。三九已至,卻和暖如春肘迎,著一層夾襖步出監(jiān)牢的瞬間甥温,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工妓布, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留姻蚓,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓匣沼,卻偏偏與公主長得像狰挡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子释涛,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354