OSに依存しない npm-script の書き方

# 概要

フロントエンドの開発中 npm-script を書いたけど、OSのコマンド差異でうまくチーム内で展開できない...
そんな悩みないですか？
その問題、 `run-script-os`パッケージを使って解決出来ます！
（簡素なノウハウですが...）

# やること

- `run-script-os` と `npm-run-all`(Option) を install

```bash
npm install --save-dev run-script-os npm-run-all
# or
yarn add --dev run-script-os npm-run-all
```

- npm-script を書く

```JSON
{
  ...
  "scripts": {
    ...
    "echo-os": "run-script-os",
    "echo-os:win32": "echo 'echo by Windows'",
    "echo-os:darwin": "echo 'echo by macOS'",
    "echo-os:linux": "echo 'echo by Linux'",
    "test-file": "run-s create-file delete-file",
    "create-file": "run-script-os",
    "create-file:win32": "type nul > empty.txt",
    "create-file:darwin:linux": "touch empty.txt",
    "delete-file": "run-script-os",
    "delete-file:win32": "del empty.txt",
    "delete-file:darwin:linux": "rm -f empty.txt",
    ...
  },
  ...
}
```

- npm-scriptを実行する

```bash
# "echo by [OS名称]"を出力する
npm run echo-os

# npm-run-allと組み合わせて、空ファイルを作成して削除する
npm run ctrl-file
```

- 実行OS毎に実行される command が切り替わる npm-script が出来る! :thumbsup:

# npm-run-os とは

[run-script-os - npm](https://www.npmjs.com/package/run-script-os)

- （簡単に言うと）nodeの実行環境(OS)毎に実行する npm-scriptを変更できるモジュール
  - Node.js の node.processに設定されている値に応じてscriptが実行される

## メリット

- このモジュールの真価が発揮されるのは、フロントエンドアプリケーションのnpmエコシステム(と言うのか?)において、OS nativeな操作を実行する必要がある場合
  - 反面、`npm module`のみで完結するのであれば不要なモジュール

- 一昔前に流行ったフロントエンドの歴戦のタスクランナー達のように、version依存などの問題回避が期待できる
  - 特に npm module の管理と合わせて、タスク管理は非常にやりやすい
  - npm modulen のバイナリを実行したいのであれば`npx`使えば良いし、非常に便利
  - 歴戦のタスクランナー達：gulp.js, Grunt等
  - （一昔前と言っても、gulpなんかは知る限り5年以上前にブームがあった気が... :thinking:）

## デメリット

- 上記の例でもわかる通り、OS nativeな操作に依存する操作が多ければ多いほどscriptが膨れ上がる
  - だいたい、課題になる `Windows`と `unix/linux系`を分けるだけでも、最低3行(1. npm-run-osの実行, 2. `Windows`依存処理, 3. `unix/linux系`依存処理)が必要になる
  - 必然的にpackage.jsonの肥大化に繋がる...
- ｵﾃﾞ ﾍﾝｽｳ ﾂｶｳﾎｳﾎｳ ﾜｶﾗﾅｲ...ｵﾌﾟｼｮﾝ ﾄﾞｳｽﾙ... :japanese_ogre:

# 例

ここでは、適当なビルドの例を想定して例をあげます。
あくまで一例ですし、書けば動作する例を示すわけでもないので参考程度に...
それぞれ、利用する要素技術については解説しないです。
（本記事の本旨からは外れますが、あらかじめ[npm-run-scriptについて](#npm-run-scriptについて)を読んでいただいた方が良いかもです）

### ビルド例

- 想定する実行フロー

1. 事前準備のNativeモジュールをビルドするbat/shを叩く
1. sassでcssビルド (build:css)
1. flowで型チェック (build:check-type)
1. prettierでフォーマット整形 (build:format)
1. babelでトランスパイル (build:transpile)

```JSON
{
  ...
  "scripts": {
    ...
    "build": "run-s pre-build build:*",
    "pre-build": "run-script-os",
    "pre-build:win32": ".¥tools¥build-native.bat", 
    "pre-build:darwin:linux": "./tools/build-native.sh", 
    "build:css": "sass sass/*.scss dist/css/test.css --style=compressed",
    "build:check-type": "flow check",
    "build:format": "prettier src/**/.js --write",
    "build:transpile": "babel src/**/ -o dist/main.js",
    ...
  },
  ...
}
```

# 備考

## npm-run-scriptについて

[npm-run-all - npm](https://www.npmjs.com/package/npm-run-all)

- npm-scriptを書くにあたって、関連するコマンドをパイプラインで繋いだりすると問題が... :thermometer_face: 
  2. セマンティックにnpm-scriptを定義したい
     - scriptを駆使して、script実行を繰り返すにしても...  
       `npm script ** && npm script ** && npm script ** ...` :scream: < OMG
  2. 開発中は、developサーバ起動してUI確認しながら開発したい
     - サーバー起動でコンソールがロックされる...バックグラウンド実行やxargs, GNU parallelで良いけど windows環境でどうする？ :thunder_cloud_rain:

上記のような問題に対して、npm-scriptを逐次/並列に実行できるようにしてくれるのが、npm-run-scriptモジュール  
使い方も非常にシンプルで、以下の2つを覚えれば良いだけです。

```
run-s: タスクを逐次実行
run-p: タスクを並列実行
```

`run-p`は、ロール開発には必須となる `webpack-dev-server`などの開発用ローカルサーバを立てたときにLISTEN状態になってしまうタスクと、後続タスクを並列で起動したい場合などで便利です。

```JSON
{
  ...
  "scripts": {
    ...
    "run-dev": "run-p run-dev-server open-browser",
    "run-dev-server": "webpack-dev-server --hot --watch-content-base --content-base dist/ --port 8080",
    "open-browser": "run-script-os",
    "open-browser:win32": "start microsoft-edge:http://localhost:8080",
    "open-browser:darwin": "open http://localhost:8080",
    "open-browser:linux": "open http://localhost:8080",
    ...
  },
  ...
}
```

npm-script内に、`run-s`や`run-p`を書けば、後続の処理を逐次/並列実行してくれる  
ね、簡単でしょ？ :innocent:

# まとめ

正直、だいたいのことは上記のnpm-scriptでなんとかなると思っている
何か複雑なことをしたければ特化したnpm moduleを利用すればいいし、npm module外のことをするにしても上記のモジュールを導入すればなんとかなる
(ちょっと気になるのは、package.jsonのscriptが肥大化すること... :sweat:)

この記事が誰かの役に立てれば幸いです！

より良い npm ライフを！ :sunglasses: