@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,39 @@
|
|
|
1
|
+
import babel, { type types } 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: t } = babel;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Mutant placer that places mutants in statements that allow it.
|
|
14
|
+
* It uses an `if` statement to do so
|
|
15
|
+
*/
|
|
16
|
+
export const statementMutantPlacer: MutantPlacer<types.Statement> = {
|
|
17
|
+
name: 'statementMutantPlacer',
|
|
18
|
+
canPlace(path) {
|
|
19
|
+
return path.isStatement();
|
|
20
|
+
},
|
|
21
|
+
place(path, appliedMutants) {
|
|
22
|
+
let statement: types.Statement = t.blockStatement([
|
|
23
|
+
t.expressionStatement(
|
|
24
|
+
mutationCoverageSequenceExpression(appliedMutants.keys()),
|
|
25
|
+
),
|
|
26
|
+
...(path.isBlockStatement() ? path.node.body : [path.node]),
|
|
27
|
+
]);
|
|
28
|
+
for (const [mutant, appliedMutant] of appliedMutants) {
|
|
29
|
+
statement = t.ifStatement(
|
|
30
|
+
mutantTestExpression(mutant.id),
|
|
31
|
+
t.blockStatement([appliedMutant]),
|
|
32
|
+
statement,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
path.replaceWith(
|
|
36
|
+
path.isBlockStatement() ? t.blockStatement([statement]) : statement,
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import babel, { type types } from '@babel/core';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
mutantTestExpression,
|
|
5
|
+
mutationCoverageSequenceExpression,
|
|
6
|
+
} from '../util/index.js';
|
|
7
|
+
|
|
8
|
+
import { MutantPlacer } from './mutant-placer.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Places the mutants with consequent of a SwitchCase node. Uses an if-statement to do so.
|
|
12
|
+
* @example
|
|
13
|
+
* case 'foo':
|
|
14
|
+
* if (stryMutAct_9fa48(0)) {} else {
|
|
15
|
+
* stryCov_9fa48(0);
|
|
16
|
+
* console.log('bar');
|
|
17
|
+
* break;
|
|
18
|
+
* }
|
|
19
|
+
*/
|
|
20
|
+
export const switchCaseMutantPlacer: MutantPlacer<types.SwitchCase> = {
|
|
21
|
+
name: 'switchCaseMutantPlacer',
|
|
22
|
+
canPlace(path) {
|
|
23
|
+
return path.isSwitchCase();
|
|
24
|
+
},
|
|
25
|
+
place(path, appliedMutants) {
|
|
26
|
+
let consequence: types.Statement = babel.types.blockStatement([
|
|
27
|
+
babel.types.expressionStatement(
|
|
28
|
+
mutationCoverageSequenceExpression(appliedMutants.keys()),
|
|
29
|
+
),
|
|
30
|
+
...path.node.consequent,
|
|
31
|
+
]);
|
|
32
|
+
for (const [mutant, appliedMutant] of appliedMutants) {
|
|
33
|
+
consequence = babel.types.ifStatement(
|
|
34
|
+
mutantTestExpression(mutant.id),
|
|
35
|
+
babel.types.blockStatement(appliedMutant.consequent),
|
|
36
|
+
consequence,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
path.replaceWith(babel.types.switchCase(path.node.test, [consequence]));
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
import { NodePath } from '@babel/core';
|
|
4
|
+
import { propertyPath } from '@stryker-mutator/util';
|
|
5
|
+
import { StrykerOptions } from '@stryker-mutator/api/core';
|
|
6
|
+
|
|
7
|
+
import { Mutant } from '../mutant.js';
|
|
8
|
+
|
|
9
|
+
import { MutantPlacer } from './mutant-placer.js';
|
|
10
|
+
|
|
11
|
+
export function throwPlacementError(
|
|
12
|
+
error: Error,
|
|
13
|
+
nodePath: NodePath,
|
|
14
|
+
placer: MutantPlacer,
|
|
15
|
+
mutants: Mutant[],
|
|
16
|
+
fileName: string,
|
|
17
|
+
): never {
|
|
18
|
+
const location = `${path.relative(process.cwd(), fileName)}:${nodePath.node.loc?.start.line}:${nodePath.node.loc?.start.column}`;
|
|
19
|
+
const message = `${placer.name} could not place mutants with type(s): "${new Intl.ListFormat('en').format(mutants.map((mutant) => mutant.mutatorName))}"`;
|
|
20
|
+
const errorMessage = `${location} ${message}. Either remove this file from the list of files to be mutated, or exclude the mutator (using ${propertyPath<StrykerOptions>()(
|
|
21
|
+
'mutator',
|
|
22
|
+
'excludedMutations',
|
|
23
|
+
)}). Please report this issue at https://github.com/stryker-mutator/stryker-js/issues/new?assignees=&labels=%F0%9F%90%9B+Bug&template=bug_report.md&title=${encodeURIComponent(
|
|
24
|
+
message,
|
|
25
|
+
)}. Original error: ${error.stack}`;
|
|
26
|
+
let builtError = new Error(errorMessage);
|
|
27
|
+
try {
|
|
28
|
+
// `buildCodeFrameError` is kind of flaky, see https://github.com/stryker-mutator/stryker-js/issues/2695
|
|
29
|
+
builtError = nodePath.buildCodeFrameError(errorMessage);
|
|
30
|
+
} catch {
|
|
31
|
+
// Idle, regular error will have to suffice
|
|
32
|
+
}
|
|
33
|
+
throw builtError;
|
|
34
|
+
}
|
package/src/mutant.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import babel, { type types } from '@babel/core';
|
|
2
|
+
import generate from '@babel/generator';
|
|
3
|
+
import {
|
|
4
|
+
Mutant as ApiMutant,
|
|
5
|
+
Location,
|
|
6
|
+
Position,
|
|
7
|
+
} from '@stryker-mutator/api/core';
|
|
8
|
+
|
|
9
|
+
import { deepCloneNode, eqNode } from './util/index.js';
|
|
10
|
+
|
|
11
|
+
const { traverse } = babel;
|
|
12
|
+
|
|
13
|
+
const generator = generate.default;
|
|
14
|
+
|
|
15
|
+
export interface Mutable {
|
|
16
|
+
mutatorName: string;
|
|
17
|
+
ignoreReason?: string;
|
|
18
|
+
replacement: types.Node;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class Mutant implements Mutable {
|
|
22
|
+
public readonly replacementCode: string;
|
|
23
|
+
public readonly replacement: types.Node;
|
|
24
|
+
public readonly mutatorName: string;
|
|
25
|
+
public readonly ignoreReason: string | undefined;
|
|
26
|
+
|
|
27
|
+
constructor(
|
|
28
|
+
public readonly id: string,
|
|
29
|
+
public readonly fileName: string,
|
|
30
|
+
public readonly original: types.Node,
|
|
31
|
+
specs: Mutable,
|
|
32
|
+
public readonly offset: Position = { column: 0, line: 0 },
|
|
33
|
+
) {
|
|
34
|
+
this.replacement = specs.replacement;
|
|
35
|
+
this.mutatorName = specs.mutatorName;
|
|
36
|
+
this.ignoreReason = specs.ignoreReason;
|
|
37
|
+
this.replacementCode = generator(this.replacement).code;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public toApiMutant(): ApiMutant {
|
|
41
|
+
return {
|
|
42
|
+
fileName: this.fileName,
|
|
43
|
+
id: this.id,
|
|
44
|
+
location: toApiLocation(this.original.loc!, this.offset),
|
|
45
|
+
mutatorName: this.mutatorName,
|
|
46
|
+
replacement: this.replacementCode,
|
|
47
|
+
statusReason: this.ignoreReason,
|
|
48
|
+
status: this.ignoreReason ? 'Ignored' : undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Applies the mutant in (a copy of) the AST, without changing provided AST.
|
|
54
|
+
* Can the tree itself (in which case the replacement is returned),
|
|
55
|
+
* or can be nested in the given tree.
|
|
56
|
+
* @param originalTree The original node, which will be treated as readonly
|
|
57
|
+
*/
|
|
58
|
+
public applied<TNode extends types.Node>(originalTree: TNode): TNode {
|
|
59
|
+
if (originalTree === this.original) {
|
|
60
|
+
return this.replacement as TNode;
|
|
61
|
+
} else {
|
|
62
|
+
const mutatedAst = deepCloneNode(originalTree);
|
|
63
|
+
let applied = false;
|
|
64
|
+
const { original, replacement } = this;
|
|
65
|
+
traverse(mutatedAst, {
|
|
66
|
+
noScope: true,
|
|
67
|
+
enter(path) {
|
|
68
|
+
if (eqNode(path.node, original)) {
|
|
69
|
+
path.replaceWith(replacement);
|
|
70
|
+
path.stop();
|
|
71
|
+
applied = true;
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
if (!applied) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Could not apply mutant ${JSON.stringify(this.replacement)}.`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return mutatedAst;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function toApiLocation(
|
|
86
|
+
source: types.SourceLocation,
|
|
87
|
+
offset: Position,
|
|
88
|
+
): Location {
|
|
89
|
+
const loc = {
|
|
90
|
+
start: toPosition(source.start, offset),
|
|
91
|
+
end: toPosition(source.end, offset),
|
|
92
|
+
};
|
|
93
|
+
return loc;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function toPosition(source: Position, offset: Position): Position {
|
|
97
|
+
return {
|
|
98
|
+
column: source.column + (source.line === 1 ? offset.column : 0), // offset is zero-based
|
|
99
|
+
line: source.line + offset.line - 1, // Stryker works 0-based internally, offset is zero based as well
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { types } from '@babel/core';
|
|
2
|
+
|
|
3
|
+
import { deepCloneNode } from '../util/index.js';
|
|
4
|
+
|
|
5
|
+
import { NodeMutator } from './node-mutator.js';
|
|
6
|
+
|
|
7
|
+
const arithmeticOperatorReplacements = Object.freeze({
|
|
8
|
+
'+': '-',
|
|
9
|
+
'-': '+',
|
|
10
|
+
'*': '/',
|
|
11
|
+
'/': '*',
|
|
12
|
+
'%': '*',
|
|
13
|
+
} as const);
|
|
14
|
+
|
|
15
|
+
export const arithmeticOperatorMutator: NodeMutator = {
|
|
16
|
+
name: 'ArithmeticOperator',
|
|
17
|
+
|
|
18
|
+
*mutate(path) {
|
|
19
|
+
if (
|
|
20
|
+
path.isBinaryExpression() &&
|
|
21
|
+
isSupported(path.node.operator, path.node)
|
|
22
|
+
) {
|
|
23
|
+
const mutatedOperator =
|
|
24
|
+
arithmeticOperatorReplacements[path.node.operator];
|
|
25
|
+
const replacement = deepCloneNode(path.node);
|
|
26
|
+
replacement.operator = mutatedOperator;
|
|
27
|
+
yield replacement;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function isSupported(
|
|
33
|
+
operator: string,
|
|
34
|
+
node: types.BinaryExpression,
|
|
35
|
+
): operator is keyof typeof arithmeticOperatorReplacements {
|
|
36
|
+
if (!Object.keys(arithmeticOperatorReplacements).includes(operator)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const stringTypes = ['StringLiteral', 'TemplateLiteral'];
|
|
41
|
+
const leftType =
|
|
42
|
+
node.left.type === 'BinaryExpression'
|
|
43
|
+
? node.left.right.type
|
|
44
|
+
: node.left.type;
|
|
45
|
+
|
|
46
|
+
if (stringTypes.includes(node.right.type) || stringTypes.includes(leftType)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import babel, { type NodePath } from '@babel/core';
|
|
2
|
+
|
|
3
|
+
import { deepCloneNode } from '../util/index.js';
|
|
4
|
+
|
|
5
|
+
import { NodeMutator } from './node-mutator.js';
|
|
6
|
+
|
|
7
|
+
const { types } = babel;
|
|
8
|
+
|
|
9
|
+
export const arrayDeclarationMutator: NodeMutator = {
|
|
10
|
+
name: 'ArrayDeclaration',
|
|
11
|
+
|
|
12
|
+
*mutate(path: NodePath): Iterable<babel.types.Node> {
|
|
13
|
+
if (path.isArrayExpression()) {
|
|
14
|
+
const replacement = path.node.elements.length
|
|
15
|
+
? types.arrayExpression()
|
|
16
|
+
: types.arrayExpression([types.stringLiteral('Stryker was here')]);
|
|
17
|
+
yield replacement;
|
|
18
|
+
}
|
|
19
|
+
if (
|
|
20
|
+
(path.isCallExpression() || path.isNewExpression()) &&
|
|
21
|
+
types.isIdentifier(path.node.callee) &&
|
|
22
|
+
path.node.callee.name === 'Array'
|
|
23
|
+
) {
|
|
24
|
+
const mutatedCallArgs = path.node.arguments.length
|
|
25
|
+
? []
|
|
26
|
+
: [types.arrayExpression()];
|
|
27
|
+
const replacement = types.isNewExpression(path.node)
|
|
28
|
+
? types.newExpression(deepCloneNode(path.node.callee), mutatedCallArgs)
|
|
29
|
+
: types.callExpression(
|
|
30
|
+
deepCloneNode(path.node.callee),
|
|
31
|
+
mutatedCallArgs,
|
|
32
|
+
);
|
|
33
|
+
yield replacement;
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import babel from '@babel/core';
|
|
2
|
+
|
|
3
|
+
const { types } = babel;
|
|
4
|
+
|
|
5
|
+
import { NodeMutator } from './index.js';
|
|
6
|
+
|
|
7
|
+
export const arrowFunctionMutator: NodeMutator = {
|
|
8
|
+
name: 'ArrowFunction',
|
|
9
|
+
|
|
10
|
+
*mutate(path) {
|
|
11
|
+
if (
|
|
12
|
+
path.isArrowFunctionExpression() &&
|
|
13
|
+
!types.isBlockStatement(path.node.body) &&
|
|
14
|
+
!(
|
|
15
|
+
types.isIdentifier(path.node.body) &&
|
|
16
|
+
path.node.body.name === 'undefined'
|
|
17
|
+
)
|
|
18
|
+
) {
|
|
19
|
+
yield types.arrowFunctionExpression([], types.identifier('undefined'));
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { types as t } from '@babel/core';
|
|
2
|
+
|
|
3
|
+
import { deepCloneNode } from '../util/index.js';
|
|
4
|
+
|
|
5
|
+
import { NodeMutator } from './index.js';
|
|
6
|
+
|
|
7
|
+
const assignmentOperatorReplacements = Object.freeze({
|
|
8
|
+
'+=': '-=',
|
|
9
|
+
'-=': '+=',
|
|
10
|
+
'*=': '/=',
|
|
11
|
+
'/=': '*=',
|
|
12
|
+
'%=': '*=',
|
|
13
|
+
'<<=': '>>=',
|
|
14
|
+
'>>=': '<<=',
|
|
15
|
+
'&=': '|=',
|
|
16
|
+
'|=': '&=',
|
|
17
|
+
'&&=': '||=',
|
|
18
|
+
'||=': '&&=',
|
|
19
|
+
'??=': '&&=',
|
|
20
|
+
} as const);
|
|
21
|
+
|
|
22
|
+
const stringTypes = Object.freeze(['StringLiteral', 'TemplateLiteral']);
|
|
23
|
+
const stringAssignmentTypes = Object.freeze(['&&=', '||=', '??=']);
|
|
24
|
+
|
|
25
|
+
export const assignmentOperatorMutator: NodeMutator = {
|
|
26
|
+
name: 'AssignmentOperator',
|
|
27
|
+
|
|
28
|
+
*mutate(path) {
|
|
29
|
+
if (
|
|
30
|
+
path.isAssignmentExpression() &&
|
|
31
|
+
isSupportedAssignmentOperator(path.node.operator) &&
|
|
32
|
+
isSupported(path.node)
|
|
33
|
+
) {
|
|
34
|
+
const mutatedOperator =
|
|
35
|
+
assignmentOperatorReplacements[path.node.operator];
|
|
36
|
+
const replacement = deepCloneNode(path.node);
|
|
37
|
+
replacement.operator = mutatedOperator;
|
|
38
|
+
yield replacement;
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function isSupportedAssignmentOperator(
|
|
44
|
+
operator: string,
|
|
45
|
+
): operator is keyof typeof assignmentOperatorReplacements {
|
|
46
|
+
return Object.keys(assignmentOperatorReplacements).includes(operator);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isSupported(node: t.AssignmentExpression): boolean {
|
|
50
|
+
// Excludes assignment operators that apply to strings.
|
|
51
|
+
if (
|
|
52
|
+
stringTypes.includes(node.right.type) &&
|
|
53
|
+
!stringAssignmentTypes.includes(node.operator)
|
|
54
|
+
) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import babel, { type NodePath } from '@babel/core';
|
|
2
|
+
|
|
3
|
+
import { NodeMutator } from './node-mutator.js';
|
|
4
|
+
|
|
5
|
+
const { types } = babel;
|
|
6
|
+
|
|
7
|
+
export const blockStatementMutator: NodeMutator = {
|
|
8
|
+
name: 'BlockStatement',
|
|
9
|
+
|
|
10
|
+
*mutate(path) {
|
|
11
|
+
if (path.isBlockStatement() && isValid(path)) {
|
|
12
|
+
yield types.blockStatement([]);
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
function isValid(path: NodePath<babel.types.BlockStatement>) {
|
|
18
|
+
return !isEmpty(path) && !isInvalidConstructorBody(path);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isEmpty(path: NodePath<babel.types.BlockStatement>) {
|
|
22
|
+
return !path.node.body.length;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks to see if a statement is an invalid constructor body
|
|
27
|
+
* @example
|
|
28
|
+
* // Invalid!
|
|
29
|
+
* class Foo extends Bar {
|
|
30
|
+
* constructor(public baz: string) {
|
|
31
|
+
* super(42);
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* @example
|
|
35
|
+
* // Invalid!
|
|
36
|
+
* class Foo extends Bar {
|
|
37
|
+
* public baz = 'string';
|
|
38
|
+
* constructor() {
|
|
39
|
+
* super(42);
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* @see https://github.com/stryker-mutator/stryker-js/issues/2314
|
|
43
|
+
* @see https://github.com/stryker-mutator/stryker-js/issues/2474
|
|
44
|
+
*/
|
|
45
|
+
function isInvalidConstructorBody(
|
|
46
|
+
blockStatement: NodePath<babel.types.BlockStatement>,
|
|
47
|
+
): boolean {
|
|
48
|
+
return Boolean(
|
|
49
|
+
blockStatement.parentPath.isClassMethod() &&
|
|
50
|
+
blockStatement.parentPath.node.kind === 'constructor' &&
|
|
51
|
+
(containsTSParameterProperties(blockStatement.parentPath) ||
|
|
52
|
+
containsInitializedClassProperties(blockStatement.parentPath)) &&
|
|
53
|
+
hasSuperExpression(blockStatement),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function containsTSParameterProperties(
|
|
58
|
+
constructor: NodePath<babel.types.ClassMethod>,
|
|
59
|
+
): boolean {
|
|
60
|
+
return constructor.node.params.some((param) =>
|
|
61
|
+
types.isTSParameterProperty(param),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function containsInitializedClassProperties(
|
|
66
|
+
constructor: NodePath<babel.types.ClassMethod>,
|
|
67
|
+
): boolean {
|
|
68
|
+
return (
|
|
69
|
+
constructor.parentPath.isClassBody() &&
|
|
70
|
+
constructor.parentPath.node.body.some(
|
|
71
|
+
(classMember) => types.isClassProperty(classMember) && classMember.value,
|
|
72
|
+
)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function hasSuperExpression(
|
|
77
|
+
constructor: NodePath<babel.types.BlockStatement>,
|
|
78
|
+
): boolean {
|
|
79
|
+
let hasSuper = false;
|
|
80
|
+
constructor.traverse({
|
|
81
|
+
Super(path) {
|
|
82
|
+
if (path.parentPath.isCallExpression()) {
|
|
83
|
+
path.stop();
|
|
84
|
+
hasSuper = true;
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
return hasSuper;
|
|
89
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import babel from '@babel/core';
|
|
2
|
+
|
|
3
|
+
import { deepCloneNode } from '../util/index.js';
|
|
4
|
+
|
|
5
|
+
const { types } = babel;
|
|
6
|
+
|
|
7
|
+
import { NodeMutator } from './index.js';
|
|
8
|
+
|
|
9
|
+
export const booleanLiteralMutator: NodeMutator = {
|
|
10
|
+
name: 'BooleanLiteral',
|
|
11
|
+
|
|
12
|
+
*mutate(path) {
|
|
13
|
+
if (path.isBooleanLiteral()) {
|
|
14
|
+
yield types.booleanLiteral(!path.node.value);
|
|
15
|
+
}
|
|
16
|
+
if (
|
|
17
|
+
path.isUnaryExpression() &&
|
|
18
|
+
path.node.operator === '!' &&
|
|
19
|
+
path.node.prefix
|
|
20
|
+
) {
|
|
21
|
+
yield deepCloneNode(path.node.argument);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import babel, { type NodePath } from '@babel/core';
|
|
2
|
+
|
|
3
|
+
import { deepCloneNode } from '../util/index.js';
|
|
4
|
+
|
|
5
|
+
import { NodeMutator } from './node-mutator.js';
|
|
6
|
+
|
|
7
|
+
const booleanOperators = Object.freeze([
|
|
8
|
+
'!=',
|
|
9
|
+
'!==',
|
|
10
|
+
'&&',
|
|
11
|
+
'<',
|
|
12
|
+
'<=',
|
|
13
|
+
'==',
|
|
14
|
+
'===',
|
|
15
|
+
'>',
|
|
16
|
+
'>=',
|
|
17
|
+
'||',
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
const { types } = babel;
|
|
21
|
+
|
|
22
|
+
export const conditionalExpressionMutator: NodeMutator = {
|
|
23
|
+
name: 'ConditionalExpression',
|
|
24
|
+
|
|
25
|
+
*mutate(path) {
|
|
26
|
+
if (isTestOfLoop(path)) {
|
|
27
|
+
yield types.booleanLiteral(false);
|
|
28
|
+
} else if (isTestOfCondition(path)) {
|
|
29
|
+
yield types.booleanLiteral(true);
|
|
30
|
+
yield types.booleanLiteral(false);
|
|
31
|
+
} else if (isBooleanExpression(path)) {
|
|
32
|
+
if (path.parent?.type === 'LogicalExpression') {
|
|
33
|
+
// For (x || y), do not generate the (true || y) mutation as it
|
|
34
|
+
// has the same behavior as the (true) mutator, handled in the
|
|
35
|
+
// isTestOfCondition branch above
|
|
36
|
+
if (path.parent.operator === '||') {
|
|
37
|
+
yield types.booleanLiteral(false);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// For (x && y), do not generate the (false && y) mutation as it
|
|
41
|
+
// has the same behavior as the (false) mutator, handled in the
|
|
42
|
+
// isTestOfCondition branch above
|
|
43
|
+
if (path.parent.operator === '&&') {
|
|
44
|
+
yield types.booleanLiteral(true);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
yield types.booleanLiteral(true);
|
|
49
|
+
yield types.booleanLiteral(false);
|
|
50
|
+
} else if (path.isForStatement() && !path.node.test) {
|
|
51
|
+
const replacement = deepCloneNode(path.node);
|
|
52
|
+
replacement.test = types.booleanLiteral(false);
|
|
53
|
+
yield replacement;
|
|
54
|
+
} else if (path.isSwitchCase() && path.node.consequent.length > 0) {
|
|
55
|
+
// if not a fallthrough case
|
|
56
|
+
const replacement = deepCloneNode(path.node);
|
|
57
|
+
replacement.consequent = [];
|
|
58
|
+
yield replacement;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function isTestOfLoop(path: NodePath): boolean {
|
|
64
|
+
const { parentPath } = path;
|
|
65
|
+
if (!parentPath) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return (
|
|
69
|
+
(parentPath.isForStatement() ||
|
|
70
|
+
parentPath.isWhileStatement() ||
|
|
71
|
+
parentPath.isDoWhileStatement()) &&
|
|
72
|
+
parentPath.node.test === path.node
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function isTestOfCondition(path: NodePath): boolean {
|
|
77
|
+
const { parentPath } = path;
|
|
78
|
+
if (!parentPath) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return (
|
|
82
|
+
parentPath.isIfStatement() /*|| parentPath.isConditionalExpression()*/ &&
|
|
83
|
+
parentPath.node.test === path.node
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isBooleanExpression(path: NodePath) {
|
|
88
|
+
return (
|
|
89
|
+
(path.isBinaryExpression() || path.isLogicalExpression()) &&
|
|
90
|
+
booleanOperators.includes(path.node.operator)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import babel from '@babel/core';
|
|
2
|
+
|
|
3
|
+
import { NodeMutator } from './node-mutator.js';
|
|
4
|
+
|
|
5
|
+
const { types: t } = babel;
|
|
6
|
+
|
|
7
|
+
const operators = {
|
|
8
|
+
'<': ['<=', '>='],
|
|
9
|
+
'<=': ['<', '>'],
|
|
10
|
+
'>': ['>=', '<='],
|
|
11
|
+
'>=': ['>', '<'],
|
|
12
|
+
'==': ['!='],
|
|
13
|
+
'!=': ['=='],
|
|
14
|
+
'===': ['!=='],
|
|
15
|
+
'!==': ['==='],
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
function isEqualityOperator(
|
|
19
|
+
operator: string,
|
|
20
|
+
): operator is keyof typeof operators {
|
|
21
|
+
return Object.keys(operators).includes(operator);
|
|
22
|
+
}
|
|
23
|
+
export const equalityOperatorMutator: NodeMutator = {
|
|
24
|
+
name: 'EqualityOperator',
|
|
25
|
+
|
|
26
|
+
*mutate(path) {
|
|
27
|
+
if (path.isBinaryExpression() && isEqualityOperator(path.node.operator)) {
|
|
28
|
+
for (const mutableOperator of operators[path.node.operator]) {
|
|
29
|
+
const replacement = t.cloneNode(path.node, true);
|
|
30
|
+
replacement.operator = mutableOperator;
|
|
31
|
+
yield replacement;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { deepCloneNode } from '../util/index.js';
|
|
2
|
+
|
|
3
|
+
import { NodeMutator } from './index.js';
|
|
4
|
+
|
|
5
|
+
const logicalOperatorReplacements = Object.freeze({
|
|
6
|
+
'&&': '||',
|
|
7
|
+
'||': '&&',
|
|
8
|
+
'??': '&&',
|
|
9
|
+
} as const);
|
|
10
|
+
|
|
11
|
+
export const logicalOperatorMutator: NodeMutator = {
|
|
12
|
+
name: 'LogicalOperator',
|
|
13
|
+
|
|
14
|
+
*mutate(path) {
|
|
15
|
+
if (path.isLogicalExpression() && isSupported(path.node.operator)) {
|
|
16
|
+
const mutatedOperator = logicalOperatorReplacements[path.node.operator];
|
|
17
|
+
|
|
18
|
+
const replacement = deepCloneNode(path.node);
|
|
19
|
+
replacement.operator = mutatedOperator;
|
|
20
|
+
yield replacement;
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function isSupported(
|
|
26
|
+
operator: string,
|
|
27
|
+
): operator is keyof typeof logicalOperatorReplacements {
|
|
28
|
+
return Object.keys(logicalOperatorReplacements).includes(operator);
|
|
29
|
+
}
|