npmでうまくいかなかった事例集

npmはおろかNode.jsすら初心者なのでたくさんのエラーに奮闘している、という記録です。

# 事象 : npm ERR! peer dep missing:
1. 環境 : Windows10 Pro バージョン21H1 に作った `node:14-buster-slim` のDockerコンテナ

```bash
# npmでローカルにインストールされているものをみたら何かエラーが出てた
$ npm ls | grep -i peer
│ │   │ └── UNMET PEER DEPENDENCY typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta
npm ERR! peer dep missing: typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta, required by tsutils@3.21.0
```

## 原因 : npmのバージョンがv7未満でpeerDependenciesがインストールされていないから
> UNMET PEER DEPENDENCY は必要なパッケージがインストールされていないために出力されているということがわかった。
> [WebPack の version UP 前に、リポジトリのpackage.json を整理した](https://gist.github.com/kemsakurai/636e275c21d41e4deb7d980d9cfb236b)

UNMET PEER DEPENDENCY・・・満たされていない(unmet)対等者(peer)依存(dependency)・・・依存関係にあるもののバージョンが条件に合っていないということなのだろうか？

```bash
$ npm ls typescript
app-name@1.0.0 /app
└── (empty)
```

「typescript」のインストール自体されていなかった。依存関係にあるならば自動でインストールしてくれればいいのに。

> Peer dependencyは，パッケージ間の依存の一種であり，依存先が自動的にはインストールされないものを指します．
> パッケージAがパッケージBに（peerではない通常の）依存をする場合，Aをnpmでインストールすると依存関係が解消され，自動的にBもインストールされます．
> [npm の has unmet peer dependency とはどういう状態ですか？ - スタック・オーバーフロー](https://ja.stackoverflow.com/questions/52936/npm-%E3%81%AE-has-unmet-peer-dependency-%E3%81%A8%E3%81%AF%E3%81%A9%E3%81%86%E3%81%84%E3%81%86%E7%8A%B6%E6%85%8B%E3%81%A7%E3%81%99%E3%81%8B#:~:text=Peer%20dependency%E3%81%AF%EF%BC%8C%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E9%96%93,B%E3%82%82%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%EF%BC%8E)

「devDependencies」に書いてある「@nuxtjs/eslint-config」の依存関係にある「tsutils」はインストールされるけれど、
「tsutils」の依存関係にある「typescript」はPEER DEPENDENCYだから自動ではインストールされなかったようだ。

```bash
$ npm ls tsutils
app-name@1.0.0 /app
└─┬ @nuxtjs/eslint-config@6.0.1
  └─┬ eslint-plugin-jest@24.7.0
    └─┬ @typescript-eslint/experimental-utils@4.33.0
      └─┬ @typescript-eslint/typescript-estree@4.33.0
        └── tsutils@3.21.0

$ grep -2 "@nuxtjs/eslint-config" package.json
  "devDependencies": {
    "@babel/eslint-parser": "^7.14.7",
    "@nuxtjs/eslint-config": "^6.0.1",
    "@nuxtjs/eslint-module": "^3.0.2",
    "@nuxtjs/vuetify": "^1.12.1",
```

[tsutils/package.json at master · ajafff/tsutils](https://github.com/ajafff/tsutils/blob/master/package.json#L54-L56)を見てみると「peerDependencies」に「typescript」が定義されている。
```json
  "peerDependencies": {
    "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
  },
```

メッセージに「ERR!」「required」を出すなら自動でインストールしてくれてもいいのに・・・と心でつぶやいていたらnpmのv7からは自動でインストールされるようになっているそうです。

> In npm versions 3 through 6, peerDependencies were not automatically installed, and would raise a warning if an invalid version of the peer dependency was found in the tree. As of npm v7, peerDependencies are installed by default.
> [package.json | npm Docs](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#peerdependencies)

```bash
# 今回、v7未満でpeerDependenciesはインストールされない
$ npm -v
6.14.16
```

### 対応 : package.jsonに追記してインストールする
「typescriptをインストールすればよい」と思うのです。
が、`npm install typescript`して自分のローカルだけにインストールすると一緒に開発している他のメンバーがいちいち「typescriptを各自でインストール」することになると思うのです。

というわけで「typescriptをpackage.jsonに追記してインストールする」ことにしました。
「typescript」は、「`devDependencies`に書いてある`@nuxtjs/eslint-config`の依存関係にある`tsutils`の依存関係にある」ということから`--save-dev`オプションを指定してpackage.jsonの「devDependencies」に追記されるようにしました。

参考 : [【いまさらですが】package.jsonのdependenciesとdevDependencies - Qiita](https://qiita.com/chihiro/items/ca1529f9b3d016af53ec)

```bash
# typescriptをpackage.jsonに追記してインストールする
$ npm install --save-dev typescript
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.2 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules/watchpack-chokidar2/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})

+ typescript@4.6.2
added 1 package from 1 contributor and audited 1879 packages in 25.225s

146 packages are looking for funding
  run `npm fund` for details

found 5 vulnerabilities (3 moderate, 2 high)
  run `npm audit fix` to fix them, or `npm audit` for details

# package.jsonの「devDependencies」に追記された
$ cat package.json 
{
# ...省略...
  "devDependencies": {
# ...省略...
    "typescript": "^4.6.2",
    "vue-jest": "^3.0.4"
  }
}

# typescripがインストールされた
$ npm ls typescript
app-name@1.0.0 /app
└── typescript@4.6.2

# エラーが解消された！
$ npm ls | grep -i peer

```














































# 事象 : UNMET OPTIONAL DEPENDENCY
1. 環境 : Windows10 Pro バージョン21H1 に作った `node:14-buster-slim` のDockerコンテナ
    - Debian GNU/Linux 10.11 x86_64

```bash
# インストールしたパッケージの一覧になんか出てる
$ npm ls | grep -i unmet
│ │ ├── UNMET OPTIONAL DEPENDENCY fsevents@2.3.2
│ │ │ ├── UNMET OPTIONAL DEPENDENCY fsevents@2.3.2
│   │ │     ├─┬ UNMET OPTIONAL DEPENDENCY fsevents@1.2.13
```

## 原因 : 利用可能であればインストールするパッケージが利用不可でインストールされていないから
満たされていない(UNMET)OPTIONAL DEPENDENCY？OPTIONAL DEPENDENCYって何？

> If a dependency can be used, but you would like npm to proceed if it cannot be found or fails to install, then you may put it in the optionalDependencies object. This is a map of package name to version or url, just like the dependencies object. The difference is that build failures do not cause installation to fail.
> [package.json | npm Docs](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#optionaldependencies)

翻訳サイトで訳しても何を言っているのかが良くわからなかった。
やはり、日本語のサイトの説明がわかりやすかった。

> optionalDependenciesは**利用可能であればインストールされるが、利用不可であっても他の処理を止めたくない**場合に使用します。
> [npmのoptionalDependenciesとは - zenn.dev](https://zenn.dev/catnose99/articles/7dfcd5b3e5b141)

そういえば、typescriptをインストールした時に警告が出ていた。

```bash
# インストール先の環境が合わないので「fsevents」のインストールがスキップされた
$ npm install --save-dev typescript
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.3.2 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.3.2: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.13 (node_modules/watchpack-chokidar2/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.13: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
```

optionalDependenciesは、利用できればインストールするが必須ではないパッケージのようです。

```bash
# fseventsはなんのパッケージに依存するか見てみる
$ npm ls fsevents
app-name@1.0.0 /app
├─┬ axios-mock-server@0.19.1
│ └─┬ chokidar@3.5.3
│   └── UNMET OPTIONAL DEPENDENCY fsevents@2.3.2 
#...省略...

# 「chokidar」のpackage.jsonをみると「optionalDependencies」に「fsevents」が定義されている
$ grep -1i optional node_modules/chokidar/package.json
  },
  "optionalDependencies": {
    "fsevents": "~2.3.2"
```

### 対応 : 必要になるまで何もしない
必須ではないパッケージなので開発でないと困る状態になるまではほっておきます。
インストール時に`--no-optional`オプションをつけて`npm install --no-optional`とすればoptionalDependenciesのパッケージはインストールされなくなります。

> Running npm install --no-optional will prevent these dependencies from being installed.
> [package.json | npm Docs](https://docs.npmjs.com/cli/v6/configuring-npm/package-json#optionaldependencies)