Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: repair caching of typescript compiler instances #13099

Merged
merged 2 commits into from Aug 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,7 @@

### Fixes

- `[jest-config]` Fix testing multiple projects with TypeScript config files ([#13099](https://github.com/facebook/jest/pull/13099))
- `[@jest/expect-utils]` Fix deep equality of ImmutableJS Record ([#13055](https://github.com/facebook/jest/pull/13055))
- `[jest-haste-map]` Increase the maximum possible file size that jest-haste-map can handle ([#13094](https://github.com/facebook/jest/pull/13094))
- `[jest-worker]` When a process runs out of memory worker exits correctly and doesn't spin indefinitely ([#13054](https://github.com/facebook/jest/pull/13054))
Expand Down
109 changes: 109 additions & 0 deletions 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('');
});
26 changes: 14 additions & 12 deletions packages/jest-config/src/readConfigFileAndSetRootDir.ts
Expand Up @@ -77,16 +77,14 @@ export default async function readConfigFileAndSetRootDir(
return configObject;
}

let registerer: Service;

// Load the TypeScript configuration
const loadTSConfigFile = async (
configPath: string,
): Promise<Config.InitialOptions> => {
// 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;

Expand All @@ -95,27 +93,31 @@ const loadTSConfigFile = async (
configObject = await configObject();
}

registerer.enabled(false);
registeredCompiler.enabled(false);

return configObject;
};

async function registerTsNode(): Promise<Service> {
if (registerer) {
return registerer;
}
let registeredCompilerPromise: Promise<Service>;

function getRegisteredCompiler() {
// Cache the promise to avoid multiple registrations
registeredCompilerPromise = registeredCompilerPromise ?? registerTsNode();
return registeredCompilerPromise;
}

async function registerTsNode(): Promise<Service> {
try {
// Register TypeScript compiler instance
const tsNode = await import('ts-node');
registerer = tsNode.register({
return tsNode.register({
compilerOptions: {
module: 'CommonJS',
},
moduleTypes: {
'**': 'cjs',
},
});
return registerer;
} catch (e: any) {
if (e.code === 'ERR_MODULE_NOT_FOUND') {
throw new Error(
Expand Down