Parcel 2 ベータ 3

Parcel チームは、Parcel 2 ベータ 3 のリリースを発表できることを嬉しく思います!今回のリリースには、JavaScript コンパイラーを Rust で完全に書き直したものが含まれており、全体的なビルドパフォーマンスが最大 10 倍向上しています。さらに、この投稿では、前回のアップデート以降に Parcel に行ったその他の改善点と、安定版 Parcel 2 リリースへのロードマップについて説明します。

Rust で記述された 10 倍高速な JavaScript コンパイラー 🚀

#

過去数か月間、私たちは JavaScript コンパイラーを Rust で書き直す作業に取り組んできました!Parcel の JavaScript コンパイラーは、コード内の依存関係 (import ステートメントや new Worker() 呼び出しなど) の検出、process.env 変数やその他の Node グローバルのインライン化、およびスコープホイスティングの実行を担当します。

さらに、Parcel は、設定された browserslist ターゲット向けに、JSX や TypeScript などの非標準構文や、React Fast Refresh などの開発機能を自動的にトランスパイルします。

以前は、これらすべてが Babel AST の上で JavaScript で実装されていました。私たちはこれを最適化するために大きな 進歩 を遂げましたが、JavaScript コンパイルは依然として Parcel の中で最も遅い部分でした。特に、スレッド間で送信するために大きな JavaScript AST をシリアライズするのに非常に時間がかかり、これらの大きなオブジェクトは JavaScript ガベージコレクターに大きな負荷をかけました。また、JavaScript コードは実行されるたびにエンジンによってコンパイルされる必要があるため、起動が遅くなります。アルゴリズムの複雑さを改善すると、言語に関係なくパフォーマンスが向上しますが、それには限界があります。

Parcel の新しい JavaScript トランスフォーマーは、SWC コンパイラーの上に Rust で記述されています。SWC は、JavaScript の解析とコード生成、および超高速な AST 変換を構築するための確固たる基盤を提供します。Rust は、予測可能なパフォーマンス、即時の起動時間、およびハードウェアレベルまで最適化する機能を提供します。

さらに、スコープホイスティングリンカーは、AST ではなく文字列のみで動作するようになり、これらの大きなオブジェクトのシリアライズとデシリアライズを回避できるため、パフォーマンスも大幅に向上します。これにより、コード生成をバンドル全体で一度に行うのではなく、すべてのファイルで並行して実行できるようになります。

全体として、これによりビルドパフォーマンスが最大 10 倍向上します!

ESBuild ベンチマークでは、Parcel は Terser なしで約 10 倍高速になり、ミニファイを有効にすると約 3 倍高速になります。

Chart showing performance of previous beta vs rust rewrite. Without terser, previous beta took 34s and the rust rewrite took 3.2s. With terser, the previous beta took 56s and the rust rewrite took 18s.

トランスパイルのパフォーマンス

#

さらに、browserslist が設定されている場合、トランスパイル、JSX や TypeScript のコンパイル、React Fast Refresh で、SWC がデフォルトで Babel に置き換わります。SWC は Babel より 20 倍高速であるため、この変更により全体的なパフォーマンスも大幅に向上するはずです。

SWC がデフォルトですが、Babel も引き続きサポートされているのでご安心ください。プロジェクトにカスタム Babel 設定がある場合は、引き続き自動的に使用されます。つまり、カスタム Babel プラグイン (例: CSS-in-JS 変換、Babel マクロなど) は引き続きすぐに使用できます。スコープホイスティング、依存関係の収集、および以前に Parcel に組み込まれていたその他のすべては Rust で実行されるようになりますが、これは完全に透過的であるはずです。

ただし、これによりビルドのパフォーマンスをさらに向上させる可能性が開かれます。.babelrc から @babel/preset-env@babel/preset-react@babel/preset-typescript を削除できるようになり、代わりに SWC によって自動的に処理されるようになります。これにより、ビルドパフォーマンスを大幅に向上させることができます。追加のカスタム Babel プラグインがある場合は、Babel 設定に残しておくことができます。そうでない場合は、Babel 設定を完全に削除できます。今後、この移行を支援するための警告を追加する予定です。

スコープホイスティングの改善

#

パフォーマンスとは別に、古いスコープホイスティングの実装でいくつかの問題が発生し、書き直しを促しました。特に、Parcel を含むいくつかの JavaScript バンドラーには、スコープホイスティングとコード分割を一緒に使用するときの実行順序に関連するバグがあります。

スコープホイスティングは、複数の JavaScript モジュールを単一のスコープに結合するプロセスです。これにより、デッドコード削除 (別名ツリーシェイキング) の有効性が向上し、モジュール間の参照を動的なプロパティルックアップではなく静的にすることで、ランタイムパフォーマンスが向上します。

各モジュールを個別の関数でラップするのではなく、複数のモジュールを同じスコープに一緒にホイストする場合、バンドル間で参照されるときに、これらのモジュールが常に正しい順序で実行されるようにするのは困難になります。

さらに、小さなモジュールが、非常に小さな出力ファイルを多数生成してページの読み込みに必要な HTTP リクエスト数を増やすのを避けるために、複数のバンドル間で重複することがあります。以前は、これによりモジュールが複数回実行される可能性があり、副作用 (例: DOM の変更) やシングルトンパターンなど、多くのものが破損する可能性がありました。

これらの問題を解決するには、スコープホイスティングコンパイラーがどのように動作するかを根本から再考する必要がありました。その結果、可能な場合はスコープホイスティングを使用し、必要な場合はグローバルモジュールレジストリを使用して CJS スタイルの関数にモジュールをラップするハイブリッドになりました。これにより、バンドル間で参照されるモジュールが正しい順序で実行され、重複したモジュールは 1 回だけ実行されるようになります。

スコープホイスティングの実装がどのように機能するかについて詳しく知りたい場合は、設計ドキュメントを参照してください。このドキュメントでは、これらのトピックについて詳しく説明しています。

ダイナミック import のツリーシェイキング

#

スコープホイスティングの実装に関連するもう 1 つの機能は、ダイナミック import() のツリーシェイキングのサポートです。Parcel は、ダイナミック import のどのプロパティがアクセスされているかを検出し、使用されていない解決済みモジュールからのエクスポートを除外できます。これは、プロミスチェイニング、async/await、分割代入、および静的オブジェクトプロパティアクセスで機能します。静的でないもの、たとえば計算されたプロパティがアクセスされる場合、すべてのエクスポートが含まれます。

Example of tree shaking dynamic import

CSS モジュールのツリーシェイキング

#

CSS モジュールのツリーシェイキングもサポートするようになりました。JavaScript で CSS モジュールをインポートすると、Parcel はどのクラスが使用されているかを追跡し、コンパイルされた CSS ファイルから未使用のセレクターを自動的に除外します。さらに、クラス名は大きなオブジェクトマップに格納されるのではなく、コンパイルされた JavaScript に自動的にインライン化されるようになりました。これにより、CSS と JS の両方の出力のバンドルサイズを削減できるはずです!

Example of tree shaking CSS modules

遅延開発ビルド

#

開発中、開発サーバーが起動する前にアプリ全体がビルドされるのを待つのはイライラする可能性があります。これは、ページ数の多い大規模なアプリを扱う場合に特に当てはまります。1 つの機能のみを操作している場合、実際にそれらに移動しない限り、他のすべての機能をビルドするのを待つ必要があるでしょうか?

Parcel は、開発サーバーを使用するときに --lazy CLI フラグをサポートするようになりました。有効にすると、Parcel はオンデマンドでリクエストされたファイルのみをビルドするため、開発ビルド時間を大幅に短縮できます。サーバーは即座に起動し、初めてページに移動すると、Parcel はそのページに必要なファイルのみをビルドします。別のページに移動すると、そのページはオンデマンドでビルドされます。以前にビルドされたページに戻ると、すぐに読み込まれます。

これは、完全に分離されたページだけでなく、ダイナミック import() でも機能します。したがって、動的にロードされた機能を備えたページがある場合、その機能はアクティブ化されるまでビルドされません。Parcel は、要求されたファイルのすべての依存関係を一度に積極的にビルドするのに十分なスマートさを持っており、それらが要求されるのを待つ必要はありません。JavaScript と一緒に使用する CSS や、参照されている画像がある場合、それらはすべて一緒にビルドされます。ネットワークウォーターフォールは発生しません!

もちろん、トレードオフとして、ページロードとダイナミックインポートは、最初にロードされたときに少し遅くなる可能性があります。ただし、Parcel のディスクキャッシュを使用すると、これは 1 回だけ発生するはずです。Parcel を再起動した場合でも、変更されなかったファイルを再構築する必要はありません。

キャッシュの信頼性

#

キャッシュといえば、私たちが取り組んできたもう 1 つの分野は、キャッシュの信頼性です。ユーザーにキャッシュを信頼してもらいたいので、キャッシュを無効にする可能性のあるすべてのものが実際にキャッシュを無効にすることを確認する必要があります。これは見た目よりもはるかに難しいことです。最新の JavaScript ビルドツールには、追跡すべき入力がたくさんあります。ディスク上のファイル、構成、環境変数、コードのコンパイルに使用された開発依存関係などです。これらのいずれかが変更されると、Parcel はキャッシュを無効にして出力を再計算する必要があります。

Parcel は、これらのすべての入力をグラフで追跡します。私たちのファイルウォッチャーは、ディスク上の変更を監視し、変更されたファイルに接続されているすべてのリクエストを無効にします。これは Parcel を再起動したときにも発生します。私たちのウォッチャーは、オペレーティングシステムレベルの API と統合して、Parcel を最後に実行してから変更されたファイルを迅速に判断します。これは、Parcel の再起動がウォッチモードを使用するのと同じくらい高速であることを意味します!

開発依存関係 HMR

#

このキャッシュ無効化に関するすべての作業の副次的なメリットは、ウォッチモードもメリットがあることです。私たちが実装したクールな機能の 1 つは、Parcel プラグインやその他のビルド依存関係の HMR です。プラグインのソースコードを変更すると、プラグインをインプレースでリロードし、以前にコンパイルしたファイルを再構築します。これにより、プラグインの操作と変更の即時確認が非常に高速になります。Parcel を再起動する必要はありません。

これは、Babel プラグイン、PostCSS プラグイン、およびビルドに関係するその他の開発依存関係でも機能します。node_modules のプラグインを編集することもでき、Parcel は自動的に再コンパイルします。これは、ビルドパイプラインで何かをデバッグする必要がある場合や、patch-package などのツールを使用する場合に役立ちます。

依存関係の削減

#

Parcel は、すぐに使用できるさまざまな言語とツールをサポートしているため、非常に簡単に開始できます。ただし、この欠点の 1 つは、Parcel をインストールすると、使用しない可能性のある多くの依存関係が含まれることです。これは、ディスクスペースとネットワーク帯域幅を消費するだけでなく、ユーザーが維持および監査する必要のある依存関係も増えます。

私たちは、非常にシンプルに使用できるようにしながら、Parcel の依存関係を減らしたいと考えました。これを行うために、必須のプラグインのみをデフォルトでインストールし、オンデマンドで追加のプラグインをプロジェクトに自動インストールするようになりました。

デフォルトでは、HTML、CSS、JavaScriptのような標準的なWeb言語のサポートが含まれています。しかし、SASSファイル、Vue、Elm、CoffeeScriptなど、Parcelが認識する他のファイルタイプを含めるとすぐに、必要なプラグインとすべてのピア依存関係がプロジェクトに自動的にインストールされます。これにより、Parcelは非常に使いやすく(設定不要)、同時にプロジェクト内の依存関係の数を減らすことができます。

ロードマップ

#

Parcel 2は数年前から開発が行われていますが、安定版リリースに向けたロードマップにおける進捗状況について、コミュニティへの情報発信が十分ではありませんでした。そこで、このセクションでは、現在の進捗状況について最新情報をお伝えします。

ベータ3は、おそらく最初のリリース候補版前の最後のベータ版となるでしょう。リリース候補版は、約1ヶ月後を目標にしています。最初のリリース候補版に向けて、以下の項目に取り組んでいます。

リリース候補版の後、パブリックAPIは凍結され、バグ修正とドキュメント作成に注力します。これはおそらく1ヶ月程度かかるでしょう。準備ができ次第、2.0をリリースします!

現時点では、バンドラー、ランタイム、バリデーターのプラグインAPIはParcel 2.0で実験的とマークされる可能性が非常に高いです。つまり、これらの機能はセマンティックバージョニングに従わず、初期の安定版Parcel 2リリース後もイテレーションを継続します。さらに、パフォーマンスの最適化など、2.0以降に向けた多くの機能と改善を計画しています。

ありがとうございます!

#

いつもベータ版を試していただき、GitHubでフィードバックをお寄せいただきありがとうございます。また、私たちの貢献者を支援するために、オープンコレクティブへの寄付も歓迎しています。