JSの最適化ツール「Prepack」を使ってみる

## Prepackとは
- Facebookから公開されたJSをAOTコンパイルするツール
- コードの最適化を行う
 - どんな変換がされるかは、[こちら](https://prepack.io/)の例のようになる
- コンパイル時に計算できるものは計算しておくので、ランタイム時の動作が早くなる
- まだ出たばっかりで初期の開発段階
- [こちら](https://prepack.io/)のロードマップを見る限り、Prepack単体でES6が動く状態になるのは、まだ先（中期以降）
 - つまりBabelなどのトランスパイラで変換したコードに対して、Prepackを実行するようにしないと変換できない

たまたま見つけた`prepack-webpack-plugin`っていうのを使って、どんな感じになるか見てみたいと思います
https://github.com/TheLarkInn/prepack-webpack-plugin

ググると`eslint-plugin-prepack`や`rollup-plugin-prepack`的なのも開発中？なんですね

## 設定
```
yarn add prepack-webpack-plugin --dev
```

```webpack.config.js
const path = require('path')
const PrepackWebpackPlugin = require('prepack-webpack-plugin').default

module.exports = {
  entry: [
    path.join(__dirname, '../src/sample')
  ],
  output: {
    path: path.join(__dirname, '../dist'),
    filename: 'bundle.js',
    publicPath: '/'
  },
  plugins: [
    new PrepackWebpackPlugin()
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /node_modules/
      },
    ]
  }
}
```

## 実行

こちらの[公式サイト](https://prepack.io/)の例から取ってきたコードで実行
（公式サイトには5つ例があるが、2つはPrepackのInitializationErrorで動かなかったので、それ以外のコード3つになります）

```sample.js
(function () {
  function hello() { return 'hello'; }
  function world() { return 'world'; }
  global.s = hello() + ' ' + world();
})();

(function () {
  function fibonacci(x) {
    return x <= 1 ? x : fibonacci(x - 1) + fibonacci(x - 2);
  }
  global.x = fibonacci(23);
})();

(function(){
  function fib(x) { return x <= 1 ? x : fib(x - 1) + fib(x - 2); }
  let x = Date.now();
  if (x === 0) x = fib(10);
  global.result = x;
})();
```

webpackでビルド

```
webpack
```

変換後　↓

```js:bundle.js
(function () {
  s = "hello world";
  x = 28657;

  var _$0 = Date.now();

  if (typeof _$0 !== "number") {
    throw new Error("Prepack model invariant violation: " + _$0);
  }

  result = _$0 === 0 ? 55 : _$0;
})();
```

sは文字列が連結された状態になってる
xは計算された状態になってる
resultもかなり最適化されてる

てかfunctionも1個になってるな

## ES6のコード
これが1番どうなるか、見たかったのですが
ES6で書かれたコードをwebpackのbabel-loaderでトランスパイルして、それをPrepackできるか試してみます

ES6のアロー関数に変更　↓

```js:sample.js
(() => {
  function hello() { return 'hello'; }
  function world() { return 'world'; }
  global.s = hello() + ' ' + world();
})();

(() => {
  function fibonacci(x) {
    return x <= 1 ? x : fibonacci(x - 1) + fibonacci(x - 2);
  }
  global.x = fibonacci(23);
})();

(() => {
  function fib(x) { return x <= 1 ? x : fib(x - 1) + fib(x - 2); }
  let x = Date.now();
  if (x === 0) x = fib(10);
  global.result = x;
})();
```

webpackでビルド（webpack.configは上記のと同じ）

```
webpack
```

変換後　↓

```js:bundle.js
(function () {
  s = "hello world";
  x = 28657;

  var _$0 = Date.now();

  if (typeof _$0 !== "number") {
    throw new Error("Prepack model invariant violation: " + _$0);
  }

  result = _$0 === 0 ? 55 : _$0;
})();
```

ちゃんと変換されたー

## 速度に関して
いつものwebpackに加えて、Prepackをするので
当然ビルド速度は落ちるのですが、どのくらい違うのか見てみました

使用するコードは、上のsample.js

Prepack有り　↓

<img width="285" alt=" 2017-05-09 17.57.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/47471ed7-8017-9070-7d9b-27c1096ee1d8.png">

11085ms

Prepack無し
<img width="283" alt=" 2017-05-09 17.59.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/168197/3be40478-0afe-5ff8-11e7-0c16c54fb05d.png">

396ms

だいぶ遅い
けど、ファイルサイズを見てもかなり最適化されたのは分かる

そのうち速度は改善されるかもしれませんが、Prepackの目指す所はそこでは無いと思うので、
今後、速度に問題が残れば、自マシンで開発時にはPrepackを使わずに、
本番やステージングにてPrepackを適用するというやり方になるかもしれません

## 感想
webpackなどの他のツールと併用して使うやり方になるか、Prepack単体でいけちゃうのか、
今後どうなるか分からないが、すごく強力なツールになるかもしれないので、追って見ていこうと思う