生産性が高くホワイトボックスなローカルSPA開発環境レシピ 〜 JSおくのほそ道 #039

こんにちは、ほそ道です。

スクラッチでSPAプロジェクトを作るとき、ボイラープレートに依存すると楽なんですが、自分でカスタマイズしながら環境構築するのも知識が深まってオツではないかとおもいます。
今回は、自分でもたまにやると忘れてしまうのでレシピをまとめておきたいと思います。
[目次はこちら](http://qiita.com/setzz/items/ad9a9c99a4fda041a563)

# 高生産性
ローカルでのSPA開発の生産性を高める為によくやる下記のような環境構築、今回は下記のようなものを扱います。

- 1.ファイルを保存したら差分ビルド
- 2.ウェブサーバを起動しつつ同時にブラウザ起動
- 3.ファイルを更新したら併せてブラウザ自動リロード

# ホワイトボックス

色んなライブラリに依存しながらブラウザ起動やオートリロードを統合的に提供してくれるようなサーバライブラリもありますが、自分で組み合わせると問題が起きたときの原因対応が容易というメリットがあると思っています。

- バージョンアップ時に動作しなくなったときなどgithubのissueにたどり着きやすい
- 機能毎にライブラリを分けているので、問題が起きたときなど一部のライブラリのみを切り離すことが容易
- 一部のライブラリを変更して、自分で組み合わせを変更し易い


# 1.ファイルを保存したら差分ビルド

[webpack](https://webpack.js.org/)のwatch機能を使います。
準備としてwebpackをインストール(今回はglobalではなくプロジェクトローカルで)して、プロジェクトルートにwebpack.config.jsを設置します。
(configに関してはあらゆるところにサンプルがあるので割愛します)

## 1-a.CLIで実行

ターミナルで実行。とりあえず動かしたい時に楽ちんです。

```shell:ターミナル
npx webpack --watch
```

## 1-b.前後処理が必要な時

だいたいそれなりのプロジェクトではファイルクレンジングやコピーなどの様々な前後処理が発生して、シェルで賄うのがしんどくなってしまうので、そんな時にはスクリプトを作って実行しています。

```js:scripts/build-watch.js
const webpack = require('webpack');
const path = require('path');

const webpackConf = require(path.resolve('./webpack.config'));
const webpackCompiler = webpack(webpackConf);

// ビルド前処理

webpackCompiler.watch({poll: true}, callback);

// ビルド後処理

function callback(err) {
  if (err) {
    process.stderr.write(err);
    process.exit();
  } else {
    // カスタムなビルド完了メッセージ
    process.stdout.write(`Webpack Build Complete At: [${new Date()}]\n`);
  }
}
```

ターミナルで実行。

```shell:ターミナル
node scripts/build-watch.js
```


# 2.ウェブサーバを起動しつつ同時にブラウザ起動

## 2-1.ウェブサーバ
[local-web-server](https://www.npmjs.com/package/local-web-server)を使います。
このライブラリを選定しているのは下記のようなSPA開発向けでよく使うURL裁きが柔軟に出来るからです。
(この話の詳細は[別記事にて](https://qiita.com/hosomichi/items/930c0efb5d955d12932e)詳しく書いています)

- 機能がURLにマッチするリソースがない場合に指定したhtmlファイルを返すことが可能
- 名前の異なるURLとHTMLファイルをマッチングするRewrite処理が可能

インストールして下記のようにターミナルで実行(リソースはpublicディレクトリに配置してます)。
--directoryオプションは-dに、--spaオプションは-sに、--rewriteオプションは-rに省略可能です。

```shell:ターミナル
$ npx ws --directory public --spa index.html --rewrite "/login -> /auth.html"
```

## 2-2.ブラウザ起動
[opener](https://www.npmjs.com/package/opener)を使います。
指定したURLでブラウザをオープンさせます。
(自分はMacを使っているのでWindowsだと勝手が違うかもしれません)

インストールして、特にブラウザを指定する必要が無ければ、URLを指定して実行するだけです。

```shell:ターミナル
npx opener http://localhost:8000
```

### 2-3.ウェブサーバと組み合わて実行
(順序の関係で一瞬ページが見つからないエラーが表示されるかもしれませんが)

```shell:ターミナル
npx opener http://localhost:8000 & npx ws -d public -s index.html
```


# 3.ファイルを更新したら併せてブラウザ自動リロード

[livereload](https://www.npmjs.com/package/livereload)を使います。
publicディレクトリにhtmlやjsのリソースがあったとして、それらのリソースに更新があればブラウザの自動リロードを行います。
このライブラリはコマンド実行に加え、htmlファイル側にも仕込みが必要です。
インストールしたら、自動リロードしたいhtmlファイルの末尾に下記を挿入しておきます。

```html:hoge.html
<body>
  ...
  <script>
    document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] +
    ':35729/livereload.js?snipver=1"></' + 'script>')
  </script>
</body>
```

実行。これもファイルの変更を見張る常駐プロセスとして動きます。

```shell:ターミナル
npx livereload public
```


# まとめ：npm scriptに設定

だいたい自分はプロジェクトメンバー向けに`npm run`出来るようにパッケージングしてしまうのでサンプルを書いておきます。

```json:package.json
{
  "scripts": {
    "build-watch": "node ./scripts/build-watch.js",
    "webserver": "npx opener http://localhost:8000 & npx ws -d public --spa index.html",
    "autoreload": "npx livereload public"
  },
  ...
}
```

余談ですがWebStormというエディタだとnpm runの実行がGUIクリックで出来るので死ぬほど楽です:smile:

以上となります。