-
-
Notifications
You must be signed in to change notification settings - Fork 3k
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
fix: resolve ERR_REQUIRE_CYCLE_MODULE for never installed modules #5294
base: main
Are you sure you want to change the base?
Conversation
…chajs#5290) Use require to handle both ESM and CJS in the latest Node.js due to loadESMFromCJS function.
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.
Thanks for getting this started! 🚀
} | ||
} | ||
}; | ||
|
||
|
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.
[Style] Nit: unrelated change, let's revert.
@JoshuaKGoldberg I have looked in to the test cases. The only test case that fails : node js consider this as Ambiguous file but from versions >=node 2.7 this is not the case. https://nodejs.org/en/blog/release/v22.7.0#module-syntax-detection-is-now-enabled-by-default the function doesn't handle this case and returns an ERR_REQUIRE_CYCLE_MODULE Error, the test case just expects this error and passes. but it is not the actual expected behaviour. i jsut did a camparison which can be found below : https://docs.google.com/spreadsheets/d/1T9AsIWK5tLsnqicjGuh9miyPGetemOgxLzGzAK3ZBJA/edit?usp=sharing please review and feedback so that I can start updating the test case |
😄 It's been a while since I've seen test cases sent in a spreadsheet. Those seem reasonable, thanks for thinking so deeply on them. It's hard to say for sure until we see them implemented in PR review though. |
…hey are no longer considered ambiguous after Node.js 22.7.
I also wanted to add that the test case to produce the specific issue #5920 is already present in the code: https://github.com/mochajs/mocha/blob/main/test/integration/esm.spec.js It can be reproduced by running with v22.12.0 |
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.
Thanks for continuing on this! I have some questions.
// In Node.js environments after version 22.11, the `loadESMFromCJS` function | ||
// is used within the `require` function to handle cases where the target file | ||
// is in ESM (ECMAScript Module) format. If the Node.js version is after 22.11, |
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.
[Docs] loadESMFromCJS
is an internal implementation of Node.js. Nothing would stop them from renaming it or refactoring to something else. We in userland don't really care about it anyway; we just care about the behavior of Node.js across versions:
// In Node.js environments after version 22.11, the `loadESMFromCJS` function | |
// is used within the `require` function to handle cases where the target file | |
// is in ESM (ECMAScript Module) format. If the Node.js version is after 22.11, | |
// When trying to require() an ESM module in Node.js >= 22.11, |
// the code will import the module without any issues. For older versions, | ||
// this `if` statement is required to properly handle ESM modules. |
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.
[Docs] Phrases like "required to properly handle ..." are often indications the docs are unclear. The fact that the code's author believed it required to handle something is implied by the code existing. What readers really need to know is why it's required.
// the code will import the module without any issues. For older versions, | |
// this `if` statement is required to properly handle ESM modules. | |
// the code will import the module without any issues. | |
// For older Node.js versions, we fall back to trying an ESM `import()`. | |
// Note that the `import()` must come after the `require()` to avoid a cyclic dependency. | |
// See https://github.com/mochajs/mocha/issues/5290. |
// This `if` statement can be removed once all Node.js environments with current | ||
// support include the `loadESMFromCJS` function. |
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 `if` statement can be removed once all Node.js environments with current | |
// support include the `loadESMFromCJS` function. | |
// This `if` statement can be removed once Mocha only supports Node.js versions | |
// that allow require(ESM): Node.js ^20.19.0 || >=22.12.0 |
That 20.19.0
is a guess, the wonderful nodejs/node#56927 hasn't been released yet.
(requireErr instanceof SyntaxError && | ||
requireErr | ||
.toString() | ||
.includes('Cannot use import statement outside a module')) |
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.
[Question] Why not go off of requireErr.code
the way the other checks do?
If there is some reason why it should go off other properties in the error, I'd think checking just requireErr.message
would be preferable to stringifying the whole thing. In theory someone could name a file test/Cannot use import statement outside a module.just-to-be-annoying.spec.js
.
} catch (err) { | ||
throw err; | ||
} |
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.
[Question] Why a catch(err)
that only has a throw err
?
const noDetectModuleRegex = /SyntaxError: Unexpected token/; | ||
const detectModuleRegex = /Cannot require\(\) ES Module/; | ||
|
||
it('with --no-experimental-detect-module', function () { |
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.
[Testing] Why remove the --no-experimental-detect-module
tests? That's still something we'll have to support.
err.code === 'ERR_MODULE_NOT_FOUND' || | ||
err.code === 'ERR_UNKNOWN_FILE_EXTENSION' || | ||
err.code === 'ERR_UNSUPPORTED_DIR_IMPORT' |
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.
[Testing] This code explicitly handles modules not being found, unknown file extensions, and unsupported ESM directory import errors. For each of those three codes, could you please either reply with the existing test or add a new one? I just want to make sure we're on the same page of what test coverage there is.
PR Checklist
status: accepting prs
Overview
The requireOrImport function was originally set up to try importing a file as an ES module first, and if that failed, it would fall back to require. However, since Node.js version 22.12, the require function now automatically handles both ES modules (ESM) and CommonJS (CJS). This caused an issue where calling require after a failed import could lead to a cyclic dependency error.
in the updated code I have used require first and if fails with requireErr.code === 'ERR_REQUIRE_ESM' which only happens if version is <22.12.0 and and target file is esm ,then use the older method.
https://nodejs.org/en/blog/release/v22.12.0
https://joyeecheung.github.io/blog/2024/03/18/require-esm-in-node-js