Skip to content
This repository was archived by the owner on Feb 26, 2025. It is now read-only.
This repository was archived by the owner on Feb 26, 2025. It is now read-only.

How could import.meta.resolve() behave? #79

@domenic

Description

@domenic

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions