kaz.dev

module-bundlerを作りながらmoduleへの理解を深める

tag: node, commonjs, esmodules


はじめに

本ブログは11tyで作成されているが、ts化に向けて色々しているうちにES Modulesなどへの理解が浅いことが身にしみてわかった。

hiroppyさんの下記記事を参考にmodule bundlerを作ることでその辺への理解を深められたらなと思っている。

コードはkobakazu0429/module-bundlerにある
各コードの詳細はブログを参照





環境構築

コミット: 6ba3e09

hiroppy/the-sample-of-module-bundlerから基礎的なもの(e.g. tests/)をコピーしつつ環境構築した





entryファイルからrequireされているファイルの列挙(準備編 node_modulesのファイルを読み込む まで)

code

基本的にはbabelを使ってtraverseするだけ
まだbundlerらしさはない





bundle(準備編 ランタイムのコード作成 まで)

code

requireの解決を行なって複数ファイルを1つのファイルにbundleできるようになった
実際にファイルに書き込んで実行すると実行できるのでちょっと感動
前回bundlerらしさがないって言ったばかりなのにもう完全にbundler

bundlerの成長は早い

node_modules.jsはreactのversionを出力するだけなのに不要なコード(reactのほとんどすべて)がbundleされているので確かにDead Code Eliminationの必要性を感じた

あとはtemplateの部分が個人的にはなんとなく興味深かった





ES Modules対応(ECMAScript Modules編 すべて)

code

import/exportなどのES Modulesに対応した (hiroppy/the-sample-of-module-bundler: 132a136まで)
完全なbundlerになった
hiroppy/the-sample-of-module-bundlerのmasterからtestsをcloneしたので未対応の部分がある(e.g. common/interopなど)

地道にastを操作してコードの書き換えを行なっており、プログラミングは魔法ではないことを再確認できたのがよかった

次はhiroppy/the-sample-of-module-bundlerのcommitを追いつつ修正をしてテストを通る予定にする





CommonJSとES ModulesのinteropとImportDeclarationの実装

code

c160d73まで
(terserによるminifyは除く)

これでcommon/interopもbundleできるようになった





minify

code

bundleされたコードをterserを使って[name].min.jsにminifyされるようにした

prettierによる整形もあったが同様にするだけなので省略した

細かいrefactorに関しても本筋ではないのでこちらも省略した





感想

CommonJSとES Modulesの歴史的背景だったり、より詳細な話には触れなかったがbundlerの大まかな仕組みが理解できたのでかなりよかった





その他

tests/entries.test.js

const { Script, runInContext, createContext } = require('vm');

というコードを見つけた

vmモジュールを初めて見た
多分サンドボックス環境を作って安全にスクリプトを実行できるのではないかと思う
いつか触ってみたい