@d-es-ign/stryker-js-instrumenter 9.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/src/create-instrumenter.d.ts +16 -0
- package/dist/src/create-instrumenter.d.ts.map +1 -0
- package/dist/src/create-instrumenter.js +15 -0
- package/dist/src/create-instrumenter.js.map +1 -0
- package/dist/src/disable-type-checks.d.ts +11 -0
- package/dist/src/disable-type-checks.d.ts.map +1 -0
- package/dist/src/disable-type-checks.js +125 -0
- package/dist/src/disable-type-checks.js.map +1 -0
- package/dist/src/file.d.ts +6 -0
- package/dist/src/file.d.ts.map +1 -0
- package/dist/src/file.js +2 -0
- package/dist/src/file.js.map +1 -0
- package/dist/src/frameworks/angular-ignorer.d.ts +16 -0
- package/dist/src/frameworks/angular-ignorer.d.ts.map +1 -0
- package/dist/src/frameworks/angular-ignorer.js +85 -0
- package/dist/src/frameworks/angular-ignorer.js.map +1 -0
- package/dist/src/frameworks/index.d.ts +4 -0
- package/dist/src/frameworks/index.d.ts.map +1 -0
- package/dist/src/frameworks/index.js +7 -0
- package/dist/src/frameworks/index.js.map +1 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +8 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/instrument-result.d.ts +7 -0
- package/dist/src/instrument-result.d.ts.map +1 -0
- package/dist/src/instrument-result.js +2 -0
- package/dist/src/instrument-result.js.map +1 -0
- package/dist/src/instrumenter-options.d.ts +5 -0
- package/dist/src/instrumenter-options.d.ts.map +1 -0
- package/dist/src/instrumenter-options.js +2 -0
- package/dist/src/instrumenter-options.js.map +1 -0
- package/dist/src/instrumenter-tokens.d.ts +6 -0
- package/dist/src/instrumenter-tokens.d.ts.map +1 -0
- package/dist/src/instrumenter-tokens.js +6 -0
- package/dist/src/instrumenter-tokens.js.map +1 -0
- package/dist/src/instrumenter.d.ts +24 -0
- package/dist/src/instrumenter.d.ts.map +1 -0
- package/dist/src/instrumenter.js +76 -0
- package/dist/src/instrumenter.js.map +1 -0
- package/dist/src/mutant-placers/expression-mutant-placer.d.ts +10 -0
- package/dist/src/mutant-placers/expression-mutant-placer.d.ts.map +1 -0
- package/dist/src/mutant-placers/expression-mutant-placer.js +144 -0
- package/dist/src/mutant-placers/expression-mutant-placer.js.map +1 -0
- package/dist/src/mutant-placers/index.d.ts +5 -0
- package/dist/src/mutant-placers/index.d.ts.map +1 -0
- package/dist/src/mutant-placers/index.js +11 -0
- package/dist/src/mutant-placers/index.js.map +1 -0
- package/dist/src/mutant-placers/mutant-placer.d.ts +8 -0
- package/dist/src/mutant-placers/mutant-placer.d.ts.map +1 -0
- package/dist/src/mutant-placers/mutant-placer.js +2 -0
- package/dist/src/mutant-placers/mutant-placer.js.map +1 -0
- package/dist/src/mutant-placers/statement-mutant-placer.d.ts +8 -0
- package/dist/src/mutant-placers/statement-mutant-placer.d.ts.map +1 -0
- package/dist/src/mutant-placers/statement-mutant-placer.js +24 -0
- package/dist/src/mutant-placers/statement-mutant-placer.js.map +1 -0
- package/dist/src/mutant-placers/switch-case-mutant-placer.d.ts +14 -0
- package/dist/src/mutant-placers/switch-case-mutant-placer.d.ts.map +1 -0
- package/dist/src/mutant-placers/switch-case-mutant-placer.js +29 -0
- package/dist/src/mutant-placers/switch-case-mutant-placer.js.map +1 -0
- package/dist/src/mutant-placers/throw-placement-error.d.ts +5 -0
- package/dist/src/mutant-placers/throw-placement-error.d.ts.map +1 -0
- package/dist/src/mutant-placers/throw-placement-error.js +17 -0
- package/dist/src/mutant-placers/throw-placement-error.js.map +1 -0
- package/dist/src/mutant.d.ts +27 -0
- package/dist/src/mutant.d.ts.map +1 -0
- package/dist/src/mutant.js +80 -0
- package/dist/src/mutant.js.map +1 -0
- package/dist/src/mutators/arithmetic-operator-mutator.d.ts +3 -0
- package/dist/src/mutators/arithmetic-operator-mutator.d.ts.map +1 -0
- package/dist/src/mutators/arithmetic-operator-mutator.js +34 -0
- package/dist/src/mutators/arithmetic-operator-mutator.js.map +1 -0
- package/dist/src/mutators/array-declaration-mutator.d.ts +3 -0
- package/dist/src/mutators/array-declaration-mutator.d.ts.map +1 -0
- package/dist/src/mutators/array-declaration-mutator.js +26 -0
- package/dist/src/mutators/array-declaration-mutator.js.map +1 -0
- package/dist/src/mutators/arrow-function-mutator.d.ts +3 -0
- package/dist/src/mutators/arrow-function-mutator.d.ts.map +1 -0
- package/dist/src/mutators/arrow-function-mutator.js +14 -0
- package/dist/src/mutators/arrow-function-mutator.js.map +1 -0
- package/dist/src/mutators/assignment-operator-mutator.d.ts +3 -0
- package/dist/src/mutators/assignment-operator-mutator.d.ts.map +1 -0
- package/dist/src/mutators/assignment-operator-mutator.js +42 -0
- package/dist/src/mutators/assignment-operator-mutator.js.map +1 -0
- package/dist/src/mutators/block-statement-mutator.d.ts +3 -0
- package/dist/src/mutators/block-statement-mutator.d.ts.map +1 -0
- package/dist/src/mutators/block-statement-mutator.js +63 -0
- package/dist/src/mutators/block-statement-mutator.js.map +1 -0
- package/dist/src/mutators/boolean-literal-mutator.d.ts +3 -0
- package/dist/src/mutators/boolean-literal-mutator.d.ts.map +1 -0
- package/dist/src/mutators/boolean-literal-mutator.js +17 -0
- package/dist/src/mutators/boolean-literal-mutator.js.map +1 -0
- package/dist/src/mutators/conditional-expression-mutator.d.ts +3 -0
- package/dist/src/mutators/conditional-expression-mutator.d.ts.map +1 -0
- package/dist/src/mutators/conditional-expression-mutator.js +81 -0
- package/dist/src/mutators/conditional-expression-mutator.js.map +1 -0
- package/dist/src/mutators/equality-operator-mutator.d.ts +3 -0
- package/dist/src/mutators/equality-operator-mutator.d.ts.map +1 -0
- package/dist/src/mutators/equality-operator-mutator.js +28 -0
- package/dist/src/mutators/equality-operator-mutator.js.map +1 -0
- package/dist/src/mutators/index.d.ts +4 -0
- package/dist/src/mutators/index.d.ts.map +1 -0
- package/dist/src/mutators/index.js +4 -0
- package/dist/src/mutators/index.js.map +1 -0
- package/dist/src/mutators/logical-operator-mutator.d.ts +3 -0
- package/dist/src/mutators/logical-operator-mutator.d.ts.map +1 -0
- package/dist/src/mutators/logical-operator-mutator.js +21 -0
- package/dist/src/mutators/logical-operator-mutator.js.map +1 -0
- package/dist/src/mutators/method-expression-mutator.d.ts +3 -0
- package/dist/src/mutators/method-expression-mutator.d.ts.map +1 -0
- package/dist/src/mutators/method-expression-mutator.js +65 -0
- package/dist/src/mutators/method-expression-mutator.js.map +1 -0
- package/dist/src/mutators/mutate.d.ts +3 -0
- package/dist/src/mutators/mutate.d.ts.map +1 -0
- package/dist/src/mutators/mutate.js +35 -0
- package/dist/src/mutators/mutate.js.map +1 -0
- package/dist/src/mutators/mutator-options.d.ts +5 -0
- package/dist/src/mutators/mutator-options.d.ts.map +1 -0
- package/dist/src/mutators/mutator-options.js +2 -0
- package/dist/src/mutators/mutator-options.js.map +1 -0
- package/dist/src/mutators/node-mutator.d.ts +6 -0
- package/dist/src/mutators/node-mutator.d.ts.map +1 -0
- package/dist/src/mutators/node-mutator.js +2 -0
- package/dist/src/mutators/node-mutator.js.map +1 -0
- package/dist/src/mutators/object-literal-mutator.d.ts +3 -0
- package/dist/src/mutators/object-literal-mutator.d.ts.map +1 -0
- package/dist/src/mutators/object-literal-mutator.js +11 -0
- package/dist/src/mutators/object-literal-mutator.js.map +1 -0
- package/dist/src/mutators/optional-chaining-mutator.d.ts +15 -0
- package/dist/src/mutators/optional-chaining-mutator.d.ts.map +1 -0
- package/dist/src/mutators/optional-chaining-mutator.js +28 -0
- package/dist/src/mutators/optional-chaining-mutator.js.map +1 -0
- package/dist/src/mutators/regex-mutator.d.ts +3 -0
- package/dist/src/mutators/regex-mutator.d.ts.map +1 -0
- package/dist/src/mutators/regex-mutator.js +53 -0
- package/dist/src/mutators/regex-mutator.js.map +1 -0
- package/dist/src/mutators/string-literal-mutator.d.ts +3 -0
- package/dist/src/mutators/string-literal-mutator.d.ts.map +1 -0
- package/dist/src/mutators/string-literal-mutator.js +36 -0
- package/dist/src/mutators/string-literal-mutator.js.map +1 -0
- package/dist/src/mutators/unary-operator-mutator.d.ts +3 -0
- package/dist/src/mutators/unary-operator-mutator.d.ts.map +1 -0
- package/dist/src/mutators/unary-operator-mutator.js +27 -0
- package/dist/src/mutators/unary-operator-mutator.js.map +1 -0
- package/dist/src/mutators/update-operator-mutator.d.ts +3 -0
- package/dist/src/mutators/update-operator-mutator.d.ts.map +1 -0
- package/dist/src/mutators/update-operator-mutator.js +17 -0
- package/dist/src/mutators/update-operator-mutator.js.map +1 -0
- package/dist/src/parsers/create-parser.d.ts +5 -0
- package/dist/src/parsers/create-parser.d.ts.map +1 -0
- package/dist/src/parsers/create-parser.js +58 -0
- package/dist/src/parsers/create-parser.js.map +1 -0
- package/dist/src/parsers/html-parser.d.ts +4 -0
- package/dist/src/parsers/html-parser.d.ts.map +1 -0
- package/dist/src/parsers/html-parser.js +96 -0
- package/dist/src/parsers/html-parser.js.map +1 -0
- package/dist/src/parsers/index.d.ts +5 -0
- package/dist/src/parsers/index.d.ts.map +1 -0
- package/dist/src/parsers/index.js +3 -0
- package/dist/src/parsers/index.js.map +1 -0
- package/dist/src/parsers/js-parser.d.ts +4 -0
- package/dist/src/parsers/js-parser.d.ts.map +1 -0
- package/dist/src/parsers/js-parser.js +50 -0
- package/dist/src/parsers/js-parser.js.map +1 -0
- package/dist/src/parsers/parse-error.d.ts +5 -0
- package/dist/src/parsers/parse-error.d.ts.map +1 -0
- package/dist/src/parsers/parse-error.js +6 -0
- package/dist/src/parsers/parse-error.js.map +1 -0
- package/dist/src/parsers/parser-context.d.ts +6 -0
- package/dist/src/parsers/parser-context.d.ts.map +1 -0
- package/dist/src/parsers/parser-context.js +2 -0
- package/dist/src/parsers/parser-context.js.map +1 -0
- package/dist/src/parsers/parser-options.d.ts +4 -0
- package/dist/src/parsers/parser-options.d.ts.map +1 -0
- package/dist/src/parsers/parser-options.js +2 -0
- package/dist/src/parsers/parser-options.js.map +1 -0
- package/dist/src/parsers/svelte-parser.d.ts +4 -0
- package/dist/src/parsers/svelte-parser.d.ts.map +1 -0
- package/dist/src/parsers/svelte-parser.js +169 -0
- package/dist/src/parsers/svelte-parser.js.map +1 -0
- package/dist/src/parsers/ts-parser.d.ts +9 -0
- package/dist/src/parsers/ts-parser.d.ts.map +1 -0
- package/dist/src/parsers/ts-parser.js +54 -0
- package/dist/src/parsers/ts-parser.js.map +1 -0
- package/dist/src/printers/html-printer.d.ts +4 -0
- package/dist/src/printers/html-printer.d.ts.map +1 -0
- package/dist/src/printers/html-printer.js +15 -0
- package/dist/src/printers/html-printer.js.map +1 -0
- package/dist/src/printers/index.d.ts +7 -0
- package/dist/src/printers/index.d.ts.map +1 -0
- package/dist/src/printers/index.js +23 -0
- package/dist/src/printers/index.js.map +1 -0
- package/dist/src/printers/js-printer.d.ts +4 -0
- package/dist/src/printers/js-printer.d.ts.map +1 -0
- package/dist/src/printers/js-printer.js +6 -0
- package/dist/src/printers/js-printer.js.map +1 -0
- package/dist/src/printers/svelte-printer.d.ts +4 -0
- package/dist/src/printers/svelte-printer.d.ts.map +1 -0
- package/dist/src/printers/svelte-printer.js +28 -0
- package/dist/src/printers/svelte-printer.js.map +1 -0
- package/dist/src/printers/ts-printer.d.ts +4 -0
- package/dist/src/printers/ts-printer.d.ts.map +1 -0
- package/dist/src/printers/ts-printer.js +9 -0
- package/dist/src/printers/ts-printer.js.map +1 -0
- package/dist/src/syntax/index.d.ts +86 -0
- package/dist/src/syntax/index.d.ts.map +1 -0
- package/dist/src/syntax/index.js +9 -0
- package/dist/src/syntax/index.js.map +1 -0
- package/dist/src/transformers/babel-transformer.d.ts +4 -0
- package/dist/src/transformers/babel-transformer.d.ts.map +1 -0
- package/dist/src/transformers/babel-transformer.js +142 -0
- package/dist/src/transformers/babel-transformer.js.map +1 -0
- package/dist/src/transformers/directive-bookkeeper.d.ts +19 -0
- package/dist/src/transformers/directive-bookkeeper.d.ts.map +1 -0
- package/dist/src/transformers/directive-bookkeeper.js +108 -0
- package/dist/src/transformers/directive-bookkeeper.js.map +1 -0
- package/dist/src/transformers/html-transformer.d.ts +4 -0
- package/dist/src/transformers/html-transformer.d.ts.map +1 -0
- package/dist/src/transformers/html-transformer.js +6 -0
- package/dist/src/transformers/html-transformer.js.map +1 -0
- package/dist/src/transformers/ignorer-bookkeeper.d.ts +18 -0
- package/dist/src/transformers/ignorer-bookkeeper.d.ts.map +1 -0
- package/dist/src/transformers/ignorer-bookkeeper.js +29 -0
- package/dist/src/transformers/ignorer-bookkeeper.js.map +1 -0
- package/dist/src/transformers/index.d.ts +6 -0
- package/dist/src/transformers/index.d.ts.map +1 -0
- package/dist/src/transformers/index.js +4 -0
- package/dist/src/transformers/index.js.map +1 -0
- package/dist/src/transformers/mutant-collector.d.ts +19 -0
- package/dist/src/transformers/mutant-collector.d.ts.map +1 -0
- package/dist/src/transformers/mutant-collector.js +25 -0
- package/dist/src/transformers/mutant-collector.js.map +1 -0
- package/dist/src/transformers/svelte-transformer.d.ts +4 -0
- package/dist/src/transformers/svelte-transformer.d.ts.map +1 -0
- package/dist/src/transformers/svelte-transformer.js +46 -0
- package/dist/src/transformers/svelte-transformer.js.map +1 -0
- package/dist/src/transformers/transformer-options.d.ts +6 -0
- package/dist/src/transformers/transformer-options.d.ts.map +1 -0
- package/dist/src/transformers/transformer-options.js +2 -0
- package/dist/src/transformers/transformer-options.js.map +1 -0
- package/dist/src/transformers/transformer.d.ts +22 -0
- package/dist/src/transformers/transformer.d.ts.map +1 -0
- package/dist/src/transformers/transformer.js +30 -0
- package/dist/src/transformers/transformer.js.map +1 -0
- package/dist/src/util/binary-operator.d.ts +2 -0
- package/dist/src/util/binary-operator.d.ts.map +1 -0
- package/dist/src/util/binary-operator.js +2 -0
- package/dist/src/util/binary-operator.js.map +1 -0
- package/dist/src/util/index.d.ts +4 -0
- package/dist/src/util/index.d.ts.map +1 -0
- package/dist/src/util/index.js +4 -0
- package/dist/src/util/index.js.map +1 -0
- package/dist/src/util/position-converter.d.ts +16 -0
- package/dist/src/util/position-converter.d.ts.map +1 -0
- package/dist/src/util/position-converter.js +126 -0
- package/dist/src/util/position-converter.js.map +1 -0
- package/dist/src/util/syntax-helpers.d.ts +60 -0
- package/dist/src/util/syntax-helpers.d.ts.map +1 -0
- package/dist/src/util/syntax-helpers.js +217 -0
- package/dist/src/util/syntax-helpers.js.map +1 -0
- package/package.json +64 -0
- package/src/create-instrumenter.ts +30 -0
- package/src/disable-type-checks.ts +153 -0
- package/src/file.ts +6 -0
- package/src/frameworks/angular-ignorer.ts +129 -0
- package/src/frameworks/index.ts +9 -0
- package/src/index.ts +8 -0
- package/src/instrument-result.ts +8 -0
- package/src/instrumenter-options.ts +5 -0
- package/src/instrumenter-tokens.ts +5 -0
- package/src/instrumenter.ts +100 -0
- package/src/mutant-placers/expression-mutant-placer.ts +210 -0
- package/src/mutant-placers/index.ts +12 -0
- package/src/mutant-placers/mutant-placer.ts +9 -0
- package/src/mutant-placers/statement-mutant-placer.ts +39 -0
- package/src/mutant-placers/switch-case-mutant-placer.ts +41 -0
- package/src/mutant-placers/throw-placement-error.ts +34 -0
- package/src/mutant.ts +101 -0
- package/src/mutators/arithmetic-operator-mutator.ts +51 -0
- package/src/mutators/array-declaration-mutator.ts +36 -0
- package/src/mutators/arrow-function-mutator.ts +22 -0
- package/src/mutators/assignment-operator-mutator.ts +59 -0
- package/src/mutators/block-statement-mutator.ts +89 -0
- package/src/mutators/boolean-literal-mutator.ts +24 -0
- package/src/mutators/conditional-expression-mutator.ts +92 -0
- package/src/mutators/equality-operator-mutator.ts +35 -0
- package/src/mutators/index.ts +3 -0
- package/src/mutators/logical-operator-mutator.ts +29 -0
- package/src/mutators/method-expression-mutator.ts +99 -0
- package/src/mutators/mutate.ts +36 -0
- package/src/mutators/mutator-options.ts +4 -0
- package/src/mutators/node-mutator.ts +6 -0
- package/src/mutators/object-literal-mutator.ts +15 -0
- package/src/mutators/optional-chaining-mutator.ts +39 -0
- package/src/mutators/regex-mutator.ts +69 -0
- package/src/mutators/string-literal-mutator.ts +49 -0
- package/src/mutators/unary-operator-mutator.ts +39 -0
- package/src/mutators/update-operator-mutator.ts +26 -0
- package/src/parsers/create-parser.ts +78 -0
- package/src/parsers/html-parser.ts +138 -0
- package/src/parsers/index.ts +5 -0
- package/src/parsers/js-parser.ts +58 -0
- package/src/parsers/parse-error.ts +9 -0
- package/src/parsers/parser-context.ts +15 -0
- package/src/parsers/parser-options.ts +3 -0
- package/src/parsers/svelte-parser.ts +255 -0
- package/src/parsers/ts-parser.ts +69 -0
- package/src/printers/html-printer.ts +20 -0
- package/src/printers/index.ts +33 -0
- package/src/printers/js-printer.ts +11 -0
- package/src/printers/svelte-printer.ts +34 -0
- package/src/printers/ts-printer.ts +14 -0
- package/src/syntax/index.ts +97 -0
- package/src/transformers/babel-transformer.ts +217 -0
- package/src/transformers/directive-bookkeeper.ts +173 -0
- package/src/transformers/html-transformer.ts +13 -0
- package/src/transformers/ignorer-bookkeeper.ts +41 -0
- package/src/transformers/index.ts +6 -0
- package/src/transformers/mutant-collector.ts +44 -0
- package/src/transformers/svelte-transformer.ts +57 -0
- package/src/transformers/transformer-options.ts +7 -0
- package/src/transformers/transformer.ts +54 -0
- package/src/util/binary-operator.ts +23 -0
- package/src/util/index.ts +3 -0
- package/src/util/position-converter.ts +145 -0
- package/src/util/syntax-helpers.ts +301 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { types } from '@babel/core';
|
|
2
|
+
import { notEmpty } from '@stryker-mutator/util';
|
|
3
|
+
|
|
4
|
+
import { File } from './file.js';
|
|
5
|
+
|
|
6
|
+
import { createParser, getFormat, ParserOptions } from './parsers/index.js';
|
|
7
|
+
import { AstFormat, HtmlAst, ScriptAst, SvelteAst } from './syntax/index.js';
|
|
8
|
+
|
|
9
|
+
const commentDirectiveRegEx = /^(\s*)@(ts-[a-z-]+).*$/;
|
|
10
|
+
const tsDirectiveLikeRegEx = /@(ts-[a-z-]+)/;
|
|
11
|
+
const startingCommentRegex = /(^\s*\/\*.*?\*\/)/gs;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Disables TypeScript type checking for a single file by inserting `// @ts-nocheck` commands.
|
|
15
|
+
* It also does this for *.js files, as they can be type checked by typescript as well.
|
|
16
|
+
* Other file types are silently ignored
|
|
17
|
+
*
|
|
18
|
+
* @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#-ts-nocheck-in-typescript-files
|
|
19
|
+
*/
|
|
20
|
+
export async function disableTypeChecks(
|
|
21
|
+
file: File,
|
|
22
|
+
options: ParserOptions,
|
|
23
|
+
): Promise<File> {
|
|
24
|
+
const format = getFormat(file.name);
|
|
25
|
+
if (!format) {
|
|
26
|
+
// Readme files and stuff don't need disabling.
|
|
27
|
+
return file;
|
|
28
|
+
}
|
|
29
|
+
if (isJSFileWithoutTSDirectives(file, format)) {
|
|
30
|
+
// Performance optimization. Only parse the file when it has a chance of containing a `// @ts-` directive
|
|
31
|
+
return {
|
|
32
|
+
...file,
|
|
33
|
+
content: prefixWithNoCheck(file.content),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const parse = createParser(options);
|
|
38
|
+
const ast = await parse(file.content, file.name);
|
|
39
|
+
switch (ast.format) {
|
|
40
|
+
case AstFormat.JS:
|
|
41
|
+
case AstFormat.TS:
|
|
42
|
+
case AstFormat.Tsx:
|
|
43
|
+
return { ...file, content: disableTypeCheckingInBabelAst(ast) };
|
|
44
|
+
case AstFormat.Html:
|
|
45
|
+
return { ...file, content: disableTypeCheckingInHtml(ast) };
|
|
46
|
+
case AstFormat.Svelte:
|
|
47
|
+
return { ...file, content: disableTypeCheckingInSvelte(ast) };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isJSFileWithoutTSDirectives(file: File, format: AstFormat) {
|
|
52
|
+
return (
|
|
53
|
+
(format === AstFormat.TS || format === AstFormat.JS) &&
|
|
54
|
+
!tsDirectiveLikeRegEx.test(file.content)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function disableTypeCheckingInBabelAst(ast: ScriptAst): string {
|
|
59
|
+
return prefixWithNoCheck(
|
|
60
|
+
removeTSDirectives(ast.rawContent, ast.root.comments),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function prefixWithNoCheck(code: string): string {
|
|
65
|
+
if (code.startsWith('#')) {
|
|
66
|
+
// first line has a shebang (#!/usr/bin/env node)
|
|
67
|
+
const newLineIndex = code.indexOf('\n');
|
|
68
|
+
if (newLineIndex > 0) {
|
|
69
|
+
return `${code.substring(0, newLineIndex)}\n// @ts-nocheck\n${code.substring(newLineIndex + 1)}`;
|
|
70
|
+
} else {
|
|
71
|
+
return code;
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
// We should leave comments, like `/** @jest-env jsdom */ at the top of the file, see #2569
|
|
75
|
+
startingCommentRegex.lastIndex = 0;
|
|
76
|
+
const commentMatch = startingCommentRegex.exec(code);
|
|
77
|
+
return `${commentMatch?.[1].concat('\n') ?? ''}// @ts-nocheck\n${code.substring(commentMatch?.[1].length ?? 0)}`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function disableTypeCheckingInHtml(ast: HtmlAst): string {
|
|
82
|
+
const sortedScripts = [...ast.root.scripts].sort(
|
|
83
|
+
(a, b) => a.root.start! - b.root.start!,
|
|
84
|
+
);
|
|
85
|
+
let currentIndex = 0;
|
|
86
|
+
let html = '';
|
|
87
|
+
for (const script of sortedScripts) {
|
|
88
|
+
html += ast.rawContent.substring(currentIndex, script.root.start!);
|
|
89
|
+
html += '\n';
|
|
90
|
+
html += prefixWithNoCheck(
|
|
91
|
+
removeTSDirectives(script.rawContent, script.root.comments),
|
|
92
|
+
);
|
|
93
|
+
html += '\n';
|
|
94
|
+
currentIndex = script.root.end!;
|
|
95
|
+
}
|
|
96
|
+
html += ast.rawContent.substring(currentIndex);
|
|
97
|
+
return html;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function disableTypeCheckingInSvelte(ast: SvelteAst): string {
|
|
101
|
+
const sortedScripts = [ast.root.moduleScript, ...ast.root.additionalScripts]
|
|
102
|
+
.filter(notEmpty)
|
|
103
|
+
.sort((a, b) => a.range.start - b.range.start);
|
|
104
|
+
let currentIndex = 0;
|
|
105
|
+
let html = '';
|
|
106
|
+
for (const script of sortedScripts) {
|
|
107
|
+
html += ast.rawContent.substring(currentIndex, script.range.start);
|
|
108
|
+
html += '\n';
|
|
109
|
+
html += prefixWithNoCheck(
|
|
110
|
+
removeTSDirectives(script.ast.rawContent, script.ast.root.comments),
|
|
111
|
+
);
|
|
112
|
+
html += '\n';
|
|
113
|
+
currentIndex = script.range.end;
|
|
114
|
+
}
|
|
115
|
+
html += ast.rawContent.substring(currentIndex);
|
|
116
|
+
return html;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function removeTSDirectives(
|
|
120
|
+
text: string,
|
|
121
|
+
comments: Array<types.CommentBlock | types.CommentLine> | null | undefined,
|
|
122
|
+
): string {
|
|
123
|
+
const directiveRanges = comments
|
|
124
|
+
?.map(tryParseTSDirective)
|
|
125
|
+
.filter(notEmpty)
|
|
126
|
+
.sort((a, b) => a.startPos - b.startPos);
|
|
127
|
+
if (directiveRanges) {
|
|
128
|
+
let currentIndex = 0;
|
|
129
|
+
let pruned = '';
|
|
130
|
+
for (const directiveRange of directiveRanges) {
|
|
131
|
+
pruned += text.substring(currentIndex, directiveRange.startPos);
|
|
132
|
+
currentIndex = directiveRange.endPos;
|
|
133
|
+
}
|
|
134
|
+
pruned += text.substring(currentIndex);
|
|
135
|
+
return pruned;
|
|
136
|
+
} else {
|
|
137
|
+
return text;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function tryParseTSDirective(
|
|
142
|
+
comment: types.CommentBlock | types.CommentLine,
|
|
143
|
+
): { startPos: number; endPos: number } | undefined {
|
|
144
|
+
const match = commentDirectiveRegEx.exec(comment.value);
|
|
145
|
+
if (match) {
|
|
146
|
+
const directiveStartPos = comment.start! + match[1].length + 2; // +2 to account for the `//` or `/*` start character
|
|
147
|
+
return {
|
|
148
|
+
startPos: directiveStartPos,
|
|
149
|
+
endPos: directiveStartPos + match[2].length + 1,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
package/src/file.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Ignorer, NodePath } from '@stryker-mutator/api/ignore';
|
|
2
|
+
|
|
3
|
+
const ANGULAR_SIGNAL_IO_FUNCTIONS = Object.freeze(['input', 'model', 'output']);
|
|
4
|
+
|
|
5
|
+
const ANGULAR_SIGNAL_QUERY_FUNCTIONS = Object.freeze([
|
|
6
|
+
'contentChild',
|
|
7
|
+
'contentChildren',
|
|
8
|
+
'viewChild',
|
|
9
|
+
'viewChildren',
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
const INPUT_MODEL_OUTPUT_CONFIG_MSG =
|
|
13
|
+
'Angular signal based input, model and output functions configuration object cannot be mutated as that causes issues with the Angular compiler.';
|
|
14
|
+
|
|
15
|
+
const SIGNAL_QUERY_OPTIONS_MSG =
|
|
16
|
+
'Angular signal query options object cannot be mutated as that causes issues with the Angular compiler.';
|
|
17
|
+
|
|
18
|
+
export class AngularIgnorer implements Ignorer {
|
|
19
|
+
public shouldIgnore(path: NodePath): string | undefined {
|
|
20
|
+
if (this.isInputModelOrOutputConfigurationObject(path)) {
|
|
21
|
+
return INPUT_MODEL_OUTPUT_CONFIG_MSG;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (this.isSignalQueryOptionsObject(path)) {
|
|
25
|
+
return SIGNAL_QUERY_OPTIONS_MSG;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#isClassFieldLike(path: NodePath): boolean {
|
|
32
|
+
return (
|
|
33
|
+
path.isClassProperty() ||
|
|
34
|
+
path.isClassPrivateProperty() ||
|
|
35
|
+
path.isClassAccessorProperty()
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Determines if the given path is a configuration object for an Angular input, model or output function.
|
|
41
|
+
* This solves the "Argument needs to be statically analyzable." error
|
|
42
|
+
*/
|
|
43
|
+
private isInputModelOrOutputConfigurationObject(path: NodePath): boolean {
|
|
44
|
+
if (
|
|
45
|
+
!path.isObjectExpression() ||
|
|
46
|
+
!path.parentPath.isCallExpression() ||
|
|
47
|
+
!path.parentPath.parentPath.isClassProperty()
|
|
48
|
+
) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const callExpression = path.parentPath;
|
|
53
|
+
const objectExpression = path;
|
|
54
|
+
|
|
55
|
+
const isRequiredSignalIOFunction =
|
|
56
|
+
callExpression.node.callee.type === 'MemberExpression' &&
|
|
57
|
+
callExpression.node.callee.object.type === 'Identifier' &&
|
|
58
|
+
ANGULAR_SIGNAL_IO_FUNCTIONS.includes(
|
|
59
|
+
callExpression.node.callee.object.name,
|
|
60
|
+
) &&
|
|
61
|
+
callExpression.node.callee.property.type === 'Identifier' &&
|
|
62
|
+
callExpression.node.callee.property.name === 'required';
|
|
63
|
+
|
|
64
|
+
const isSignalIOFunction =
|
|
65
|
+
callExpression.node.callee.type === 'Identifier' &&
|
|
66
|
+
ANGULAR_SIGNAL_IO_FUNCTIONS.includes(callExpression.node.callee.name);
|
|
67
|
+
|
|
68
|
+
const isOutput =
|
|
69
|
+
callExpression.node.callee.type === 'Identifier' &&
|
|
70
|
+
callExpression.node.callee.name === 'output';
|
|
71
|
+
|
|
72
|
+
if (isRequiredSignalIOFunction || isOutput) {
|
|
73
|
+
// The { alias: '...' } should be the first argument of the call expression
|
|
74
|
+
return (
|
|
75
|
+
callExpression.node.arguments.length >= 1 &&
|
|
76
|
+
callExpression.node.arguments[0] === objectExpression.node
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (isSignalIOFunction) {
|
|
81
|
+
// The { alias: '...' } should be the second argument of the call expression
|
|
82
|
+
return (
|
|
83
|
+
callExpression.node.arguments.length >= 2 &&
|
|
84
|
+
callExpression.node.arguments[1] === objectExpression.node
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Determines if the given path is a configuration object for an Angular signal query function.
|
|
93
|
+
* This solves the "Argument needs to be statically analyzable." error
|
|
94
|
+
*/
|
|
95
|
+
private isSignalQueryOptionsObject(path: NodePath): boolean {
|
|
96
|
+
if (
|
|
97
|
+
!path.isObjectExpression() ||
|
|
98
|
+
!path.parentPath.isCallExpression() ||
|
|
99
|
+
!this.#isClassFieldLike(path.parentPath.parentPath)
|
|
100
|
+
) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const callExpression = path.parentPath;
|
|
105
|
+
const objectExpression = path;
|
|
106
|
+
|
|
107
|
+
const callee = callExpression.node.callee;
|
|
108
|
+
|
|
109
|
+
const isQueryFn =
|
|
110
|
+
callee.type === 'Identifier' &&
|
|
111
|
+
ANGULAR_SIGNAL_QUERY_FUNCTIONS.includes(callee.name);
|
|
112
|
+
|
|
113
|
+
const isRequiredQueryFn =
|
|
114
|
+
callee.type === 'MemberExpression' &&
|
|
115
|
+
callee.object.type === 'Identifier' &&
|
|
116
|
+
ANGULAR_SIGNAL_QUERY_FUNCTIONS.includes(callee.object.name) &&
|
|
117
|
+
callee.property.type === 'Identifier' &&
|
|
118
|
+
callee.property.name === 'required';
|
|
119
|
+
|
|
120
|
+
if (!isQueryFn && !isRequiredQueryFn) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
callExpression.node.arguments.length >= 2 &&
|
|
126
|
+
callExpression.node.arguments[1] === objectExpression.node
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { PluginKind, declareClassPlugin } from '@stryker-mutator/api/plugin';
|
|
2
|
+
|
|
3
|
+
import { AngularIgnorer } from './angular-ignorer.js';
|
|
4
|
+
|
|
5
|
+
export const strykerPlugins = [
|
|
6
|
+
declareClassPlugin(PluginKind.Ignore, 'angular', AngularIgnorer),
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
export const frameworkPluginsFileUrl = import.meta.url;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './frameworks/index.js';
|
|
2
|
+
export * from './create-instrumenter.js';
|
|
3
|
+
export * from './instrumenter.js';
|
|
4
|
+
export * from './instrument-result.js';
|
|
5
|
+
export * from './instrumenter-options.js';
|
|
6
|
+
export * from './disable-type-checks.js';
|
|
7
|
+
export type { ParserOptions } from './parsers/index.js';
|
|
8
|
+
export * from './file.js';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
import { tokens, commonTokens } from '@stryker-mutator/api/plugin';
|
|
4
|
+
import { Logger } from '@stryker-mutator/api/logging';
|
|
5
|
+
import { MutateDescription } from '@stryker-mutator/api/core';
|
|
6
|
+
|
|
7
|
+
import { createParser } from './parsers/index.js';
|
|
8
|
+
import { transform, MutantCollector } from './transformers/index.js';
|
|
9
|
+
import { print } from './printers/index.js';
|
|
10
|
+
import { InstrumentResult } from './instrument-result.js';
|
|
11
|
+
import { InstrumenterOptions } from './instrumenter-options.js';
|
|
12
|
+
import { instrumenterTokens } from './instrumenter-tokens.js';
|
|
13
|
+
import { File } from './file.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The instrumenter is responsible for
|
|
17
|
+
* * Generating mutants based on source files
|
|
18
|
+
* * Instrumenting the source code with the mutants placed in `mutant switches`.
|
|
19
|
+
* * Adding mutant coverage expressions in the source code.
|
|
20
|
+
* @see https://github.com/stryker-mutator/stryker-js/issues/1514
|
|
21
|
+
*/
|
|
22
|
+
export class Instrumenter {
|
|
23
|
+
public static inject = tokens(
|
|
24
|
+
commonTokens.logger,
|
|
25
|
+
instrumenterTokens.createParser,
|
|
26
|
+
instrumenterTokens.print,
|
|
27
|
+
instrumenterTokens.transform,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
private readonly logger: Logger,
|
|
32
|
+
private readonly _createParser = createParser,
|
|
33
|
+
private readonly _print = print,
|
|
34
|
+
private readonly _transform = transform,
|
|
35
|
+
) {}
|
|
36
|
+
|
|
37
|
+
public async instrument(
|
|
38
|
+
files: readonly File[],
|
|
39
|
+
options: InstrumenterOptions,
|
|
40
|
+
): Promise<InstrumentResult> {
|
|
41
|
+
this.logger.debug(
|
|
42
|
+
'Instrumenting %d source files with mutants',
|
|
43
|
+
files.length,
|
|
44
|
+
);
|
|
45
|
+
const mutantCollector = new MutantCollector();
|
|
46
|
+
const outFiles: File[] = [];
|
|
47
|
+
let mutantCount = 0;
|
|
48
|
+
const parse = this._createParser(options);
|
|
49
|
+
for (const { name, mutate, content } of files) {
|
|
50
|
+
const ast = await parse(content, name);
|
|
51
|
+
this._transform(ast, mutantCollector, {
|
|
52
|
+
options,
|
|
53
|
+
mutateDescription: toBabelLineNumber(mutate),
|
|
54
|
+
logger: this.logger,
|
|
55
|
+
});
|
|
56
|
+
const mutatedContent = this._print(ast);
|
|
57
|
+
outFiles.push({
|
|
58
|
+
name,
|
|
59
|
+
mutate,
|
|
60
|
+
content: mutatedContent,
|
|
61
|
+
});
|
|
62
|
+
if (this.logger.isDebugEnabled()) {
|
|
63
|
+
const nrOfMutantsInFile = mutantCollector.mutants.length - mutantCount;
|
|
64
|
+
mutantCount = mutantCollector.mutants.length;
|
|
65
|
+
this.logger.debug(
|
|
66
|
+
`Instrumented ${path.relative(process.cwd(), name)} (${nrOfMutantsInFile} mutant(s))`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const mutants = mutantCollector.mutants.map((mutant) =>
|
|
71
|
+
mutant.toApiMutant(),
|
|
72
|
+
);
|
|
73
|
+
this.logger.info(
|
|
74
|
+
'Instrumented %d source file(s) with %d mutant(s)',
|
|
75
|
+
files.length,
|
|
76
|
+
mutants.length,
|
|
77
|
+
);
|
|
78
|
+
return {
|
|
79
|
+
files: outFiles,
|
|
80
|
+
mutants,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function toBabelLineNumber(range: MutateDescription): MutateDescription {
|
|
86
|
+
if (typeof range === 'boolean') {
|
|
87
|
+
return range;
|
|
88
|
+
} else {
|
|
89
|
+
return range.map(({ start, end }) => ({
|
|
90
|
+
start: {
|
|
91
|
+
column: start.column,
|
|
92
|
+
line: start.line + 1,
|
|
93
|
+
},
|
|
94
|
+
end: {
|
|
95
|
+
column: end.column,
|
|
96
|
+
line: end.line + 1,
|
|
97
|
+
},
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import babel, { type NodePath } from '@babel/core';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
mutantTestExpression,
|
|
5
|
+
mutationCoverageSequenceExpression,
|
|
6
|
+
} from '../util/syntax-helpers.js';
|
|
7
|
+
|
|
8
|
+
import { MutantPlacer } from './mutant-placer.js';
|
|
9
|
+
|
|
10
|
+
const { types } = babel;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Will set the identifier of anonymous function expressions if is located in a variable declaration.
|
|
14
|
+
* Will treat input as readonly. Returns undefined if not needed.
|
|
15
|
+
* @example
|
|
16
|
+
* const a = function() { }
|
|
17
|
+
* becomes
|
|
18
|
+
* const a = function a() {}
|
|
19
|
+
*/
|
|
20
|
+
function classOrFunctionExpressionNamedIfNeeded(
|
|
21
|
+
path: NodePath<babel.types.Expression>,
|
|
22
|
+
): babel.types.Expression | undefined {
|
|
23
|
+
if (
|
|
24
|
+
(path.isFunctionExpression() || path.isClassExpression()) &&
|
|
25
|
+
!path.node.id
|
|
26
|
+
) {
|
|
27
|
+
if (
|
|
28
|
+
path.parentPath.isVariableDeclarator() &&
|
|
29
|
+
types.isIdentifier(path.parentPath.node.id)
|
|
30
|
+
) {
|
|
31
|
+
path.node.id = path.parentPath.node.id;
|
|
32
|
+
return path.node;
|
|
33
|
+
} else if (
|
|
34
|
+
path.parentPath.isObjectProperty() &&
|
|
35
|
+
types.isIdentifier(path.parentPath.node.key) &&
|
|
36
|
+
path.getStatementParent()?.isVariableDeclaration()
|
|
37
|
+
) {
|
|
38
|
+
path.node.id = path.parentPath.node.key;
|
|
39
|
+
return path.node;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Will set the identifier of anonymous arrow function expressions if is located in a variable declaration.
|
|
47
|
+
* Will treat input as readonly. Returns undefined if not needed.
|
|
48
|
+
* @example
|
|
49
|
+
* const a = () => { }
|
|
50
|
+
* becomes
|
|
51
|
+
* const a = (() => { const a = () => {}; return a; })()
|
|
52
|
+
*/
|
|
53
|
+
function arrowFunctionExpressionNamedIfNeeded(
|
|
54
|
+
path: NodePath<babel.types.Expression>,
|
|
55
|
+
): babel.types.Expression | undefined {
|
|
56
|
+
if (
|
|
57
|
+
path.isArrowFunctionExpression() &&
|
|
58
|
+
path.parentPath.isVariableDeclarator() &&
|
|
59
|
+
types.isIdentifier(path.parentPath.node.id)
|
|
60
|
+
) {
|
|
61
|
+
return types.callExpression(
|
|
62
|
+
types.arrowFunctionExpression(
|
|
63
|
+
[],
|
|
64
|
+
types.blockStatement([
|
|
65
|
+
types.variableDeclaration('const', [
|
|
66
|
+
types.variableDeclarator(path.parentPath.node.id, path.node),
|
|
67
|
+
]),
|
|
68
|
+
types.returnStatement(path.parentPath.node.id),
|
|
69
|
+
]),
|
|
70
|
+
),
|
|
71
|
+
[],
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function nameIfAnonymous(
|
|
78
|
+
path: NodePath<babel.types.Expression>,
|
|
79
|
+
): babel.types.Expression {
|
|
80
|
+
return (
|
|
81
|
+
classOrFunctionExpressionNamedIfNeeded(path) ??
|
|
82
|
+
arrowFunctionExpressionNamedIfNeeded(path) ??
|
|
83
|
+
path.node
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isMemberOrCallOrNonNullExpression(path: NodePath) {
|
|
88
|
+
return isCallExpression(path) || isMemberOrNonNullExpression(path);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isMemberOrNonNullExpression(
|
|
92
|
+
path: NodePath,
|
|
93
|
+
): path is NodePath<
|
|
94
|
+
| babel.types.MemberExpression
|
|
95
|
+
| babel.types.OptionalMemberExpression
|
|
96
|
+
| babel.types.TSNonNullExpression
|
|
97
|
+
> {
|
|
98
|
+
return isMemberExpression(path) || path.isTSNonNullExpression();
|
|
99
|
+
}
|
|
100
|
+
function isMemberExpression(
|
|
101
|
+
path: NodePath,
|
|
102
|
+
): path is NodePath<
|
|
103
|
+
babel.types.MemberExpression | babel.types.OptionalMemberExpression
|
|
104
|
+
> {
|
|
105
|
+
return path.isMemberExpression() || path.isOptionalMemberExpression();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isCallExpression(
|
|
109
|
+
path: NodePath,
|
|
110
|
+
): path is NodePath<
|
|
111
|
+
babel.types.CallExpression | babel.types.OptionalCallExpression
|
|
112
|
+
> {
|
|
113
|
+
return path.isCallExpression() || path.isOptionalCallExpression();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function isValidExpression(path: NodePath<babel.types.Expression>) {
|
|
117
|
+
const parent = path.parentPath;
|
|
118
|
+
return (
|
|
119
|
+
!isObjectPropertyKey() &&
|
|
120
|
+
!isPartOfChain() &&
|
|
121
|
+
!parent.isTaggedTemplateExpression() &&
|
|
122
|
+
!isPartOfDeleteExpression() &&
|
|
123
|
+
!isPartOfAssignmentExpression()
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Determines if the expression is property of an object.
|
|
128
|
+
* @example
|
|
129
|
+
* const a = {
|
|
130
|
+
* 'foo': 'bar' // 'foo' here is an object property
|
|
131
|
+
* };
|
|
132
|
+
*/
|
|
133
|
+
function isObjectPropertyKey() {
|
|
134
|
+
return parent.isObjectProperty() && parent.node.key === path.node;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Determines if the expression is part of a call/member chain.
|
|
139
|
+
* @example
|
|
140
|
+
* // bar is part of chain, foo is NOT part of the chain:
|
|
141
|
+
* foo.bar.baz();
|
|
142
|
+
* foo.bar?.baz()
|
|
143
|
+
* foo.bar;
|
|
144
|
+
* foo.bar!;
|
|
145
|
+
* foo.bar();
|
|
146
|
+
* foo?.bar();
|
|
147
|
+
* baz[foo.bar()]
|
|
148
|
+
* bar?.baz[0]
|
|
149
|
+
*/
|
|
150
|
+
function isPartOfChain() {
|
|
151
|
+
return (
|
|
152
|
+
isMemberOrCallOrNonNullExpression(path) &&
|
|
153
|
+
((isMemberExpression(parent) &&
|
|
154
|
+
!(parent.node.computed && parent.node.property === path.node)) ||
|
|
155
|
+
parent.isTSNonNullExpression() ||
|
|
156
|
+
(isCallExpression(parent) && parent.node.callee === path.node))
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Determines if the expression is part of a delete expression.
|
|
162
|
+
* @returns true if the expression is part of a delete expression
|
|
163
|
+
* @example
|
|
164
|
+
* delete foo.bar;
|
|
165
|
+
*/
|
|
166
|
+
function isPartOfDeleteExpression() {
|
|
167
|
+
return parent.isUnaryExpression() && parent.node.operator === 'delete';
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Determines if the expression is part of an assignment expression.
|
|
172
|
+
* @returns true if the expression is part of an assignment expression
|
|
173
|
+
* @example
|
|
174
|
+
* foo.bar = 42;
|
|
175
|
+
* initialNodes.filter((n) => n.id === 'tiptilt')[0].className = tiptiltState;
|
|
176
|
+
*/
|
|
177
|
+
function isPartOfAssignmentExpression() {
|
|
178
|
+
return parent.isAssignmentExpression() && parent.node.left === path.node;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Places the mutants with a conditional expression: `global.activeMutant === 1? mutatedCode : originalCode`;
|
|
184
|
+
*/
|
|
185
|
+
export const expressionMutantPlacer = {
|
|
186
|
+
name: 'expressionMutantPlacer',
|
|
187
|
+
canPlace(path) {
|
|
188
|
+
return path.isExpression() && isValidExpression(path);
|
|
189
|
+
},
|
|
190
|
+
place(path, appliedMutants) {
|
|
191
|
+
// Make sure anonymous functions and classes keep their 'name' property
|
|
192
|
+
let expression = nameIfAnonymous(path);
|
|
193
|
+
|
|
194
|
+
// Add the mutation coverage expression
|
|
195
|
+
expression = mutationCoverageSequenceExpression(
|
|
196
|
+
appliedMutants.keys(),
|
|
197
|
+
expression,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// Now apply the mutants
|
|
201
|
+
for (const [mutant, appliedMutant] of appliedMutants) {
|
|
202
|
+
expression = types.conditionalExpression(
|
|
203
|
+
mutantTestExpression(mutant.id),
|
|
204
|
+
appliedMutant,
|
|
205
|
+
expression,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
path.replaceWith(expression);
|
|
209
|
+
},
|
|
210
|
+
} satisfies MutantPlacer<babel.types.Expression>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { expressionMutantPlacer } from './expression-mutant-placer.js';
|
|
2
|
+
import { MutantPlacer } from './mutant-placer.js';
|
|
3
|
+
import { statementMutantPlacer } from './statement-mutant-placer.js';
|
|
4
|
+
import { switchCaseMutantPlacer } from './switch-case-mutant-placer.js';
|
|
5
|
+
|
|
6
|
+
export * from './mutant-placer.js';
|
|
7
|
+
export * from './throw-placement-error.js';
|
|
8
|
+
export const allMutantPlacers: readonly MutantPlacer[] = Object.freeze([
|
|
9
|
+
expressionMutantPlacer,
|
|
10
|
+
statementMutantPlacer,
|
|
11
|
+
switchCaseMutantPlacer,
|
|
12
|
+
]);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { NodePath, types } from '@babel/core';
|
|
2
|
+
|
|
3
|
+
import { Mutant } from '../mutant.js';
|
|
4
|
+
|
|
5
|
+
export interface MutantPlacer<TNode extends types.Node = types.Node> {
|
|
6
|
+
name: string;
|
|
7
|
+
canPlace(path: NodePath): boolean;
|
|
8
|
+
place(path: NodePath<TNode>, appliedMutants: Map<Mutant, TNode>): void;
|
|
9
|
+
}
|