2023.9.16
npm run, yarn, npxでコマンドがどのように実行されるか
概要
npm run <command>
or yarn <command>
や npx <command>
を良く実行していると思いますが、それらがどのように実行されているのかをまとめてみます。
準備
今回は適当なディレクトリで yarn create next-app
or npx create-next-app
を実行してNext.jsのプロジェクトを作成し、確認していきます。
確認だけなので私は /tmp/my-app
というディレクトリに作成しました。
詳細を確認していく
package.json
今回作成したプロジェクトの package.json
は以下のようになっています。
json_____package.json_____{
"name": "my-app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@types/node": "20.6.2",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"autoprefixer": "10.4.15",
"eslint": "8.49.0",
"eslint-config-next": "13.4.19",
"next": "13.4.19",
"postcss": "8.4.29",
"react": "18.2.0",
"react-dom": "18.2.0",
"tailwindcss": "3.3.3",
"typescript": "5.2.2"
}
}
scriptsを見てみると "dev": "next dev"
というスクリプトが定義されています。
scriptsで定義してあるスクリプトは npm run <command>
or yarn <command>
で実行できます。
この記事は terminalでどのようにコマンドが実行できるのかグローバルにインストールされたyarnで確認する の続きみたいなものなのでこの記事でもyarnで確認していきます。
yarn dev
yarn dev
を実行するとpackage.jsonのscriptsで定義した dev
が実行されるので next dev
が実行されます。yarn <command>
で実行されるコマンドは以下の流れで実行できるか確認されます。
・そのプロジェクトの node_modules/.bin
にコマンドが存在するか確認し、存在すれば実行
・上記に存在しない場合は $PATH
に定義されたpathのリストからコマンドが存在するか確認し、存在すれば実行、存在しなければエラーで終了
node_modules/.bin
ということで node_modules/.bin
を見てみます。
このディレクトリに移動し ls -la
します。
bash_____terminal_____ls -la
~~省略~~
lrwxr-xr-x 1 kobayashi wheel 21 9 16 18:34 next -> ../next/dist/bin/next
~~省略~~
next
がありました。
また next
はシンボリックリンクになっているのでシンボリックリンク先も見ておきます。
bash_____terminal_____ls -la
drwxr-xr-x 5 kobayashi wheel 160 9 16 18:34 .
drwxr-xr-x 19 kobayashi wheel 608 9 16 18:34 ..
-rwxr-xr-x 1 kobayashi wheel 5554 9 9 22:38 next
-rw-r--r-- 1 kobayashi wheel 31 9 9 22:38 next.d.ts
-rwxr-xr-x 1 kobayashi wheel 3031 9 9 22:38 next.map
next
が確認できました。
中身は以下のようになっています(長いので最初の方だけ貼ります)。
javascript_____next_____#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const _log = /*#__PURE__*/ _interop_require_wildcard(require("../build/output/log"));
const _index = /*#__PURE__*/ _interop_require_default(require("next/dist/compiled/arg/index.js"));
const _constants = require("../lib/constants");
const _commands = require("../lib/commands");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _getRequireWildcardCache(nodeInterop) {
if (typeof WeakMap !== "function") return null;
var cacheBabelInterop = new WeakMap();
var cacheNodeInterop = new WeakMap();
return (_getRequireWildcardCache = function(nodeInterop) {
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
})(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
if (!nodeInterop && obj && obj.__esModule) {
return obj;
}
// 続く
ということで yarn dev
を実行するとこのファイルが実行されることがわかりました。
もっと細かいことを言うとそもそもなんで yarn <command>
で package.json
のscriptsで定義されたコマンドが実行されるのか気になる人は GitHubのyarnリポジトリ からソースを見てみてください。
ちゃんと読まなくてもChatGPTに要約してもらうだけでも見ないよりいいと思います。
npxではどうなるか
yarn <command>
について理解できたところで今度はnpxについて触れていきます。
npxもコマンドを実行できますが package.json
のscriptsにあるスクリプトを実行する機能はありません。
npxはコマンドを実行するとき package.json
に関係なく以下の流れでコマンドの実行を試みます。
・そのプロジェクトの node_modules/.bin
にコマンドが存在するか確認し、存在すれば実行
・上記に存在しない場合は $PATH
に定義されたpathのリストからコマンドが存在するか確認し、存在すれば実行
・上記に存在しない場合はnpmレジストリに登録されているパッケージを一時的にダウンロードして実行、npmレジストリからも見つからなければ終了yarn <command>
でコマンドを実行したときと比べると3番目が異なります。
そのため、上記の1番目、2番目の順に実行できなければnpmレジストラから対象のパッケージを一時的にダウンロードして実行することになります。
だから npx create-next-app
をすると npmレジストリに登録された create-next-app を一時的にダウンロードして実行してくれるという仕組みになっています( create-next-app
というパッケージを node_modules/.bin
またはグローバルにインストールしている人はいないと思うので)。
まとめ
とりあえず使っている人もある程度いると思うのでまとめてみました。
これで楽しいnpmライフを送っていただければと思います。