-
Notifications
You must be signed in to change notification settings - Fork 29.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
module: resolver & spec hardening /w refactoring #40510
Conversation
/cc @nodejs/modules @nodejs/loaders (hope to get around to reviewing this myself, but it could be a while). |
54276f3
to
687c6ad
Compare
PR-URL: #40510 Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: James M Snell <[email protected]>
Landed in f0dcac8. |
|
||
> 1. Assert: _url_ corresponds to an existing file. | ||
> 2. Let _pjson_ be the result of **READ\_PACKAGE\_SCOPE**(_url_). | ||
> 3. If _url_ ends in _".mjs"_, then | ||
> 1. Return _"module"_. | ||
> 4. If _url_ ends in _".cjs"_, then | ||
> 1. Return _"commonjs"_. | ||
> 5. If _pjson?.type_ exists and is _"module"_, then | ||
> 5. If _url_ ends in _".json"_, then | ||
> 1. Return _"json"_. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn’t the case unless --experimental-json-modules
is enabled; and even then, assert { type: 'json' }
will be required, which I’d think should be mentioned here. Perhaps this would be better added when #40250 lands?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact that this spec doesn't mandate the assertion doesn't mean Node.js core can't mandate the assertion. It's more a question of layering I think. I'm definitely open to explicitly calling out that the assertion is mandated if we have full consensus on that, just wanted to do the minimal change initially.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern is more that this spec seems to imply that Node can’t mandate the assertion. As in, if the URL ends in .json
, that’s the end of the story: the spec says so. I would maybe revise this to:
> 5. If _url_ ends in _".json"_, and the import assertion _type_ is _"json"_, then
1. Return _"json"_.
It already went to the TSC that the assertion will be mandated, at least at first (when the PR that adds support lands). If/when it ever becomes optional, we can update this spec accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would need to also include the initial _assertions_
parameter as an explicit argument into the resolution function to do this, perhaps a follow-up PR then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that’s part of #40250. We could maybe include the spec update in that PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way I interpreted that was file name ending in ".json"
is the browser equivalent of having a response header Content-Type: application/json
. To me, the fact a .json
file is considered as JSON by Node.js spec does not mean it can be imported without an assertion (the same way the HTML spec do not accept to load a module if the Content-Type is application/json
). I don't think we should make the _assertions_
parameter as an explicit argument into the resolution function, a .json
file should always be considered as JSON, the assertion check should come after that.
> 8. Otherwise, | ||
> 1. Set _format_ the module format of the content type associated with the | ||
> URL _resolved_. | ||
> 9. Load _resolved_ as module format, _format_. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is describing Node’s internal behavior, but does this section correspond only with the user loader resolve
hook, or both the resolve
and load
hooks? Because for user loaders, resolve
returning format
is optional; it’s load
that does the definitive determination of format
, to handle cases like an HTTPS loader where we don’t know the format until we get a network response with a Content-Type
header.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I interpreted this the same as Geoffrey—that resolve
's format is authoritative rather than suggestive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah this isn't really specifyin a resolve hook at this point so much as just specifying how to load overall. I wouldn't interpret resolve as being equivalent to any concept of a resolve hook here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t have a suggestion for how to do so, but if you can think of a way to make it clearer that this doesn’t correspond directly with the resolve
hook, I think that would make this better. The fact that the section heading is **ESM\_RESOLVE**(_specifier_, _parentURL_)
, when those are two of the arguments passed into the resolve
hook, seems to strongly imply a similarity.
Or maybe the spec could correlate with the resolve
and load
hooks, describing the hooks’ “default” behavior? Then when people write their own versions, they have a reference of how those hooks “should” be if they were to follow Node’s example. I guess that would mean splitting this section into two, one for resolve
and one for load
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way to think of this is more like HOST_RESOLVE_IMPORTED_MODULE
in being a single resolver that covers the requirements like it does in ECMA262, as opposed to any fine-grained hooks.
@guybedford cool, thanks for this 😀 @DerekNonGeneric thanks for the heads up! FYI @arcanis @jonaskello (I know you have raw versions of these utils in your stuff) |
resolved = | ||
pathToFileURL(real + (StringPrototypeEndsWith(path, sep) ? '/' : '')); | ||
resolved.search = search; | ||
resolved.hash = hash; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know I missed the ship (I was away this weekend): I think a code comment would be useful here, something like
resolved = | |
pathToFileURL(real + (StringPrototypeEndsWith(path, sep) ? '/' : '')); | |
resolved.search = search; | |
resolved.hash = hash; | |
} | |
// preserve the original extras onto the true fileUrl | |
resolved = | |
pathToFileURL(real + (StringPrototypeEndsWith(path, sep) ? '/' : '')); | |
resolved.search = search; | |
resolved.hash = hash; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed more comments are always good - let's follow up on this.
Also my apologies for not including the Yes boat has sailed on this one but comments are all good points, I've responded above. |
PR-URL: #40510 Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: James M Snell <[email protected]>
PR-URL: #40510 Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: James M Snell <[email protected]>
This PR includes the following hardening / refactorings to the ESM resolver & specification:
.
,..
andnode_modules
segments in package exports or package exports pattern replacements. This error checking is now extended to be case-insensitive and also check for percent encodings of these segments in the spec and resolver. Previouslyimport 'pkg/.%2e/.%2e/outside.js'
would be a way to bypass the restriction of only being able to resolve exports within the package (note this is not a security issue, because this is a usability / encapsulation feature and not a security feature)..
as invalid per the spec, that has been corrected with a regex check instead of a for loop.file:
scheme. We then indicate that for nonfile:
schemes the associated content type can be used without explicitly specifying the source. This effectively generalizes the resolver to specify thedata:
behaviours already implemented today and also paves the way in the resolver for other URL schemes in future. We don't currently specify the MIME types directly, although that could be done here as well.stream/web
would not have been detected as a valid builtin name since it would fail the valid package name test. Moving the builtin check higher up in the spec fixes this.ESM_FORMAT
spec function is extended to support the".json"
file type per the JSON assertions implementation work.