diff --git a/e2e/__tests__/typescriptConfigFile.test.ts b/e2e/__tests__/typescriptConfigFile.test.ts new file mode 100644 index 000000000000..70d40dc2908a --- /dev/null +++ b/e2e/__tests__/typescriptConfigFile.test.ts @@ -0,0 +1,109 @@ +/** + * 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 {tmpdir} from 'os'; +import * as path from 'path'; +import {cleanup, writeFiles} from '../Utils'; +import runJest from '../runJest'; + +const DIR = path.resolve(tmpdir(), 'typescript-config-file'); + +beforeEach(() => cleanup(DIR)); +afterEach(() => cleanup(DIR)); + +test('works with single typescript config that imports something', () => { + writeFiles(DIR, { + '__tests__/mytest.alpha.js': "test('alpha', () => expect(1).toBe(1));", + '__tests__/mytest.common.js': "test('common', () => expect(1).toBe(1));", + 'alpha.config.ts': ` + import commonRegex from './common'; + export default { + testRegex: [ commonRegex, '__tests__/mytest.alpha.js' ] + };`, + 'common.ts': "export default '__tests__/mytest.common.js$';", + }); + + const {stdout, stderr, exitCode} = runJest( + DIR, + ['--projects', 'alpha.config.ts'], + { + skipPkgJsonCheck: true, + }, + ); + + expect(stderr).toContain('PASS __tests__/mytest.alpha.js'); + expect(stderr).toContain('PASS __tests__/mytest.common.js'); + expect(stderr).toContain('Test Suites: 2 passed, 2 total'); + expect(exitCode).toBe(0); + expect(stdout).toBe(''); +}); + +test('works with multiple typescript configs', () => { + writeFiles(DIR, { + '__tests__/mytest.alpha.js': "test('alpha', () => expect(1).toBe(1));", + '__tests__/mytest.beta.js': "test('beta', () => expect(1).toBe(1));", + 'alpha.config.ts': ` + export default { + testRegex: '__tests__/mytest.alpha.js' + };`, + 'beta.config.ts': ` + export default { + testRegex: '__tests__/mytest.beta.js' + };`, + }); + + const {stdout, stderr, exitCode} = runJest( + DIR, + ['--projects', 'alpha.config.ts', 'beta.config.ts'], + { + skipPkgJsonCheck: true, + }, + ); + + expect(stderr).toContain('PASS __tests__/mytest.alpha.js'); + expect(stderr).toContain('PASS __tests__/mytest.beta.js'); + expect(stderr).toContain('Test Suites: 2 passed, 2 total'); + expect(exitCode).toBe(0); + expect(stdout).toBe(''); +}); + +test('works with multiple typescript configs that import something', () => { + writeFiles(DIR, { + '__tests__/mytest.alpha.js': "test('alpha', () => expect(1).toBe(1));", + '__tests__/mytest.beta.js': "test('beta', () => expect(1).toBe(1));", + '__tests__/mytest.common.js': "test('common', () => expect(1).toBe(1));", + 'alpha.config.ts': ` + import commonRegex from './common'; + export default { + testRegex: [ commonRegex, '__tests__/mytest.alpha.js' ] + };`, + 'beta.config.ts': ` + import commonRegex from './common'; + export default { + testRegex: [ commonRegex, '__tests__/mytest.beta.js' ] + };`, + 'common.ts': "export default '__tests__/mytest.common.js$';", + }); + + const {stdout, stderr, exitCode} = runJest( + DIR, + ['--projects', 'alpha.config.ts', 'beta.config.ts'], + { + skipPkgJsonCheck: true, + }, + ); + + expect(stderr).toContain('PASS __tests__/mytest.alpha.js'); + expect(stderr).toContain('PASS __tests__/mytest.beta.js'); + expect(stderr).toContain('PASS __tests__/mytest.common.js'); + expect(stderr.replace('PASS __tests__/mytest.common.js', '')).toContain( + 'PASS __tests__/mytest.common.js', + ); + expect(stderr).toContain('Test Suites: 4 passed, 4 total'); + expect(exitCode).toBe(0); + expect(stdout).toBe(''); +}); diff --git a/packages/jest-config/src/readConfigFileAndSetRootDir.ts b/packages/jest-config/src/readConfigFileAndSetRootDir.ts index e4361c8c1f54..76d927cd9a20 100644 --- a/packages/jest-config/src/readConfigFileAndSetRootDir.ts +++ b/packages/jest-config/src/readConfigFileAndSetRootDir.ts @@ -77,16 +77,14 @@ export default async function readConfigFileAndSetRootDir( return configObject; } -let registerer: Service; - // Load the TypeScript configuration const loadTSConfigFile = async ( configPath: string, ): Promise => { - // Register TypeScript compiler instance - await registerTsNode(); + // Get registered TypeScript compiler instance + const registeredCompiler = await getRegisteredCompiler(); - registerer.enabled(true); + registeredCompiler.enabled(true); let configObject = interopRequireDefault(require(configPath)).default; @@ -95,19 +93,24 @@ const loadTSConfigFile = async ( configObject = await configObject(); } - registerer.enabled(false); + registeredCompiler.enabled(false); return configObject; }; -async function registerTsNode(): Promise { - if (registerer) { - return registerer; - } +let registeredCompilerPromise: Promise; + +const getRegisteredCompiler = async () => { + // Cache the promise to avoid multiple registrations + registeredCompilerPromise = registeredCompilerPromise ?? registerTsNode(); + return registeredCompilerPromise; +}; +const registerTsNode = async () => { try { + // Register TypeScript compiler instance const tsNode = await import('ts-node'); - registerer = tsNode.register({ + return tsNode.register({ compilerOptions: { module: 'CommonJS', }, @@ -115,7 +118,6 @@ async function registerTsNode(): Promise { '**': 'cjs', }, }); - return registerer; } catch (e: any) { if (e.code === 'ERR_MODULE_NOT_FOUND') { throw new Error( @@ -125,4 +127,4 @@ async function registerTsNode(): Promise { throw e; } -} +};