gulp + babel7 + uglify 導入方法(2019年1月)

## はじめに

本記事では、[gulp-uglify](https://www.npmjs.com/package/gulp-uglify)を使ってJavasciptを圧縮する方法について記載します。
また、それに伴って、ES6(2015)のコードをES5に変換するがありましたので、それについても記載します。

意外とすんなりできなかったので、失敗例とうまく言ったやり方を書いておきます。

## まとめ
- uglifyはES6を圧縮できないからbabelを使う
- 圧縮には`@babel/core`, `@babel/preset-env`を使う
- そもそも`uglify`じゃなく`babel-preset-minify`を使えばよかったかも？

## うまく行ったやり方

```:ファイル構成
- src/sample_es6.js
- gulpfile.js
```

`src/sample_es6.js`は今回の変換対象のJSです。
ES6で使えるconst、let、アロー関数式を含んでいます。

```javascript:src/sample_es6.js
// ES6
'use strict';

const MESSAGE = "Hello!";

let greet = () => {
    console.log(MESSAGE);
  };

greet();
```

gulpfile.jsには、babelによる変換および圧縮を行うタスクを記載します。
`default`タスクなので、gulpを実行するとそのタスクが実行されます。

```javascript:gulpfile.js
const gulp = require('gulp');
const uglify = require('gulp-uglify');
const rename = require('gulp-rename');
const babel = require('gulp-babel');

gulp.task("default", function () {
  return gulp.src('src/sample_es6.js')
    .pipe(babel({
      "presets": ["@babel/preset-env"]
    }))
    .pipe(uglify())
    .pipe(rename({
      extname: '.min.js'
    }))
    .pipe(gulp.dest('dest/'));
});
```

必要なパッケージをインストールします。

```bash
# yarn add --dev gulp
# yarn add --dev gulp-uglify gulp-rename
# yarn add --dev gulp-babel @babel/core @babel/preset-env
```
`babel-core`, `babel-preset-env`というパッケージも存在しますが、
babel7では、`@babel/`から始まるパッケージをインストールする必要があります。

これで、gulpを実行しましょう。

```bash
# gulp
```

destディレクトリに、圧縮されたJSファイルができます。
成功です。

```javascript:dest/sample_es5.min.js
"use strict";var MESSAGE="Hello!",greet=function(){console.log(MESSAGE)};greet();
```

ここまでやった状態のファイルをGitHubに載せておきます。
https://github.com/y-tsuzaki/test-gulp-babel-uglify


ところが……
[babel-preset-minify](https://www.npmjs.com/package/babel-preset-minify)とかいうパッケージを見つけました。
もしかすると、uglifyではなくbabel関連パッケージを使った方がシンプルだったかもしれません。

そのうち試してみます。

以下、つまずいた点を記載しておきます。

## 失敗1 : ES6(ES2015)が圧縮できない。

**結論：uglifyはES5しか圧縮できません！**

https://qiita.com/ichigo_daifuku3/items/9f559b646d5b2508039d を参考に圧縮を試みました。

```bash
# yarn add --dev gulp
# yarn add --dev gulp-uglify gulp-rename
```

```javascript:gulpfile.js
const gulp = require('gulp');
const uglify = require('gulp-uglify');
const rename = require('gulp-rename');

gulp.task("default", function() {
  return gulp.src('src/sample_es6.js')
      .pipe(uglify())
      .pipe(rename({
          extname: '.min.js'
      }))
      .pipe(gulp.dest('dest/'));
});
```
タスクの実行時に次のようなエラーが出て圧縮できませんでした。

```bash
[18:49:46] GulpUglifyError: unable to minify JavaScript
Caused by: SyntaxError: Unexpected token: keyword (const)
File: /mnt/d/work/_study/gulp-uglify-babel/test01/src/sample_es6.js
Line: 4
```

「`const`なんてキーワードしらねーよ！」っていうエラーですね。

残念ながら、uglifyはES5しか対応しておらず、
ES6(ES2015)のコードは圧縮できずエラーとなってしまうようです。
なので、uglifyで圧縮する前に、ES6(ES2015)からES5にコンパイルしましょう。
コンパイルには、**babel(gulp-babel)**が必要です。

じゃあ、それを設定すればいいんだろと思ったんですが、それもまたうまくいきませんでした。

## 失敗2: babelを普通にインストールしても動かない

**結論： インストールするパッケージが違う**

http://cly7796.net/wp/other/try-using-babel-in-gulp/
https://qiita.com/gigazombie/items/4227cdf25580a6c539f8

このあたりを参考にやってみました

```bash
# yarn add --dev babel-preset-es2015 gulp-babel babel-core
```
```bash
warning babel-preset-es2015@6.24.1: 🙌  Thanks for using Babel: we recommend using babel-preset-env now: please read babeljs.io/env to update!
```
babel-preset-es2015のインストールで「babel-preset-envがおすすめですよ」とやんわり怒られました。
まあ警告なので、いったん無視します。


```javascript:gulpfile.js
const gulp = require('gulp');
const uglify = require('gulp-uglify');
const rename = require('gulp-rename');
const babel = require('gulp-babel');

gulp.task("default", function() {
  return gulp.src('src/sample_es6.js')
      .pipe(babel({
          "presets":["babel-preset-es2015"]
      })) 
      .pipe(uglify())
      .pipe(rename({
          extname: '.min.js'
      }))
      .pipe(gulp.dest('dest/'));
});
```

```bash
# gulp
internal/modules/cjs/loader.js:583
    throw err;
    ^

Error: Cannot find module '@babel/core'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
    at Function.Module._load (internal/modules/cjs/loader.js:507:25)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:20:18)
    at Object.<anonymous> (/mnt/d/work/_study/gulp-uglify-babel/test02/node_modules/gulp-babel/index.js:7:15)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
```

タスクを実行すると`@babel/core`が見つかんねーぞ的なエラーが。
`babel-core`インストールしたんだけどなんで？と思ったんですが、
`gulp-babel`は`babel-core`じゃなくて`@babel/core`が必要みたいです。
（バージョアップでパッケージ名が変わったようです。）

ちなみに、`@babel`の部分は、スコープモジュール(scoped module)というらしいです。
https://docs.npmjs.com/about-scopes

というわけで、インストールするパッケージを変えてやり直してみましょう。

```bash
yarn remove babel-core
yarn add --dev @babel/core
```

これでもう一度`gulp`を実行します。

```bash
# gulp
[19:24:12] Error in plugin "gulp-babel"
Message:
    Plugin/Preset files are not allowed to export objects, only functions. In /mnt/d/work/_study/gulp-uglify-babel/test02/node_modules/babel-preset-es2015/lib/index.js
```
なんだかよくわかんない感じのエラーが発生しました。
ググった感じだとbabel-preset-es2015のバージョンがgulp-babelとあってないっぽいようです。
babel-preset-es2015をインストールした時に、
`babel-preset-env`がおすすめですよと書いていましたが、それでもだめだったので、
`@babel/preset-env`を入れてみます。

```bash
# yarn remove babel-preset-es2015
# yarn add --dev @babel/preset-env
```

```javascript:gulpfile.js
const gulp = require('gulp');
const uglify = require('gulp-uglify');
const rename = require('gulp-rename');
const babel = require('gulp-babel');

gulp.task("default", function () {
  return gulp.src('src/sample_es6.js')
    .pipe(babel({
      "presets": ["@babel/preset-env"]
    }))
    .pipe(uglify())
    .pipe(rename({
      extname: '.min.js'
    }))
    .pipe(gulp.dest('dest/'));
});
```

`gulp`を実行しましょう。

```bash
# gulp
[19:40:22] Using gulpfile /mnt/d/work/_study/gulp-uglify-babel/test02/gulpfile.js
[19:40:25] Starting 'default'...
[19:40:28] Finished 'default' after 2.71 s
```

ようやくビルドできるようになりました！！

それではまた。