babelでトランスコンパイルしてminifyする方法

# はじめに
自分のNodeプロジェクトでbabelを入れてみる事にしました。minifyはgulpでやっていたのですが、babelで出来る事が分かったので切り替えました。その過程でいくつか躓いたのでメモを残しておきます。

# 環境

Debian 9.3
node v8.9.4
npm  5.6.0

ちなみにwindowsでも確認してあります。

# 前準備

まずプロジェクトとテストに使うjsファイルを作成します。

```bash
$ mkdir babel-test
$ cd babel-test
$ mkdir js
$ echo '[1, 2, 3].map((n) => n + 1);' > ./js/example.js
```

これで次のようなファイルが出来ました。プログラムは公式の例を参考にしています。

```javascript:./js/example.js
[1, 2, 3].map((n) => n + 1);
```

次に必要なモジュールを入れていきます。

```bash
$ npm init -y
$ npm install babel-cli babel-preset-env babel-minify babel-preset-minify rimraf -D
```

なお、既存のプロジェクトに追加した時は、`Cannot find module core-js/library/fn/object/key`のようなエラーが出て動きませんでした。この時はnode_modulesを全て消して、モジュールを新しくインストールし直したら解決しました。

# babel-cliを試す

トランスコンパイルするコマンドが`babel-cli`です。次のように実行出来ます。

```bash
$ ./node_modules/.bin/babel ./js/ -d ./jsts
js/example.js -> jsts/example.js
```

成功したっぽいのでファイルを確認しましょう。

```javascript:./jsts/example.js
[1, 2, 3].map(n => n + 1);
```

、、、変わってません。
公式にも書いてありますが、ちゃんとトランスコンパイルするようには、`.babelrc`で`babel-preset-env`を使うように設定する必要があります。

```bash
$ touch .babelrc
```

次のように編集します。

```json:.babelrc
{
  "presets": ["env"],
}
```

これで試してみます。

```bash
$ ./node_modules/.bin/babel ./js/ -d ./jsts
js/example.js -> jsts/example.js
```

次のようにアロー関数が普通の関数になっています。

```javascript:./jsts/example.js
"use strict";

[1, 2, 3].map(function (n) {
  return n + 1;
})
```

# Production環境でminifyする

さて、次にminifyですが、出来たらproduction環境だけでminifyしたいですよね。（デバッグが面倒すぎるので)
.babelrcを次のように設定します。

```json:.babelrc
{
  "presets": ["env"],
  "env": {
    "production": {
      "presets": ["minify"]
    }
  }
}
```

"env"はpresetsのenvと混合しそうですが別物で、環境変数によって設定を変える時に使います。環境変数の設定方法は[公式ドキュメント](https://babeljs.io/docs/en/config-files#apienv)に書いてあります。今回はexportでBABEL_ENVの設定を変えます。なお、Windowsの場合は`SET BABEL_ENV=production`です。

```bash
$ export BABEL_ENV=production
$ ./node_modules/.bin/babel ./js/ -d ./jsts
Error: Cannot read property 'contexts' of null
```

何故かエラーになりました。試しに次のようにコマンドを直接実行してみます。

```
$ ./node_modules/.bin/minify ./js/ -d ./jsts
Error: Cannot read property 'contexts' of null
```

これ以外にも`Error: Couldn't find intersection`などのエラーが出る事もありました。色々とググって[解決法](https://github.com/babel/minify/issues/904)を見つけたので、試してみました。

```json:.babelrc
{
  "presets": [
    "env"
  ],
  "ignore": [],
  "env": {
    "production": {
      "presets": [
        [
          "minify",
          {
            "builtIns": false,
            "evaluate": false,
            "mangle": false
          }
        ]
      ]
    }
  }
}
```

```bash
$ ./node_modules/.bin/babel ./js/ -d ./jsts
js/example.js -> jsts/example.js
```

```javascript:.jsts/example.js
"use strict";[1,2,3].map(function(a){return a+1});
```

出来た \\(^^)/

# `presets`について

さて、.babelrcの"env"は環境変数によって処理を切り替える、、、と書きましたが、これは少し間違いです。例えば次の例を見て下さい。

```json:.babelrc
{
  "presets": ["env"],
  "env": {
    "production": {
      "presets": ["minify"]
    }
  }
}
```

上記の設定でbabelコマンドを実行すると、production環境では`babel-preset-env`の処理を行った後に、`babel-preset-minify`が処理されます。つまりパイプされています。上記の書き方だと`presets`が上書きされているわけではない事に注意して下さい。試しに一行目の`"presets": ["env"]`を削除して実行すると、次のようにminifyだけされたファイルが出力されます。


# コンパイルを簡単にする

上記のコマンドを毎回入れるのは面倒ですので、npm run buildでコンパイル出来るようにしましょう。 "script"の所に次の一行を追加します。

```json:.package.json
  "scripts": {
    "build": "./node_modules/.bin/rimraf ./jstc && ./node_modules/.bin/babel ./js/ -d ./jsts",
  },
```

rimrafはフォルダを消すコマンドです。一旦クリーンアップしてから新たに作るようにしています。
なお、Windowsの場合は`".\\node_modules\\.bin\\babel"`のようなパスの書き方でないと動きませんでした。

さてコマンドを実行しましょう。

```bash
$ npm run build

> babel-test@1.0.0 build ~/babel-test
> babel ./js/ -d ./jsts

js/example.js -> jsts/example.js
```

出来た v(^^)v


# まとめ
今回はひとまずminifyまで試したかったのでここまでですが。まだ色々とやる事はありそうなので、順次更新しようと思います。