Rails 5-6時代に使われたWebpackerは、公式に「has been retired」とされ、Rails 7ではimportmapが標準となりました。
Rails7はimportmapとは別に、JavaScriptのバンドラやCSSのフレームワークを導入するしくみを用意しています。2つのgem、 JavaScript Bundling(jsbundling-rails) と CSS Bundling(cssbundling-rails) です。この2つは、Rails 6に入れることもできます。
概要
JavaScript Bundling は、JavaScriptのバンドラ(esbuildやwebpack)を使い、JSファイルを app/assets の下に出力するものです。CSS Bundling は、CSSのフレームワーク(Bootstrapやbulma)をsassコマンドで app/assets の下に出力します。バンドラやCSSフレームワークはyarnコマンド でnode_modules 下にインストールされます。
各コマンドは、package.json の中で指定されます。開発環境では、foreman(bin/dev)を使ってRailsサーバー、JSの変更監視、CSSの変更監視、の3つを動かします。
なお、この記事では CSS Bundling が扱えるCSSのうち、tailwindとpostcssについては試していません。この2つではsassコマンドは使われません。
背景
DHHはRails 7のJavaScriptについていろいろ語っていますが、私の考えでは、もはやIEを配慮する必要はない という環境の変化が大きいと思います。つまり、JSファイルにあれこれ変換をかけたりせずに、Web標準の機能をそののまま使えばいい、バンドラが必要なら好きなものを使ってちょ、というのが今後のRailsのノリなのではないでしょうか。
Webpackerを使うとデフォルトでBabelやcore-jsが入ってしまいます。IE対応なしにすれば、そのぶんアプリケーションをすっきりさせて軽くできます。
インストール
Rails 7では、アプリケーション作成時に -j
オプションで使用するバンドラ(importmap/webpack/esbuild/rollup)を指定できます。importmap以外を選ぶと、JavaScript Bundling が入ります。--css
オプションでCSSフレームワーク(bootstrap/bulma)を指定すると、CSS Bundling が入ります(--css sass
とすると、フレームワークなしでsassコマンドが使えます)。
% rails new app -j esbuild --css bootstrap
Rails 6、7にあとからインストールするときは、Gemfile で指定してbundle install
します。
gem "jsbundling-rails"
gem "cssbundling-rails"
あとからインストールしたときは、Rakeコマンド javascript:install:何々
や css:install:何々
でインストールと初期設定ができます。
app/assets/stylesheets/application.css が上書きされてしまうので、既存のアプリケーションに導入する場合はバックアップしておいてください。
% bin/rails javascript:install:esbuild
% bin/rails css:install:bootstrap
package.json中のコマンド
package.json には、2つのコマンドが加わります。yarn build
とするとバンドラ(以下の例はesbuild)がJSファイルを変換します。yarn build:css
ではsassコマンドがscssファイルを変換します。
なお、production環境にデプロイするときは、assets:precompile の実行時にこの2つのコマンドが実行されます。
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds",
"build:css": "sass ./app/assets/stylesheets/application.bootstrap.scss ./app/assets/builds/application.css --no-source-map --load-path=node_modules"
}
package.json 中のesbuildやsassのオプションは、好きに変えられます。入力/出力ファイルの場所や名前を変えたければ、ここで指定します。
webpackの場合は webpack.config.js で、rollupでは rollup.config.js で入力/出力ファイルを指定できます。
JSとCSSの置き場所
JavaScript Bundling でバンドル前のエントリーポイントとなるファイルは、app/javascript の直下の application.js となります。Webpackerの場合(app/javascript/packs)とは場所が変わるのでご注意ください。
CSS Bundling では、app/assets/stylesheets の下に application.bootstrap.scss(Bootstrapの場合)のような名前のファイルができます。
JSとCSSの出力先
JS、CSSともに、出力されるファイルは app/assets/builds の下に入ります。app/assets/config/manifest.js には、アセットパイプラインが app/assets/builds の下のファイルを使うように指定されています。
//= link_directory ../stylesheets .css
の行がある場合は、削除されることにご注意ください。
//= link_tree ../images
//= link_tree ../builds
// //= link_directory ../stylesheets .css は削除される
buildsの下に出力されたファイルは、昔ながらの stylesheet_link_tag や javascript_include_tag でアセットパイプライン経由でリンクできます。つまり、バンドラやsassで出力されたファイルは、さらにアセットパイプラインで処理されることになるわけです。
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
Procfile.dev
アプリケーションのルートにできる Procfile.dev では、一度に3つのプログラム(rails、JSバンドラ、sass)が常駐するように設定してあります。--watch オプションによってファイルを監視し、変更があれば自動的に変換します。
web: bin/rails server -p 3000
js: yarn build --watch
css: yarn build:css --watch
binディレクトリの下のdevコマンドでは、foremanを使って Procfile.dev のコマンドを動かしています。foremanがないときは自動的にインストールします。
#!/usr/bin/env bash
if ! command -v foreman &> /dev/null
then
echo "Installing foreman..."
gem install foreman
fi
foreman start -f Procfile.dev "$@"
開発環境でRailsを動かすときは、rails s
の代わりに bin/dev
を使います。
% bin/dev
以上が、JavaScript Bundling と CSS Bundling のしくみです。
既存のJavaScript/CSSと同居させる
既存のRailsアプリケーションのJavaScriptやCSSと同居させつつ移行したいときは、コマンドのオプションや設定ファイルでファイル名を変更すればよいでしょう。
まずCSSですが、app/assets/config/manifest.js で//= link_directory ../stylesheets .css
の行を復活させます。
//= link_tree ../images
//= link_tree ../builds
//= link_directory ../stylesheets .css
app/assets の下に application2 というディレクトリを作り、app/assets/stylesheets/application.bootstrap.scss (bootstrapの場合)を移動して application2.scss にリネームします。
package.json でsassコマンドの入力/出力ファイルの名前を変更します。
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds",
"build:css": "sass ./app/assets/stylesheets2/application2.scss ./app/assets/builds/application2.css --no-source-map --load-path=node_modules"
}
JavaScriptはそのままでもいいですが、試しに app/javascript/builds というディレクトリを作って app/javascript/application.js を移動し、application2.js にリネームします。
package.json でesbuildコマンドに渡すディレクトリ名を変更します。
"scripts": {
"build": "esbuild app/javascript/builds/*.* --bundle --sourcemap --outdir=app/assets/builds",
"build:css": "sass ./app/assets/stylesheets2/application2.scss ./app/assets/builds/application2.css --no-source-map --load-path=node_modules"
}
webpackの場合は、設定ファイル webpack.config.js で入力/出力先を変更できます。
entry: {
application2: "./app/javascript/builds/application2.js"
},
既存のアプリケーションの中で、一部のページに JavaScript Bundling と CSS Bundling を使った新しいレイアウトを導入することにします。新しいレイアウトテンプレートを作り、JavaScriptとCSSを指定します。
<%= stylesheet_link_tag "application2", media: "all", "data-turbo-track": "reload" %>
<%= javascript_include_tag "application2", "data-turbo-track": "reload", defer: true %>
なお、Rails 7ではgemの sassc-rails がデフォルトで入らないので、既存のassetsで.scssファイルを使い続けたいときは、Gemfileで指定しておく必要があります。
gem "sassc-rails"