-
Notifications
You must be signed in to change notification settings - Fork 251
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(typescript): Disable type checking (#2446)
Disable type checks by inserting `// @ts-nocheck` and removing all other `// @ts-xxx` directives. It can be configured with: ```js { "disableTypeChecks": "{test,src,lib}/**/*.{js,ts,jsx,tsx,html,vue}" // configure a pattern or `false` here "warnings": { "preprocessorErrors": true // enable/disable error logging here (default: true) } } ``` You can disable it completely by setting `disableTypeChecks` to `false`. The default is `"{test,src,lib}/**/*.{js,ts,jsx,tsx,html,vue}"`. The disabling of type checks is done in the `DisableTypeChecksPreprocessor` class. It calls the function in the `instrumenter` package to do the actual disabling. The reason for this is that files need to be parsed to distinguish between code and comments (`@ts-xxx` directives are only removed when they appear inside comments). It also works with HTML and vue files, so it also supports use cases where type checking is done inside vue files (with the vue-cli). Whenever parsing a file results in an error, a warning is logged and mutation testing will still proceed. The warning can be disabled by setting `warnings.preprocessorErrors` to `false`. This setting replaces both `sandbox.fileHeaders` and `sandbox.stripComments`, since those resulted in a lot of problems (see #2438 ).
- Loading branch information
Showing
35 changed files
with
890 additions
and
286 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,18 @@ | ||
import { tokens, Injector, commonTokens, PluginContext } from '@stryker-mutator/api/plugin'; | ||
|
||
import { disableTypeChecks } from '@stryker-mutator/instrumenter'; | ||
|
||
import { coreTokens } from '../di'; | ||
|
||
import { TSConfigPreprocessor } from './ts-config-preprocessor'; | ||
import { FileHeaderPreprocessor } from './file-header-preprocessor'; | ||
import { FilePreprocessor } from './file-preprocessor'; | ||
import { MultiPreprocessor } from './multi-preprocessor'; | ||
import { StripCommentsPreprocessor } from './strip-comments-preprocessor'; | ||
import { DisableTypeChecksPreprocessor } from './disable-type-checks-preprocessor'; | ||
|
||
createPreprocessor.inject = tokens(commonTokens.injector); | ||
export function createPreprocessor(injector: Injector<PluginContext>): FilePreprocessor { | ||
return new MultiPreprocessor([ | ||
injector.injectClass(StripCommentsPreprocessor), | ||
injector.provideValue(coreTokens.disableTypeChecksHelper, disableTypeChecks).injectClass(DisableTypeChecksPreprocessor), | ||
injector.injectClass(TSConfigPreprocessor), | ||
injector.injectClass(FileHeaderPreprocessor), | ||
]); | ||
} |
61 changes: 61 additions & 0 deletions
61
packages/core/src/sandbox/disable-type-checks-preprocessor.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import path = require('path'); | ||
|
||
import minimatch = require('minimatch'); | ||
import { commonTokens, tokens } from '@stryker-mutator/api/plugin'; | ||
import { File, StrykerOptions } from '@stryker-mutator/api/core'; | ||
import type { disableTypeChecks } from '@stryker-mutator/instrumenter'; | ||
import { Logger } from '@stryker-mutator/api/logging'; | ||
import { propertyPath, PropertyPathBuilder } from '@stryker-mutator/util'; | ||
|
||
import { coreTokens } from '../di'; | ||
import { isWarningEnabled } from '../utils/objectUtils'; | ||
|
||
import { FilePreprocessor } from './file-preprocessor'; | ||
|
||
/** | ||
* Disabled type checking by inserting `@ts-nocheck` atop TS/JS files and removing other @ts-xxx directives from comments: | ||
* @see https://github.com/stryker-mutator/stryker/issues/2438 | ||
*/ | ||
export class DisableTypeChecksPreprocessor implements FilePreprocessor { | ||
public static readonly inject = tokens(commonTokens.logger, commonTokens.options, coreTokens.disableTypeChecksHelper); | ||
constructor(private readonly log: Logger, private readonly options: StrykerOptions, private readonly impl: typeof disableTypeChecks) {} | ||
|
||
public async preprocess(files: File[]): Promise<File[]> { | ||
if (this.options.disableTypeChecks === false) { | ||
return files; | ||
} else { | ||
const pattern = path.resolve(this.options.disableTypeChecks); | ||
let warningLogged = false; | ||
const outFiles = await Promise.all( | ||
files.map(async (file) => { | ||
if (minimatch(path.resolve(file.name), pattern)) { | ||
try { | ||
return await this.impl(file, { plugins: this.options.mutator.plugins }); | ||
} catch (err) { | ||
if (isWarningEnabled('preprocessorErrors', this.options.warnings)) { | ||
warningLogged = true; | ||
this.log.warn( | ||
`Unable to disable type checking for file "${ | ||
file.name | ||
}". Shouldn't type checking be disabled for this file? Consider configuring a more restrictive "${propertyPath<StrykerOptions>( | ||
'disableTypeChecks' | ||
)}" settings (or turn it completely off with \`false\`)`, | ||
err | ||
); | ||
} | ||
return file; | ||
} | ||
} else { | ||
return file; | ||
} | ||
}) | ||
); | ||
if (warningLogged) { | ||
this.log.warn( | ||
`(disable "${PropertyPathBuilder.create<StrykerOptions>().prop('warnings').prop('preprocessorErrors')}" to ignore this warning` | ||
); | ||
} | ||
return outFiles; | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
packages/core/test/unit/sandbox/disable-type-checks-preprocessor.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import path = require('path'); | ||
|
||
import { File } from '@stryker-mutator/api/core'; | ||
import { assertions, testInjector } from '@stryker-mutator/test-helpers'; | ||
import sinon = require('sinon'); | ||
|
||
import { expect } from 'chai'; | ||
|
||
import { coreTokens } from '../../../src/di'; | ||
import { DisableTypeChecksPreprocessor } from '../../../src/sandbox/disable-type-checks-preprocessor'; | ||
|
||
describe(DisableTypeChecksPreprocessor.name, () => { | ||
let sut: DisableTypeChecksPreprocessor; | ||
let disableTypeCheckingStub: sinon.SinonStub; | ||
|
||
beforeEach(() => { | ||
disableTypeCheckingStub = sinon.stub(); | ||
sut = testInjector.injector.provideValue(coreTokens.disableTypeChecksHelper, disableTypeCheckingStub).injectClass(DisableTypeChecksPreprocessor); | ||
}); | ||
|
||
['.ts', '.tsx', '.js', '.jsx', '.html', '.vue'].forEach((extension) => { | ||
it(`should disable type checking a ${extension} file by default`, async () => { | ||
const fileName = `src/app${extension}`; | ||
const expectedFile = new File(path.resolve(fileName), 'output'); | ||
const inputFile = new File(path.resolve(fileName), 'input'); | ||
const input = [inputFile]; | ||
disableTypeCheckingStub.resolves(expectedFile); | ||
const output = await sut.preprocess(input); | ||
expect(disableTypeCheckingStub).calledWith(inputFile); | ||
assertions.expectTextFilesEqual(output, [expectedFile]); | ||
}); | ||
}); | ||
|
||
it('should be able to override "disableTypeChecks" glob pattern', async () => { | ||
testInjector.options.disableTypeChecks = 'src/**/*.ts'; | ||
const expectedFile = new File(path.resolve('src/app.ts'), 'output'); | ||
const input = [new File(path.resolve('src/app.ts'), 'input')]; | ||
disableTypeCheckingStub.resolves(expectedFile); | ||
const output = await sut.preprocess(input); | ||
assertions.expectTextFilesEqual(output, [expectedFile]); | ||
}); | ||
|
||
it('should not disable type checking when the "disableTypeChecks" glob pattern does not match', async () => { | ||
testInjector.options.disableTypeChecks = 'src/**/*.ts'; | ||
const expectedFiles = [new File(path.resolve('test/app.spec.ts'), 'input')]; | ||
disableTypeCheckingStub.resolves(new File('', 'not expected')); | ||
const output = await sut.preprocess(expectedFiles); | ||
assertions.expectTextFilesEqual(output, expectedFiles); | ||
}); | ||
|
||
it('should not disable type checking if "disableTypeChecks" is set to `false`', async () => { | ||
const input = [ | ||
new File(path.resolve('src/app.ts'), '// @ts-expect-error\nfoo.bar();'), | ||
new File(path.resolve('test/app.spec.ts'), '/* @ts-expect-error */\nfoo.bar();'), | ||
new File(path.resolve('testResources/project/app.ts'), '/* @ts-expect-error */\nfoo.bar();'), | ||
]; | ||
testInjector.options.disableTypeChecks = false; | ||
const output = await sut.preprocess(input); | ||
assertions.expectTextFilesEqual(output, input); | ||
}); | ||
|
||
it('should not crash on error, instead log a warning', async () => { | ||
const input = [new File('src/app.ts', 'input')]; | ||
const expectedError = new Error('Expected error for testing'); | ||
disableTypeCheckingStub.rejects(expectedError); | ||
const output = await sut.preprocess(input); | ||
expect(testInjector.logger.warn).calledWithExactly( | ||
'Unable to disable type checking for file "src/app.ts". Shouldn\'t type checking be disabled for this file? Consider configuring a more restrictive "disableTypeChecks" settings (or turn it completely off with `false`)', | ||
expectedError | ||
); | ||
expect(testInjector.logger.warn).calledWithExactly('(disable "warnings.preprocessorErrors" to ignore this warning'); | ||
assertions.expectTextFilesEqual(output, input); | ||
}); | ||
}); |
Oops, something went wrong.