-
Notifications
You must be signed in to change notification settings - Fork 72
How could import.meta.resolve() behave? #79
Description
Related: #75, #18, #76, whatwg/html#3871.
Background: What is import.meta.resolve()?
The original idea of import.meta.resolveURL(x)
was that it would be sugar for (new URL(x, import.meta.url)).href
. So, you could do import.meta.resolveURL("./x")
inside https://example.com/path/to/y
and you would get "https://example.com/path/to/x"
.
But note that in this formulation, you could also do import.meta.resolveURL("x")
and get the same result. That's a little strange, since import "x"
would not give you that URL.
It would also be ideal if we could do, like Node.js's require.resolve(), a function that gave you a URL from a "package name", of the type provided by import maps. Let's call this import.meta.resolveMapped()
. So for example, given the simple example map from the readme at https://example.com/index.html
, import.meta.resolveMapped("lodash")
would give "https://example.com/node_modules/lodash-es/lodash.js"
.
We then noticed that having two functions is confusing and redundant. Let's merge them! The result is import.meta.resolve(x)
, which gets the URL that would be fetched by the module system if you did import(x)
.
Side note: what are the use cases?
We don't have a great list of canonical use cases for this function. whatwg/html#3871 has some, and elsewhere in this issue tracker people like @bicknellr and @justinfagnani have mentioned web components-related use cases.
Maybe gardening up a master list, with code examples, would be a good next step.
The problem: fallbacks
This proposal's introduction of fallbacks for user-supplied packages throws a wrinkle into our plans. In particular, there's no longer a synchronous way to figure out what to do in the "package" case. E.g. in that example, it's no longer clear what import.meta.resolveMapped("jquery")
, or import.meta.resolve("jquery")
, should return.
Solution ideas
Return an array
That is, import.meta.resolve("jquery")
returns an array containing the normalized absolute URLs that would be potentially used, e.g. ["https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js", "https://example.com/node_modules/jquery/dist/jquery.js"]
.
This seems hard for consumers to code against, and makes it hard to refactor between fallbacks and no-fallbacks.
Return a time-varying value
For example, it could return null
, or an array like the above, while resolution is pending. Once resolution happens (via some actual fetch), it could return the resolved-to URL.
This similarly seems hard for consumers to code against.
Return a promise
The drawback here is developer ergonomics. E.g. if you are trying to use this inside a component, in order to programmatically set up linkEl.href = import.meta.resolve("jquery")
, you no longer can; you need to make that async.
This could become less-terrible with top-level await, plus of course the usual mitigations like web packaging/preload/etc. to make sure that top-level await is not overly blocking.
Lazy
In this variant, calling import.meta.resolve()
does not do any fetches itself. Instead it waits for some actual fetch to happen (e.g. via import()
or the use of an import:
URL). The promise may then end up pending for a long time.
This feels like a bit of a footgun.
Eager
In this variant, calling import.meta.resolve()
causes a fetch to happen, which updates the appropriate resolution cache (see #76 for some discussions on what that means).
This feels kind of inappropriate for a resolution function to perform I/O.
Punt on this
For example, return null
for all fallback cases (except maybe we could do built-in module cases?).
This still makes refactoring hard; you could break parts of your app by moving from no-fallback to fallback.
Return a request, or a promise for a response
In this variant we get even further from what is typically thought of as a "resolution" function. Instead we return something which could be consumed by various endpoints that currently take a URL. This is a long-standing idea, see whatwg/fetch#49 and whatwg/html#3972. But it might solve all the same use cases.
So far I like this the most. It needs a bit more exploration, but it seems like it could work.