Managing Javascript the easy way in Rails 7
Stuart Harrison
Posted on April 25, 2022
Rails 7 is here, and with it, comes a whole new way of managing Javscript with "UJS with import maps plus Turbo and Stimulus from Hotwire as the defaults".
I'm a pretty experienced dev, but I must admit, I struggled to wrestle with these concepts, and I had a very short greenfield project that I wanted to get shipped and out the door as quickly as possible.
I wanted an approach that mirrored what I was used to with Rails 6 and Webpacker, but while still using something that felt new, and wouldn't succumb to bitrot as soon as I shipped it.
After some background research and deliberation with colleagues, I eventually settled on JSbundling. This allows a similar approach to asset building to the rest of the Javascript world, but with the added magic from the asset pipeline we know and love.
First, add jsbundling-rails
to the Gemfile:
gem "jsbundling-rails", "~> 1.0"
And run bundle install
Then run the generator (I'm using rollup
, as it's marginally easier to grasp than Webpack, and is focussed wholly on building JS):
bin/rails javascript:install:rollup
This adds the following to your Rails app:
- An
app/assets/builds
folder - This will contain your built JS, which will then be served via the asset pipeline - Add
app/javascript/application.js
file - This is the entrypoint for all your application's frontend Javascript - A
bin/dev
file - This should be how you run your application in development. It runs (and optionally installs if you don't have it) Foreman, so you can run your server and build Javsacript on the fly - A
Procfile.dev
file - This tells Foreman what processes to run when you run thebin/dev
script - A
rollup.config.js
file - This tells Rollup how to build the Javascript in yourapplication.js
In addition, it also adds some extra bits and bobs to your .gitignore
(so you don't commit the built files to git), and adds the builds
folder to the assets/manifest.js
file. It also adds the necessary dependencies to your package.json, as well as an NPM command to build your JS.
When you start the server with bin/dev
, your application will spin up as usual, whilst also watching your application.js adn rollup.config.js for any changes and building the result.
Some gotchas
This mainly worked for me off the bat, however, I had some teething problems.
Firstly, I wanted to use Babel to make sure my JS was compatible with older browsers (we still have to support IE11 as we work with Government clients). Adding this to me rollup.config.js seemed to work:
import resolve from "@rollup/plugin-node-resolve"
import { babel } from '@rollup/plugin-babel';
export default {
input: "app/javascript/application.js",
output: {
file: "app/assets/builds/application.js",
format: "es",
inlineDynamicImports: true,
sourcemap: true
},
plugins: [
babel({
babelHelpers: 'runtime',
exclude: 'node_modules/**',
presets: [
[
'@babel/preset-env', {
'useBuiltIns': 'usage',
'corejs': '3'
}
]
],
plugins: ['@babel/plugin-transform-runtime']
}),
resolve()
]
}
(I also had to install @babel/plugin-transform-runtime
and @babel/runtime
for this to work).
Secondly, I like to keep my JS nice and modular, with seperate files for each piece of functionality, and import
ing each file. For this to work, I had to set the rollup output
format to iife
(Immediately Invoked Function Expression), which wraps a function around all your code, so it runs as soon as the JS is loaded. Now, my rollup config looks something like this:
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs';
import { babel } from '@rollup/plugin-babel';
export default {
input: 'app/javascript/application.js',
output: {
file: 'app/assets/builds/application.js',
format: 'iife',
inlineDynamicImports: true,
sourcemap: true,
},
plugins: [
commonjs(),
babel({
babelHelpers: 'runtime',
exclude: 'node_modules/**',
presets: [
[
'@babel/preset-env', {
'useBuiltIns': 'usage',
'corejs': '3'
}
]
],
plugins: ['@babel/plugin-transform-runtime']
}),
resolve(),
]
}
And that's it! Hopefully this will save you hours of painful googling, and prevent you from getting migraine from staring at the same config file for hours on end!
Posted on April 25, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.