1.9K Views
March 14, 25
スライド概要
2025/03/14 toranoana.deno #20
https://yumenosora.connpass.com/event/342361/
Working at Deno Land, Studying at Georgia Tech
Deep Dive into deno lint plugin 2025/03/14 (Fri) toranoana.deno #20 Yusuke Tanaka
Who am I 米ジョージア工科大学のオンライン修士 課程に通いながらDeno Land Inc.で Deno Deployを作っています🦕 最近はisolate cluster in k8sのリソー ス管理、スケーリングロジックなどをやっ ています GitHub: https://github.com/magurotuna 𝕏: https://x.com/yusuktan LinkedIn: https://www.linkedin.com/in/yusuktan/
今日の内容
deno lint ついにプラグイン対応 🎉
Timeline of deno lint plugin ● 2020/06/18: discussion: plugin support というissueが立つ ● 2020/11/29: PoCがマージされる ● ・・・しばらくコアチームの関心外になる・・・ ● 2024/10/09: Deno 2 リリース、ツールチェーン改善のリソースが空く ● 2024/12/03: feat(lint): add JavaScript plugin support PRが立つ ● 2025/02/06: マージされる ● 2025/02/19: Deno 2.2の目玉機能の1つとしてリリース
How the PoC worked ● 前提知識: deno lintはRustで実装されているが、プラグインはJS/TS で書けるようにしたい ○ つまりRustとJS/TSの境界をまたぐ必要がある ● ユーザーはプラグインのファイルパスを指定して、リンターを起動 ● リンターはJS/TSで書かれたプラグインを実行するためのRuntime (≒V8 isolate) を立ち上げる ● このRuntimeが指定されたプラグインをimportして実行し、結果をRust 側に送る
What was bad about PoC ● パフォーマンス ○ Rust側でパースされたリント対象のJS/TSのASTを、JS/TS側の世 界にもっていくのが重たい ● プラグインのAPIの機能が不足している ○ 「この種類のASTノードにきたときに、このチェックをする」という最低 限のことしかできない ○ コメントにアクセスできない ○ Rust側で使用可能な静的解析結果を利用できない ■ 変数スコープ解析、制御フロー解析など
Design goal of our plugin architecture ● JS/TSで書ける ○ c.f. 同じくRust製のリンターであるbiomeはGridQLという専用クエリ 言語を採用 ○ 他にはWasmを使ったプラグイン機構も有力候補 ● JS/TSで書かれたプラグインを実行することによるオーバーヘッドが最小 限に抑えられている ● ESLintに近い、多くの人に馴染みのあるAPIである ● サンドボックス環境で実行され、赤の他人が書いたプラグインを安心して 実行できる ○ ネットワーク、ファイルシステム、環境変数へのアクセス不可
The answers ● JS/TSで書ける ○ A. 書ける! Denoランタイムの柔軟性を最大限活用(後述) ● JS/TSで書かれたプラグインを実行することによるオーバーヘッドが最小 限に抑えられている ○ A. データ構造を工夫することにより達成 ● ESLintに近い、多くの人に馴染みのあるAPIである ○ A. がんばって達成 ● サンドボックス環境で実行され、赤の他人が書いたプラグインを安心して 実行できる ○ A. Yes!! Denoの最大の強み
Deno sandbox ● ベースとなるV8の上に複数の層を加えることで普段 deno runで利用す るランタイムが構築されている ● 追加する「層」は柔軟にカスタマイズすることが可能 ● deno lintのプラグインを実行するときには、ほぼV8そのままのランタ イムが使用される ○ ファイルシステム、環境変数、ネットワークなどに対するアクセスはで きない ● 必ずしも信頼できない人が作ったプラグインを心配なく実行できる ○ ESLintのプラグインだとそうはいかない
How to minimize the overhead Rust <-> JS ● リント対象のプログラムをRust側でパース、ASTに変換 ● TSのchecker.ts (2.94MB, ~53000行)をベンチマークに使用 ● ASTをserde_jsonでJSONにしてJSに渡し、JSでJSON.parseする と、2.91秒かかる ● Rust->JSに送るデータ構造を徹底的に最適化することで、ASTを走査す るときのデシリアライズが不要になるようにした ○ 0.62秒に短縮 ● 詳しくはdeno lintプラグイン機構の設計をメインで担当したMarvinの ブログ記事を参照
木構造を効率的に 表現するための データ構造 工夫 各ノードが固定長サイズ インデックスからバイトオフ セットを計算することでデシリ アライズ不要に
ESLint deno lint (悪意のあるコード部分も揃えたかったのですが、Deno側ではプラグインを実行するときにイベントループが回されないっぽく、 asyncな処理を行えなかったためwriteTextFileSyncで代用しています)
Running malicious ESLint plugin 環境変数を読み取って外部サーバーPOSTするようなコードが含まれる ESLintプラグインを実行してしまうと…… 😈 環境変数が送信されてしまった 😈
Running malicious deno lint plugin 環境変数を読み取ってどこかに書き込むコードが含まれるプラグインを実行してしまうと …… 🤕 そもそも環境変数が取得できなかった 🤕 (うっかり--allow-env をつけてしまったとしても、そもそも deno lintは--allow-envを受け付けないので問題なし)
Wrap up ● deno lintであなたの独自ルールが書けるように since Deno v2.2 ● 強み ○ 馴染みのあるAPI体系、JS/TSで書ける ○ 効率的なデータフォーマット ○ サンドボックスで実行されることの安心感 ● lintルールを書いてみると「プログラムって木なんだなあ」と改めて感じら れて勉強になる(過去の自分はなった)のでおすすめ
Thank you all 🦕