一日一処: npm installとかでピロピロ動いてるあれがなにか突き止める(前編)
npm
npmはNode.jsで使用されるパッケージ管理システムだ。これをコマンドとして使用して、インストールnpm install
などを行うと、ピロピロと画面上が賑やかだ。これについて、すでに仕組みを知っている人もいるため、この記事では、全くその仕組を理解していない人に向けて、どのような仕組みか突き止める過程を紹介する。
ソースコードを見る
まずは、npmのソースコードを見ることが大切だと思う。npm/cliというパッケージがある。cliはコマンド経由で処理を実行する、インターフェイスという意味だ。今回は、将来的にも参照が可能なように、現時点でのバージョンである10.4.0のソースコードで探索する。
これが、実際に使われているそれだ。ただ、このファイルの中から、いきなり、インストールの機構を探し出すのは職人芸だ。そんな超能力者のようなことはできないので、地道に探すことになるが、絶対にその記述がある場所がある。それがテストだ。
テストを覗く
npm/cliのリポジトリの中を覗くと、testフォルダがあるのがわかる。更にその中に、commandsフォルダがある。誰が何を言おうと、あからさまにnpm install
のコマンドに関するテストが入っていそうだ。
調べてみると、install用のテストファイルが確かにある。すぐに見つけられたので、このさき、長くかからないようで安心した。
ファイルを眺めていると、以下のようなコードがいくつかある。これが、インストールコマンドの呼び出しだろう。
await npm.exec('install', [])
これを見ると分かる通り、installに関するプログラムをimportしているのではなく、npm.exec
を経由して実行している様だ。では、この関数がどこにあるか探していこう。
npm.exec関数
だいたい、本体に関連する機能は、libフォルダにあることが多い。そのため、リポジトリのこのフォルダを見ると、npm.jsファイルがある。内容はクラスになっており、そのメソッドとして、execが存在していることを確認できる。
メソッドでは、以下のメソッドを実行して、テストファイルで渡しているinstall
が設定していることがわかる。メソッド内では、それ以降、この返却値を用いて、コマンド自体を実行しているようだ。
this.setCmd(cmd)
さらに、setCmd
では、Commandクラスのインスタンス化をしているようだ。ここで使用されているcmd
という静的メソッドは、与えられた文字列と同じ名称のファイルをrequire
で読み込んでいる。これで、installのコマンドファイルの位置がわかった。
return require(`./commands/${command}.js`)
ここまでの内容を見ると、複雑なことをしていない様で、少し安心した。
早速、読み込まれているinstallのファイルを見てみる。
このファイルには、Installクラスが定義されており、メソッドは2つ。注目すべきはexecメソッドだろう。これをみると、なにやら設定を定義しているように見える。後半の方に行くと、runScript関数を実行している。名称からもこれが、インストールの処理を実行しているように思える。
runScript関数
runScriptは何かと、読み込んでいる場所をみる。
const runScript = require('@npmcli/run-script')
せっかく、npm/cliを覗いているのに、スコープモジュールで別のものを参考にしている様だ。仕方いないので、現地へ赴こう。バージョンは、7.0.4だ。これは、今回確認していたnpm/cliのpackage.jsonに記述されたバージョンと同じものだ。
即座にエントリーポイントが分からなかったので、package.jsonのファイルを見ると判明した。
"main": "lib/run-script.js",
index.jsなどにしてもらえれば、簡単なのだが、とおもいつつ、このファイルを見に行こう。内容は非常にシンプルで、15行ほどしか書かれていない。正直、すべてがこれくらいのコード量だと見るのにつかれない。
return runScriptPkg(options)
ファイルの内部では、上記の様にrunScriptPkg関数を実行している。というより、むしろこの関数以外のそれらしい実行はない。これを定義しているファイルに移動する。
この関数をみると、コマンドを実行しているようなコードが有るが、その前に気になるのが、以下のコメントだ。
// you wouldn't like me when I'm angry...
このファイルに至るまでにもいくつかの注釈コメントは目にしたが、これは意味がわからない。「あなたが私を好きではないのは、私が怒っているときだ」とのことだ。直訳だが意味がわからない。調べると、Wikipediaにて、"a catchphrase of the Hulk, a comic book character"というハルクのキャッチフレーズだということを知った。そして、これまた知らなかったのだが、ハルクの人間としての名前は、Bruce Bannerだそうだ。よく見ると、関数名はBruceだし、内部では、Bannerを用いている。なるほど?少なくとも、このbruce関数は、出力する情報を整形してくれる機能だと言うことはわかった。(変化するという点でハルクなのか・・・?)
後半へ続く
他のパッケージまで移動して、確認をしてきたが、それらしい記述にまだぶつかっていない。実行そのものに注視してきたため、どこかで見落としもあるかもしれない。次回の投稿では、観点も少し変えつつ、確認を行っていく。
また、これに限った話ではないが、このようにせっかくオープンソースなのだから、ソースコードそのものを探索することも技術向上の糧となるので挑戦してほしい。個人的には、Reactのソースコードは頭が痛くなる。
Discussion