iOS 9をターゲットにしたBabel + webpackビルドはminifyに注意が必要


Babel 7 + Webpack 4 の組み合わせで iOS >= 9 をターゲットにproductionビルドしたとき、iOS 9のSafariで Syntax Error: Unexpected token '>' というエラーが出てしまう事象が発生しました。

不具合の原因

webpackのproductionビルド時に適用されるminifyによって、オブジェクトリテラルのショートハンドがアロー関数式に圧縮されるのが原因です。
iOS 9のSafariはオブジェクトリテラルのショートハンドは使えますが、アロー関数式は使えません

サンプルコード

https://github.com/ryo-utsunomiya/poc-babel-webpack-ios9 にも上げてます

以下のようなコードを iOS >= 9 をターゲットにしてビルドすると、このコードはiOS 9のSafariで利用可能な構文の範囲で実装されているため、トランスパイルなしで出力されます。

export default {
  greeting(name) {
    return `Hello, ${name}`;
  }
};

このコードをwebpackでproductionビルドすると、以下のような出力になります。

function(e,t,r){"use strict";r.r(t),t.default={greeting:e=>`Hello, ${e}`}}

前述したとおり、オブジェクトリテラルのショートハンド greeting(name){return...} が、アロー関数式 greeting:e=>... に圧縮されています。
この圧縮は、両方の構文をサポートしているブラウザに対して行うなら安全なのですが、iOS 9のように部分的なサポートしかない場合、適用してはいけません。

修正方法

Minimizerのオプションで、明示的に arrows の圧縮を無効化します。現在のwebpack(4.17.2)ではUglifyjsWebpackPluginを利用しています。このプラグインをdevDependenciesにインストールし、UglifyJsPluginの初期化時に明示的にオプションを渡すようにすればOKです。

const UglifyjsPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
  ...
  optimization: {
    minimizer: [new UglifyjsPlugin({
      uglifyOptions: {
        compress: {
          arrows: false, // オブジェクトリテラルからアロー関数式への圧縮を無効化
        }
      }
    })]
  }
}

なお、UglifyjsPluginが使用しているuglify-esはメンテナンスが打ち切られ、terserというforkに移行が進められています。
近いうちに、uglifyjs-webpack-pluginはDeprecatedになる可能性があるので、代替となるTerserWebpackPluginを使っておいた方がよいかもしれないです。APIはほぼ同じです。

const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  ...
  optimization: {
    minimizer: [new TerserPlugin({
      terserOptions: {
        compress: {
          arrows: false, // オブジェクトリテラルからアロー関数式への圧縮を無効化
        }
      }
    })]
  }
}