Skip to content

Commit

Permalink
fix(typescript-checker): resolve tsconfig files correctly
Browse files Browse the repository at this point in the history
In order to override options, like `noEmit: true`, the TypeScript checker tries to discover your referenced tsconfig files. It does this by parsing tsconfig and resolving the `projectReferences`. However, it always tried to resolved them from the `cwd` (with `path.resolve`).

I.e. this option overriding didn't work:

```
subdir/
├── child-a
│   └── tsconfig.json
├── child-b
│   └── tsconfig.json
└── tsconfig.json
```

Where you run Stryker with:

```json
{
  "tsconfig": "subdir/tsconfig.json"
}
```
  • Loading branch information
nicojs committed Mar 6, 2021
1 parent a664cba commit 8cf9e8c
Show file tree
Hide file tree
Showing 6 changed files with 25 additions and 21 deletions.
7 changes: 4 additions & 3 deletions packages/typescript-checker/src/tsconfig-helpers.ts
@@ -1,4 +1,4 @@
import { resolve } from 'path';
import path from 'path';

import ts from 'typescript';
import semver from 'semver';
Expand Down Expand Up @@ -65,10 +65,11 @@ export function overrideOptions(parsedConfig: { config?: any }, useBuildMode: bo
/**
* Retrieves the referenced config files based on parsed configuration
* @param parsedConfig The parsed config file
* @param fromDirName The directory where to resolve from
*/
export function retrieveReferencedProjects(parsedConfig: { config?: any }): string[] {
export function retrieveReferencedProjects(parsedConfig: { config?: any }, fromDirName: string): string[] {
if (Array.isArray(parsedConfig.config?.references)) {
return parsedConfig.config?.references.map((reference: any) => resolve(ts.resolveProjectReferencePath(reference)));
return parsedConfig.config?.references.map((reference: any) => path.resolve(fromDirName, ts.resolveProjectReferencePath(reference)));
}
return [];
}
2 changes: 1 addition & 1 deletion packages/typescript-checker/src/typescript-checker.ts
Expand Up @@ -159,7 +159,7 @@ export class TypescriptChecker implements Checker {
if (parsedConfig.error) {
return content; // let the ts compiler deal with this error
} else {
for (const referencedProject of retrieveReferencedProjects(parsedConfig)) {
for (const referencedProject of retrieveReferencedProjects(parsedConfig, path.dirname(fileName))) {
this.allTSConfigFiles.add(referencedProject);
}
return overrideOptions(parsedConfig, buildModeEnabled);
Expand Down
Expand Up @@ -23,8 +23,7 @@ describe('Typescript checker on a project with project references', () => {
let sut: TypescriptChecker;

beforeEach(() => {
process.chdir(resolveTestResource());
testInjector.options.tsconfigFile = 'tsconfig.root.json';
testInjector.options.tsconfigFile = resolveTestResource('tsconfig.root.json');
sut = testInjector.injector.injectFunction(createTypescriptChecker);
return sut.init();
});
Expand Down
Expand Up @@ -23,7 +23,7 @@ describe('Typescript checker on a single project', () => {
let sut: TypescriptChecker;

beforeEach(() => {
process.chdir(resolveTestResource());
testInjector.options.tsconfigFile = resolveTestResource('tsconfig.json');
sut = testInjector.injector.injectFunction(createTypescriptChecker);
return sut.init();
});
Expand Down
Expand Up @@ -4,7 +4,6 @@ import { testInjector } from '@stryker-mutator/test-helpers';
import { expect } from 'chai';

import { createTypescriptChecker } from '../../src';
import { TypescriptChecker } from '../../src/typescript-checker';

const resolveTestResource = (path.resolve.bind(
path,
Expand All @@ -17,24 +16,25 @@ const resolveTestResource = (path.resolve.bind(
) as unknown) as typeof path.resolve;

describe('Typescript checker errors', () => {
let sut: TypescriptChecker;

beforeEach(() => {
sut = testInjector.injector.injectFunction(createTypescriptChecker);
});

it('should reject initialization if initial compilation failed', async () => {
process.chdir(resolveTestResource('compile-error'));
await expect(sut.init()).rejectedWith('TypeScript error(s) found in dry run compilation: add.ts(2,3): error TS2322:');
testInjector.options.tsconfigFile = resolveTestResource('compile-error', 'tsconfig.json');
const sut = testInjector.injector.injectFunction(createTypescriptChecker);
await expect(sut.init()).rejectedWith(
'TypeScript error(s) found in dry run compilation: testResources/errors/compile-error/add.ts(2,3): error TS2322:'
);
});

it('should reject initialization if tsconfig was invalid', async () => {
process.chdir(resolveTestResource('invalid-tsconfig'));
await expect(sut.init()).rejectedWith('TypeScript error(s) found in dry run compilation: tsconfig.json(1,1): error TS1005:');
testInjector.options.tsconfigFile = resolveTestResource('invalid-tsconfig', 'tsconfig.json');
const sut = testInjector.injector.injectFunction(createTypescriptChecker);
await expect(sut.init()).rejectedWith(
'TypeScript error(s) found in dry run compilation: testResources/errors/invalid-tsconfig/tsconfig.json(1,1): error TS1005:'
);
});

it("should reject when tsconfig file doesn't exist", async () => {
process.chdir(resolveTestResource('empty-dir'));
testInjector.options.tsconfigFile = resolveTestResource('empty-dir', 'tsconfig.json');
const sut = testInjector.injector.injectFunction(createTypescriptChecker);
await expect(sut.init()).rejectedWith(
`The tsconfig file does not exist at: "${resolveTestResource(
'empty-dir',
Expand Down
Expand Up @@ -110,11 +110,15 @@ describe('typescript-helpers', () => {

describe(retrieveReferencedProjects.name, () => {
it('should result in an empty array when references are missing', () => {
expect(retrieveReferencedProjects({ config: { compilerOptions: {} } })).deep.eq([]);
expect(retrieveReferencedProjects({ config: { compilerOptions: {} } }, '')).deep.eq([]);
});

it('should retrieve referenced projects', () => {
expect(retrieveReferencedProjects({ config: { references: [{ path: 'some.json' }] } })).deep.eq([path.resolve('some.json')]);
expect(retrieveReferencedProjects({ config: { references: [{ path: 'some.json' }] } }, process.cwd())).deep.eq([path.resolve('some.json')]);
});

it('should resolve from given dirname', () => {
expect(retrieveReferencedProjects({ config: { references: [{ path: 'some.json' }] } }, 'a/b')).deep.eq([path.resolve('a', 'b', 'some.json')]);
});
});

Expand Down

0 comments on commit 8cf9e8c

Please sign in to comment.