npm でローカルインストールしたパッケージを CLI でコマンド実行する方法（npm-srcipts, npx）

# はじめに
npm でローカルインストールしたパッケージを CLI でコマンド実行する方法が複数あるようなので、それぞれについて比較しました。

# どうして npm インストールしただけではコマンド実行できないの？

例えば cowsay というパッケージを `npm install cowsay` でローカルインストールをした場合、いきなり `cowsay` とコマンドを実行することができません。

`cowsay` と実行すると、以下のように `command not found: cowsay` と怒られます。

```shell
# cowsay パッケージのローカルインストール
$ npm install cowsay
+ cowsay@1.4.0

# ローカルインストールされたパッケージの確認
$ npm list --depth=0
typescript-node-base@1.0.0 /Users/sugurutakahashi/git/typescript-node-base
└── cowsay@1.4.0

# cowsay コマンドの実行（パスが通っていないのでエラーになる）
$ cowsay "hoge"
zsh: command not found: cowsay
```

**このように実行できない理由は単純で、ローカルインストールしただけではパッケージにパスが通っていないためです。**

# コマンド実行する方法

ローカルインストールしたパッケージのコマンドを実行するには以下の3つ（4つ目はグローバルインストールなのでカウント外）が挙げられます。

- **方法1：パスを通す**
  - `./node_modules/.bin/<package>` のようにパスを通しながら実行する

- **方法2：npm-srcipts**
  - package.json の npm-srcipts を登録して `npm run <key>` と実行する

- **方法3：npx（おすすめ！）**
  - `npx <package>` とコマンドを実行する

- **（方法4：グローバルインストール）**
  - `npm install -g  <package>` 後に `<package>` とコマンド実行する

# 実行してみる

## 方法1： パスを通す

npm によってローカルインストールすると `./node_modules/.bin/` にパッケージがインストールされるのでパスを指定してあげるだけです。

```shell
# ローカルインストール先のパスを指定した cowsay コマンドの実行
$ ./node_modules/.bin/cowsay "hoge" 
 ______
< hoge >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

# $(npm bin) を使用しても同様にローカルインストール先のパスを指定したことになる（./node_modules/.bin/<package> = $(npm bin)/<package>）
$ $(npm bin)/cowsay "hoge"   
 ______
< hoge >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
```

## 方法2： npm-srcipts

package.json の `scripts` の項目に `{ "scripts" : { "key" : "value" }}` と `"value"` にコマンドを指定したのちに `$ npm run <key>` と実行します。

今回、 `key` と `value` を同じ値にしておりますが、`key` に設定する値はなんでも良いです。

また、`value` に設定するコマンドに引数のオプションを指定したい場合は `--` のあとに指定します。
ex） `$ npm run <key> -- <valueで指定したコマンドに対する引数>`


```shell
# package.json の scripts に cowsay を追加（npm run scripts<key> とコマンドを実行するとローカルインストール先のパスを通しながら scripts<value> コマンドを実行する）
$ vi package.json
{
  (省略)
  "dependencies": {
    "cowsay": "^1.4.0"
  },
  "scripts": {
+   "cowsay": "cowsay"
  },
  (省略)
}

# npm-scripts での cowsay コマンドの実行
$ npm run cowsay -- "hoge" # npm-scripts の引数のオプションは -- のあとに指定する（-- なくても大丈夫な場合もあるがつけた方が無難）

> typescript-node-base@1.0.0 cowsay /Users/sugurutakahashi/git/typescript-node-base
> cowsay "hoge"

 ______
< hoge >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

# npm run 実行時に -s (= --silent) の オプションをつけると出力がすっきりします
$ npm run -s cowsay -- "hoge"
 ______
< hoge >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
```

## 方法3： npx

> 公式:[npx](https://www.npmjs.com/package/npx)

`npx <package>` とコマンドを実行するとローカルにインストールした npm パッケージを実行することができます。

```shell
# npx <package> コマンドでもローカルインストール先のパスを通しながら <package> のコマンドを実行できる（ローカルインストールしている場合は ./node_modules/.bin/<package> = npx <package>）
$ npx cowsay "hoge"
 ______
< hoge >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
```

また、ローカルに存在しないパッケージは一時的にパッケージをインストールしてコマンド実行を実行してくれます。
そのようにインストールしたパッケージはコマンド実行に削除されます。

```shell
# node_modules のない適当なディレクトリに移動
$ cd ../
$ npm list --depth=0
/Users/sugurutakahashi/git
└── (empty)

# npx <package> コマンド実行時にローカルインストール先（node_modules）にパッケージが存在しない場合は、一時的にローカルにパッケージをインストールして実行する（実行後は一時的にインストールしたパッケージは削除される）
$ npx cowsay "hoge"
npx: 10個のパッケージを1.636秒でインストールしました。
 ______
< hoge >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
```

ローカルにパッケージがあってもなくても使用できるのは便利ですね。

## 方法4： グローバルインストール

もちろんグローバルインストールすればローカルインストールする必要もないし npm-scripts に登録する必要もないし、npx と指定する必要もありませんが、その環境でしか実行できなくなります。

```shell
# もちろん実行できない
$ cowsay "hoge"
zsh: command not found: cowsay

# グローバルインストール
$ npm install -g cowsay
$ cowsay "hoge"
 ______
< hoge >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
```

# 考察

## これらの方法の使い分けについて

基本的に `npm-srcipts` または `npx` での実行をお勧めします。

まず、パスを通して実行するのであれば `npx` で実行することができます。
なので、わざわざパスを通しながら実行する機会はないと思われます。

ローカルインストールしたパッケージのコマンドを頻繁に実行するコマンドであったりプロジェクトで共有すべきコマンドであれば `npm-srcipts` に登録したほうがいいと思います。

一方で、たまにしか実行しないコマンドであったり、動作の検証であれば、わざわざ `npm-srcipts` に登録せずに `npx` での実行でいいと思います。

むしろ、むやみに `npm-srcipts` に登録すると管理する対象が増えるのでお勧めしません。

## npx のみローカルインストールの有無にかかわらず実行できる

`npx` の機能としてローカルインストールされていないパッケージは、一時的にパッケージをインストールして実行してくれます。

たまにしか実行しないコマンドであったり、動作の検証であれば、ローカルインストールすらせず `npx` 経由で毎回インストールしながらコマンド実行するのもありだと思います。

時間はかかりますが、検証の結果、不要になったパッケージのアンインストールのし忘れなどがないというメリットがあります。

【参考】
[npxでnodeモジュールを実行する](https://qiita.com/tatakahashiap/items/1c4ab221c4993e7c4ebf)

## それって本当にグローバルインストールする必要がありますか？

よくネットに転がっている記事はやたらとグローバルインストールさせてきます。
そのとき安易にグローバルインストールしないほうがいいです。

グローバルインストールされたパッケージは環境が異なると使用することができません。

大抵の場合、`npx` を使って一時的にインストールするだけで済むケースがほとんどです。

無闇にグローバルインストールせずに `npx` を使ってみてはいかがでしょうか？

# まとめ

|ケース|インストール先|CLIでの実行方法|
|:----|:----|:----|
|・よく実行する <br>・プロジェクトで共有して管理したい|ローカルインストール|・npm-srcipts|
|・まあまあ実行する<br>・今のところ自分だけ知っていればいい<br>・バージョンを管理したい|ローカルインストール|・npx<br>・パスを指定する|
|・たまにしか実行しない<br>・検証中である<br>・ちょっと時間がかかってもよい<br>・環境に影響をかけたくない<br>・とりあえず実行させたい|npx による一時的インストール|・npx|
|・それ以外<br>・どうしてもグローバルインストールしたい|グローバルインストール|・普通に実行する|