Migrating from Webpacker to the New Bundling Approach
You may have heard that Rails 7 will have three great answers to Javascript in 2021. Well, you don’t have to wait until Rails 7 is released to switch from webpacker
to the new, simpler bundling approach, using jsbundling-rails
and cssbundling-rails
.
If you’re on Webpacker today, it’s a very modest jump to switch to one of the bundlers made available through the jsbundling-rails gem. You don’t even have to stick with Webpack. Fundamentally, they all work in the same way: take an entry point, produce a build.
This isn’t a step-by-step guide, and probably won’t age well (as things are still in flux with the new bundling gems), but having migrated a couple Rails apps from webpacker
to the new bundling approach, I have some tips that will save you time and headaches.
1. Use Foreman for process management
If you’re not using it already, use the foreman gem to manage your various processes. These days, it’s common and expected to have multiple processes running in development. You are rarely just going to run rails s
, as you’ll probably need a background worker process, and potentially others. If you are coming from webpacker
, you’ll be dropping the bin/webpack-dev-server
process for these new build processes.
Set up a Procfile.dev
for all your development processes like this:
web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq
js: yarn build --watch
css: yarn build:css --watch
Note: the foreman
docs recommend you not add it to your Gemfile.
2. Use multiple entrypoints when spanning old and new approaches
If you were the middle of a migration to webpacker
from the classic Sprockets approach (like I was), and still have JS under app/assets/javascripts
, you could just setup separate entrypoints. Just make sure they have different names, so there isn’t a collision:
In your application.html.erb
:
<%= javascript_include_tag "legacy", "application" %>
In your manifest.js
:
//= link_tree ../builds
//= link_tree ../images
//= link legacy.js
3. Let Sprockets handle compression
You probably still want to do asset compression through Sprockets, with uglifier
for JS and sassc-rails
for CSS.
Note: for uglifier to work with ES6, you need to enable harmony mode. (See the README.)
config.assets.js_compressor = Uglifier.new(harmony: true)
Even if your app doesn’t use SASS (e.g. if you are using Tailwind) sassc-rails
is still the most common gem used for CSS compression. The Edge Rails Guides say:
The sassc-rails gem is automatically used for CSS compression if included in the Gemfile and no config.assets.css_compressor option is set.
4. Copy CSS assets and update asset paths
If you include CSS packages with their own assets (like FontAwesome with font files), you can use postcss
as the build tool, and use the postcss-copy
plugin, configured like so:
require('postcss-copy')({
dest: 'app/assets/builds'
}),
This will ensure that assets are copied to the builds folder. However, this doesn’t update asset paths for digested assets in production. See this discussion and this (currently-open) PR.
Until cssbundling-rails
is updated to enable this, you can add a Sprockets postprocessor class, like those suggested in the discussion and PR, that will update asset paths to use the digested versions.
5. Make sure build scripts run before test suite
If you are using Minitest, the bundling gems will automatically enhance the test:prepare
task so that your build scripts run before your tests. But if you are using RSpec, you’ll need to do this yourself.
You’ll want to add the Rake::Task
enhance calls found in the build Rake files for cssbundling-rails and jsbundling-rails to your Rakefile, something like this:
if Rake::Task.task_defined?("spec:prepare")
Rake::Task["spec:prepare"].enhance(["css:build", "javascript:build"])
end
Also, make sure you run rails spec
rather than rspec
so that it picks up the enhancements.
6. Use esbuild-rails
for JS file globbing
If you are dropping webpacker
for esbuild
, that means you’ll lose the file globbing used, for example, when loading all the Stimulus controllers in a directory. The best approach right now appears to be using Chris Oliver’s plugin, esbuild-rails
. See his GoRails video for details.
Update 2021-10-20: bonus tip from Breno Gazzola
7. Handling multiple CSS entrypoints
Just like with Javascript, you can also have multiple CSS entrypoints. To do this with the sass
build option, for example, place all your entrypoints in one folder, the sources in another, and adjust the packages.json
file:
build:css": "sass ./app/assets/stylesheets/entrypoints:./app/assets/builds --no-source-map --load-path=node_modules"
Need help building or maintaining a Rails app?
Jeremy is currently booked until mid-2023, but always happy to chat.