I'm not really happy with the current JS stack in Rails - importmaps-rails, jsbundling-rails, propshaft, turbo. Coming from Asset Pipeline it all feels too unnecessary complicated. Maybe it comes in handy for big apps with tons of JS but for normal apps i just don't know why.
But i like ESM modules and what they offer - better cacheing and composability.
Is it possible to use JS modules and dependencies with classic Asset Pipeline? This post will be mostly living reference for my projects to unify how to handle JS code.
# Gemfile
gem "sprockets-rails"
gem "terser"
# app/assets/config/manifest.js
//= link_tree ../images
//= link_tree ../stylesheets .css
//= link_tree ../javascripts .js
Write scripts in /app/assets/javascripts. I name the files with suffix '.m.js'
Use Terser for JS minification.
# config/environments/production.rb
Rails.application.configure do
config.assets.js_compressor = :terser
end
In app layout it's perfectly possible to write importmaps by hand like that:
# app/views/layouts/application.html.erb
<script type="importmap">
{
"imports": {
"application": "<%= asset_path('application.m.js') %>",
"foo": "<%= asset_path('foo.m.js') %>"
}
</script>
And also module scripts:
<script type=module>
import 'application';
</script>
Module scripts can be of course linked and you can prepare some easy include helper for them
If you need some external dependencies from NPM, use jspm.io and their online generator for importmaps with deps and copy and adjust your map in layout manually.
Use rails-ujs