LoginSignup
21
5

More than 3 years have passed since last update.

Babelのpresets設定をNext.jsで理解してみる

Last updated at Posted at 2019-10-05

はじめに

フロントのフレームワークを使う上で避けられない、Babelさん。
ですが最近のフレームワークは「インストール手順」にならうだけで、Babelの変換もHMRも当たり前のように動いてくれます。
便利な反面、理解できていないとBabel自体の設定を変更しなければいけない時にハマってしまいます…。
今回は、presetsでハマった私の内容と解決方法を後世の私のために残します。

  • Babel : JavaScriptの新しい記法を現ブラウザで動くように変換してくれる
  • Jest : JavaScriptのシンプルなテストフレームワーク
  • Next.js : React.jsのフレームワーク

目的と前提

  • Next.js(v4)の環境にJestを導入したい
  • Jestのバージョンは何でも良い
  • テスト導入のためだけに現プロジェクトの大きな変更をしない
.babelrc
  "presets": [
    "next/babel"
  ],

実施したこと、ハマったこと

公式サイトを読んで素直にJestをインストール

React.jsが動く環境下だったので、同じFaceBook製のJestを選択しました。
公式のバージョンは24.9(2019年10月時点)でした。
手順にならってjestbabel-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はインストールするだけでなく、.babelrcpresetsにセットしなければいけません。
つまりはプロジェクトに多大な影響が…?
とはいえJestの動作確認のため、一旦設定してみましょう。

.babelrc
  "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のバージョンを変更

こちらもpresetsnext/babelで固定にして実施しました。
ここまでくると分かっていましたが、動きませんでした。

ジレンマ

ここで少し整理してみましょう。
.babelrcファイルのpresetsnext/babelからbabel-preset-envに変更するとJestが動作します。
ですが画面側が動かなくなります。
.babelrcファイルのpresetsnext/babelのままにするとJestが動作しません。

私はここで詰みを感じていました。
Next.jsのバージョンが低いから動かせないんだ、仕方ないんだ、と。
ですが冷静になった私はここで、自分で勝手にルールを決めていたことに気づきました。

presetsのnext/babelを変更してはいけない、のではありません。
変更しつつプロジェクトに影響を与えなければいいのです。

next/babelとは

さてここまで単語だけはたくさんでてきたnext/babelさんですが、実はよく分かっていませんでした。
そもそもpresetsとはなんぞや、の段階でした。
next/babelで検索してもnuxt/babelさんが邪魔してきますので、理解力の欠如に拍車がかかりますね。

さて、よく公式を読みなさいという言葉がありますが、本当にその通りでした。
この場合はbabelNextかの問題がありますが、どちらも読むことをおすすめします。

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
.babelrc
{
  "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のpresetspluginには省略記法があるみたいです。
この辺りはプロジェクトの方々と相談すると良さそうですね。

.babelrc
{
  "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でしょうか?

.babelrc
{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    "transform-es2015-modules-commonjs",
  ]
}

↓ 結果

 PASS  .\sum.spec.js
  √ adds 1 + 2 to equal 3 (3ms)

はい、動きました~。ビンゴですね!
これでNext.jsのpackageを無視する暴挙もすることなく、スマートにjestを動かすことができました。

あとは画面側に影響を与えないように、jestだけに設定したら完了です!

.babelrc
{
  "presets": [
    "next/babel"
  ],
  "env": {
    "test": {
      "plugins": ["transform-es2015-modules-commonjs"]
    }
  }
}

ではでは、良いプログラムライフを!

21
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
5