npmパッケージをgitリポジトリから取得したときに動くnpm scriptsを調べてみた

# やってみたこと

node.jsのアプリをいくつか書いていたら、共通の処理が出てきたのでライブラリ化したくなった。
でもあれやこれや…な理由でパブリックには公開できなかった。:cry:
そこでnpmのリポジトリには公開せず、プライベートなgitリポジトリから`npm i git+https://<git-repo>`としてインポートすることにした。

[npm prepublish の現状と今後どう変わっていくか](https://qiita.com/ndxbn/items/f0cd2b13a3268254f2aa)という記事でnpm scriptsが実行される順序がまとめられていたので参考にしていたのだが、「publishされていないパッケージをgitのリポジトリから直接取ってきたらどうなんねん？」という疑問が湧いてきたので調べてみた。

## 検証環境

とりあえずnpm 5系と6系のふたつだけ調べました。
検証に使用したPCはmacOS Mojaveの入ったMacBook Proです。

1つ目
* node v8.11.4
* npm 5.6.0

2つ目
* node v8.12.0
* npm 6.4.1

## 検証方法

まずは下のようなpackage.jsonを用意する。scripts以外は基本的に`npm init -y`で吐き出しただけ。

```json:package.json
{
  "name": "npm-script-timings",
  "description": "This project is used to check timings when each npm scripts run.",
  "version": "1.0.0",
  "keywords": [],
  "license": "ISC",
  "main": "index.js",
  "scripts": {
    "prepublish": "echo \"##### prepublish #####\"",
    "prepare": "echo \"##### prepare #####\"",
    "prepublishOnly": "echo \"##### prepublishOnly #####\"",
    "prepack": "echo \"##### prepack #####\"",
    "pack": "echo \"##### pack #####\"",
    "postpack": "echo \"##### postpack #####\"",
    "publish": "echo \"##### publish #####\"",
    "postpublish": "echo \"##### postpublish #####\"",
    "preinstall": "echo \"##### preinstall #####\"",
    "install": "echo \"##### install #####\"",
    "postinstall": "echo \"##### postinstall #####\"",
    "preuninstall": "echo \"##### preuninstall #####\"",
    "uninstall": "echo \"##### uninstall #####\"",
    "postuninstall": "echo \"##### postuninstall #####\"",
    "preversion": "echo \"##### preversion #####\"",
    "version": "echo \"##### version #####\"",
    "postversion": "echo \"##### postversion #####\"",
    "pretest": "echo \"##### pretest #####\"",
    "test": "echo \"##### test #####\"",
    "posttest": "echo \"##### posttest #####\"",
    "prestop": "echo \"##### prestop #####\"",
    "stop": "echo \"##### stop #####\"",
    "poststop": "echo \"##### poststop #####\"",
    "prestart": "echo \"##### prestart #####\"",
    "start": "echo \"##### start #####\"",
    "poststart": "echo \"##### poststart #####\"",
    "prerestart": "echo \"##### prerestart #####*\"",
    "restart": "echo \"##### restart #####\"",
    "postrestart": "echo \"##### postrestart #####\"",
    "preshrinkwrap": "echo \"##### preshrinkwrap #####\"",
    "shrinkwrap": "echo \"##### shrinkwrap #####\"",
    "postshrinkwrap": "echo \"##### postshrinkwrap #####\""
  }
}
```

このpackage.jsonをプライベートgitリポジトリにpushし、別プロジェクトから`npm i git+https://<git-repo>.git`したときのコンソールを見てみる。
今回はこのプロジェクトを`npm-script-timings`という名前のリポジトリにして、下のようなディレクトリ構成を取って確認してみた。別プロジェクトと言いながらサブディレクトリに含めてしまっているのは特に意味はありません。

```shell
npm-script-timings
├── package-lock.json
├── package.json        # 上記のpackage.json
└── another-project     # npm-script-timingsをインストールするプロジェクト
```


## 結果

パス中に登場してしまっている以下の名前は置換しています。

* ホームディレクトリ -> $HOME
* gitリポジトリのURL -> $GIT_REPO

### npm 5.6.0の場合

インストール対象のスクリプトは、`prepack -> postpack -> preinstall -> install -> postinstall`の順で動作するようです。

```
> npm-script-timings@1.0.0 prepack $HOME/.npm/_cacache/tmp/git-clone-d3d05fce
> echo "##### prepack #####"

##### prepack #####

> npm-script-timings@1.0.0 postpack $HOME/.npm/_cacache/tmp/git-clone-d3d05fce
> echo "##### postpack #####"

##### postpack #####

> npm-script-timings@1.0.0 preinstall $HOME/.ghq/$GIT_REPO/another-project/node_modules/npm-script-timings
> echo "##### preinstall #####"

##### preinstall #####

> npm-script-timings@1.0.0 install $HOME/.ghq/$GIT_REPO/another-project/node_modules/npm-script-timings
> echo "##### install #####"

##### install #####

> npm-script-timings@1.0.0 postinstall $HOME/.ghq/$GIT_REPO/another-project/node_modules/npm-script-timings
> echo "##### postinstall #####"

##### postinstall #####
added 1 package in 7.137s
```

### npm 6.4.1の場合

基本的な動作は`npm 5.6.0`の場合と同じようですが、なぜか`prepack`と`postpack`が2回ずつ走っています。
そこまで問題にはならない気もしますが、意図した動作ではなさそう？

```
> npm-script-timings@1.0.0 prepack $HOME/.npm/_cacache/tmp/git-clone-8b6cee0a
> echo "##### prepack #####"

##### prepack #####

> npm-script-timings@1.0.0 postpack $HOME/.npm/_cacache/tmp/git-clone-8b6cee0a
> echo "##### postpack #####"

##### postpack #####

> npm-script-timings@1.0.0 prepack $HOME/.npm/_cacache/tmp/git-clone-79212f62
> echo "##### prepack #####"

##### prepack #####

> npm-script-timings@1.0.0 postpack $HOME/.npm/_cacache/tmp/git-clone-79212f62
> echo "##### postpack #####"

##### postpack #####

> npm-script-timings@1.0.0 preinstall $HOME/.ghq/$GIT_REPO/npm-script-timings/another-project/node_modules/npm-script-timings
> echo "##### preinstall #####"

##### preinstall #####

> npm-script-timings@1.0.0 install $HOME/.ghq/$GIT_REPO/npm-script-timings/another-project/node_modules/npm-script-timings
> echo "##### install #####"

##### install #####

> npm-script-timings@1.0.0 postinstall $HOME/.ghq/$GIT_REPO/npm-script-timings/another-project/node_modules/npm-script-timings
> echo "##### postinstall #####"

##### postinstall #####
added 1 package and audited 1 package in 12.331s
found 0 vulnerabilities
```

## まとめ

npmリポジトリから取得するときと違って、gitリポジトリから直接取得しようとしたときには`npm prepack`と`npm postpack`が追加で動作するようです。
npmリポジトリに登録をしないときには`npm publish`をするタイミングがないので代わりに`npm pack`が行われる、というイメージでしょうか。

## おまけ

[TypeScript で作ったパッケージを TravisCI で npm に公開するときの Tips](https://qiita.com/naokikimura/items/f1c8903eec86ec1de655#npm-npmignore-%E3%81%A7%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AB%E5%90%AB%E3%82%81%E3%82%8B%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%88%B6%E5%BE%A1%E3%81%99%E3%82%8B)でも述べられてる`.gitignore`と`.npmignore`の動作に気付かずに同じハマり方しました…。