You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I have 'smoke tested' the code to be tested by running it outside the real test suite to get a better sense of whether the problem is in the code under test, my usage of Mocha, or Mocha itself.
I want to provide a PR to resolve this
Expected
The recent changes in mocha@11.7.x for deciding whether to load a mocha test file as CommonJS or ESM should not use CommonJS for .mjs files. Specifically I mean for the code path that runs requireModule() in lib/nodejs/esm-utils.js.
All versions of Node.js that support require(esm)and have the additional module hook handling changes (e.g. Node.js ~22.15.0, some minor version of 24.x), will run custom module loader hooks for the imports in a file loaded by require(some-esm-file), but will NOT run the custom module loader hooks for the some-esm-file itself.
tl;dr: Even in the latest versions of Node.js, doing require("./some-esm-file.mjs") has different behaviour with custom module loaders compared to doing await import("./some-esm-file.mjs")
This is a small example with a test file that (a) uses .mjs to indicate to Node that it should be loaded as an ES module, and (b) depends on a loader hook (--experimental-loader=./hook.mjs) to be run when the test file is loaded for the tests to pass.
The README shows that this test passes when using mocha@11.6.0, but fails with mocha@11.7.x. You must run with Node.js v20.19.x or v22.14.0 to see both test cases fail. Running with v22.15.0 or later will fail one of the two test cases.
A believe a good fix would be to make this change:
diff --git a/lib/nodejs/esm-utils.js b/lib/nodejs/esm-utils.js
index 3cea8af2..773f0829 100644
--- a/lib/nodejs/esm-utils.js+++ b/lib/nodejs/esm-utils.js@@ -90,6 +90,10 @@ const tryImportAndRequire = async (file, esmDecorator) => {
// and `require.cache` effective, while allowing us to load ESM modules
// and CJS modules in the same way.
const requireModule = async (file, esmDecorator) => {
+ if (path.extname(file) === '.mjs') {+ return formattedImport(file, esmDecorator);+ }+
try {
return require(file);
} catch (err) {
This does the same ".mjs" guard as was already added for the tryImportAndRequire function in the same file. The idea is that the ".mjs" extension is a clear sign that the file should be loaded with import() and not with require().
Bug Report Checklist
faqlabel, but none matched my issue.Expected
The recent changes in mocha@11.7.x for deciding whether to load a mocha test file as CommonJS or ESM should not use CommonJS for
.mjsfiles. Specifically I mean for the code path that runsrequireModule()in lib/nodejs/esm-utils.js.Actual
"esm-utils.js" includes:
which leads to
require(file)being used to load a test file if running with a version of node that supports therequire(esm)feature. However:require(esm)support (and hence pass theif (process.features.require_module)condition), but do not include implement module.registerHooks() to run synchronous module customization hooks in thread nodejs/node#55698 (and possibly other Node.js module hook handling changes, I'm not perfectly clear on which Node.js commits are relevant). The result is that a custom module loader is not used for that loaded file, or the files it later imports.require(esm)and have the additional module hook handling changes (e.g. Node.js ~22.15.0, some minor version of 24.x), will run custom module loader hooks for theimports in a file loaded byrequire(some-esm-file), but will NOT run the custom module loader hooks for thesome-esm-fileitself.tl;dr: Even in the latest versions of Node.js, doing
require("./some-esm-file.mjs")has different behaviour with custom module loaders compared to doingawait import("./some-esm-file.mjs")Minimal, Reproducible Example
https://github.com/trentm/repro-mocha-11-mjs-import-hooks-issue
This is a small example with a test file that (a) uses
.mjsto indicate to Node that it should be loaded as an ES module, and (b) depends on a loader hook (--experimental-loader=./hook.mjs) to be run when the test file is loaded for the tests to pass.NODE_NO_WARNINGS=1 node --experimental-loader=./hook.mjs ./node_modules/.bin/mocha 'test/**/*.test.mjs'The README shows that this test passes when using mocha@11.6.0, but fails with mocha@11.7.x. You must run with Node.js v20.19.x or v22.14.0 to see both test cases fail. Running with v22.15.0 or later will fail one of the two test cases.
Versions
Additional Info
A believe a good fix would be to make this change:
This does the same ".mjs" guard as was already added for the
tryImportAndRequirefunction in the same file. The idea is that the ".mjs" extension is a clear sign that the file should be loaded withimport()and not withrequire().