diff --git a/e2e/esm-load-order/__tests__/test.js b/e2e/esm-load-order/__tests__/test.js new file mode 100644 index 000000000000..420f2a671f7f --- /dev/null +++ b/e2e/esm-load-order/__tests__/test.js @@ -0,0 +1,4 @@ +import '../esm-package'; +import '../cjs-package'; + +it('load order is preserved', () => expect(Registrar['esm-package']).toEqual({})); diff --git a/e2e/esm-load-order/cjs-package/index.cjs b/e2e/esm-load-order/cjs-package/index.cjs new file mode 100644 index 000000000000..c1e01fd932db --- /dev/null +++ b/e2e/esm-load-order/cjs-package/index.cjs @@ -0,0 +1 @@ +require('../esm-package/index.cjs'); diff --git a/e2e/esm-load-order/cjs-package/package.json b/e2e/esm-load-order/cjs-package/package.json new file mode 100644 index 000000000000..dc6f3f4f88b1 --- /dev/null +++ b/e2e/esm-load-order/cjs-package/package.json @@ -0,0 +1,4 @@ +{ + "type": "commonjs", + "main": "./index.cjs" +} diff --git a/e2e/esm-load-order/esm-package/index.cjs b/e2e/esm-load-order/esm-package/index.cjs new file mode 100644 index 000000000000..ea0568263619 --- /dev/null +++ b/e2e/esm-load-order/esm-package/index.cjs @@ -0,0 +1 @@ +module.exports = Registrar['esm-package']; diff --git a/e2e/esm-load-order/esm-package/index.js b/e2e/esm-load-order/esm-package/index.js new file mode 100644 index 000000000000..4c19a9d1eb83 --- /dev/null +++ b/e2e/esm-load-order/esm-package/index.js @@ -0,0 +1,3 @@ +globalThis.Registrar = {}; + +Registrar['esm-package'] = {}; diff --git a/e2e/esm-load-order/esm-package/package.json b/e2e/esm-load-order/esm-package/package.json new file mode 100644 index 000000000000..6a930f659226 --- /dev/null +++ b/e2e/esm-load-order/esm-package/package.json @@ -0,0 +1,11 @@ +{ + "type": "module", + "exports": { + ".": { + "node": { + "require": "./index.cjs", + "import": "./index.js" + } + } + } +} diff --git a/e2e/esm-load-order/package.json b/e2e/esm-load-order/package.json new file mode 100644 index 000000000000..6c275ecd246a --- /dev/null +++ b/e2e/esm-load-order/package.json @@ -0,0 +1,8 @@ +{ + "type": "module", + "jest": { + "transform": {}, + "testEnvironment": "node", + "preserveLoadOrder": true + } +} diff --git a/packages/jest-config/src/Defaults.ts b/packages/jest-config/src/Defaults.ts index 7ec03aa39273..f542d0a971bf 100644 --- a/packages/jest-config/src/Defaults.ts +++ b/packages/jest-config/src/Defaults.ts @@ -64,6 +64,7 @@ const defaultOptions: Config.DefaultOptions = { notifyMode: 'failure-change', openHandlesTimeout: 1000, passWithNoTests: false, + preserveLoadOrder: false, prettierPath: 'prettier', resetMocks: false, resetModules: false, diff --git a/packages/jest-config/src/ValidConfig.ts b/packages/jest-config/src/ValidConfig.ts index 73a89e7333bf..6612b38e6df6 100644 --- a/packages/jest-config/src/ValidConfig.ts +++ b/packages/jest-config/src/ValidConfig.ts @@ -120,6 +120,7 @@ export const initialOptions: Config.InitialOptions = { onlyFailures: false, openHandlesTimeout: 1000, passWithNoTests: false, + preserveLoadOrder: false, preset: 'react-native', prettierPath: '/node_modules/prettier', projects: ['project-a', 'project-b/'], @@ -279,6 +280,7 @@ export const initialProjectOptions: Config.InitialProjectOptions = { modulePathIgnorePatterns: ['/build/'], modulePaths: ['/shared/vendor/modules'], openHandlesTimeout: 1000, + preserveLoadOrder: false, preset: 'react-native', prettierPath: '/node_modules/prettier', reporters: [ diff --git a/packages/jest-config/src/index.ts b/packages/jest-config/src/index.ts index 8523938629ca..398ee92edb31 100644 --- a/packages/jest-config/src/index.ts +++ b/packages/jest-config/src/index.ts @@ -116,6 +116,7 @@ const groupOptions = ( openHandlesTimeout: options.openHandlesTimeout, outputFile: options.outputFile, passWithNoTests: options.passWithNoTests, + preserveLoadOrder: options.preserveLoadOrder, projects: options.projects, randomize: options.randomize, replname: options.replname, @@ -178,6 +179,7 @@ const groupOptions = ( modulePathIgnorePatterns: options.modulePathIgnorePatterns, modulePaths: options.modulePaths, openHandlesTimeout: options.openHandlesTimeout, + preserveLoadOrder: options.preserveLoadOrder, prettierPath: options.prettierPath, reporters: options.reporters, resetMocks: options.resetMocks, diff --git a/packages/jest-config/src/normalize.ts b/packages/jest-config/src/normalize.ts index b317a0e86691..8fa05753cdbe 100644 --- a/packages/jest-config/src/normalize.ts +++ b/packages/jest-config/src/normalize.ts @@ -922,6 +922,7 @@ export default async function normalize( case 'openHandlesTimeout': case 'outputFile': case 'passWithNoTests': + case 'preserveLoadOrder': case 'randomize': case 'replname': case 'resetMocks': diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 4f1ca172f3a8..48a7a5acdc7f 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -758,23 +758,32 @@ export default class Runtime { } if (module.status === 'unlinked') { + const preserveLoadOrder = + this._config.preserveLoadOrder || this._globalConfig?.preserveLoadOrder; // ensure that every import fully evaluates before any siblings are allowed to import. - let linkPromiseChain = Promise.resolve(); + let linkPromiseChain = (Promise).resolve(); // since we might attempt to link the same module in parallel, stick the promise in a weak map so every call to // this method can await it this._esmModuleLinkingMap.set( module, module.link((specifier: string, referencingModule: VMModule) => { - linkPromiseChain = linkPromiseChain.then(async () => { - const mod = await this.resolveModule( - specifier, - referencingModule.identifier, - referencingModule.context - ); - await this.linkAndEvaluateModule(mod); - return mod; - }); - return linkPromiseChain; + if (preserveLoadOrder) { + linkPromiseChain = linkPromiseChain.then(async () => { + const mod = await this.resolveModule( + specifier, + referencingModule.identifier, + referencingModule.context, + ); + await this.linkAndEvaluateModule(mod); + return mod; + }); + return linkPromiseChain; + } + return this.resolveModule( + specifier, + referencingModule.identifier, + referencingModule.context, + ); }), ); } diff --git a/packages/jest-schemas/src/raw-types.ts b/packages/jest-schemas/src/raw-types.ts index f340582fc106..13659b70f3ad 100644 --- a/packages/jest-schemas/src/raw-types.ts +++ b/packages/jest-schemas/src/raw-types.ts @@ -288,6 +288,7 @@ export const RawInitialOptions = Type.Partial( outputFile: Type.String(), passWithNoTests: Type.Boolean(), preset: Type.Union([Type.String(), Type.Null()]), + preserveLoadOrder: Type.Boolean(), prettierPath: Type.Union([Type.String(), Type.Null()]), projects: Type.Array( Type.Union([ diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index 5055d386195e..b565d8655e0e 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -182,6 +182,7 @@ export type DefaultOptions = { notifyMode: NotifyMode; openHandlesTimeout: number; passWithNoTests: boolean; + preserveLoadOrder: boolean; prettierPath: string; resetMocks: boolean; resetModules: boolean; @@ -290,6 +291,7 @@ export type GlobalConfig = { openHandlesTimeout: number; passWithNoTests: boolean; projects: Array; + preserveLoadOrder: boolean; randomize?: boolean; replname?: string; reporters?: Array; @@ -357,6 +359,7 @@ export type ProjectConfig = { modulePaths?: Array; openHandlesTimeout: number; preset?: string; + preserveLoadOrder: boolean; prettierPath: string; reporters: Array; resetMocks: boolean; @@ -446,6 +449,7 @@ export type Argv = Arguments< onlyFailures: boolean; outputFile: string; preset: string | null | undefined; + preserveLoadOrder: boolean; prettierPath: string | null | undefined; projects: Array; randomize: boolean; diff --git a/packages/test-utils/src/config.ts b/packages/test-utils/src/config.ts index 383d0afc0768..d22966c054d1 100644 --- a/packages/test-utils/src/config.ts +++ b/packages/test-utils/src/config.ts @@ -43,6 +43,7 @@ const DEFAULT_GLOBAL_CONFIG: Config.GlobalConfig = { openHandlesTimeout: 1000, outputFile: undefined, passWithNoTests: false, + preserveLoadOrder: false, projects: [], replname: undefined, reporters: [], @@ -99,6 +100,7 @@ const DEFAULT_PROJECT_CONFIG: Config.ProjectConfig = { modulePathIgnorePatterns: [], modulePaths: [], openHandlesTimeout: 1000, + preserveLoadOrder: false, prettierPath: 'prettier', reporters: [ 'default',