importmap-rails
importmap-rails copied to clipboard
Are there plans for importmap-rails to support scopes?
In my project (using Rails 7 with importmap-rails) I am trying to import a module which has a complex dependency graph. In particular, two dependencies (let's call them A
and B
) depend on a third one (C
) although they require different versions of it, and so I end up in dependency hell.
This isn't a problem according to the WICG importmap spec, because you could use scopes to include both versions depending on which scope C
is being imported from, like so:
"scopes": {
"https://ga.jspm.io/npm:path-to-A/": {
"C": "https://ga.jspm.io/npm:[email protected]/index.js"
},
"https://ga.jspm.io/npm:path-to-B/": {
"C": "https://ga.jspm.io/npm:[email protected]/index.js"
}
}
In its current state, importmap-rails
doesn't make use of the scopes
object and only ever parses the imports
part of a generated importmap, in fact it ignores any scoped imports. A one-to-one map of names to paths is generated, and as a result, a singular module name can only ever point to a particular version of it. This potentially renders a complex importmap unusable. Is this something we could potentially have support for?
Happy to see an attempt at supporting this explored. But don't want to dramatically complicate the implementation to get there. Do have a stab.
This is a pretty important feature. But I think dependency conflicts are kind of rare.
What's more important to me, is that we at least get some alert about subdependencies being incompatible. At the moment, I think if there are different versions of subdependencies, importmap will silently pin one of them, and we'd never know unless it causes issues, right? Maybe I'm wrong.
I haven't fully thought this through, but perhaps a solution here could also simplify importmap.rb
with respect to transitive dependencies? Currently, pin
ning will download other packages that the target package relies on, which causes extra work to maintain/track/understand what the actual top-level dependencies of an application are. It would be great if importmap.rb
were more like a package.json
set of direct dependencies and less like a lockfile. It seems as if "scopes"
might permit specifying the transitive dependencies as keys of the mapping. Then, if the vendor/javascript/
directory mirrored the "imports"
vs "scopes"
structure, removing a dependency could remove the corresponding vendored files, and a dev wouldn't need to figure out top-level vs. transitive deps.
@aprescott management of transitive dependencies is one of the reasons we (actually @dkniffin) ended up implementing https://github.com/Quimbee/importmap-package-manager as a wrapper for importmap-rails. It pretty much allows us to use importmap.rb
as if it were package.json
(*), and we declare only the top-level dependencies in config/importmap_packages.yml
.
We've been using it in production since Sep 2023 or so and it's been very helpful.
(*) UPDATE: I was wrong. Actually we leave importmap.rb
to be used only for our internal JS dependencies and then declare our 3rd-party dependencies in config/importmap_packages.yml
. Then we run rails importmap_package_manager:update
which autogenerates config/importmap-packages-lock.rb
. And this file acts as package-lock.json
or yarn.lock
. It then is treated as a complement of importmap.rb
.
@aprescott Yep, what @oboxodo said. I was hoping importmap-rails would natively have some way to distinguish between dependencies and subdepenencies, so I opened #174, but apparently it's outside the scope of this project. So I made my own!
The other important thing our library does is support version ranges, as described in #173. With those two features, it now behaves much more like you'd expect from a package manager type system.
I don't think scopes will solve those issues, but I might be wrong. As I understand it, scopes are for avoiding dependency conflicts. I do think it's important to have support for that inside importmap-rails, because I don't think there's a way for us to include that in a wrapper library.