JavaScript
webpack
babel

@babel/plugin-transform-runtime で Promise が変換されずハマった

はじめに

タイトルの通り @babel/plugin-transform-runtime を使用して、Promise がネイティブで実装されていない IE でも Promise を使用したコードが動作するようにしようと思ったのですが、何故か上手くいかず IE で 'Promise' is not defined エラーが出てしまう問題にかなりの時間ハマりました。
@babel/polyfill 使えばええやん...って感じですが、それでは根本解決していないので、頑なに transform-runtime を使うぞ〜〜!!と時間をかなり割いてしまった次第です。

最初に結論としてハマったコードと修正後のコードを載せ、その説明や過程を綴ります。

ハマったコード

babel-loaderの記述
{
  test: /\.js$/,
  exclude: /node_modules/,
  use: [{
    loader: 'babel-loader',
    options: {
      presets: ['@babel/preset-env'],
      plugins: ['@babel/plugin-transform-runtime']
    }
  }]
},

修正後のコード

@babel/runtime ではなく @babel/runtime-corejs2
npm install or yarn add する必要があります。

babel-loaderの記述(修正後)
{
  test: /\.js$/,
  exclude: /node_modules/,
  use: [
      {
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env'],
        plugins: [
          ['@babel/plugin-transform-runtime', { 'corejs': 2 }]
        ],
      }
    },
  ]
},

解説

Babel6(babel-plugin-transform-runtime) から Babel7(@babel/plugin-transform-runtime) へアップデートされた後どうやら少し仕様が変わっていたっぽいです。
Babel6 では babel-plugin-transform-runtime のインストールと下記の記述で済みました。

babel-loaderのoptions
// Babel6
options: {
  presets: ['env'],
  plugins: ['transform-runtime']
}

Babel7 では @babel/plugin-transform-runtime と、その依存関係にある @babel/runtime@babel/runtime-corejs2 のどちらかをインストールする必要があります。

babel-loaderのoptions
// Babel7
options: {
  presets: ['@babel/preset-env'],
  plugins: [
    ['@babel/plugin-transform-runtime', { 'corejs': 2 }]
  ],
}

@babel/runtime と @babel/runtime-corejs2 の違い

上記公式ページに記載されてますが、下記はそれを簡単に解釈したものです。

@babel/runtime

@babel/runtime にはヘルパーだけが含まれ polyfill は含まれません
babel はコードを変換する際に重複した関数を出力することがあるので、ヘルパーはその部分を自身の関数(ヘルパ関数)に置き換えます(参照させるようにします)。

@babel/runtime-corejs2

@babel/runtime と同じく、babelが出力する重複した関数をヘルパー関数に置き換える(参照させる)動作をします。ただし @babel/runtime-corejs2 は ヘルパーだけでなく、core-js を含んでおり、Promise や Symbol などのネイティブ実装されていない関数を core-js 関数に置き換えてくれるため、polyfill と同じような効果があります。

ハマった経緯と解決の過程

ハマった訳

  • 新たに開発環境を作る際、以前 Babel6 を使用していたが Babel7 で割と仕様が変わっていたこと。
  • transform-runtime を使用すれば Promise などのコードが polyfill される〜くらいの軽い認識でいたこと。
  • @babel/plugin-transform-runtime を読んでも Babel6 と違って @babel/runtime も入れる必要があるんだ〜くらいにしか認識できなかったこと。
  • webpack の babel-loader の説明にも @babel/runtime-corejs2 ではなく @babel/runtime の例しか載ってなかったこと。
  • babel-loader で同じく使用している @babel/env-preset やバージョンの依存関係などを疑いはじめたこと。
  • 色々検索をかけたりしてみて、様々な関係ありそうでない issue を読み漁っていたこと

解決に至ったきっかけ

Promise has not been converted
上記の issue で、まさに自分が現在困っていることと、その解決策が提示されていました。
その後、Upgrade to Babel7 を読んで納得した次第です。

最後に

解決まで割と時間を割いてしまったので、しっかり記事として残しておこうと思いました。
同じようなことで悩んでいる方への参考になればいいなと思います。