JavaScript
ECMAScript
React
babel
react-native

React Native で "TransformError Couldn't find preset ..." で怒られるときの対処法

More than 1 year has passed since last update.

症状

react-native start でサーバを立ち上げると, node_modules 配下に含まれているライブラリが依存しているBabelのプリセットが反応して

TransformError Couldn't find preset "es2015"
TransformError Couldn't find preset "airbnb"

などとエラーになってしまう問題。

(備考) .babelrc の動作

親ディレクトリの .babelrc の設定を子の .babelrc で上書きし,最終的に生成された設定で子ディレクトリ内のトランスパイルを行う。

原因

npm と, React Native の方針の違いに起因する。

npm の方針

ホスティングされるパッケージはすべて CommonJS と ES5 までの実装で動作するようにトランスパイル済みであることを想定している。つまり,そもそも

node_modules以下はトランスパイル対象にならない」

ということを前提としている。ゆえに,公開されるパッケージが .babelrc を含んでいても,普通のプロジェクトではそのディレクトリがトランスパイル対象から除外されるため,トランスパイラに影響を及ぼすことは無い。

React Native の方針

通常の JavaScript コードをネイティブコードに変換する必要があるため, babel-preset-react-native を用いて

node_modules以下もトランスパイル対象にする」

という方針を採る。そのため, 依存パッケージがもし .babelrc を含んでいる場合,それに使用されているプリセットをすべて必要としてしまう。

対処法

パッケージインストール後に node_modules 下の .babelrc をすべて削除する

現状これが最善の解決策。npm scripts に以下の設定を追記。

package.json の例
{
  "scripts": {
    "clean-underlying-babelrc": "(find node_modules -type f -name .babelrc | grep -v /react-native/ | xargs rm) || true",
    "post-install": "npm run clean-underlying-babelrc",
  }
}

ライブラリ作者に .babelrc.npmignore の設定で除外するように求める

この対応をライブラリ作者全員がとってくれれば最善ではあるが,React Native 固有の事情であるため,拒否される場合もあるだろう。

.npmignore の例
/.*
/*
!/src/
!/es/
!/lib/

必要ないプリセットもすべてプロジェクトの .babelrc で記述する

一応動作はするが,新しく追加するパッケージがあるたびに,新しいプリセットに依存していないかどうかを確認する必要があるため,安定した解決策とは言えない。それに,なんとなく負けた気分…

絶対にやりたくない
{
  "preset": ["react-native", "es2015", "stage-0", "stage-1", "stage-2", "stage-3", "stage-4", "env", "airbnb"]
}

その他

React Native プロジェクトはデフォルトで babel-preset-react-native を使用するため,自分で .babelrc を置かなくてもトランスパイル可能であるが,ホームディレクトリに .babelrc がある場合はそれを読んでしまうため,結局置いておくほうが無難。上に述べた対策を実施しつつ,

{
  "preset": ["react-native"]
}

を書いておく。実際 ES.Next の使用をふんだんに使おうとすると

{
  "preset": ["react-native", "env"]
}

というように, babel-preset-env などを追加せざるを得ない。