9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Qiita株式会社Advent Calendar 2022

Day 2

@babel/preset-envは必要なプラグインをどう判定しているのか調査した

Last updated at Posted at 2022-12-01

Qiita株式会社のカレンダーの2日目は @kyntk が担当します。


今年はIEのサポート終了がありましたね。
今まではIEのサポートをするためにJavaScriptやCSSで使えない記法があったり、ポリフィルを入れていたりしていましたが、モダンブラウザだけを対応するようになるとこれらの設定が不要になりました。

ES2015以前の環境をサポートするために@babel/preset-envがよく使われます。
今回は、preset-envの設定を変更するにあたって、うっかりサポート対象環境で動かないバグを生んだり、逆にあまり無駄なコードを削れなかったりしないように、内部でどのような処理が行われているのか調べてみました。実際にbabelのソースコードを読みながらまとめています。

調査時点のbabelのバージョンは「7.20.6」です

分かったこと

はじめに結論ですが、Babelを使ってJavaScriptがどのように変換されるかを知るためにこのあたりが重要になりそうです。

  • まずは自分のアプリケーションがサポートするターゲット環境を決める or 把握する
    • browserslistを使っている場合、npx browserslistなどで一覧を取得できます
    • ブラウザのstatsデータをアップデートするためにもcaniuse-liteのDBも更新する
  • すべてのターゲットをサポートするようにpluginが追加されるので、どのプラグインが追加されるかを把握する
  • core-jsのポリフィルの読み込みもターゲットを参照していて、useBuiltInsで設定ができる

前提: Babelの役割

ドキュメントには以下のようにあります。

  • Transform syntax
  • Polyfill features that are missing in your target environment (through a third-party polyfill such as core-js)
  • Source code transformations (codemods)
  • And more! (check out these videos for inspiration)

BabelはJavaScriptを別のJavaScriptに変換をしたり、ポリフィルを追加したりします。

JavaScriptを変換する理由としては、実行時には各ブラウザの環境で動作するコードにしたいものの、実装自体は別のモダンな書き方を使いたいといったものが挙げられます。
一方ポリフィルは、新しい記法を古い記法に変換するのではなく、新しい記法のまま使えるようにJavaScriptの関数の定義などをしているものです。

これを行うために様々なpluginが用意されており、必要なpluginを選択することでどのように変換するかを決めることができます。

pluginについて

pluginのリストはこちらです。

すべてのpluginがコードの変換をするというわけではなく、JavaScriptのparseをするためのpluginなどもあるようです。

また個人的なイメージとして、今まではBabelは「ES2015に対応していない環境のためのツール」という印象を持っていました。しかし、TC39 Proposalsのpluginもあり、モダンブラウザにもまだ実装されていないものを先取りして使うこともできるようです。

presetについて

presetはpluginのセットです。例えばpreset-envを使うと、pluginを1つ1つ選択しなくてもES2015未対応の環境でも動作するコードにすることができます。
また、core-jsのようなBabel以外のポリフィルを、必要なものだけを選択して追加できる設定もあります。

presetだけでなくpluginも同時に設定することができ、その場合はpluginのほうが先に実行されます。

@babel/preset-env について

preset-envは、ターゲットブラウザの設定をすることで、その環境で動作するように必要なプラグインとポリフィルを選択してくれます。

例えばtargetsの中に「IE 11」が入っていると、IE 11で動作するJavaScriptを生成することができます。

targetsの設定方法

babelの設定としてtargetsがありますが、targetsを直接指定せずに、.browserslistrcを置いておくと、そちらを参照します。

babel-helper-compilation-targets

targetsには> 0.25%のような記述もすることができますが、具体的にどのブラウザのどのバージョンをサポートするかをbabel-helper-compilation-targetsで取得しています。

browserslistから対応しているバージョンを取得して、各ブラウザその一番低いバージョン1つをピックアップしたリストが作られます。

ブラウザごとのstatsデータなどはcaniuse-liteのdbを参照しているので、これをアップデートしておかないと最新ブラウザなどが反映されないので注意しましょう。

pluginの選択

targetsのリストができたので、必要なpluginを選択します。

このファイルには、各pluginごとにその機能が実装されているブラウザの最低バージョンが記載されています。
targetとなるブラウザのうち1つでもこのバージョンを下回っているときにはpluginとして選択されます。

この処理自体はbabel-helper-compilation-targetsで行っているようです。

bugfixesについて

たとえば、特定のブラウザの実装にバグがあったときでも、その記法が機能するようにコードの変換をするのですが、その際、元のコードよりもかなり多くのコードが生成されることがありました。
それも、バグがあるブラウザだけでなく、バグのないブラウザでも読み込まれるファイルサイズが大きくなることに繋がります。

これを防ぐために、bugfixes: trueの場合はできる限りモダンで壊れないシンタックスに変換するようにします。

具体例はこちらにまとまっています。

ポリフィルの選択

pluginの他に、core-jsによるポリフィルの読み込み設定ができます。targetsに合わせて必要なポリフィルだけを読み込むコードに書き換えてくれます。

例えば、useBuiltIns: 'entry'にすると、

import "core-js";

targetsに合わせて以下のように必要なものだけをimportするように変換してくれます。
これにより1つ1つ管理しなくても、不要なポリフィルを読み込まずに済み、バンドルサイズが大きくなるのを防ぎます。

import "core-js/modules/es6.array.copy-within.js";
import "core-js/modules/es6.array.fill.js";
import "core-js/modules/es6.array.filter.js";
import "core-js/modules/es6.array.find.js";
import "core-js/modules/es6.array.find-index.js";
import "core-js/modules/es7.array.flat-map.js";
import "core-js/modules/es6.array.from.js";
import "core-js/modules/es7.array.includes.js";
import "core-js/modules/es6.array.iterator.js";
import "core-js/modules/es6.array.map.js";
import "core-js/modules/es6.array.of.js";
import "core-js/modules/es6.array.slice.js";
import "core-js/modules/es6.array.species.js";
import "core-js/modules/es6.date.to-primitive.js";
import "core-js/modules/es6.function.has-instance.js";
// ...

core-jsについて

ポリフィルがたくさん実装されているライブラリです。
ES2015へのポリフィルだけでなく、ES2023などProposalのものも含まれています。

まとめ

  • まずは自分のアプリケーションがサポートするターゲット環境を決める or 把握する
    • browserslistを使っている場合、npx browserslistなどで一覧を取得できます
    • ブラウザのstatsデータをアップデートするためにもcaniuse-liteのDBも更新する
  • すべてのターゲットをサポートするようにpluginが追加されるので、どのプラグインが追加されるかを把握する
  • core-jsのポリフィルの読み込みもターゲットを参照していて、useBuiltInsで設定ができる

このあたりの設定を理解していないと、うっかりサポート対象環境で動かないバグにつながったり、逆に安全に倒そうと思うばかりにあまり無駄なコードを削れなかったりというのがあるので、知れてよかったです。


Qiita株式会社のカレンダー3日目は @degudegu2510@masato930 が担当します!

ぜひ、Qiita株式会社のカレンダーを購読設定して、明日の記事もご覧いただけると嬉しいです。

参考

9
0
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
9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?