diff --git a/CHANGELOG.md b/CHANGELOG.md index de22b98fd823..02728ab400e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ### Fixes +- `[jest-runtime]` Support importing CJS from ESM using `import` statements ([#9850](https://github.com/facebook/jest/pull/9850)) + ### Chore & Maintenance ### Performance @@ -19,8 +21,8 @@ ### Fixes - `[expect]` Restore support for passing functions to `toHaveLength` matcher ([#9796](https://github.com/facebook/jest/pull/9796)) -- `[jest-circus]` Throw on nested test definitions ([#9828](https://github.com/facebook/jest/pull/9828)) - `[jest-changed-files]` `--only-changed` should include staged files ([#9799](https://github.com/facebook/jest/pull/9799)) +- `[jest-circus]` Throw on nested test definitions ([#9828](https://github.com/facebook/jest/pull/9828)) - `[jest-each]` `each` will throw an error when called with too many arguments ([#9818](https://github.com/facebook/jest/pull/9818)) - `[jest-runner]` Don't print warning to stdout when using `--json` ([#9843](https://github.com/facebook/jest/pull/9843)) diff --git a/e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap b/e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap index 14cb786f3ddb..be56a4394e76 100644 --- a/e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap +++ b/e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap @@ -2,7 +2,7 @@ exports[`on node >=12.16.0 runs test with native ESM 1`] = ` Test Suites: 1 passed, 1 total -Tests: 8 passed, 8 total +Tests: 9 passed, 9 total Snapshots: 0 total Time: <> Ran all test suites. diff --git a/e2e/native-esm/__tests__/native-esm.test.js b/e2e/native-esm/__tests__/native-esm.test.js index 5a406a9137c7..8f1800e57c12 100644 --- a/e2e/native-esm/__tests__/native-esm.test.js +++ b/e2e/native-esm/__tests__/native-esm.test.js @@ -10,6 +10,7 @@ import {createRequire} from 'module'; import {dirname, resolve} from 'path'; import {fileURLToPath} from 'url'; import staticImportedStateful from '../stateful.mjs'; +import staticImportedStatefulFromCjs from '../fromCjs.mjs'; import {double} from '../index'; test('should have correct import.meta', () => { @@ -78,6 +79,10 @@ test('import from mjs and import(mjs) should share caches', async () => { expect(staticImportedStateful()).toBe(6); }); +test('import cjs via import statement', () => { + expect(staticImportedStatefulFromCjs(4)).toBe(2); +}); + test('handle unlinked dynamic imports', async () => { const {double: deepDouble} = await import('../dynamicImport'); diff --git a/e2e/native-esm/fromCjs.mjs b/e2e/native-esm/fromCjs.mjs new file mode 100644 index 000000000000..6f625f8ff51e --- /dev/null +++ b/e2e/native-esm/fromCjs.mjs @@ -0,0 +1,8 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +export {default} from './commonjs.cjs'; diff --git a/packages/jest-resolve/src/shouldLoadAsEsm.ts b/packages/jest-resolve/src/shouldLoadAsEsm.ts index ca49e3fdda75..a1938e70d5e6 100644 --- a/packages/jest-resolve/src/shouldLoadAsEsm.ts +++ b/packages/jest-resolve/src/shouldLoadAsEsm.ts @@ -7,11 +7,11 @@ import {dirname, extname} from 'path'; // @ts-ignore: experimental, not added to the types -import {SourceTextModule} from 'vm'; +import {SyntheticModule} from 'vm'; import type {Config} from '@jest/types'; import readPkgUp = require('read-pkg-up'); -const runtimeSupportsVmModules = typeof SourceTextModule === 'function'; +const runtimeSupportsVmModules = typeof SyntheticModule === 'function'; const cachedFileLookups = new Map(); const cachedDirLookups = new Map(); diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index af6cc31ef0ec..001de2f98ec9 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -112,7 +112,7 @@ const EVAL_RESULT_VARIABLE = 'Object.'; type RunScriptEvalResult = {[EVAL_RESULT_VARIABLE]: ModuleWrapper}; -const runtimeSupportsVmModules = typeof SourceTextModule === 'function'; +const runtimeSupportsVmModules = typeof SyntheticModule === 'function'; /* eslint-disable-next-line no-redeclare */ class Runtime { @@ -351,27 +351,7 @@ class Runtime { const module = new SourceTextModule(transformedFile.code, { context, identifier: modulePath, - importModuleDynamically: ( - specifier: string, - referencingModule: VMModule, - ) => { - const resolved = this._resolveModule( - referencingModule.identifier, - specifier, - ); - if ( - this._resolver.isCoreModule(resolved) || - this.unstable_shouldLoadAsEsm(resolved) - ) { - return this.loadEsmModule(resolved); - } - - return this.loadCjsAsEsm( - referencingModule.identifier, - resolved, - context, - ); - }, + importModuleDynamically: this.linkModules.bind(this), initializeImportMeta(meta: ImportMeta) { meta.url = pathToFileURL(modulePath).href; }, @@ -379,11 +359,7 @@ class Runtime { this._esmoduleRegistry.set(cacheKey, module); - await module.link((specifier: string, referencingModule: VMModule) => - this.loadEsmModule( - this._resolveModule(referencingModule.identifier, specifier), - ), - ); + await module.link(this.linkModules.bind(this)); await module.evaluate(); } @@ -395,6 +371,25 @@ class Runtime { return module; } + private async linkModules(specifier: string, referencingModule: VMModule) { + const resolved = this._resolveModule( + referencingModule.identifier, + specifier, + ); + if ( + this._resolver.isCoreModule(resolved) || + this.unstable_shouldLoadAsEsm(resolved) + ) { + return this.loadEsmModule(resolved); + } + + return this.loadCjsAsEsm( + referencingModule.identifier, + resolved, + referencingModule.context, + ); + } + async unstable_importModule( from: Config.Path, moduleName?: string,