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

TypeScript: Override some quality options #391

Closed
nicojs opened this issue Sep 22, 2017 · 2 comments
Closed

TypeScript: Override some quality options #391

nicojs opened this issue Sep 22, 2017 · 2 comments

Comments

@nicojs
Copy link
Member

nicojs commented Sep 22, 2017

When transpiling typescript code, we should override some of the compiler options designed for code quality. For example: if we now mutate an if statement to if(false) and the user has the code quality option allowUnreachableCode=false the mutant will not be tested and is treated as a false positive (basically).

I don't think this is what we want. If you think about it: indeed, nobody in their right mind would mutate an if statement like that and typescript is right to report a warning when done so, but at the same time, that is not what we want to test when we mutate the if-statement. We want tot see if your tests can detect the side effect of never going in the if statement.

See https://www.typescriptlang.org/docs/handbook/compiler-options.html for a list of compiler options. I think we should override the following:

  • allowUnreachableCode should be true
  • noUnusedLocals should be false
  • noUnusedParameters should be false

Another way to look at it: we're mutating code, of course the code quality will be less. That doesn't mean that the mutant is invalid.

@Mrna1
Copy link

Mrna1 commented Mar 22, 2019

@nicojs: Is it possible to add an overriding module from tsconfig?
For example:

For mutation I need
"module": "commonjs",
For compilation and deployment to our environment
"module": "esnext",

Thank you

@nicojs
Copy link
Member Author

nicojs commented Mar 26, 2019

Hi @Mrna1 , thanks for asking 😄

The easiest way is to use TS's extends property. So basically create a second tsconfig file: tsconfig.stryker.json and extends and override only the "module" option. Then specify that in your stryker.conf.js file.

For now it is not possible to override tsconfig options directly from Stryker. See #1437 for more info on that.

nicojs added a commit that referenced this issue Jun 18, 2020
Introducing the typescript type-checker plugin. 

## Features

👽 Type check each mutant. Invalid mutants will be marked as `CompileError` in your Stryker report.  
🧒 Easy to setup, only your `tsconfig.json` file is needed.  
🔢 Type check is done in-memory, no side effects on disk.  
🎁 Support for both single typescript projects as well as projects with project references (`--build` mode).  

## Configuring

You can configure the typescript checker in the `stryker.conf.js` (or `stryker.conf.json`) file.

```js
// stryker.conf.json
{
  "checkers": ["typescript"],
  "typescriptChecker": {
    "tsconfigFile": "tsconfig.json"
  }
}
```

### `typescriptChecker.tsconfigFile` [`string`]

Default: `'tsconfig.json'`

The path to your [tsconfig](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html). Project references _are supported_, `--build` mode will be enabled automatically when references are found in your tsconfig.json file.

## Implemenation

The `@stryker-mutator/typescript-checker` is not using the [typescript language service api](https://github.com/Microsoft/TypeScript/wiki/Using-the-Language-Service-API), like the current [typescript transpiler](https://github.com/stryker-mutator/stryker/blob/f44008993a543dc3f38ca99516f56d315fdcb735/packages/typescript/src/transpiler/TranspilingLanguageService.ts#L23) is. Instead, it's using the [compiler API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API). The language service API wasn't really designed for our use case. For example, it doesn't support project references, nor will it ever (read this somewhere, unfortunately, cannot find the source). It also took a lot of code to get working, with a pretty high number of bugs 🐛. Even in our own project, we needed separate `tsconfig.stryker.json` config files. Very annoying.

The compiler API is somewhat more low-level. We're starting the compiler on `initialize()` method of the checker API. If the initial build fails, we're rejecting the promise. Each mutant is checked basically the same way as it was in the old typescript transpiler. Namely, the previous mutant is removed, the new mutant is placed and the typescript compiler runs. If it errors, the `CheckStatus.CompilError` status is reported with the error text in the `reason` field.

There are some nifty tricks to be found:

1. During `initialize()` the typescript compiler starts. We're reading all files from disk synchronously and storing them in memory. After the initial build, all files should be in-memory, no further files are pulled in.
    * We're using the `ts.createSolutionBuilderWithWatch`. This creates a compiler with `--build` mode enabled. This seems to work for both single-project solutions as for a multi project solution with project references. This api is available since ts `3.6`, so we're throwing an error if a previous version of typescript is found.
1. The way to inform the typescript compiler that a file has changed is to "pretend" as if the file changed on disk. The file system watcher is informed in-memory of the change. This triggers a rebuild.
1. In order to still override compiler options as specified in #391, we're transforming each tsconfig.json file as it is read from disk. This way we can force the compiler options we want. The config file can also be named differently, each tsconfig file is scanned for `references` in order to make sure we're only transforming the tsconfig files that are used.
1. In order to ensure the best performance, `--noEmit` is forced. This will prevent the typescript compiler from outputting code. Unfortunately  `--noEmit` is forbidden for `--build` mode, which is why `--emitDeclarationsOnly` is forced for those projects.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants