Skip to content

Commit

Permalink
fix: always load .mjs files via import (#15447)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB authored Jan 14, 2025
1 parent 1e60073 commit 43fedb5
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 41 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
- [**BREAKING**] `--testPathPattern` is now `--testPathPatterns`
- [**BREAKING**] Specifying `testPathPatterns` when programmatically calling `watch` must be specified as `new TestPathPatterns(patterns)`, where `TestPathPatterns` can be imported from `@jest/pattern`
- `[jest-reporters, jest-runner]` Unhandled errors without stack get correctly logged to console ([#14619](https://github.com/jestjs/jest/pull/14619))
- `[jest-util]` Always load `mjs` files with `import` ([#15447](https://github.com/jestjs/jest/pull/15447))
- `[jest-worker]` Properly handle a circular reference error when worker tries to send an assertion fails where either the expected or actual value is circular ([#15191](https://github.com/jestjs/jest/pull/15191))
- `[jest-worker]` Properly handle a BigInt when worker tries to send an assertion fails where either the expected or actual value is BigInt ([#15191](https://github.com/jestjs/jest/pull/15191))

Expand Down
4 changes: 3 additions & 1 deletion e2e/esm-config/js/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
* LICENSE file in the root directory of this source tree.
*/

const displayName = await Promise.resolve('Config from js file');

export default {
displayName: 'Config from js file',
displayName,
testEnvironment: 'node',
};
4 changes: 3 additions & 1 deletion e2e/esm-config/mjs/jest.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
* LICENSE file in the root directory of this source tree.
*/

const displayName = await Promise.resolve('Config from mjs file');

export default {
displayName: 'Config from mjs file',
displayName,
testEnvironment: 'node',
};
17 changes: 17 additions & 0 deletions packages/jest-config/src/__tests__/normalize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,23 @@ jest
...realFs,
statSync: () => ({isDirectory: () => true}),
};
})
.mock('jest-util', () => {
const realUtil =
jest.requireActual<typeof import('jest-util')>('jest-util');

return {
...realUtil,
requireOrImportModule: (filePath: string, interop = true) => {
const result = require(filePath);

if (interop) {
return realUtil.interopRequireDefault(result).default;
}

return result;
},
};
});

let root: string;
Expand Down
70 changes: 42 additions & 28 deletions packages/jest-util/src/requireOrImportModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,39 @@ import {isAbsolute} from 'path';
import {pathToFileURL} from 'url';
import interopRequireDefault from './interopRequireDefault';

async function importModule(
filePath: string,
applyInteropRequireDefault: boolean,
) {
try {
const moduleUrl = pathToFileURL(filePath);

// node `import()` supports URL, but TypeScript doesn't know that
const importedModule = await import(
/* webpackIgnore: true */ moduleUrl.href
);

if (!applyInteropRequireDefault) {
return importedModule;
}

if (!importedModule.default) {
throw new Error(
`Jest: Failed to load ESM at ${filePath} - did you use a default export?`,
);
}

return importedModule.default;
} catch (error: any) {
if (error.message === 'Not supported') {
throw new Error(
`Jest: Your version of Node does not support dynamic import - please enable it or use a .cjs file extension for file ${filePath}`,
);
}
throw error;
}
}

export default async function requireOrImportModule<T>(
filePath: string,
applyInteropRequireDefault = true,
Expand All @@ -19,40 +52,21 @@ export default async function requireOrImportModule<T>(
);
}
try {
if (filePath.endsWith('.mjs')) {
return importModule(filePath, applyInteropRequireDefault);
}

const requiredModule = require(filePath);
if (!applyInteropRequireDefault) {
return requiredModule;
}
return interopRequireDefault(requiredModule).default;
} catch (error: any) {
if (error.code === 'ERR_REQUIRE_ESM') {
try {
const moduleUrl = pathToFileURL(filePath);

// node `import()` supports URL, but TypeScript doesn't know that
const importedModule = await import(
/* webpackIgnore: true */ moduleUrl.href
);

if (!applyInteropRequireDefault) {
return importedModule;
}

if (!importedModule.default) {
throw new Error(
`Jest: Failed to load ESM at ${filePath} - did you use a default export?`,
);
}

return importedModule.default;
} catch (innerError: any) {
if (innerError.message === 'Not supported') {
throw new Error(
`Jest: Your version of Node does not support dynamic import - please enable it or use a .cjs file extension for file ${filePath}`,
);
}
throw innerError;
}
if (
error.code === 'ERR_REQUIRE_ESM' ||
error.code === 'ERR_REQUIRE_ASYNC_MODULE'
) {
return importModule(filePath, applyInteropRequireDefault);
} else {
throw error;
}
Expand Down
22 changes: 11 additions & 11 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10555,8 +10555,8 @@ __metadata:
linkType: hard

"express@npm:^4.17.3":
version: 4.21.1
resolution: "express@npm:4.21.1"
version: 4.21.2
resolution: "express@npm:4.21.2"
dependencies:
accepts: ~1.3.8
array-flatten: 1.1.1
Expand All @@ -10577,7 +10577,7 @@ __metadata:
methods: ~1.1.2
on-finished: 2.4.1
parseurl: ~1.3.3
path-to-regexp: 0.1.10
path-to-regexp: 0.1.12
proxy-addr: ~2.0.7
qs: 6.13.0
range-parser: ~1.2.1
Expand All @@ -10589,7 +10589,7 @@ __metadata:
type-is: ~1.6.18
utils-merge: 1.0.1
vary: ~1.1.2
checksum: 5ac2b26d8aeddda5564fc0907227d29c100f90c0ead2ead9d474dc5108e8fb306c2de2083c4e3ba326e0906466f2b73417dbac16961f4075ff9f03785fd940fe
checksum: 3aef1d355622732e20b8f3a7c112d4391d44e2131f4f449e1f273a309752a41abfad714e881f177645517cbe29b3ccdc10b35e7e25c13506114244a5b72f549d
languageName: node
linkType: hard

Expand Down Expand Up @@ -16032,11 +16032,11 @@ __metadata:
linkType: hard

"nanoid@npm:^3.3.7":
version: 3.3.7
resolution: "nanoid@npm:3.3.7"
version: 3.3.8
resolution: "nanoid@npm:3.3.8"
bin:
nanoid: bin/nanoid.cjs
checksum: d36c427e530713e4ac6567d488b489a36582ef89da1d6d4e3b87eded11eb10d7042a877958c6f104929809b2ab0bafa17652b076cdf84324aa75b30b722204f2
checksum: dfe0adbc0c77e9655b550c333075f51bb28cfc7568afbf3237249904f9c86c9aaaed1f113f0fddddba75673ee31c758c30c43d4414f014a52a7a626efc5958c9
languageName: node
linkType: hard

Expand Down Expand Up @@ -17047,10 +17047,10 @@ __metadata:
languageName: node
linkType: hard

"path-to-regexp@npm:0.1.10":
version: 0.1.10
resolution: "path-to-regexp@npm:0.1.10"
checksum: ab7a3b7a0b914476d44030340b0a65d69851af2a0f33427df1476100ccb87d409c39e2182837a96b98fb38c4ef2ba6b87bdad62bb70a2c153876b8061760583c
"path-to-regexp@npm:0.1.12":
version: 0.1.12
resolution: "path-to-regexp@npm:0.1.12"
checksum: ab237858bee7b25ecd885189f175ab5b5161e7b712b360d44f5c4516b8d271da3e4bf7bf0a7b9153ecb04c7d90ce8ff5158614e1208819cf62bac2b08452722e
languageName: node
linkType: hard

Expand Down

0 comments on commit 43fedb5

Please sign in to comment.