-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Discussion: Towards a module map proposal #2547
Comments
I think we should start higher-level.
|
The other thing I meant to add is that rather than shortname -> URL you really want this to be shortname -> request (or structure that informs the creation of a request). That way you can centralize subresource integrity, referrer policies, credentials, any additional request headers, etc. |
Largely these use cases can already be implemented in service workers today. For the script type module, we already have this resolver callback from the ES module specification that is returned from the primary parsing within the JS engine, unlike for other assets. An approach for a general resolver could possibly be extended to other assets in future. I'd be hesitant to combine the use cases too much for the initial design though, as those resolvers are fully defined and there is the issue of backwards compatibility which we don't have for script type module here by design. Certainly a dynamic resolver could be extended to work beyond the Window object and to return more detailed request information. I wonder what other possibilities there are within the service worker scope that we don't already have today, and beyond what a more general dynamic resolver could offer though? |
The way I'm thinking about it is that we don't need a resolver until we demonstrate service workers are not good enough in some way as service workers solve this problem in a generic manner that works for many types of resources. One small problem with service workers is that the actual fetch will not reveal the shortname; it will only reveal an absolute URL. You'd have to extract the original shortname somehow. |
This is kind of true, but not really, because of the small problem you mention. In practice what people have to do is process all .js file response bodies, parse out My previous thoughts on this: whatwg/loader#147 (comment) In general I am hesitant to move on trying to solve this problem until we have experience with people using the service-worker-rewriting hack to assemble web apps, and seeing what capabilities they need. |
@domenic yeah it is a hack, although it would be interesting to have the numbers on the approach. While there may be a preloader cost to having a dynamic resolver, the performance benefit gained is being able to update a child module URL through the dynamic resolver, without having to update the specifiers in all its parent modules in the application. The parent modules can then stay in the browser cache of users, so what is lost in the preload scanner is more than gained by the potential of primed caches already having those modules. This could be a useful performance technique for modular production apps, and a benefit for using modules in production. I was just trying to get an idea of what the shape of a spec solution might look like here, and if the use case is valued yet. It's a bit of a shame to have to be using hacks for this with the new shiny script type module, but specs must follow experience definitely. |
@guybedford that is an interesting benefit that when I was discussing this with @dherman we had not quite appreciated. FWIW, a solution we were thinking of is that the service worker gets a new hook where it gets the "raw" input (perhaps not limited to the "barespecifier", but also other attributes if applicable) from script/stylesheets/HTML/etc. and the ability to translate that into a Request which is then immediately used by the fetch event in that service worker. This has quite a few implications throughout the ecosystem though so "userland" experimentation would be good to see first. |
Note that you can get that benefit with service workers today: const map = {
"/js/foo.js": "/js/foo.d34db33f.js",
"/js/bar.js": "/js/bar.783ade0.js",
/// ...
};
self.onfetch = /* proxy based on map */ Now |
@annevk I'd still argue that this script resolver is fundamentally different to a module resolver. The module resolver is an open hook in V8 today, which throws on plain names, making a space for something to be specified here; while other resource resolvers don't have either of these luxuries. Certainly what we do for modules, could potentially be followed by other resources, but as I say I'm hesitant to lump these concerns immediately together when the constraints are different. @domenic that's an interesting approach. It would work well for an app build output where we can assume all the modules are in the same folder, but breaks down if you want the resolver to map to another folder, as any dependencies of the dependency are then still resolved to the original location. For example with: const map = {
"/js/foo.js": "/js/foo-folder/foo.d34db33f.js",
}; where |
An implementation detail in V8 is not really sufficient reason not to look for a general solution, I think. As for your code example, are you sure that's how the base URLs end up playing out? I'm pretty sure that's wrong. The base URL is the response's URL, not the request's URL. |
@annevk I get your point. I suppose I'm just weary of there being some deeper concerns to worry about with other resources. It's also worth noting in the simple case this isn't a general resolver for modules, it's just a "bare name resolver" for modules, which is a simpler problem. CSS and other sources could likely benefit from a similar bare resolver though. Although you'd know any potential catches here better than I. The example is correct for the hack described. Redirects use the response URL so don't have this exact same problem though - the relative resolution will work out correctly with redirects, but the problem is moved to a problem of potential duplicate execution if another importer imports the resolved redirect URL directly. |
What I'm saying is that it will end up being parsed relative to |
Ah right, because the |
Thanks so much for the feedback and discussion here. It really helps a lot to clarify the space, and it seems like any further steps towards a proposal should be revisited when there is a little more implementation experience from these types of approaches. |
I opened #2640 to discuss the general idea further since there's quite a bit of interest in it and pointing everyone to a closed thread gives the wrong impression. |
There is still an open question about how to implement custom resolvers for the bare module specifiers such as
import $ from 'jquery'
within script type module. I'm putting some of my thoughts down here with the hope of hearing some feedback on what might work best for the platform, and where current sentiments lie. If this isn't the best place for this discussion please let me know too and I can potentially move it elsewhere.The benefit of supporting plain resolvers is that plain names allow better caching. I can have a far-future expire on a module that includes a bare import to say
jquery
, while controlling the exact dependency resolution dynamically. Upgrading jQuery can be done without refreshing my users' cache of the module that uses jQuery. So there is this natural benefit for CDNs and browser caches.There seem to be two main approaches that can be taken to its integration as far as I can tell:
An example of a dynamic resolver might be something like:
The resolver might be asynchronous or synchronous.
An example of a declarative resolver might be something like:
Of course the above syntax can be ripped apart, but the principles of the resolver mechanics being written declaratively is the point.
It seems like the dynamic resolver is the one we have precedent for in the loader specification, and it also allows a much wider possibility of use cases, while the declarative form would need to be carefully designed to cater for the right use cases. The declarative form seems like it has the potential to be more elegant though, making the resolver a closer part of the web platform instead of everything always being JS. In many ways an HTML structure can better resemble the resolver data.
What would be the reasons to prefer the dynamic or declarative approach? And would there be interest in starting to refine some proposals further along these lines?
The text was updated successfully, but these errors were encountered: