Converting your project to use importmaps
Importmaps allow you to use modern JavaScript modules without a complicated build process or an additional ecosystem. Modern browsers can handle serving these modules and leverage HTTP/2 to efficiently deliver many small assets. We can finally start using the syntax import SomeModule from 'some_path/some_module.js and no longer search the depths of the Internet for custom JavaScript builds. Visit the official documentation for more information.
Overview
The main goal of this importmaps integration within PMACS Frontend was to be fully backwards compatible with the existing Sprockets setup, while unlocking the ability to use JavaScript modules, or ESM. Careful attention has been taken to not force the use of importmaps and allow your project to function exactly like it used to – one big JavaScript file served up front. While importmaps are not a perfect solution, they represent a good path forward and are an excellent option for most projects!
Pros
- Allows the use of Javascript modules (no more need to expose everything globally)
- Easier to debug code in the browser
- Enforces the latest conventions in your project structure
- Get automatic updates for things like Stimulus and Turbo via the standard gems
- No extra build steps
- Better caching in the browser with smaller pieces of code that can be invalidated without the entire bundle
- No need for a package manager – all dependencies are served via the asset pipeline
Cons
- Not as flexible as esbuild/vite
- No support for CSS importing
Installation
Before you begin, please make sure your project meets the criteria in the checklist below:
- [ ] Your project is running
pmacs-frontend 2.0.0or greater - [ ] Your project is using Turbo and not Turbolinks (The generated importmaps do not add the proper data attributes for Turbolinks, so it's time to rip that band-aid off!)
- [x] You love JavaScript!
If you meet the criteria above, start moving into the future by following these instructions...
Full importmap and JavaScript conversion
Update your project to use importmaps and migrate ALL of your JavaScript code to live in app/javascript. Using this directory is now the preferred convention.
rails generate pmacs_frontend:import_maps install
Your project should now be ready to use importmaps!
Confirm that the initializer located in config/initializers/pmacs_frontend.rb looks similar to below:
PmacsFrontend.configure do |config| config.application_title = 'Application with importmaps!' # Rest of config ... config.import_maps_enabled = true end
Confirm that an importmap config was created in config/importmap.rb and that it looks similar to below:
# Pin npm packages by running ./bin/importmap # Add your custom pins here below. PMACS Frontend automatically pins stimulus and the main app module. # Uncomment this line to enable turbo # pin "@hotwired/turbo-rails", to: "turbo.min.js"
Spot check your legacy application.js file in app/javascript/application.js. This should not contain any references to PMACS Frontend and should only include code from your project. This is the file you will most likely need to manually touch-up after the automated process, depending on the amount of complexity and customization included in your project.
Converted project structure
If you have made it through all the installation steps, your project should now be running with importmaps and your project structure will have slightly changed.
app/
├── assets/
│ └── config/
│ └── manifest.js
└── javascript/
├── application.js
├── controllers/
│ ├── application.js
│ ├── custom_controller.js
│ └── index.js
├── on_ready
├── on_ready.js
└── pmacs_frontend_app.js
Important file details
The table below briefly describes some of the important files and what purpose they serve.
| File | Description |
|---|---|
application.js | Legacy JavaScript entry point that brings in assets controlled by Sprockets |
pmacs_frontend_app.js | Main entrypoint for apps using importmaps |
How to import a JavaScript module
We have finally made it to the fun part! Let's walk through how to create, import, and use a small JavaScript module.
- Create file
app/javascript/rant_responses.js Add the following content inside the file:
export function continueRant() { console.log("Mm-hmm... Go on...") } export function rantOver() { console.error ("Enough already! Your rant can't be more than ${this.maxValue} characters long! Seriously, I'll start charging by the character."); };
Now we will make this module usable by pinning it inside the config
config/importmap.rb:# The name in the first argument can be anything you want! Generally, it should be a close match to the script name or something semantically meaningful. pin "rant_responses", to: "rant_responses.js"
Now lets use these utils in a Stimulus controller:
import { rantOver } from "rant_responses"; import { Controller } from "@hotwired/stimulus" export default class CountCharsController extends Controller { static targets = [ "showLength" ]; static values = { max: { type: Number, default: 50 } }; count(event) { this.showLengthTarget.innerHTML = event.target.value.length + "/" + this.maxValue if (event.target.value.length > this.maxValue) { rantOver(); } } }
<div data-controller="count-chars"> <label class="required" for="rant">Write your rant here!</label> <textarea class="form-control" name="rant" id="rant" data-action="input->count-chars#count"></textarea> <span data-count-chars-target="showLength"></span> </div>