New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal to facilitate multiple import maps #69
Conversation
Hello @zzeligg. I'm not an active contributor to the repo but still thought of suggesting an alternate idea. In the hope to keep only one # config/importmap.rb
scope :admin do
pin "admin", to: "admin.js"
# ... other pins for the `:admin` scope ...
end
scope :public do
pin "public", to: "public.js"
# ... other pins for the `:public` scope ...
end
# The `:default` argument can also be omitted and it will be assumed
scope :default do
pin "application", to: "application.js"
# ... other pins for the `:default` scope ...
end And then inside each of the view layout files, we can do: <%# views/layouts/admin.erb %>
<%= javascript_importmap_tags "admin", scope: :admin %> <%# views/layouts/public.erb %>
<%= javascript_importmap_tags "public", scope: :public %> <%# views/layouts/public.erb %>
<%# Takes the `entry_point` as `"application"` and `:scope` as `:default` if not specified %>
<%= javascript_importmap_tags %> The only problem is that the Import Map spec also has a |
Thanks @radiantshaw, that's actually a good idea. If interest for out-of-the-box support for multiple import maps is intended, this would make for good specs. My intent for now was to have the bare minimum to achieve it. And as to avoid the possible confusion with the
It could be implemented by creating a class which encapsulates a collection of |
I went ahead and implemented it. So, basically, the use case of having a single importmap is pretty much preserved, except for the name of the importmap instance in the application, which I renamed to I have also added an attribute, The helpers can still be called with default parameters, in which case they will render the default importmap, but a map name can be given such as I have added tests to make sure the default usage remains intact (with no specific So basically, from a usage standpoint, not much has changed. You can define a specific map like so:
|
|
||
test "importmaps instance has an specific map" do | ||
h = generate_importmap_json('public') | ||
assert_equal h['imports'].keys, [ 'public', 'md5', 'controllers/hello_controller', 'controllers', 'helpers/requests' ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I understand going through the code and this line, this doesn't include the "application"
part. I feel we should do this instead:
pin "whatever" # This should be included globally instead of only inside the `:default` map
map :public do
pin "more_whatever" # This should be only included in the `:public` map
end
Basically, the above should generate:
{
"imports": {
"whatever": "/assets/whatever.js",
"more_whatever": "/assets/more_whatever.js"
}
}
for the :public
Import Map, and:
{
"imports": {
"whatever": "/assets/whatever.js"
}
}
for the :default
Import Map.
This will allow not having to repeat a pin
/pin_all_from
if something should be included everywhere. Thoughts?
This would be a big plus for me as well. I have various pages where I only need some of the imported libraries and not all of them, and that would help on reducing the extra data that is added to the html on each response with the list of all import maps, especially because some of those libraries depend on others, and thus it's a bit long list of useless data |
Thank you for putting this together @zzeligg. We've also got an application with distinct scopes and this is a must-have feature in order to adopt importmap. After implementing this in our application, I'd agree with @radiantshaw that including the default pins in all named maps helps clean up the definitions. For us, it makes sense to name all maps and not use the default scope independently. I've organized our maps into
|
I think there's something interesting here, but I'm not convinced it's worth it. I just hit this case in HEY where we needed two substantially different entry points, and my first instinct was "well we need separate import maps!". But an import map is basically just a load path. You don't have to use all of it! So in my case, I ended up just mapping those extra pins I needed for the alternate case in the global import map, and then having that separate entry point that only referred to a small subset of the pins. The one drawback of this approach is that the preloads may differ, but that seems like something we could probably solve without introducing the complexity of separate blocks in the import map. |
@dhh What if you have a situation where you're using a lot of files, but for a different entry point, you only need a very minor subset? In that case, we'd be shipping load paths that might never get used. I know that a couple of bytes is not a big deal, but it might become one if there are tons of load paths. Just my two cents, but feel free to disagree if you feel it's not what the project wants.
Maybe we can think of this PR as a solution to this problem; the side effect of which also includes not shipping unused load paths? And not the other way around? |
That was exactly my case. Our import map is probably 150 files. For this different entry point, I just needed 3. The 147 useless lines were just such a small part of the overall picture that it wasn't worth addressing. But yeah, I can see the issue with the preload setup. In that case, I'm almost thinking we could have a way of passing in a preload list, rather than codifying it as part of the importmap. |
@dhh what's interesting about reading Hey's source code is that the sign in page is ~40K, of which ~30.3K is just the importmap/preload part. I'm worried that for projects with many Javascript dependencies/code it'd be something that would prevent people to use it (or at least having them thinking deep about it). Especially when you have totally different scopes and users (eg. buyers, sellers, platform admins) Turbo and the browser would only parse that once on the first page load, but it's still data that would need to be sent through the wire (Turbo Frames and Streams excluded) |
That's intentional since we get to setup all the libraries while someone is typing in their user name and password. We definitely could have made a slimmer entry point if we didn't want to preload as much, though. Anyway, that weight is all from loading actual libraries! So it's about the preloads, not the import map. So I'd love to explore a different way to do the preloads. Maybe it's just an array passed into the helper tag to setting up? |
Going to close this in favor of an open invitation to explore a way to handle different preloads rather than hardcoding them in the import map. |
just to understand the situation, is the entrypoint strictly bound to Ended up here as I was looking for a different path for my entrypoint (ex. |
Being in the process of upgrading and modernizing a Rails application, I plan to use import maps. However, my app has 3 definite scopes which each have their own functionality and layout (public, admin and the actual application scope, where authenticated users perform their activities).
A single 'application' import map shared between scopes and layouts does not cut it for my application (for performance reasons and for eventual security concerns).
My proposal is therefore to add such functionality to importmap-rails.
This PR is rather light and represents the minimum changeset to this gem that allow to use it with more than a single import map (i.e. these changes would allow to use the helpers as is).
The changes are to the helpers module, simply adding the
ImportMap::Map
instance as a parameter in 2 of its methods. The added parameter value defaults toRails.application.importmap
, therefore it does not break the existing usage, but does allow to use the helpers methods with differentImportMap::Map
instances (instead of having to rewrite complete helpers in the application that reproduce the same thing with another importmap instance).That being said, as it is, one still has to go against the grain when defining multiple import maps instances, for example:
config/importmap.rb
files be named and where should they exist?app.config.importmap.paths
for multiple import maps?If there is interest for more functionality to be added to importmap-rails to support multiple import maps, I will gladly contribute towards it. Otherwise, at least the changes in this PR make it still possible to use the gem without having to redefine custom helpers in the application.