SWC製のExecutorをリリースした
特徴と仕組み
コード、依存関係を全てプロセス単位で繋げvmインスタンスで実行しています。
実行環境のvmではStability:2 Stable(安定的機能)のvm.Scriptを採用しました。
TypeScript形式の変換にはタイトル通りswcの@swc/coreを採用しています。
node --importではESMが強制され、 node --requireではrequire、CJSが強制されます。
このコンテキストを回避するために全てのコードをcommonjsに変換しトランスパイルしています。
全てCJSに変換するためCJSとESMが混在する環境で安定的に動きます。
実はこれは0.0.x段階でのtype: moduleを付けたESMしかサポートしないコンセプトとはかけ離れていますが、vm.Scriptで実行するためどちらも可能となりました。
この0.x.0のコンセプトはNext.jsをcommonjsとして起動しコードを実行するnext dev --turbopackから影響を受けました。
インストール
pnpm i -D rscute@latest
npxで実行
npx rscute file.ts
プログラム
const { JIT } = require('rscute');
await JIT(absolutePath)
await import()
をawait JIT()
に置き換える
JITはrscuteのmainに登録されてるメイン関数でありCLI rscute自身がJIT関数で動いているためセルフ実行していることになります。
await import()や require()をforでループしてファイルを実行する際に起きていた圧倒的ボトルネックをVM(V8) JIT Compilerのインスタンス上で実行することで解決出来たと思います。
実現できたこと
最初の課題がインメモリ上での実行でした。
0.0.x段階でオンデマンドにディスク上にトランスパイルしたコピーtempを生成し実行、プロセス終了時に削除していました。これはファイルが多すぎる場合ディスク負荷も心配ですが、実行速度がディスクから実行されるため顕著に遅かったです。
そこで考えたのが依存解決のパス解決をそもそもしないでコード単位で集めたデータを実行するアプローチでした。 ここで発生するボトルネックはプロセス単位で作られる1インスタンスを作るためのコストに集約されます。
npxでbinを検索すること自体に約80ms(0.08秒)がかかるためできるだけ実ファイルもしくはbinを直接起動してあげると速さに困ることはなさそうです。
速さのベンチマークですが、比較対象が多すぎるのととりあえず使ってみないとわからないところもあると思うため敢えてここでは載せません。
最後に特徴ですが、register系は単発ファイルにおいてnodeと同一程度並のパフォーマンスを出しますが、依存関係が多い場合にNode.jsのローダーに丸投げしているため重たくなっていきます、今回の様に変換にswcを使い依存関係の処理を自前でやることでモジュールローダーに頼らない高速な処理が実現出来たみたいです。
Discussion
こんにちは!
Node v22には
--experimental-strip-types
や--experimental-transform-types
オプションをつけてtsを実行できるようになっています。そちらも内部実装ではswcを使っているはずです。こちらとの比較も気になりました!とはいえtsxも実行できるのはすごいですね!@ryoppippiさん コメントありがとうございます!!
Node v22の
--experimental-strip-types
と--experimental-transform-types
今も実験的機能なのはもちろんですがstrip-typesの内部は@swc/wasm-typescriptで型を削る仕組みなのでパス解決まで行うと相当難しいと思っています。globやtest runnerなどもそうですが、公式のNode.js機能よりもサードパーティの方が強かったりします。
実際にはNode.js公式ドキュメントでライブラリ側を例にするドキュメントを書いているので
Node.jsチームが
--experimental
を本格的にサポートする意思があるのか不透明なところではあります。パス解決がサポートされていたら作らないでそっち使っています!
ここにパス解決やtsconfig.jsonの設定に依存する実行はサードパーティのTSXなどを使うことが推奨として書かれているみたいです。esbuildやswc, oxcの成熟にネイティブのNode.jsが追いつけるかどうか次第ですが期待しています。
@Refirstさん!ありがとうございます!なるほどそうですね!パス解決を考えると
rscute
のほうが良いのは確かに合点がいきました!詳細なコメントありがとうございます!!!個人的にはプロジェクトで
jiti
、importx
、tsx.import
を使うことが多いので、ここら辺の代替になることを願っています!