babel-loader で node_modules 内のパッケージが変換できない
babel-loader の定番設定 exclude: /node_modules/ は依存パッケージを丸ごと変換対象から外す。
素の構文で配布されたパッケージだけを例外にする書き方に変えれば解決する。
公開:
要約
自分のコードは変換されるのに、依存パッケージ由来のファイルで構文エラーが出るときは、exclude: /node_modules/ が効いています。
この 1 行は「node_modules の中は変換しない」という指示なので、素の構文のまま配布されたパッケージはそのままバンドルに入ります。
babel-loader は、この用途のために exclude を条件オブジェクトで書く形を用意しています。
// webpack.config.js
{
test: /\.(?:js|mjs|cjs)$/,
exclude: {
and: [/node_modules/],
not: [/unfetch/, /d3-array|d3-scale/],
},
use: { loader: "babel-loader" },
}not に挙げたパッケージだけが変換対象に戻ります。
よくある原因
exclude: /node_modules/が一律に効いている: これはビルド時間を守るための定番設定で、ほとんどの場合は正しい。
ただし依存パッケージが変換を必要とする場合は、そのパッケージだけを例外にする必要がある。- 依存パッケージが素の構文で配布されている: 近年は「変換は利用側の責任」という方針で、オプショナルチェーンやクラスフィールドをそのまま含んだまま公開するパッケージが増えている。
babel-loader の公式リポジトリも、レガシーな対象をサポートする場合はnode_modules配下の変換が必要になると説明している。 mainが変換前のソースを指している:moduleやexportsの指す先が変換済みでも、webpack の解決結果が変換前のファイルになっていることがある。testに.mjsが入っていない: ESM で配布されたパッケージは.mjsを持つ。test: /\.js$/だけだと loader に渡らず、変換されないまま通過する。
解決策
1. exclude を条件オブジェクトに変える
正規表現ひとつでは「node_modules を除く。
ただしこれは除かない」を表現できません。
babel-loader の公式リポジトリが示している書き方は次の形です。
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(?:js|mjs|cjs)$/,
exclude: {
and: [/node_modules/],
not: [/unfetch/, /d3-array|d3-scale/, /@hapi[\/\\]joi-date/],
},
use: {
loader: "babel-loader",
options: {
presets: [["@babel/preset-env", { targets: "ie 11" }]],
},
},
},
],
},
};and / not は webpack の条件(Condition)の記法で、babel-loader 固有の拡張ではありません。
2. 対象パッケージを名指しで特定する
not に何を書くかは、ビルドエラーが指しているファイルのパスから決めます。
ERROR in ./node_modules/some-lib/dist/index.js 12:24
Module parse failed: Unexpected token (12:24)
You may need an appropriate loader to handle this file typeこの場合は /some-lib/ を not に足します。
パッケージ名を丸ごと書くと、そのパッケージが依存している別のパッケージまでは含まれない点に注意してください。
エラーが次のパッケージに移ったら、同じ手順を繰り返します。
3. .mjs を test に含める
拡張子の指定漏れは見落としやすい原因です。
// before
test: /\.js$/,
// after
test: /\.(?:js|mjs|cjs)$/,4. targets を実態に合わせる
そもそも古いブラウザを対象にしていなければ、依存パッケージの変換は不要です。@babel/preset-env の対象が広すぎないかを見直すと、not に足すパッケージ自体が減ります。
対象の決め方は /babel/preset-env-not-transpiling/ を参照してください。
{
"browserslist": ["> 0.5%", "last 2 versions", "not dead"]
}なお node_modules を丸ごと変換対象にするとビルド時間が大きく伸びます。
範囲は必要なパッケージだけに絞ってください。