-
Notifications
You must be signed in to change notification settings - Fork 29.1k
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
test_runner: mock_loader resolve the cjs and esm formats respectively. #53846
test_runner: mock_loader resolve the cjs and esm formats respectively. #53846
Conversation
Review requested:
|
Could you amend your commits to be |
I will do that. Thank you! |
bdfa404
to
94b1014
Compare
I changed to use specifier only when there is no parentURL. |
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 just got changed a few days ago in order to unflag detect-module
: #53619. Please let that land first, then rebase and ensure that your changes still work when detection is enabled by default.
lib/test/mock_loader.js
Outdated
const { defaultResolve } = require('internal/modules/esm/resolve'); | ||
const { | ||
defaultGetFormatWithoutErrors, | ||
} = require('internal/modules/esm/get_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.
It feels wrong to me that the mock loader would be using module internals directly rather than the public API. This interdependence will make it harder to refactor modules stuff since non-public changes might break the test runner.
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.
As far as I understand, mock_loader is an internal module resolver when using module mock. So this has to decide the format internally, is there an public API that can solve it? I'm sorry if it's something I haven't found. Thanks!
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.
You should be able to use the next hook in the chain, I'd think.
aa8fe42
to
bad16b3
Compare
test/fixtures/module-mocking/wrong-import-after-module-mocking.js
Outdated
Show resolved
Hide resolved
lib/test/mock_loader.js
Outdated
const req = createRequire(context.parentURL); | ||
specifier = pathToFileURL(req.resolve(specifier)).href; |
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.
As long as we're comfortable depending on internal things, we should skip the createRequire
call (which is quite heavy) and call directly the CJS resolver.
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 wanted to figure out how not to call createRequire, but this would be difficult because the existing cjs filenameResolve relies on a thoroughly generated require.
#53846 (comment)
If we think of a direction that does not depend on the internal module, I think this problem can also be solved by creating a new filename resolve for cjs and esm.
test/fixtures/module-mocking/wrong-import-after-module-mocking.js
Outdated
Show resolved
Hide resolved
@syi0808 If you can verify that this won't break with @nodejs/test_runner Is there a way to refactor the mock loader to not depend on modules internals? I'd prefer not to keep having issues with modules updates breaking the mock loader or test runner updates necessitating changes in modules code. The current implementation has one small reference to modules internals that I bet could be refactored out, and I feel like this PR could solve the referenced issue without needing internals. If it can't currently be done without internals, I think that's perhaps a sign that we should create new modules public APIs, like |
If you think it can be refactored to not use the module internals, that would be great. My feeling from working on the mock loader is that we may need some other public API. I know during development I found it frustrating trying to normalize all of the different possible inputs (path, file URL, file URL string, core, CJS, ESM, relative, absolute, etc.) into something consistent. |
Can we just have the user resolve before passing it in? Something like |
That seems like a worse DX IMO since it punts the problem to users. It also seems like functionality that at least some customization hooks authors will run into. IIRC, |
It is currently, but we could expose it. I think it's very low risk because it's essentially already public via |
If there's no disagreement with the new API coming out for I will test the |
I think if we make the new API it should be its own PR. But before that, are we sure this PR needs it? You have access to |
I think this PR is necessary. Basically, the purpose of fixing is as follows. Suppose you call a mock.module inside an ESM file. This will destroy the existing require behavior, regardless of which file format or package within the mock.module. This is because mock.module adds a hook, and inside the mock_loader, even though parentURL is in ESM format, it resolves like CJS. The import_loader breaks this despite the incorrect import format because the mock_loader hands over the resolved url to the nextResolve.
In the currently implementation, we cannot prevent this behavior. In order to resolve this, we have to rely on nextResolve, as you think inside the mock_loader, but the mock.module doesn't know what hooks will come before and after depending on the timing of the call. The purpose of resolve the specifier in mock_loader is to match the URL that was locked in
|
Is the mock loader trying to define a mock for when a specifier is required, imported, or both? Because of exports conditions, the same specifier might resolve as different things when it's imported or when it's required. I think it's hazard prone to try to guess which module system the user wants the mock for; I know it's less convenient, but requiring the user to pass in a URL instance would eliminate the ambiguity. |
I also don't want the user to guess the module system. I just didn't want the existing import behavior to be broken just by mocking the module. To be more specific, call CJS
But this case is still not covered. In conclusion, this pr is just exception handling. As you said, it would be nice to hand over this exception to the user, but if they use mock.module, they should pay attention to all import syntax. |
e8e47ad
to
4c9cc82
Compare
4c9cc82
to
45fb6b1
Compare
You're volunteering PRs so that makes you part of the core team 😄 If it's possible to achieve the fix you're aiming for in this PR without using modules internals, I would prefer such a solution. If not, it's better to fix a bug than leave it broken, and we can follow up with how to address the issues related to the mock loader. |
Sorry, I can't think of a way for the mock_loader to not dependent on the cjs and module format resolve method. Or shall we wait for this to find a better idea? |
I think the time has come to propose a new API that is merge before this is left unattended. I'm going to create a resolve function that integrates CJS and ESM. What do you think? @GeoffreyBooth |
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 have another approach that would simplify the loader code greatly IMO, but let's land this first, as it's easier to iterate on a follow-up PR than for me to make a very large suggestion in this PR
test/fixtures/module-mocking/wrong-import-after-module-mocking.js
Outdated
Show resolved
Hide resolved
Co-authored-by: Antoine du Hamel <[email protected]>
I don't know if the failed CI is related to this PR. |
Landed in 5bfebfe |
Are you going to open a follow-up PR for this? |
PR-URL: #53846 Fixes: #53807 Reviewed-By: Antoine du Hamel <[email protected]>
This issue was caused by recovering specifier in cjs resolve without format(esm,cjs) check in mock_loader.
So I did resolve logic differently for each format. And also added test.
Fixes: #53807