I'm having trouble using importmap-rails
to manage npm packages. I would ideally like it to function like yarn where it keeps track of versions, dependencies and downloads everything for you with a command like yarn install
.
But it seems like importmap-rails
is only set up to use a cdn when you pin something. Is this true? Or can it download packages and their dependencies with a command like yarn?
If it can't do that, can I use importmap-rails
with yarn as my package manager? I tried this with underscore.js and ran into some issues.
yarn add underscore
, saw it was downloaded correctly and then added node_modules
to the asset paths so that propshaft
and importmap-rails
would know to look there:config/application.rb
config.assets.paths << Rails.root.join('node_modules')
Then pinned the underscore folder:
# config/importmap.rb
pin_all_from 'node_modules/underscore', under: 'underscore'
Then imported the functionality I needed:
// from a custom js file
import debounce from 'underscore/modules/debounce';
Running ./bin/importmap json
showed that the debounce.js file was found:
{
"imports": {
"application": "/assets/application-bd82d6ab.js",
"underscore/amd/_baseCreate": "/assets/underscore/amd/_baseCreate-7eccab16.js",
"underscore/amd/_baseIteratee": "/assets/underscore/amd/_baseIteratee-5f781255.js",
"underscore/amd/_cb": "/assets/underscore/amd/_cb-dd403ec6.js",
"underscore/amd/_chainResult": "/assets/underscore/amd/_chainResult-4b98e265.js",
"underscore/amd/_collectNonEnumProps": "/assets/underscore/amd/_collectNonEnumProps-ed4c206f.
...
"underscore/modules/debounce": "/assets/underscore/modules/debounce-decaec4e.js",
And in the browser I didn't get any errors for debounce.js, it was fingerprinted and found, but inside of that file it was using relative paths to import other functions which caused 404 errors. Also there was a get request going to every single file in underscore, which is also not good.
15:41:34 web.1 | Completed 200 OK in 5482ms (Views: 2431.0ms | ActiveRecord: 557.1ms (24 queries, 6 cached) | GC: 368.1ms)
15:41:34 web.1 |
15:41:34 web.1 |
15:41:34 web.1 | Started GET "/assets/underscore/amd/_baseCreate-7eccab16.js" for ::1 at 2024-12-20 15:41:34 -0500
15:41:35 web.1 | Started GET "/assets/underscore/amd/_baseIteratee-5f781255.js" for ::1 at 2024-12-20 15:41:35 -0500
15:41:35 web.1 | Started GET "/assets/underscore/amd/_cb-dd403ec6.js" for ::1 at 2024-12-20 15:41:35 -0500
15:41:35 web.1 | Started GET "/assets/underscore/amd/_chainResult-4b98e265.js" for ::1 at 2024-12-20 15:41:35 -0500
15:41:35 web.1 | Started GET "/assets/underscore/amd/_collectNonEnumProps-ed4c206f.js" for ::1 at 2024-12-20 15:41:35 -0500
15:41:35 web.1 | Started GET "/assets/underscore/amd/_createAssigner-aa588b74.js" for ::1 at 2024-12-20 15:41:35 -0500
15:41:36 web.1 | Started GET "/assets/underscore/amd/_createEscaper-3ea6026c.js" for ::1 at 2024-12-20 15:41:36 -0500
15:41:36 web.1 | Started GET "/assets/underscore/amd/_createPredicateIndexFinder-f577fa32.js" for ::1 at 2024-12-20 15:41:36 -0500
...
debounce-decaec4e.js:1
GET http://localhost:3000/assets/underscore/modules/restArguments.js 404 (Not Found)
debounce-decaec4e.js:2
GET http://localhost:3000/assets/underscore/modules/now.js net::ERR_ABORTED 404 (Not Found)
debounce-decaec4e.js
import restArguments from './restArguments.js';
import now from './now.js';
So, can I use importmap-rails with yarn or instead of yarn for npm packages?
I've googled around and combed the documentation and there is only this brief description: https://github.com/rails/importmap-rails/blob/main/README.md#using-npm-packages-via-javascript-cdns
For extra context I just updated our rails 7 app to rails 8, so we just went from sprockets, yarn, webpacker to propshaft with importmap-rails. Happy to be without webpacker, but missing the yarn functionality.
You can use yarn
to get the packages but you have to use ES module package:
pin "underscore", to: "underscore/underscore-esm.js"
import { debounce } from "underscore"
If you need just one function:
pin "/assets/underscore/modules/debounce.js", to: "underscore/modules/debounce.js"
# debounce dependencies
pin "/assets/underscore/modules/now.js", to: "underscore/modules/now.js"
pin "/assets/underscore/modules/restArguments.js", to: "underscore/modules/restArguments.js"
which will allow for relative imports:
// app/javascript/application.js
// relative to the asset url
import debounce from "./underscore/modules/debounce.js"
To clarify the relative part, imports are relative to the url, and you can use ./
and ../
as needed to match the importmap, given you have the correct pin setup:
application => import
application => matches importmap
/assets/application-c10b436c.js => OK
⌄
./underscore/modules/debounce.js => import
/assets/underscore/modules/debounce.js => resolve relative url
/assets/underscore/modules/debounce.js => matches importmap
/assets/underscore/modules/debounce-1fc53fe7.js => OK
⌄
./restArguments.js => import
/assets/underscore/modules/restArguments.js => resolve relative url
/assets/underscore/modules/restArguments.js => matches importmap
/assets/underscore/modules/restArguments-708796bd.js => OK