発明のための再発明

内部動作のような、プログラムを書くときの参考になることを書きます

yarnを使ってimportが解決されるまでを追う

yarn add から webpackがimportを解決するまでを追う

Javascriptでは、Yarnとwebpackを使うのが主流なので、それらがどのような動きをしているかを追った。

事前準備

動きを追うために以下を用意した

  1. コード
  2. Verdaccio

コード

コードの配置は以下

.  
├── build  
│   └── bundle.js  
├── node_modules  
│   ├── my-isarray  
│   ├── webpack -> ../../../.config/yarn/link/webpack  
│   ├── webpack-cli  
├── package.json  
├── src  
│   └── app.js  
├── webpack.config.js  
└── yarn.lock  

各ファイルの中身は、

  • my-isarray
console.log("test-isarray");  

export default function(obj) {  
    console.log("my-isarray is called");  
    return Array.isArray(obj);  
}  
  • src/app.js
import a from "my-isarray";  

(function() {  
    console.log("src************");  
})()  

a();  
  • webpack.config.js
module.exports = {  
  mode: "none",  
  entry: "./src/app.js",  
  output: {  
    filename: "bundle.js",  
    path: __dirname + "/build"  
  }  
}  

Verdaccio

プライベートnpm のレポジトリ用にVerdaccioを使った。
dockerが用意されているので、configを弄った後に自作のpackageを登録すれば完了する

Yarnとwebpackのコミットハッシュ

調査に使用したコードのハッシュ値は以下である

Yarn: c58bd58e029ab5a69d75742c6ae82aa96cef0140 (v1.11.0-0)
webpack: 5ade57451073ef0993379d2cc6b80d616e86ef5d (v4.19.0)

動きを追う

大まかな流れは画像の様になる

f:id:mrasu:20180923170303j:plain

yarn add

最初に、yarn addをしてnode_modulesに展開されるまでを追う

  1. npmからメタデータを取得する
    npmのAPIを使用して、hash値やバージョンなどの最新情報を取得する。
  2. npmから~/.cache/yarn/v2に保存
    メタデータと以前にダウンロードしたものが違うものであればtarballをダウンロードして、展開する。
    この時、直接 node_modules にダウンロードするのではなく、~/.cache/yarn/v2のような共通ディレクトリにダウンロードすることで、別プロジェクトが使うときにも使いまわせるようになっている。
  3. 対象のnode_modulesディレクトリへコピー
    ~/.cache/yarn/v2に保存したパッケージ内容を対象ディレクトリにコピーする。

webpackを使って対象パッケージを使用する

次に、webpackが"ビルド"として行っている「ファイルの結合」の流れを追う

  1. Parserを通して各ファイルの依存関係を発見する
    Acornを使用してパースしている。
    Acornの結果を基に、webpack内にあるParser.jsがフックポイントを作って回る。
    このフックポイントの一つがimportを通ったときに発火するようになっているので、発火した場所を見ることで各ファイルが依存したパッケージを知ることが出来る。
  2. Resolverが名前解決する
    enhanced-resolveという、デフォルトのResolverがwebpackから切り出されている。
    これを利用することで、各ファイルが依存しているパッケージをnode_modulesから選び出している。
  3. webpackに登録されたLoader,Pluginsを実行し、各ファイル・全体を変更する
  4. 結果を基にファイルをつなぎ合わせる

以上

以上で、yarnがパッケージをダウンロードしてwebpackがファイルに結合するまでの流れが追えた。

読んでいて思ったこと

Yarn

yarnでは、jestとflowを使用していて、「やはり、Facebook製」といった感じを出している。
やることを絞っているためか、コードは結構シンプルで読みやすい。
上に書いた事以外にも、ネストや重複した依存関係の解決があるのでそこはまた読んでみたい所

webpack

webpackはプラグインの仕組みを使っていて、他の人が勝手にプラグインを書いて好きに公開できるようになっている。
この仕組みはwebpackを下支えしている良い仕組みだと思う。
ただし、コードを見ると、制御できている人が居るのか疑問なレベルの複雑さを持っている。
読み慣れていないせいかもしれないが、コールバック地獄がかなり読みづらかった。
また、hooksという各PluginやLoaderに対するフックポイントをまとめた変数があるのだが、フックポイントが多すぎてどこがいつ影響するのかわかりづらい。
あと、デフォルトのプラグインがまとめてlib直下に送られているのは流石にどうかと思う・・・