はじめに
フロントのフレームワークを使う上で避けられない、Babelさん。
ですが最近のフレームワークは「インストール手順」にならうだけで、Babelの変換もHMRも当たり前のように動いてくれます。
便利な反面、理解できていないとBabel自体の設定を変更しなければいけない時にハマってしまいます…。
今回は、presetsでハマった私の内容と解決方法を後世の私のために残します。
- Babel : JavaScriptの新しい記法を現ブラウザで動くように変換してくれる
- Jest : JavaScriptのシンプルなテストフレームワーク
- Next.js : React.jsのフレームワーク
目的と前提
- Next.js(v4)の環境にJestを導入したい
- Jestのバージョンは何でも良い
- テスト導入のためだけに現プロジェクトの大きな変更をしない
"presets": [
"next/babel"
],
実施したこと、ハマったこと
公式サイトを読んで素直にJestをインストール
React.jsが動く環境下だったので、同じFaceBook製のJestを選択しました。
公式のバージョンは24.9
(2019年10月時点)でした。
手順にならってjest
とbabel-jest
をインストールしました。
そしてテスト用のプログラムを書いて実行してみると、エラーになりました。
Plugin/Preset files are not allowed to export objects, only functions.
直訳してみましょう。
プラグイン/プリセットファイルは、オブジェクトのみをエクスポートできません。関数のみです。
さて、つまり……? 頭の中はハテナマークです。
ネットでエラーを調べてみると、どうやらbabel-preset-env
が足りないなどの理由で失敗しているようです。
なるほど、ではその通りインストールしてみましょう。
npm i -D babel-core babel-preset-env
さて、ここで問題が起きました。
このbabel-preset-env
はインストールするだけでなく、.babelrc
のpresets
にセットしなければいけません。
つまりはプロジェクトに多大な影響が…?
とはいえJestの動作確認のため、一旦設定してみましょう。
"presets": [
["babel-preset-env",
{
"targets": {
"node": "current"
}
}
]
],
こうなります。そして実行すると、
PASS ./sum.spec.js
√ adds 1 + 2 to equal 3 (2ms)
成功しました。
ちなみにJestの公式サイトではv24.9
の場合はbabel7の例が載っていますが、上記のようにbabel6でも動作します。
ですが案の定、プロジェクトではエラーの嵐です。とても押し通せるとは思えません…。
Next.jsのバージョンを上げてみる
要は.babelrc
のpresetsをnext/babel
から変更しなければいいんだ、という認識です。
ここを変えずにNext.jsのバージョンを上げてJestが動作するかを検証してみました。
結論から言うと、動作しました。
そもそもNext.js(v9)のcreate-appが公開されていて、それはnext/babel
で動作していました。
ですがこの手段はメリットよりもデメリットが上回りました。はい。
検証結果
バージョン | プロジェクト画面 | Jest |
---|---|---|
Next4 | ○ | × |
Next6.1 | △ | × |
Next7 | × | ○ |
Next8.1 | × | ○ |
Next9 | × | ○ |
きれいに分かれましたね!
画面側が動くときはJestが動作せず、Jestが動作するときは画面が動かず…。
まるで運命に翻弄される男女のようだね(謎)。
Jestのバージョンを変更
こちらもpresets
はnext/babel
で固定にして実施しました。
ここまでくると分かっていましたが、動きませんでした。
ジレンマ
ここで少し整理してみましょう。
.babelrc
ファイルのpresets
をnext/babel
からbabel-preset-env
に変更するとJestが動作します。
ですが画面側が動かなくなります。
.babelrc
ファイルのpresets
をnext/babel
のままにするとJestが動作しません。
私はここで詰みを感じていました。
Next.jsのバージョンが低いから動かせないんだ、仕方ないんだ、と。
ですが冷静になった私はここで、自分で勝手にルールを決めていたことに気づきました。
presetsのnext/babel
を変更してはいけない、のではありません。
変更しつつプロジェクトに影響を与えなければいいのです。
next/babel
とは
さてここまで単語だけはたくさんでてきたnext/babel
さんですが、実はよく分かっていませんでした。
そもそもpresetsとはなんぞや、の段階でした。
next/babel
で検索してもnuxt/babel
さんが邪魔してきますので、理解力の欠如に拍車がかかりますね。
さて、よく公式を読みなさいという言葉がありますが、本当にその通りでした。
この場合はbabel
かNext
かの問題がありますが、どちらも読むことをおすすめします。
Next.js #customizing-babel-config
上記リンクはNext.jsの公式サイトです。
こちらを読んでみると、next/babel
とはどうやら複数の機能を持った1つの集合名のようです。
バーベキュー満喫セットとか、入学式おめでとうセットみたいなもので、そこに必要なものが一式揃えられています。
アプリケーションを動作させるのに必要なものがすべて入っています。
- preset-env
- preset-react
- preset-typescript
- plugin-proposal-class-properties
- plugin-proposal-object-rest-spread
- plugin-transform-runtime
- styled-jsx
なるほど…?
つまりpresets:[]
とplugins:[]
に上記を記述するのと、presets[]
にnext/babel
だけを記述するのは同じ意味だということですね。
そう考えると記述の短縮にもなるし、なんだかnext/babel
に親しみを持てるようになってきましたね。
各プラグインの意味合いは、また気になるようでしたら調べてみてください。
さて、ここまで理解した私は1つのひらめきを頼りに調べ物をしました。
Next.jsとbabelの依存を確認
普段は何気なくインストールしているnode_modules
ですが、中を覗くと10000以上のパッケージがあります。
それを1つ1つ読み解いている時間はないので、いつの間にか「読む」という手段そのものを忘れていました。
今回はnode_modules/next
フォルダを見ました。
中にはpackage.json
があり、babelの記述もあります。
そうです。
Next.jsのバージョンと各パッケージのバージョンは依存しています。
そのため、Next.jsのバージョンアップを行うとnext/babel
でも動作するようになったわけですね。
Next.js(v4)と同じバージョンのpresetとpluginをインストール
npm i -D babel-core@v6.26.0 babel-generator@6.26.0 babel-plugin-module-resolver@2.7.1 babel-plugin-react-require@3.0.0 babel-plugin-syntax-dynamic-import@6.18.0 babel-plugin-transform-class-properties@6.24.1 babel-plugin-transform-es2015-modules-commonjs@6.26.0 babel-plugin-transform-object-rest-spread@6.26.0 babel-plugin-transform-react-jsx-source@6.22.0 babel-plugin-transform-react-remove-prop-types@0.4.8 babel-plugin-transform-runtime@6.23.0 babel-preset-env@1.6.0 babel-preset-react@6.24.1 babel-runtime@6.26.0 babel-template@6.26.0
{
"presets": [
"babel-preset-env",
"babel-preset-react"
],
"plugins": [
"babel-plugin-react-require",
"babel-plugin-syntax-dynamic-import",
"babel-plugin-transform-class-properties",
"babel-plugin-transform-es2015-modules-commonjs",
"babel-plugin-transform-object-rest-spread",
"babel-plugin-transform-react-jsx-source",
"babel-plugin-transform-react-remove-prop-types",
"babel-plugin-transform-runtime"
]
}
実際のプロジェクトではmodule-resolver
の設定などもすると思いますが、こんな感じ。
実行するとJestはうまく動作してくれました。
ちなみにjestのv24.9
では動作せずにv22.4.4
にバージョンダウンすることで動作しました。
(この辺りは調べきれていないのでご存知の方はコメントして下さると嬉しいです)
画面の方も内部的には同じものを読み込んでいるので、エラーは起きませんでした。
結論はNext.jsのpackageの一部を無視するという暴挙に落ち着いたわけですね…。
余談
Babelのpresets
やplugin
には省略記法があるみたいです。
この辺りはプロジェクトの方々と相談すると良さそうですね。
{
"presets": ["env","react"],
"plugins": [
"react-require",
"syntax-dynamic-import",
"transform-class-properties",
"transform-es2015-modules-commonjs",
"transform-object-rest-spread",
"transform-react-jsx-source",
"transform-react-remove-prop-types",
"transform-runtime"
]
}
少し長いあとがき
ここまでお付き合いをいただきましてありがとうございます。
さて、察しのいい方なら読んでいて「ん?」となったところがあるかもしれません。
next/babel
を指定するのとnext/package.json
の中身をすべて記述するのは、同じ意味です。
では結局、next/babel
を指定するのと同じでjestは失敗するのでは?ということですね。
そもそもnext/babel
にはbabel-preset-env
も含まれていたので、失敗する意味がますます分かりません。
やり直し
私は1つの予想を立てました。
Next(v4)のnext/babel
には足りないプラグインがあり、それを補えばJestは上手く動作するのでは? と。
まずはjestをインストールします。
Next(v4)のnext/babel
で使っているbabelは6なので、そちらに合わせてjestもv22にしてみましょう。
npm i -D jest@v22 babel-jest@v22
jest@v22
の時はpackage.jsonに下記を追加する必要があります。
"jest": {
"testURL": "http://localhost"
},
そして実行すると、jest@v24
の時とは違うエラーが出てきました。
import { sum } from './sum'
^
SyntaxError: Unexpected token {
これはES6の構文解釈ができていない、というエラーですね。
これを見る限り、やはりjestのバージョンはNext.jsと合わせた方が良さそうですね。
(jest@v24
のときは理由がよく分からなかったです…)
では不足しているプラグインを足していきましょう。
やはり1番怪しそうなのは、babel-plugin-transform-es2015-modules-commonjs
でしょうか?
{
"presets": [
"next/babel"
],
"plugins": [
"transform-es2015-modules-commonjs",
]
}
↓ 結果
PASS .\sum.spec.js
√ adds 1 + 2 to equal 3 (3ms)
はい、動きました~。ビンゴですね!
これでNext.jsのpackageを無視する暴挙もすることなく、スマートにjestを動かすことができました。
あとは画面側に影響を与えないように、jestだけに設定したら完了です!
{
"presets": [
"next/babel"
],
"env": {
"test": {
"plugins": ["transform-es2015-modules-commonjs"]
}
}
}
ではでは、良いプログラムライフを!