Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

jsbundling-rails README(翻訳)

概要

MITライセンスに基づいて翻訳・公開いたします。

rails/jsbundling-rails - GitHub

  • 2022/09/21: 初版公開
  • 2023/08/31: 更新
  • 2023/10/24: 更新(Bunを追加)
  • 2024/04/17: 更新(pnpmを追加)

jsbundling-rails README(翻訳)

jsbundling-rails gemは、Bunesbuildrollup.jswebpackのいずれかを用いてJavaScriptコードをバンドルし、Railsのアセットパイプラインで配信します。このgemが提供するのは、新しいRailsアプリケーションで上述の好みのバンドラーを利用できるインストーラと、バンドルした出力ファイルをGitなどのソースコード管理に登録されない形でapp/assets/buildsディレクトリに保持する規約です(インストーラはこのディレクトリをデフォルトで.gitignoreに追加します)。

この方法で開発を行うには、ターミナルでyarn build --watchを実行してバンドラーをwatchモードで起動しておきます(puma-devなどを使っていない場合は、Railsサーバーを別のターミナルで実行してください)。また、./bin/devを実行すればRailsサーバーとJavaScriptビルドウォッチャーをまとめて起動できます(cssbundling-railsを使っている場合はCSSビルドウォッチャーも起動します)。

このバンドラーがプロジェクトディレクトリ内のJavaScriptファイルの変更を検出するたびに、以下の1を2にバンドルします(設定済みの他のエントリポイントもすべて同様にバンドルされます)。

  1. app/javascript/application.js
  2. app/assets/builds/application.js

これで、<%= javascript_include_tag "application", defer: true %>と記述する標準的なアセットパイプラインの手法を引き続き用いて、ビルド出力をレイアウト内で参照できるようになります。

アプリケーションをproduction環境にデプロイすると、assets:precompileタスクにjavascript:buildタスクがアタッチされ(package.jsonにあるすべてのパッケージ依存関係がJavaScriptパッケージマネージャ(bunnpmpnpmyarn)でインストールされるようにします)、続いてpackage.jsonで定義されているビルドスクリプトを実行してすべてのエントリポイントをdevelopment環境のときと同様に処理します。アセットパイプラインは後者のファイルを拾い上げてダイジェスト化し、他のアセットパイプラインファイルと同様にpublic/assetsディレクトリにコピーします。

これと同じことが、テストでバンドラーがtest:prepareにアタッチするときにも行われ、テスト開始前にJavaScriptコードが確実にバンドルされるようになります。利用するテストライブラリでtest:prepare rakeタスクが呼び出されない場合は、テストを開始する前にテストスイートでjavascript:buildを実行してJavaScriptコードがバンドルされるようにしておいてください。

概要は以上です。

バンドラーのオプションは、package.jsonファイル内のbuildスクリプトで設定することも、インストーラが生成するファイルで設定することもできます(Bunの場合はbun.config.js、rollup.jsの場合はrollup.config.js、webpackの場合はwebpack.config.json: なおesbuildにはデフォルトの設定ファイルフォーマットがありませんが、esbuildをAPIとして使うハックを用いるつもりはありません)。

既にWebpackerを使っていて、jsbundling-railsに移行すべきかどうかを検討中の方は、Webpacker(Shakapacker)とjsbundling-railsの比較を参照してください。Webpackerからの移行方法についてはWebpacker→jsbundling-rails+webpackアップグレード手順を参照してください。

Webpackのcode splittinghot module reloadingなどの機能を使いたい方は、Webpackerの公式forkであるShakapackerの利用をご検討ください。

🔗 インストール

esbuild、rollup、webpackのいずれかをインストールしている場合は、システムにnodeも事前にインストールしておく必要があります。また、npx 7.1.0以降も必要です。

Bunを使っている場合は、システムにBunのランタイムが既にインストールされている必要があります。

以下を実行します。

./bin/bundle add jsbundling-rails
./bin/rails javascript:install:[bun|esbuild|rollup|webpack]

Rails 7以降は、新規アプリケーション作成時に利用するバンドラーをrails new myapp -j [bun|esbuild|rollup|webpack]のように事前に指定できます。

🔗 FAQ

🔗 Q1: Windowsで*.*のようなglob構文が使えない場合の回避策はありますか?

esbuildのデフォルトのビルドスクリプトは、複数のエントリポイントを自動コンパイルするためにapp/javascript/*.*というglob構文に依存しています。Windowsではこのglob構文はデフォルトで利用できないので、package.jsonファイル内のビルドスクリプトを変更して、コンパイルしたいエントリポイントのリストを手動で追加する必要があります。

🔗 Q2: Bunやesbuildがapplication.cssを上書きしてしまいます

esbuildまたはBunを利用している場合は、application.js内でCSSをインポートすると、ビルド時にapp/assets/builds/application.jsファイルと app/assets/builds/application.cssファイルが両方作成されます。後者のファイルはcssbundling-railsで生成される app/assets/builds/application.cssファイルと競合します。

解決方法は、Bunやesbuild、またはcssbuindlingのいずれかの出力ファイル名(およびその参照)を変更することです。どちらの名前もpackage.jsonファイルで指定されます。

🔗 Q3: JavaScriptコードで静的アセットを参照する方法は?

仮にapp/javascript/images/example.pngという画像があり、esbuildでビルドしたフロントエンドコード内でこの画像を参照する必要があるとします。

  1. app/javascript/images/example.pngに画像を作成する。
  2. package.jsonファイル内の"scripts"エントリと"build"エントリの下で、 esbuildスクリプトに以下の引数を追加する。
    • --loader:.png=file
      pngファイルをビルドディレクトリにコピーするようesbuildに指示する。
    • --asset-names=[name]-[hash].digested
      ファイル名末尾に.digestedを追加して、sprocketsやpropshaftがダイジェストハッシュを末尾に追加しないようesbuildに指示する。
  3. esbuildを実行すると、pngファイルがapp/assets/builds/example-5SRKKTLZ.pngのような形でコピーされる。

  4. フロントエンドコードでは、 import Example from "../images/example.png"のように画像を元のファイル名で参照できる。
  5. これで、画像ファイル自身をインポートファイル名で参照できるようになる(例: Reactの場合は<img src={Example} />)。
  6. 画像へのパスは /assets/example-5SRKKTLZ.digested.pngに解決される(この画像はアセットパイプラインによって配信される)。

ライセンス

JavaScript Bundling for Rails is released under the MIT License.

関連記事

cssbundling-rails README(翻訳)

Rails: Webpacker→jsbundling-rails+webpackアップグレード手順(翻訳)

Rails: Webpacker v5からShakapacker v6へのアップグレードガイド(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。