🏃

npm run XXX を実行する時、何が発生した?

2023/04/09に公開

はじめに

こんにちは。
full-stack developerを目指しているShenです。
かなり前に、面接する時に、「npm run XXX を実行する時、何が発生しましたか?」という質問ありました。この件について、ちょっと深く探りたいと思います。

npm run XXX を実行する時

以下のpackage.jsonがあるとしたら、ターミナルにnpm run devを入力した時、何が発生しますか?

{
  "name": "bookmanagement",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon -e ts --exec \"npm run build && npm start\"",
    "start": "node ./dist/app.js",
    "build": "tsc"
  }
}

はい、scriptsdevのコマンドを探して、nodemon -e ts --exec \"npm run build && npm start\"を実行しますね。

追加質問

なぜnodemon .....を直接実行しなくて、npm run devを実行しますか?

当時は、「うん、npm run devはかなり短くて、実行しやすいではないでしょうか? 毎回長いコマンドを打つと結構効率が悪いと思います」と答えました。
面接終了後、ターミナルにnodemon -e ts --exec \"npm run build && npm start\"を実行してみました。

Unknown command: nodemon

え?

そうか、そうか。OSにnodemonをインストールされていないから、怒られているか。

なんでOSに存在しないのに、npm run devは効きますか?

それでは、めっちゃでデカいnode_modulesフォルダを開きましょうか。
その中に、./binというデレクトリがあります。
そして、パッケージ名のjsファイルを開きましょう。今回はnodemon.jsです。

#!/usr/bin/env node

const cli = require('../lib/cli');
const nodemon = require('../lib/');
const options = cli.parse(process.argv);

nodemon(options);

const fs = require('fs');

// checks for available update and returns an instance
const pkg = JSON.parse(fs.readFileSync(__dirname + '/../package.json'));

if (pkg.version.indexOf('0.0.0') !== 0 && options.noUpdateNotifier !== true) {
  require('simple-update-notifier')({ pkg });
}

npm run devが実行されると、package.jsonと同じ階層のnode_modules/.binフォルダの中に、nodemonの実行スクリプトを見つけたら、該当ファイルを実行します。

node xxxx がないのに、.jsファイルはどうやって実行されますか?

以下のshebangという記述があるためです。ここで、OS環境に入れているnodeで該当.jsファイルを実行してます。

#!/usr/bin/env node

ターミナルを開いて、以下のコマンドを入力してみてください。

whereis node

あれ、なんでうちのnodeが/usr/local/binにインストールされていて、/usr/bin/envは何んですか?という質問があるかもしれないです。

npmは「ユーザはどのpathにnodeをインストールしている」ということはわかりません。そして、/usr/bin/envを使って、ユーザがインストールしているnodeを見つけて、実行しています。

ちなみに、以下のコマンドを実行したら、nodeにはいれます。

env node

終わりに

npm run XXX を叩いた時、以下の順序で実行処理発生します。

  1. package.jsonと同じ階層のnode_modules/.binフォルダに実行できるファイルを探しに行きます。
  2. 1が見つけたら、実行します。
  3. 1が見つけられなかったら、globalでインストールされているnode_modules/.binに実行できるファイルを探しに行きます。
  4. 3が見つけられなかったら、pathの環境変数の中で、実行できるものを探しに行きます。

Discussion