-
Notifications
You must be signed in to change notification settings - Fork 17
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
default resolve
should not throw ERR_UNSUPPORTED_ESM_URL_SCHEME
#138
Comments
I'm not sure that's correct - semantically, if the URL scheme is unsupported, then the resolution can't happen. For instance, let's say you have For instance, let's say I write a tool validating that all the import statements in my project are resolvable, and this tool checks it by calling |
That would have a consequence on export async function resolve(specifier, context, next) {
try {
return await next(specifier, context);
} catch (err) {
if (err?.code === "ERR_MODULE_NOT_FOUND" && specifier.endsWith(".js")) {
try {
return await next(specifier.replace(/\.js$/, ".ts"), context);
} catch (err2) {
if (err2) {
err2.cause = err;
throw err2;
}
throw err;
}
}
throw err;
}
}
To clarify, |
Why not accept all valid URL-s as valid during resolve? They will fail in the load phase anyway.
That's a good point. Also @aduh95's point above, which points out that resolution checks for the existence of a file, and so could not be able to do that for an unknown extension. But @aduh95 proposes a solution to that, which makes sense: throw the exception in default resolve only for non |
I feel this discussion is quite similar to the one we had some time ago regarding import attributes, we might need another hoook for loaders to say what import attribute key they support, maybe we could add what protocols the support as well. |
You're right! I just modified my toy TS loader and removed the
Hmm.... yes! But @arcanis raises a valid concern: semantically, should That's a good question. In a way, it's a question for the JS standards group that is working on If the answer is "yes", then |
@arcanis just checked MDN on this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta/resolve#description
So you're actually not "allowed" to use Having seen this, I would submit that even Node.js shouldn't throw This also means that @aduh95 code that checks for |
According to the HTML spec, browsers don't perform any check on the passed specifier string. I just tried to pass |
Yup. The only thing they do is check the import map of the current HTML to see if the URL needs to be mapped. |
So given the above definition of For any non-bare specifier, the default Node.js resolver should do only absolutization of the URL, and should not verify anything else about the final URL (e.g. it should not verify existence of the file, nor support of the URL scheme). |
I feel like there’s a reason it currently happens in |
@GeoffreyBooth as @aduh95 showed in his loader code above (and I can attest also happens in my But this doesn't mean that it should be like that. In the browser world, it is explicitly not like that. And my guess is that most HTTP loaders won't check for the existence of the file in the resolve phase. ESM in Node.js was always about aligning ourselves with the standardand not doing a separate thing. And the spec says not to check for existence of files in I suggest we align with the standard now, before it's too late and ESM loaders move out from experimental. (and at least don't check the scheme is supported in the load phase and not the resolve phase 😊) |
BTW, not checking for existence of files in the resolve phase, will mean that for relative paths, loading will be faster, because one Moreover, this will mean that less loaders will have a |
Okay, want to make a PR? |
Yes! I'll go all the way and include not checking for existence of files in the resolve phase, along with not checking if the scheme is supported in the resolve phase. |
For fairness' sake, I'd like to note that this will break loaders that use the existing node resolver to check for the existence of files. E.g. my |
For the record, I feel a little uneasy making |
I can do two PR-s. One that removes the check for schema from the default resolver. I think this is the less controversial one, and will benefit loader writers and will benefit loader chaining (see below on why). This PR also won't break anything (I think). The next PR, which we can all discuss on, is moving the file existence checking from the resolve phase to the loading phase. For the record, I am for moving it, both for browser compatibility, and for performance reasons. For the record, I do want the second PR to pass, if only for performance reasons. |
Why removing the check for It's all about loader chaining. Let's say I have two loaders:
i.e. Except this doesn't work! Because Node.js (rightly) resolves using the The order that the user needs to use it Now if Node.js in it's default resolver wouldn't check the schema in the resolver, then the order could have been the natural order |
I'm not sure it's a good reason to make the change you propose - it works in the example you provide, but make it slightly more complex and it falls apart. Take the following:
Assuming a setup where each
Regardless of the loader order, something won't work:
It perhaps can be tweaked so that the first The proper way to handle this case is to not call the followup loader. In the majority of cases, the resolution should actually be recursive and restart from the start, not from As a bonus, doing that also clears most of the issues around resolution order: if resolvers call |
@arcanis I'm sorry, but I couldn't follow your example, because I'm not quite sure what But regarding this:
I'm assuming you meant not calling |
Recommending loaders to call There will always be exceptions, but the general point remains: significant changes on intermediate resolutions should re-enter the resolution pipeline, not continue from where they are, otherwise they'll put strong constraints on the hook order, as you noticed in your http,import-map example. |
What's |
The first resolve function in the chain, just like |
Why is the first resolve in the chain "privileged"? It seems like you're adding complexity ( I have to admit that I couldn't figure out what you were saying about making the loader chain impervious to order. Is this an API change to the loaders? If so, how does it make it not important whether the |
Fixes nodejs/loaders#138 PR-URL: nodejs#47824 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Fixes nodejs/loaders#138 PR-URL: #47824 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Fixes nodejs/loaders#138 PR-URL: #47824 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Fixes nodejs/loaders#138 PR-URL: nodejs#47824 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Guy Bedford <guybedford@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
While working on my ESM loaders talk for JSNation and NodeTLV conferences (repo here and draft slides here), I wrote a toy loader that supports HTTP[S]. This is the (obviously) simplistic code:
Unfortunately, this won't work. If you use it, you will get an
ERR_UNSUPPORTED_ESM_URL_SCHEME
because the default Node.js resolver doesn't support HTTPS. So, unfortunately, I had to write (and demonstrate in my talk) that this loader needs a resolver, even though the only thing it does is support loading HTTPS:Thinking about it more, I realized that the only reason I need to write this resolver is that Node.js doesn't recognize the HTTP scheme in its resolver. But if Node.js would have not recognized the scheme in the loader, then I wouldn't have needed to write this resolver at all.
The same logic goes for
ERR_UNKNOWN_FILE_EXTENSION
and writing, for example, a TypeScript loader.So my point is this: both checks for unknown scheme and unknown file extension should move from the default Node.js resolver to the default Node.js loader. This will obviate the need for some loaders to implement a
resolve
which mostly replicates the Node.js logic, just for the sake of avoiding these exceptions.I would gladly implement the change myself, but I wanted to check with the loaders team before tackling this task.
The text was updated successfully, but these errors were encountered: