diff --git a/CHANGELOG.md b/CHANGELOG.md index a1296b155310..77dbc93912c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- `[jest-runner, jest-runtime]` fix: `require.main` undefined with `createRequire()` ([#10610](https://github.com/facebook/jest/pull/10610)) - `[jest-validate]` Show suggestion only when unrecognized cli param is longer than 1 character ([#10604](https://github.com/facebook/jest/pull/10604)) - `[jest-validate]` Validate `testURL` as CLI option ([#10595](https://github.com/facebook/jest/pull/10595)) diff --git a/e2e/__tests__/requireMainAfterCreateRequire.test.ts b/e2e/__tests__/requireMainAfterCreateRequire.test.ts new file mode 100644 index 000000000000..6bd837366974 --- /dev/null +++ b/e2e/__tests__/requireMainAfterCreateRequire.test.ts @@ -0,0 +1,22 @@ +/** + * 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. + */ +import * as path from 'path'; +import {onNodeVersions} from '@jest/test-utils'; +import runJest from '../runJest'; + +onNodeVersions('>=12.2.0', () => { + test('`require.main` not undefined after createRequire', () => { + const {stdout} = runJest('require-main-after-create-require'); + + expect(stdout).toBe( + path.join( + __dirname, + '../require-main-after-create-require/__tests__/parent.test.js', + ), + ); + }); +}); diff --git a/e2e/require-main-after-create-require/__tests__/parent.test.js b/e2e/require-main-after-create-require/__tests__/parent.test.js new file mode 100644 index 000000000000..a3de637eb5e6 --- /dev/null +++ b/e2e/require-main-after-create-require/__tests__/parent.test.js @@ -0,0 +1,14 @@ +/** + * 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. + */ +const Module = require('module'); +const path = require('path'); + +test('require child from parent', () => { + // createRequire with a different file + const newRequire = Module.createRequire(path.resolve('./empty.js')); + expect(() => newRequire('./child')).not.toThrow(); +}); diff --git a/e2e/require-main-after-create-require/child.js b/e2e/require-main-after-create-require/child.js new file mode 100644 index 000000000000..6e8e5de6a885 --- /dev/null +++ b/e2e/require-main-after-create-require/child.js @@ -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. + */ +// Will throw if require.main is null +process.stdout.write(require.main.filename); diff --git a/e2e/require-main-after-create-require/empty.js b/e2e/require-main-after-create-require/empty.js new file mode 100644 index 000000000000..b6861b5fb248 --- /dev/null +++ b/e2e/require-main-after-create-require/empty.js @@ -0,0 +1,6 @@ +/** + * 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. + */ diff --git a/e2e/require-main-after-create-require/package.json b/e2e/require-main-after-create-require/package.json new file mode 100644 index 000000000000..eccb477ccc2b --- /dev/null +++ b/e2e/require-main-after-create-require/package.json @@ -0,0 +1,6 @@ +{ + "jest": { + "testEnvironment": "node" + } +} + diff --git a/packages/jest-runner/src/runTest.ts b/packages/jest-runner/src/runTest.ts index 36d4ebb03cd9..85aadd468329 100644 --- a/packages/jest-runner/src/runTest.ts +++ b/packages/jest-runner/src/runTest.ts @@ -151,15 +151,22 @@ async function runTestInternal( const cacheFS = {[path]: testSource}; setGlobal(environment.global, 'console', testConsole); - const runtime = new Runtime(config, environment, resolver, cacheFS, { - changedFiles: context?.changedFiles, - collectCoverage: globalConfig.collectCoverage, - collectCoverageFrom: globalConfig.collectCoverageFrom, - collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, - coverageProvider: globalConfig.coverageProvider, - sourcesRelatedToTestsInChangedFiles: - context?.sourcesRelatedToTestsInChangedFiles, - }); + const runtime = new Runtime( + config, + environment, + resolver, + cacheFS, + { + changedFiles: context?.changedFiles, + collectCoverage: globalConfig.collectCoverage, + collectCoverageFrom: globalConfig.collectCoverageFrom, + collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom, + coverageProvider: globalConfig.coverageProvider, + sourcesRelatedToTestsInChangedFiles: + context?.sourcesRelatedToTestsInChangedFiles, + }, + path, + ); const start = Date.now(); diff --git a/packages/jest-runtime/src/__mocks__/createRuntime.js b/packages/jest-runtime/src/__mocks__/createRuntime.js index 5c0864d5369a..c2d832e063d3 100644 --- a/packages/jest-runtime/src/__mocks__/createRuntime.js +++ b/packages/jest-runtime/src/__mocks__/createRuntime.js @@ -47,6 +47,9 @@ module.exports = async function createRuntime(filename, config) { config, environment, Runtime.createResolver(config, hasteMap.moduleMap), + undefined, + undefined, + filename, ); for (const path of config.setupFiles) { diff --git a/packages/jest-runtime/src/cli/index.ts b/packages/jest-runtime/src/cli/index.ts index 2f1a9b1ccd57..c49da6cce333 100644 --- a/packages/jest-runtime/src/cli/index.ts +++ b/packages/jest-runtime/src/cli/index.ts @@ -84,7 +84,14 @@ export async function run( setGlobal(environment.global, 'jestProjectConfig', config); setGlobal(environment.global, 'jestGlobalConfig', globalConfig); - const runtime = new Runtime(config, environment, hasteMap.resolver); + const runtime = new Runtime( + config, + environment, + hasteMap.resolver, + undefined, + undefined, + filePath, + ); for (const path of config.setupFiles) { // TODO: remove ? in Jest 26 diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index c03500e3f758..f079120bd20f 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -162,6 +162,7 @@ class Runtime { private _isolatedModuleRegistry: ModuleRegistry | null; private _moduleRegistry: ModuleRegistry; private _esmoduleRegistry: Map>; + private _testPath: Config.Path | undefined; private _resolver: Resolver; private _shouldAutoMock: boolean; private _shouldMockModuleCache: BooleanMap; @@ -184,6 +185,8 @@ class Runtime { resolver: Resolver, cacheFS: Record = {}, coverageOptions?: ShouldInstrumentOptions, + // TODO: Make mandatory in Jest 27 + testPath?: Config.Path, ) { this._cacheFS = new Map(Object.entries(cacheFS)); this._config = config; @@ -208,6 +211,7 @@ class Runtime { this._isolatedMockRegistry = null; this._moduleRegistry = new Map(); this._esmoduleRegistry = new Map(); + this._testPath = testPath; this._resolver = resolver; this._scriptTransformer = new ScriptTransformer(config); this._shouldAutoMock = config.automock; @@ -1365,17 +1369,7 @@ class Runtime { Object.defineProperty(moduleRequire, 'main', { enumerable: true, - get() { - let mainModule = from.parent; - while ( - mainModule && - mainModule.parent && - mainModule.id !== mainModule.parent.id - ) { - mainModule = mainModule.parent; - } - return mainModule; - }, + value: this._testPath ? this._moduleRegistry.get(this._testPath) : null, }); return moduleRequire; }