importmap-rails icon indicating copy to clipboard operation
importmap-rails copied to clipboard

Are there plans for importmap-rails to support scopes?

Open mpkuczer opened this issue 2 years ago • 5 comments

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?

mpkuczer avatar Aug 24 '22 12:08 mpkuczer

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.

dhh avatar Jun 18 '23 11:06 dhh

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.

dkniffin avatar Aug 24 '23 19:08 dkniffin

I haven't fully thought this through, but perhaps a solution here could also simplify importmap.rb with respect to transitive dependencies? Currently, pinning 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 avatar Apr 25 '24 18:04 aprescott

@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.

oboxodo avatar Apr 26 '24 13:04 oboxodo

@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.

dkniffin avatar Apr 26 '24 14:04 dkniffin