Skip to content
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: baseURL argument to import.meta.resolve #8077

Open
guybedford opened this issue Jul 5, 2022 · 2 comments
Open

Proposal: baseURL argument to import.meta.resolve #8077

guybedford opened this issue Jul 5, 2022 · 2 comments

Comments

@guybedford
Copy link
Contributor

guybedford commented Jul 5, 2022

import.meta.resolve provides a contextual resolver at the module level allowing custom module resolution to be exposed to users without actually importing those modules. This allows resolving URLs that are not necessarily modules as well as determining the resolved URL for any module specifier.

Problem Statement

import.meta.resolve has limited functionality in that it only permits resolving bare specifiers within the scope of the current module. When wanting to resolve bare specifiers which exist in other scopes, one would need to load a module within one of those other scopes first to get hold of an import.meta.resolve with the ability to resolve relative to that scope.

This could be useful for determining the resolution of a dependency of a dependency when the import map is not known. This is not otherwise possible since the root import map cannot be retrieved from the HTML page.

Proposal

The proposal is the addition of an optional baseURL argument to resolve. In #8074 (comment) this is done via a direct second argument, although as @domenic mentions it might be worth making this an options bag to support future extensibility scenarios:

import.meta.resolve(specifier, { baseURL })

When not provided, the default baseURL of the current module context would be maintained per the current implementation.

Implementation Interest

Node.js has been shipping this feature since Feb 2020 (https://nodejs.org/dist/latest-v18.x/docs/api/esm.html#importmetaresolvespecifier-parent). Deno has an open PR to support import.meta.resolve along with this feature (denoland/deno#15074).

Compatibility Concerns

For implementers providing this feature, the options bag approach is a great suggestion. If some implementers ship import.meta.resolve(specifier, baseURL) and others ship import.meta.resolve(specifier, { baseURL }) that risks creating an interop hazard between runtimes. For this reason standardizing on an approach can ensure users get consistent support when performing custom resolutions.

Alternative Designs

One benefit of this proposal is its simplicity in gaining cross-platform support for the feature in a way that can interop between runtimes since import.meta.resolve as it is designed today simply provides a default baseURL.

Alternative designs might include a global or host-specific method. Alternatively there may just be implementation divergence, the point of posting this issue is exactly to avoid these scenarios where server-side runtimes and browsers have differing behaviour. Significant work is already being done in Node.js to update our import.meta.resolve implementation to align with browsers, and this forms part of that effort.

@domenic
Copy link
Member

domenic commented Jul 6, 2022

So, let's remember https://whatwg.org/faq#adding-new-features step 1.

I'd like more detail on the problem statement. What are some examples of (browser) code which would use this feature? "determining the resolution of a dependency of a dependency when the import map is not known" is basically just restating the functionality of the feature. What is an example of web app code that would need to do this? Can you point to examples that exist today on the web, using e.g. AMD modules or transpiled Node.js modules?

@nayeemrmn
Copy link

nayeemrmn commented Jul 15, 2022

One benefit of a global resolver is that it makes it easier for behaviour-preserving bundlers to hardcode import.meta.resolve(singleArg) (which of course is now already specced):

// input `https://example.com/foo.js`:
console.log(import.meta.resolve("./bar.js"));

// bundled `https://example.com/foo.js`:
const __importMeta = {
  url: "https://example.com/foo.js",
  resolve(specifier, options = {}) {
    return import.meta.resolve(specifier, { baseURL: "https://example.com/foo.js", ...options });
  }
};
console.log(__importMeta.resolve("./bar.js"));

From #8074 (comment):

  • I don't think a global resolver belongs on import.meta, because it's no longer specific to the current module like all existing import.meta properties are. Instead you just seem to be using it as a sort of weird global namespace. Something like self.resolveModuleSpecifier() or HTMLScriptElement.resolve() would make more sense to me.

I think this is a very good point, to add to it I don't understand what the benefit was of import.meta.resolve("./bar.js") over self.resolveModuleSpecifier("./bar.js", { baseURL: import.meta.url }). Problems like the above are resolved naturally, and of course a global resolver API would be eventually be proposed. Is there a semantic problem with this I'm not seeing? If this was discussed somewhere I missed it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants