@systemfsoftware/stryker-js-instrumenter 0.1.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/dist/index.mjs ADDED
@@ -0,0 +1,2114 @@
1
+ import { PluginKind, commonTokens, declareClassPlugin, tokens } from "@systemfsoftware/stryker-js-plugin-api/plugin";
2
+ import path from "path";
3
+ import { INSTRUMENTER_CONSTANTS as ID } from "@systemfsoftware/stryker-js-plugin-api/core";
4
+ import babel, { File, types } from "@babel/core";
5
+ import { deepFreeze, notEmpty, propertyPath } from "@systemfsoftware/stryker-js-util";
6
+ import { satisfies } from "semver";
7
+ import generator from "@babel/generator";
8
+ import * as weaponRegex from "weapon-regex";
9
+ import "@systemfsoftware/stryker-js-plugin-api/ignore";
10
+ import "@systemfsoftware/stryker-js-plugin-api/logging";
11
+ import { createRequire } from "module";
12
+ //#region src/instrumenter-tokens.ts
13
+ const instrumenterTokens = Object.freeze({
14
+ createParser: "instrumenterCreateParser",
15
+ print: "instrumenterPrint",
16
+ transform: "instrumenterTransform"
17
+ });
18
+ //#endregion
19
+ //#region src/parsers/parse-error.ts
20
+ var ParseError = class extends Error {
21
+ constructor(message, fileName, location) {
22
+ super(`Parse error in ${fileName} (${location.line}:${location.column}) ${message}`);
23
+ }
24
+ };
25
+ //#endregion
26
+ //#region src/parsers/html-parser.ts
27
+ const TSX_SCRIPT_TYPES = Object.freeze(["tsx", "text/tsx"]);
28
+ const TS_SCRIPT_TYPES = Object.freeze([
29
+ "ts",
30
+ "text/typescript",
31
+ "typescript"
32
+ ]);
33
+ const JS_SCRIPT_TYPES = Object.freeze([
34
+ "js",
35
+ "text/javascript",
36
+ "javascript",
37
+ "module"
38
+ ]);
39
+ async function parse$2(text, originFileName, context) {
40
+ return {
41
+ originFileName,
42
+ rawContent: text,
43
+ format: "html",
44
+ root: await ngHtmlParser(text, originFileName, context)
45
+ };
46
+ }
47
+ async function ngHtmlParser(text, fileName, parserContext) {
48
+ const ngParser = await import("angular-html-parser");
49
+ const { rootNodes, errors } = ngParser.parse(text, {
50
+ canSelfClose: true,
51
+ allowHtmComponentClosingTags: true,
52
+ isTagNameCaseSensitive: true
53
+ });
54
+ if (errors.length !== 0) {
55
+ const firstError = errors[0];
56
+ if (firstError === void 0) throw new Error("HTML parser reported errors but first error is missing");
57
+ throw new ParseError(firstError.msg, fileName, toSourceLocation(firstError.span.start));
58
+ }
59
+ const scriptsAsPromised = [];
60
+ ngParser.visitAll(new class extends ngParser.RecursiveVisitor {
61
+ visitElement(el, context) {
62
+ const scriptFormat = getScriptType(el);
63
+ if (scriptFormat) scriptsAsPromised.push(parseScript(el, scriptFormat));
64
+ super.visitElement(el, context);
65
+ }
66
+ }(), rootNodes);
67
+ return { scripts: await Promise.all(scriptsAsPromised) };
68
+ async function parseScript(el, scriptFormat) {
69
+ const endSourceSpan = el.endSourceSpan;
70
+ if (endSourceSpan === null || endSourceSpan === void 0) throw new Error("HTML element without an end source span");
71
+ const content = text.substring(el.startSourceSpan.end.offset, endSourceSpan.start.offset);
72
+ const ast = await parserContext.parse(content, fileName, scriptFormat);
73
+ if (ast) {
74
+ const offset = el.startSourceSpan.end;
75
+ const rootStart = ast.root.start;
76
+ if (rootStart === null || rootStart === void 0) throw new Error("Babel File node without a start offset");
77
+ const rootEnd = ast.root.end;
78
+ if (rootEnd === null || rootEnd === void 0) throw new Error("Babel File node without an end offset");
79
+ ast.root.start = rootStart + offset.offset;
80
+ ast.root.end = rootEnd + offset.offset;
81
+ return {
82
+ ...ast,
83
+ offset: {
84
+ column: offset.offset,
85
+ line: offset.line
86
+ }
87
+ };
88
+ }
89
+ return ast;
90
+ }
91
+ }
92
+ function toSourceLocation({ line, col }) {
93
+ return {
94
+ line: line + 1,
95
+ column: col
96
+ };
97
+ }
98
+ function getScriptType(element) {
99
+ if (element.name === "script") {
100
+ if (!element.attrs.some((attr) => attr.name === "src")) {
101
+ const type = element.attrs.find((attr) => attr.name === "type") ?? element.attrs.find((attr) => attr.name === "lang");
102
+ if (type) {
103
+ const typeToLower = type.value.toLowerCase();
104
+ if (TSX_SCRIPT_TYPES.includes(typeToLower)) return "tsx";
105
+ if (TS_SCRIPT_TYPES.includes(typeToLower)) return "ts";
106
+ if (JS_SCRIPT_TYPES.includes(typeToLower)) return "js";
107
+ } else return "js";
108
+ }
109
+ }
110
+ }
111
+ //#endregion
112
+ //#region src/parsers/js-parser.ts
113
+ const { types: types$15, parseAsync: parseAsync$1 } = babel;
114
+ function isParserPlugin(value) {
115
+ return typeof value === "string" || Array.isArray(value);
116
+ }
117
+ function isParserPluginArray(value) {
118
+ return value.every(isParserPlugin);
119
+ }
120
+ function getParserPlugins(override) {
121
+ if (override === null || override === void 0) return defaultPlugins;
122
+ if (isParserPluginArray(override)) return override;
123
+ throw new Error("Invalid parser plugins: expected ParserPlugin[]");
124
+ }
125
+ const defaultPlugins = [
126
+ "doExpressions",
127
+ "objectRestSpread",
128
+ "classProperties",
129
+ "exportDefaultFrom",
130
+ "exportNamespaceFrom",
131
+ "asyncGenerators",
132
+ "functionBind",
133
+ "functionSent",
134
+ "dynamicImport",
135
+ "numericSeparator",
136
+ "importMeta",
137
+ "optionalCatchBinding",
138
+ "optionalChaining",
139
+ "classPrivateProperties",
140
+ ["pipelineOperator", { proposal: "minimal" }],
141
+ "nullishCoalescingOperator",
142
+ "bigInt",
143
+ "throwExpressions",
144
+ "logicalAssignment",
145
+ "classPrivateMethods",
146
+ "v8intrinsic",
147
+ "partialApplication",
148
+ ["decorators", { decoratorsBeforeExport: false }],
149
+ "jsx"
150
+ ];
151
+ function createParser$1({ plugins: pluginsOverride }) {
152
+ return async function parse(text, fileName) {
153
+ const plugins = getParserPlugins(pluginsOverride);
154
+ const ast = await parseAsync$1(text, {
155
+ parserOpts: { plugins: [...plugins] },
156
+ filename: fileName,
157
+ sourceType: "module"
158
+ });
159
+ if (ast === null || ast === void 0) throw new Error(`Expected ${fileName} to contain a babel.types.file, but it yielded null`);
160
+ if (types$15.isProgram(ast)) throw new Error(`Expected ${fileName} to contain a babel.types.file, but was a program`);
161
+ return {
162
+ originFileName: fileName,
163
+ rawContent: text,
164
+ format: "js",
165
+ root: ast
166
+ };
167
+ };
168
+ }
169
+ //#endregion
170
+ //#region src/util/position-converter.ts
171
+ /**
172
+ * A class that can convert a string offset back to line / column.
173
+ * Grabbed from TypeScript code base
174
+ * @see https://github.com/microsoft/TypeScript/blob/aa9b6953441b53f8b14072c047f0519b611150c4/src/compiler/scanner.ts#L503
175
+ */
176
+ var PositionConverter = class {
177
+ text;
178
+ _lineStarts;
179
+ get lineStarts() {
180
+ if (!this._lineStarts) this._lineStarts = this.computeLineStarts(this.text);
181
+ return this._lineStarts;
182
+ }
183
+ constructor(text) {
184
+ this.text = text;
185
+ }
186
+ positionFromOffset(offset) {
187
+ const lineNumber = this.computeLineOfPosition(offset);
188
+ const lineStart = this.lineStarts[lineNumber];
189
+ if (lineStart === void 0) throw new Error("Line start not found for computed line number");
190
+ return {
191
+ line: lineNumber,
192
+ column: offset - lineStart
193
+ };
194
+ }
195
+ computeLineOfPosition(offset) {
196
+ let lineNumber = binarySearch(this.lineStarts, offset);
197
+ if (lineNumber < 0) {
198
+ lineNumber = ~lineNumber - 1;
199
+ if (lineNumber === -1) throw new Error("position cannot precede the beginning of the file");
200
+ }
201
+ return lineNumber;
202
+ }
203
+ computeLineStarts(text) {
204
+ const result = [];
205
+ let pos = 0;
206
+ let lineStart = 0;
207
+ while (pos < text.length) {
208
+ const ch = text.charCodeAt(pos);
209
+ pos++;
210
+ switch (ch) {
211
+ case 13:
212
+ if (text.charCodeAt(pos) === 10) pos++;
213
+ result.push(lineStart);
214
+ lineStart = pos;
215
+ break;
216
+ case 10:
217
+ result.push(lineStart);
218
+ lineStart = pos;
219
+ break;
220
+ default: if (ch > 127 && isLineBreak(ch)) {
221
+ result.push(lineStart);
222
+ lineStart = pos;
223
+ }
224
+ }
225
+ }
226
+ result.push(lineStart);
227
+ return result;
228
+ }
229
+ };
230
+ function binarySearch(array, value) {
231
+ if (!array.length) return -1;
232
+ let low = 0;
233
+ let high = array.length - 1;
234
+ while (low <= high) {
235
+ const middle = low + (high - low >> 1);
236
+ const midValue = array[middle];
237
+ if (midValue === void 0) throw new Error("Binary search middle value is missing");
238
+ switch (compare(midValue, value)) {
239
+ case -1:
240
+ low = middle + 1;
241
+ break;
242
+ case 0: return middle;
243
+ case 1: high = middle - 1;
244
+ }
245
+ }
246
+ return ~low;
247
+ }
248
+ function compare(a, b) {
249
+ return a < b ? -1 : a > b ? 1 : 0;
250
+ }
251
+ function isLineBreak(ch) {
252
+ return ch === 10 || ch === 13 || ch === 8232 || ch === 8233;
253
+ }
254
+ //#endregion
255
+ //#region src/util/babel-generator.ts
256
+ /**
257
+ * `@babel/generator` is CommonJS. Under Node's own ESM interop a default import
258
+ * of it is the module's `exports` object, so the code generator sits behind
259
+ * `.default` — the shape upstream reaches for, because upstream ships one
260
+ * emitted file per source file. This package ships a bundle, where the default
261
+ * import is already the function and `.default` is `undefined`, which fails at
262
+ * the first mutant with `generator is not a function` rather than at build
263
+ * time. Resolving both shapes once keeps the printers and the mutant's
264
+ * replacement code identical under either layout.
265
+ */
266
+ const generate = typeof generator === "function" ? generator : generator.default;
267
+ //#endregion
268
+ //#region src/mutant.ts
269
+ const { traverse: traverse$2 } = babel;
270
+ var Mutant$1 = class {
271
+ id;
272
+ fileName;
273
+ original;
274
+ offset;
275
+ replacementCode;
276
+ replacement;
277
+ mutatorName;
278
+ ignoreReason;
279
+ constructor(id, fileName, original, specs, offset = {
280
+ column: 0,
281
+ line: 0
282
+ }) {
283
+ this.id = id;
284
+ this.fileName = fileName;
285
+ this.original = original;
286
+ this.offset = offset;
287
+ this.replacement = specs.replacement;
288
+ this.mutatorName = specs.mutatorName;
289
+ this.ignoreReason = specs.ignoreReason;
290
+ this.replacementCode = generate(this.replacement).code;
291
+ }
292
+ toApiMutant() {
293
+ const loc = this.original.loc;
294
+ if (loc === void 0 || loc === null) throw new Error("Babel node without a source location");
295
+ return {
296
+ fileName: this.fileName,
297
+ id: this.id,
298
+ location: toApiLocation(loc, this.offset),
299
+ mutatorName: this.mutatorName,
300
+ replacement: this.replacementCode,
301
+ ...this.ignoreReason === void 0 ? {} : { statusReason: this.ignoreReason },
302
+ ...this.ignoreReason === void 0 ? {} : { status: "Ignored" }
303
+ };
304
+ }
305
+ /**
306
+ * Applies the mutant in (a copy of) the AST, without changing provided AST.
307
+ * Can the tree itself (in which case the replacement is returned),
308
+ * or can be nested in the given tree.
309
+ *
310
+ * Returns a plain node rather than the argument's own type: whether the
311
+ * replacement fits a given position is the placer's claim, and the placer
312
+ * checks it with a Babel predicate. A generic return would have to assert it
313
+ * here, where nothing can check it.
314
+ * @param originalTree The original node, which will be treated as readonly
315
+ */
316
+ applied(originalTree) {
317
+ if (originalTree === this.original) return this.replacement;
318
+ else {
319
+ const mutatedAst = deepCloneNode(originalTree);
320
+ let applied = false;
321
+ const { original, replacement } = this;
322
+ traverse$2(mutatedAst, {
323
+ noScope: true,
324
+ enter(path) {
325
+ if (eqNode(path.node, original)) {
326
+ path.replaceWith(replacement);
327
+ path.stop();
328
+ applied = true;
329
+ }
330
+ }
331
+ });
332
+ if (!applied) throw new Error(`Could not apply mutant ${JSON.stringify(this.replacement)}.`);
333
+ return mutatedAst;
334
+ }
335
+ }
336
+ };
337
+ function toApiLocation(source, offset) {
338
+ return {
339
+ start: toPosition(source.start, offset),
340
+ end: toPosition(source.end, offset)
341
+ };
342
+ }
343
+ function toPosition(source, offset) {
344
+ return {
345
+ column: source.column + (source.line === 1 ? offset.column : 0),
346
+ line: source.line + offset.line - 1
347
+ };
348
+ }
349
+ //#endregion
350
+ //#region src/mutators/arithmetic-operator-mutator.ts
351
+ const arithmeticOperatorReplacements = Object.freeze({
352
+ "+": "-",
353
+ "-": "+",
354
+ "*": "/",
355
+ "/": "*",
356
+ "%": "*"
357
+ });
358
+ const arithmeticOperatorMutator = {
359
+ name: "ArithmeticOperator",
360
+ *mutate(path) {
361
+ if (path.isBinaryExpression() && isSupported$3(path.node.operator, path.node)) {
362
+ const mutatedOperator = arithmeticOperatorReplacements[path.node.operator];
363
+ const replacement = deepCloneNode(path.node);
364
+ replacement.operator = mutatedOperator;
365
+ yield replacement;
366
+ }
367
+ }
368
+ };
369
+ function isSupported$3(operator, node) {
370
+ if (!Object.keys(arithmeticOperatorReplacements).includes(operator)) return false;
371
+ const stringTypes = ["StringLiteral", "TemplateLiteral"];
372
+ const leftType = node.left.type === "BinaryExpression" ? node.left.right.type : node.left.type;
373
+ if (stringTypes.includes(node.right.type) || stringTypes.includes(leftType)) return false;
374
+ return true;
375
+ }
376
+ //#endregion
377
+ //#region src/mutators/array-declaration-mutator.ts
378
+ const { types: types$14 } = babel;
379
+ const arrayDeclarationMutator = {
380
+ name: "ArrayDeclaration",
381
+ *mutate(path) {
382
+ if (path.isArrayExpression()) yield path.node.elements.length ? types$14.arrayExpression() : types$14.arrayExpression([types$14.stringLiteral("Stryker was here")]);
383
+ if ((path.isCallExpression() || path.isNewExpression()) && types$14.isIdentifier(path.node.callee) && path.node.callee.name === "Array") {
384
+ const mutatedCallArgs = path.node.arguments.length ? [] : [types$14.arrayExpression()];
385
+ yield types$14.isNewExpression(path.node) ? types$14.newExpression(deepCloneNode(path.node.callee), mutatedCallArgs) : types$14.callExpression(deepCloneNode(path.node.callee), mutatedCallArgs);
386
+ }
387
+ }
388
+ };
389
+ //#endregion
390
+ //#region src/mutators/arrow-function-mutator.ts
391
+ const { types: types$13 } = babel;
392
+ const arrowFunctionMutator = {
393
+ name: "ArrowFunction",
394
+ *mutate(path) {
395
+ if (path.isArrowFunctionExpression() && !types$13.isBlockStatement(path.node.body) && !(types$13.isIdentifier(path.node.body) && path.node.body.name === "undefined")) yield types$13.arrowFunctionExpression([], types$13.identifier("undefined"));
396
+ }
397
+ };
398
+ //#endregion
399
+ //#region src/mutators/assignment-operator-mutator.ts
400
+ const assignmentOperatorReplacements = Object.freeze({
401
+ "+=": "-=",
402
+ "-=": "+=",
403
+ "*=": "/=",
404
+ "/=": "*=",
405
+ "%=": "*=",
406
+ "<<=": ">>=",
407
+ ">>=": "<<=",
408
+ "&=": "|=",
409
+ "|=": "&=",
410
+ "&&=": "||=",
411
+ "||=": "&&=",
412
+ "??=": "&&="
413
+ });
414
+ const stringTypes = Object.freeze(["StringLiteral", "TemplateLiteral"]);
415
+ const stringAssignmentTypes = Object.freeze([
416
+ "&&=",
417
+ "||=",
418
+ "??="
419
+ ]);
420
+ const assignmentOperatorMutator = {
421
+ name: "AssignmentOperator",
422
+ *mutate(path) {
423
+ if (path.isAssignmentExpression() && isSupportedAssignmentOperator(path.node.operator) && isSupported$2(path.node)) {
424
+ const mutatedOperator = assignmentOperatorReplacements[path.node.operator];
425
+ const replacement = deepCloneNode(path.node);
426
+ replacement.operator = mutatedOperator;
427
+ yield replacement;
428
+ }
429
+ }
430
+ };
431
+ function isSupportedAssignmentOperator(operator) {
432
+ return Object.keys(assignmentOperatorReplacements).includes(operator);
433
+ }
434
+ function isSupported$2(node) {
435
+ if (stringTypes.includes(node.right.type) && !stringAssignmentTypes.includes(node.operator)) return false;
436
+ return true;
437
+ }
438
+ //#endregion
439
+ //#region src/mutators/block-statement-mutator.ts
440
+ const { types: types$12 } = babel;
441
+ const blockStatementMutator = {
442
+ name: "BlockStatement",
443
+ *mutate(path) {
444
+ if (path.isBlockStatement() && isValid(path)) yield types$12.blockStatement([]);
445
+ }
446
+ };
447
+ function isValid(path) {
448
+ return !isEmpty(path) && !isInvalidConstructorBody(path);
449
+ }
450
+ function isEmpty(path) {
451
+ return !path.node.body.length;
452
+ }
453
+ /**
454
+ * Checks to see if a statement is an invalid constructor body
455
+ * @example
456
+ * // Invalid!
457
+ * class Foo extends Bar {
458
+ * constructor(public baz: string) {
459
+ * super(42);
460
+ * }
461
+ * }
462
+ * @example
463
+ * // Invalid!
464
+ * class Foo extends Bar {
465
+ * public baz = 'string';
466
+ * constructor() {
467
+ * super(42);
468
+ * }
469
+ * }
470
+ * @see https://github.com/stryker-mutator/stryker-js/issues/2314
471
+ * @see https://github.com/stryker-mutator/stryker-js/issues/2474
472
+ */
473
+ function isInvalidConstructorBody(blockStatement) {
474
+ return Boolean(blockStatement.parentPath.isClassMethod() && blockStatement.parentPath.node.kind === "constructor" && (containsTSParameterProperties(blockStatement.parentPath) || containsInitializedClassProperties(blockStatement.parentPath)) && hasSuperExpression(blockStatement));
475
+ }
476
+ function containsTSParameterProperties(constructor) {
477
+ return constructor.node.params.some((param) => types$12.isTSParameterProperty(param));
478
+ }
479
+ function containsInitializedClassProperties(constructor) {
480
+ return constructor.parentPath.isClassBody() && constructor.parentPath.node.body.some((classMember) => types$12.isClassProperty(classMember) && classMember.value);
481
+ }
482
+ function hasSuperExpression(constructor) {
483
+ let hasSuper = false;
484
+ constructor.traverse({ Super(path) {
485
+ if (path.parentPath.isCallExpression()) {
486
+ path.stop();
487
+ hasSuper = true;
488
+ }
489
+ } });
490
+ return hasSuper;
491
+ }
492
+ //#endregion
493
+ //#region src/mutators/boolean-literal-mutator.ts
494
+ const { types: types$11 } = babel;
495
+ const booleanLiteralMutator = {
496
+ name: "BooleanLiteral",
497
+ *mutate(path) {
498
+ if (path.isBooleanLiteral()) yield types$11.booleanLiteral(!path.node.value);
499
+ if (path.isUnaryExpression() && path.node.operator === "!" && path.node.prefix) yield deepCloneNode(path.node.argument);
500
+ }
501
+ };
502
+ //#endregion
503
+ //#region src/mutators/conditional-expression-mutator.ts
504
+ const booleanOperators = Object.freeze([
505
+ "!=",
506
+ "!==",
507
+ "&&",
508
+ "<",
509
+ "<=",
510
+ "==",
511
+ "===",
512
+ ">",
513
+ ">=",
514
+ "||"
515
+ ]);
516
+ const { types: types$10 } = babel;
517
+ const conditionalExpressionMutator = {
518
+ name: "ConditionalExpression",
519
+ *mutate(path) {
520
+ if (isTestOfLoop(path)) yield types$10.booleanLiteral(false);
521
+ else if (isTestOfCondition(path)) {
522
+ yield types$10.booleanLiteral(true);
523
+ yield types$10.booleanLiteral(false);
524
+ } else if (isBooleanExpression(path)) {
525
+ if (path.parent?.type === "LogicalExpression") {
526
+ if (path.parent.operator === "||") {
527
+ yield types$10.booleanLiteral(false);
528
+ return;
529
+ }
530
+ if (path.parent.operator === "&&") {
531
+ yield types$10.booleanLiteral(true);
532
+ return;
533
+ }
534
+ }
535
+ yield types$10.booleanLiteral(true);
536
+ yield types$10.booleanLiteral(false);
537
+ } else if (path.isForStatement() && !path.node.test) {
538
+ const replacement = deepCloneNode(path.node);
539
+ replacement.test = types$10.booleanLiteral(false);
540
+ yield replacement;
541
+ } else if (path.isSwitchCase() && path.node.consequent.length > 0) {
542
+ const replacement = deepCloneNode(path.node);
543
+ replacement.consequent = [];
544
+ yield replacement;
545
+ }
546
+ }
547
+ };
548
+ function isTestOfLoop(path) {
549
+ const { parentPath } = path;
550
+ if (!parentPath) return false;
551
+ return (parentPath.isForStatement() || parentPath.isWhileStatement() || parentPath.isDoWhileStatement()) && parentPath.node.test === path.node;
552
+ }
553
+ function isTestOfCondition(path) {
554
+ const { parentPath } = path;
555
+ if (!parentPath) return false;
556
+ return parentPath.isIfStatement() && parentPath.node.test === path.node;
557
+ }
558
+ function isBooleanExpression(path) {
559
+ return (path.isBinaryExpression() || path.isLogicalExpression()) && booleanOperators.includes(path.node.operator);
560
+ }
561
+ //#endregion
562
+ //#region src/mutators/equality-operator-mutator.ts
563
+ const { types: t$2 } = babel;
564
+ const operators = {
565
+ "<": ["<=", ">="],
566
+ "<=": ["<", ">"],
567
+ ">": [">=", "<="],
568
+ ">=": [">", "<"],
569
+ "==": ["!="],
570
+ "!=": ["=="],
571
+ "===": ["!=="],
572
+ "!==": ["==="]
573
+ };
574
+ function isEqualityOperator(operator) {
575
+ return Object.keys(operators).includes(operator);
576
+ }
577
+ const equalityOperatorMutator = {
578
+ name: "EqualityOperator",
579
+ *mutate(path) {
580
+ if (path.isBinaryExpression() && isEqualityOperator(path.node.operator)) for (const mutableOperator of operators[path.node.operator]) {
581
+ const replacement = t$2.cloneNode(path.node, true);
582
+ replacement.operator = mutableOperator;
583
+ yield replacement;
584
+ }
585
+ }
586
+ };
587
+ //#endregion
588
+ //#region src/mutators/logical-operator-mutator.ts
589
+ const logicalOperatorReplacements = Object.freeze({
590
+ "&&": "||",
591
+ "||": "&&",
592
+ "??": "&&"
593
+ });
594
+ const logicalOperatorMutator = {
595
+ name: "LogicalOperator",
596
+ *mutate(path) {
597
+ if (path.isLogicalExpression() && isSupported$1(path.node.operator)) {
598
+ const mutatedOperator = logicalOperatorReplacements[path.node.operator];
599
+ const replacement = deepCloneNode(path.node);
600
+ replacement.operator = mutatedOperator;
601
+ yield replacement;
602
+ }
603
+ }
604
+ };
605
+ function isSupported$1(operator) {
606
+ return Object.keys(logicalOperatorReplacements).includes(operator);
607
+ }
608
+ //#endregion
609
+ //#region src/mutators/method-expression-mutator.ts
610
+ const { types: types$9 } = babel;
611
+ const replacements = /* @__PURE__ */ new Map([
612
+ ["charAt", null],
613
+ ["endsWith", "startsWith"],
614
+ ["every", "some"],
615
+ ["filter", null],
616
+ ["reverse", null],
617
+ ["slice", null],
618
+ ["sort", null],
619
+ ["substr", null],
620
+ ["substring", null],
621
+ ["toLocaleLowerCase", "toLocaleUpperCase"],
622
+ ["toLowerCase", "toUpperCase"],
623
+ ["trim", null],
624
+ ["trimEnd", "trimStart"],
625
+ ["min", "max"],
626
+ ["setDate", "setTime"],
627
+ ["setFullYear", "setMonth"],
628
+ ["setHours", "setMinutes"],
629
+ ["setSeconds", "setMilliseconds"],
630
+ ["setUTCDate", "setTime"],
631
+ ["setUTCFullYear", "setUTCMonth"],
632
+ ["setUTCHours", "setUTCMinutes"],
633
+ ["setUTCSeconds", "setUTCMilliseconds"]
634
+ ]);
635
+ const noReverseRemplacements = ["getUTCDate", "setUTCDate"];
636
+ for (const [key, value] of Array.from(replacements)) if (value && !noReverseRemplacements.includes(key)) replacements.set(value, key);
637
+ const methodExpressionMutator = {
638
+ name: "MethodExpression",
639
+ *mutate(path) {
640
+ if (!(path.isCallExpression() || path.isOptionalCallExpression())) return;
641
+ const { callee } = path.node;
642
+ if (!(types$9.isMemberExpression(callee) || types$9.isOptionalMemberExpression(callee)) || !types$9.isIdentifier(callee.property)) return;
643
+ const newName = replacements.get(callee.property.name);
644
+ if (newName === void 0) return;
645
+ if (newName === null) {
646
+ yield deepCloneNode(callee.object);
647
+ return;
648
+ }
649
+ const nodeArguments = path.node.arguments.map((argumentNode) => deepCloneNode(argumentNode));
650
+ const mutatedCallee = types$9.isMemberExpression(callee) ? types$9.memberExpression(deepCloneNode(callee.object), types$9.identifier(newName), false, callee.optional) : types$9.optionalMemberExpression(deepCloneNode(callee.object), types$9.identifier(newName), false, callee.optional);
651
+ yield types$9.isCallExpression(path.node) ? types$9.callExpression(mutatedCallee, nodeArguments) : types$9.optionalCallExpression(mutatedCallee, nodeArguments, path.node.optional);
652
+ }
653
+ };
654
+ //#endregion
655
+ //#region src/mutators/object-literal-mutator.ts
656
+ const { types: types$8 } = babel;
657
+ const objectLiteralMutator = {
658
+ name: "ObjectLiteral",
659
+ *mutate(path) {
660
+ if (path.isObjectExpression() && path.node.properties.length > 0) yield types$8.objectExpression([]);
661
+ }
662
+ };
663
+ //#endregion
664
+ //#region src/mutators/optional-chaining-mutator.ts
665
+ const { types: t$1 } = babel;
666
+ /**
667
+ * Mutates optional chaining operators
668
+ * Note that the AST for optional chaining might not be what you expect. Nodes of type `OptionalMemberExpression` can be either optional or not-optional
669
+ *
670
+ * For example: In this expression: `foo?.bar.baz` the `.baz` member expression is of type `OptionalMemberExpression`, because it is part of an optional chain, but is is _not_ optional.
671
+ * Only the `.bar` optional member expression is optional.
672
+ *
673
+ * @example
674
+ * foo?.bar -> foo.bar
675
+ * foo?.[1] -> foo[1]
676
+ * foo?.() -> foo()
677
+ */
678
+ const optionalChainingMutator = {
679
+ name: "OptionalChaining",
680
+ *mutate(path) {
681
+ if (path.isOptionalMemberExpression() && path.node.optional) yield t$1.optionalMemberExpression(t$1.cloneNode(path.node.object, true), t$1.cloneNode(path.node.property, true), path.node.computed, false);
682
+ if (path.isOptionalCallExpression() && path.node.optional) yield t$1.optionalCallExpression(t$1.cloneNode(path.node.callee, true), path.node.arguments.map((arg) => t$1.cloneNode(arg, true)), false);
683
+ }
684
+ };
685
+ //#endregion
686
+ //#region src/mutators/regex-mutator.ts
687
+ const { types: types$7 } = babel;
688
+ /**
689
+ * Checks that a string literal is an obvious regex string literal
690
+ * @param path The string literal to checks
691
+ * @example
692
+ * new RegExp("\\d{4}");
693
+ */
694
+ function isObviousRegexString(path) {
695
+ return path.parentPath.isNewExpression() && types$7.isIdentifier(path.parentPath.node.callee) && path.parentPath.node.callee.name === RegExp.name && path.parentPath.node.arguments[0] === path.node;
696
+ }
697
+ function getFlags(path) {
698
+ if (types$7.isStringLiteral(path.node.arguments[1])) return path.node.arguments[1].value;
699
+ }
700
+ const weaponRegexOptions = { mutationLevels: [1] };
701
+ const regexMutator = {
702
+ name: "Regex",
703
+ *mutate(path) {
704
+ if (path.isRegExpLiteral()) for (const replacementPattern of mutatePattern(path.node.pattern, path.node.flags)) yield types$7.regExpLiteral(replacementPattern, path.node.flags);
705
+ else if (path.isStringLiteral() && isObviousRegexString(path)) {
706
+ const parentPath = path.parentPath;
707
+ if (parentPath.isNewExpression()) {
708
+ const flags = getFlags(parentPath);
709
+ for (const replacementPattern of mutatePattern(path.node.value, flags)) yield types$7.stringLiteral(replacementPattern);
710
+ }
711
+ }
712
+ }
713
+ };
714
+ function mutatePattern(pattern, flags) {
715
+ if (pattern.length) try {
716
+ return weaponRegex.mutate(pattern, flags, weaponRegexOptions).map((mutant) => mutant.pattern);
717
+ } catch (err) {
718
+ const message = err instanceof Error ? err.message : typeof err === "string" ? err : JSON.stringify(err) ?? "Unknown error";
719
+ console.error(`[RegexMutator]: The Regex parser of weapon-regex couldn't parse this regex pattern: "${pattern}". Please report this issue at https://github.com/stryker-mutator/weapon-regex/issues. Inner error: ${message}`);
720
+ }
721
+ return [];
722
+ }
723
+ //#endregion
724
+ //#region src/mutators/string-literal-mutator.ts
725
+ const { types: types$6 } = babel;
726
+ const stringLiteralMutator = {
727
+ name: "StringLiteral",
728
+ *mutate(path) {
729
+ if (path.isTemplateLiteral()) {
730
+ const firstQuasi = path.node.quasis[0];
731
+ if (firstQuasi === void 0) throw new Error("Template literal without quasis");
732
+ const replacement = path.node.quasis.length === 1 && firstQuasi.value.raw.length === 0 ? "Stryker was here!" : "";
733
+ yield types$6.templateLiteral([types$6.templateElement({ raw: replacement })], []);
734
+ }
735
+ if (path.isStringLiteral() && isValidParent(path)) yield types$6.stringLiteral(path.node.value.length === 0 ? "Stryker was here!" : "");
736
+ }
737
+ };
738
+ function isValidParent(child) {
739
+ const { parent } = child;
740
+ return !isImportExportRelated(parent) && !isJsxOrExpressionRelated(parent) && !isObjectOrClassPropertyKey(parent, child) && !isDisallowedCallExpression(parent);
741
+ }
742
+ function isImportExportRelated(parent) {
743
+ return types$6.isImportDeclaration(parent) || types$6.isExportDeclaration(parent) || types$6.isImportOrExportDeclaration(parent) || types$6.isTSExternalModuleReference(parent);
744
+ }
745
+ function isJsxOrExpressionRelated(parent) {
746
+ return types$6.isJSXAttribute(parent) || types$6.isExpressionStatement(parent) || types$6.isTSLiteralType(parent) || types$6.isObjectMethod(parent);
747
+ }
748
+ function isObjectOrClassPropertyKey(parent, child) {
749
+ return types$6.isObjectProperty(parent) && parent.key === child.node || types$6.isClassProperty(parent) && parent.key === child.node;
750
+ }
751
+ function isDisallowedCallExpression(parent) {
752
+ return isRequireCall(parent) || isSymbolCall(parent) || isImportCall(parent);
753
+ }
754
+ function isRequireCall(parent) {
755
+ return types$6.isCallExpression(parent) && types$6.isIdentifier(parent.callee, { name: "require" });
756
+ }
757
+ function isSymbolCall(parent) {
758
+ return types$6.isCallExpression(parent) && types$6.isIdentifier(parent.callee, { name: "Symbol" });
759
+ }
760
+ function isImportCall(parent) {
761
+ return types$6.isCallExpression(parent) && types$6.isImport(parent.callee);
762
+ }
763
+ //#endregion
764
+ //#region src/mutators/unary-operator-mutator.ts
765
+ const { types: types$5 } = babel;
766
+ var UnaryOperator = /* @__PURE__ */ function(UnaryOperator) {
767
+ UnaryOperator["+"] = "-";
768
+ UnaryOperator["-"] = "+";
769
+ UnaryOperator["~"] = "";
770
+ return UnaryOperator;
771
+ }(UnaryOperator || {});
772
+ const unaryOperatorMutator = {
773
+ name: "UnaryOperator",
774
+ *mutate(path) {
775
+ if (path.isUnaryExpression() && isSupported(path.node.operator) && path.node.prefix) {
776
+ const mutatedOperator = UnaryOperator[path.node.operator];
777
+ yield isPlusOrMinus(mutatedOperator) ? types$5.unaryExpression(mutatedOperator, deepCloneNode(path.node.argument)) : deepCloneNode(path.node.argument);
778
+ }
779
+ }
780
+ };
781
+ function isSupported(operator) {
782
+ return Object.keys(UnaryOperator).includes(operator);
783
+ }
784
+ function isPlusOrMinus(operator) {
785
+ return operator === "-" || operator === "+";
786
+ }
787
+ //#endregion
788
+ //#region src/mutators/update-operator-mutator.ts
789
+ const { types: types$4 } = babel;
790
+ var UpdateOperators = /* @__PURE__ */ function(UpdateOperators) {
791
+ UpdateOperators["++"] = "--";
792
+ UpdateOperators["--"] = "++";
793
+ return UpdateOperators;
794
+ }(UpdateOperators || {});
795
+ //#endregion
796
+ //#region src/mutators/mutate.ts
797
+ const allMutators = [
798
+ arithmeticOperatorMutator,
799
+ arrayDeclarationMutator,
800
+ arrowFunctionMutator,
801
+ blockStatementMutator,
802
+ booleanLiteralMutator,
803
+ conditionalExpressionMutator,
804
+ equalityOperatorMutator,
805
+ logicalOperatorMutator,
806
+ methodExpressionMutator,
807
+ objectLiteralMutator,
808
+ stringLiteralMutator,
809
+ unaryOperatorMutator,
810
+ {
811
+ name: "UpdateOperator",
812
+ *mutate(path) {
813
+ if (path.isUpdateExpression()) yield types$4.updateExpression(UpdateOperators[path.node.operator], deepCloneNode(path.node.argument), path.node.prefix);
814
+ }
815
+ },
816
+ regexMutator,
817
+ optionalChainingMutator,
818
+ assignmentOperatorMutator
819
+ ];
820
+ //#endregion
821
+ //#region src/transformers/mutant-collector.ts
822
+ var MutantCollector = class {
823
+ _mutants = [];
824
+ get mutants() {
825
+ return this._mutants;
826
+ }
827
+ /**
828
+ * Adds mutants to the internal mutant list.
829
+ * @param fileName file name that houses the mutant
830
+ * @param original The node to mutate
831
+ * @param mutables the named node mutation to be added
832
+ * @param contextPath the context where these mutants are found and should be placed as close by as possible
833
+ * @param offset offset of mutant nodes
834
+ * @returns The mutant (for testability)
835
+ */
836
+ collect(fileName, original, mutable, offset = {
837
+ line: 0,
838
+ column: 0
839
+ }) {
840
+ const mutant = new Mutant$1(this._mutants.length.toString(), fileName, original, mutable, offset);
841
+ this._mutants.push(mutant);
842
+ return mutant;
843
+ }
844
+ hasPlacedMutants(fileName) {
845
+ return this.mutants.some((mutant) => mutant.fileName === fileName && !mutant.ignoreReason);
846
+ }
847
+ };
848
+ //#endregion
849
+ //#region src/mutant-placers/mutant-placer.ts
850
+ /**
851
+ * Narrows an applied mutant to the node kind a placer emits. `applied()` hands
852
+ * back a plain node — whether it fits this position is the placer's claim, and
853
+ * `canPlace` is what established it, so a mismatch here means the placer was
854
+ * handed a mutant it never accepted.
855
+ */
856
+ function nodeOfKind(mutant, node, isKind, kind) {
857
+ if (!isKind(node)) throw new Error(`Cannot place mutant ${mutant.id}: expected ${kind}, got ${node.type}`);
858
+ return node;
859
+ }
860
+ //#endregion
861
+ //#region src/mutant-placers/expression-mutant-placer.ts
862
+ const { types: types$3 } = babel;
863
+ /**
864
+ * Will set the identifier of anonymous function expressions if is located in a variable declaration.
865
+ * Will treat input as readonly. Returns undefined if not needed.
866
+ * @example
867
+ * const a = function() { }
868
+ * becomes
869
+ * const a = function a() {}
870
+ */
871
+ function classOrFunctionExpressionNamedIfNeeded(path) {
872
+ if ((path.isFunctionExpression() || path.isClassExpression()) && !path.node.id) {
873
+ if (path.parentPath.isVariableDeclarator() && types$3.isIdentifier(path.parentPath.node.id)) {
874
+ path.node.id = path.parentPath.node.id;
875
+ return path.node;
876
+ } else if (path.parentPath.isObjectProperty() && types$3.isIdentifier(path.parentPath.node.key) && path.getStatementParent()?.isVariableDeclaration()) {
877
+ path.node.id = path.parentPath.node.key;
878
+ return path.node;
879
+ }
880
+ }
881
+ }
882
+ /**
883
+ * Will set the identifier of anonymous arrow function expressions if is located in a variable declaration.
884
+ * Will treat input as readonly. Returns undefined if not needed.
885
+ * @example
886
+ * const a = () => { }
887
+ * becomes
888
+ * const a = (() => { const a = () => {}; return a; })()
889
+ */
890
+ function arrowFunctionExpressionNamedIfNeeded(path) {
891
+ if (path.isArrowFunctionExpression() && path.parentPath.isVariableDeclarator() && types$3.isIdentifier(path.parentPath.node.id)) return types$3.callExpression(types$3.arrowFunctionExpression([], types$3.blockStatement([types$3.variableDeclaration("const", [types$3.variableDeclarator(path.parentPath.node.id, path.node)]), types$3.returnStatement(path.parentPath.node.id)])), []);
892
+ }
893
+ function nameIfAnonymous(path) {
894
+ return classOrFunctionExpressionNamedIfNeeded(path) ?? arrowFunctionExpressionNamedIfNeeded(path) ?? path.node;
895
+ }
896
+ function isMemberOrCallOrNonNullExpression(path) {
897
+ return isCallExpression(path) || isMemberOrNonNullExpression(path);
898
+ }
899
+ function isMemberOrNonNullExpression(path) {
900
+ return isMemberExpression(path) || path.isTSNonNullExpression();
901
+ }
902
+ function isMemberExpression(path) {
903
+ return path.isMemberExpression() || path.isOptionalMemberExpression();
904
+ }
905
+ function isCallExpression(path) {
906
+ return path.isCallExpression() || path.isOptionalCallExpression();
907
+ }
908
+ function isValidExpression(path) {
909
+ const parent = path.parentPath;
910
+ return !isObjectPropertyKey() && !isPartOfChain() && !parent.isTaggedTemplateExpression() && !isPartOfDeleteExpression() && !isPartOfAssignmentExpression();
911
+ /**
912
+ * Determines if the expression is property of an object.
913
+ * @example
914
+ * const a = {
915
+ * 'foo': 'bar' // 'foo' here is an object property
916
+ * };
917
+ */
918
+ function isObjectPropertyKey() {
919
+ return parent.isObjectProperty() && parent.node.key === path.node;
920
+ }
921
+ /**
922
+ * Determines if the expression is part of a call/member chain.
923
+ * @example
924
+ * // bar is part of chain, foo is NOT part of the chain:
925
+ * foo.bar.baz();
926
+ * foo.bar?.baz()
927
+ * foo.bar;
928
+ * foo.bar!;
929
+ * foo.bar();
930
+ * foo?.bar();
931
+ * baz[foo.bar()]
932
+ * bar?.baz[0]
933
+ */
934
+ function isPartOfChain() {
935
+ return isMemberOrCallOrNonNullExpression(path) && (isMemberExpression(parent) && !(parent.node.computed && parent.node.property === path.node) || parent.isTSNonNullExpression() || isCallExpression(parent) && parent.node.callee === path.node);
936
+ }
937
+ /**
938
+ * Determines if the expression is part of a delete expression.
939
+ * @returns true if the expression is part of a delete expression
940
+ * @example
941
+ * delete foo.bar;
942
+ */
943
+ function isPartOfDeleteExpression() {
944
+ return parent.isUnaryExpression() && parent.node.operator === "delete";
945
+ }
946
+ /**
947
+ * Determines if the expression is part of an assignment expression.
948
+ * @returns true if the expression is part of an assignment expression
949
+ * @example
950
+ * foo.bar = 42;
951
+ * initialNodes.filter((n) => n.id === 'tiptilt')[0].className = tiptiltState;
952
+ */
953
+ function isPartOfAssignmentExpression() {
954
+ return parent.isAssignmentExpression() && parent.node.left === path.node;
955
+ }
956
+ }
957
+ /**
958
+ * Places the mutants with a conditional expression: `global.activeMutant === 1? mutatedCode : originalCode`;
959
+ */
960
+ const expressionMutantPlacer = {
961
+ name: "expressionMutantPlacer",
962
+ canPlace(path) {
963
+ return path.isExpression() && isValidExpression(path);
964
+ },
965
+ place(path, appliedMutants) {
966
+ let expression = nameIfAnonymous(path);
967
+ expression = mutationCoverageSequenceExpression(appliedMutants.keys(), expression);
968
+ for (const [mutant, appliedMutant] of appliedMutants) expression = types$3.conditionalExpression(mutantTestExpression(mutant.id), nodeOfKind(mutant, appliedMutant, types$3.isExpression, "an expression"), expression);
969
+ path.replaceWith(expression);
970
+ }
971
+ };
972
+ //#endregion
973
+ //#region src/mutant-placers/statement-mutant-placer.ts
974
+ const { types: t } = babel;
975
+ /**
976
+ * Mutant placer that places mutants in statements that allow it.
977
+ * It uses an `if` statement to do so
978
+ */
979
+ const statementMutantPlacer = {
980
+ name: "statementMutantPlacer",
981
+ canPlace(path) {
982
+ return path.isStatement();
983
+ },
984
+ place(path, appliedMutants) {
985
+ let statement = t.blockStatement([t.expressionStatement(mutationCoverageSequenceExpression(appliedMutants.keys())), ...path.isBlockStatement() ? path.node.body : [path.node]]);
986
+ for (const [mutant, appliedMutant] of appliedMutants) statement = t.ifStatement(mutantTestExpression(mutant.id), t.blockStatement([nodeOfKind(mutant, appliedMutant, t.isStatement, "a statement")]), statement);
987
+ path.replaceWith(path.isBlockStatement() ? t.blockStatement([statement]) : statement);
988
+ }
989
+ };
990
+ //#endregion
991
+ //#region src/mutant-placers/switch-case-mutant-placer.ts
992
+ /**
993
+ * Places the mutants with consequent of a SwitchCase node. Uses an if-statement to do so.
994
+ * @example
995
+ * case 'foo':
996
+ * if (stryMutAct_9fa48(0)) {} else {
997
+ * stryCov_9fa48(0);
998
+ * console.log('bar');
999
+ * break;
1000
+ * }
1001
+ */
1002
+ const switchCaseMutantPlacer = {
1003
+ name: "switchCaseMutantPlacer",
1004
+ canPlace(path) {
1005
+ return path.isSwitchCase();
1006
+ },
1007
+ place(path, appliedMutants) {
1008
+ let consequence = babel.types.blockStatement([babel.types.expressionStatement(mutationCoverageSequenceExpression(appliedMutants.keys())), ...path.node.consequent]);
1009
+ for (const [mutant, appliedMutant] of appliedMutants) {
1010
+ const switchCase = nodeOfKind(mutant, appliedMutant, babel.types.isSwitchCase, "a switch case");
1011
+ consequence = babel.types.ifStatement(mutantTestExpression(mutant.id), babel.types.blockStatement(switchCase.consequent), consequence);
1012
+ }
1013
+ path.replaceWith(babel.types.switchCase(path.node.test, [consequence]));
1014
+ }
1015
+ };
1016
+ //#endregion
1017
+ //#region src/mutant-placers/throw-placement-error.ts
1018
+ function throwPlacementError(error, nodePath, placer, mutants, fileName) {
1019
+ const location = `${path.relative(process.cwd(), fileName)}:${nodePath.node.loc?.start.line}:${nodePath.node.loc?.start.column}`;
1020
+ const message = `${placer.name} could not place mutants with type(s): "${new Intl.ListFormat("en").format(mutants.map((mutant) => mutant.mutatorName))}"`;
1021
+ const errorMessage = `${location} ${message}. Either remove this file from the list of files to be mutated, or exclude the mutator (using ${propertyPath()("mutator", "excludedMutations")}). 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(message)}. Original error: ${error.stack}`;
1022
+ let builtError = new Error(errorMessage);
1023
+ try {
1024
+ builtError = nodePath.buildCodeFrameError(errorMessage);
1025
+ } catch {}
1026
+ throw builtError;
1027
+ }
1028
+ //#endregion
1029
+ //#region src/mutant-placers/index.ts
1030
+ const allMutantPlacers = Object.freeze([
1031
+ expressionMutantPlacer,
1032
+ statementMutantPlacer,
1033
+ switchCaseMutantPlacer
1034
+ ]);
1035
+ //#endregion
1036
+ //#region src/util/babel-file.ts
1037
+ /**
1038
+ * Wraps a parsed AST the way Babel's own pipeline does, so
1039
+ * `NodePath#buildCodeFrameError` can render a code frame
1040
+ * (https://github.com/babel/babel/issues/11889). Without the wrapper a
1041
+ * placement failure reports no source context.
1042
+ */
1043
+ function createBabelFile(filename, code, ast) {
1044
+ return new File({ filename }, {
1045
+ code,
1046
+ ast
1047
+ });
1048
+ }
1049
+ //#endregion
1050
+ //#region src/transformers/directive-bookkeeper.ts
1051
+ const WILDCARD = "all";
1052
+ const DEFAULT_REASON = "Ignored using a comment";
1053
+ var IgnoreRule = class {
1054
+ mutatorNames;
1055
+ line;
1056
+ ignoreReason;
1057
+ previousRule;
1058
+ constructor(mutatorNames, line, ignoreReason, previousRule) {
1059
+ this.mutatorNames = mutatorNames;
1060
+ this.line = line;
1061
+ this.ignoreReason = ignoreReason;
1062
+ this.previousRule = previousRule;
1063
+ }
1064
+ matches(mutatorName, line) {
1065
+ const lineMatches = () => this.line === void 0 || this.line === line;
1066
+ const mutatorMatches = () => this.mutatorNames.includes(mutatorName) || this.mutatorNames.includes(WILDCARD);
1067
+ return lineMatches() && mutatorMatches();
1068
+ }
1069
+ findIgnoreReason(mutatorName, line) {
1070
+ if (this.matches(mutatorName, line)) return this.ignoreReason;
1071
+ return this.previousRule.findIgnoreReason(mutatorName, line);
1072
+ }
1073
+ };
1074
+ var RestoreRule = class extends IgnoreRule {
1075
+ constructor(mutatorNames, line, previousRule) {
1076
+ super(mutatorNames, line, void 0, previousRule);
1077
+ }
1078
+ };
1079
+ const rootRule = { findIgnoreReason() {} };
1080
+ /**
1081
+ * Responsible for the bookkeeping of "// Stryker" directives like "disable" and "restore".
1082
+ */
1083
+ var DirectiveBookkeeper = class {
1084
+ logger;
1085
+ allMutators;
1086
+ originFileName;
1087
+ strykerCommentDirectiveRegex = /^\s?Stryker (disable|restore)(?: (next-line))? ([a-zA-Z, ]+)(?::(.+)?)?/;
1088
+ currentIgnoreRule = rootRule;
1089
+ allMutatorNames;
1090
+ constructor(logger, allMutators, originFileName) {
1091
+ this.logger = logger;
1092
+ this.allMutators = allMutators;
1093
+ this.originFileName = originFileName;
1094
+ this.allMutatorNames = this.allMutators.map((x) => x.name.toLowerCase());
1095
+ }
1096
+ processStrykerDirectives({ loc, leadingComments }) {
1097
+ if (!leadingComments) return;
1098
+ for (const comment of leadingComments) {
1099
+ const matchResult = this.strykerCommentDirectiveRegex.exec(comment.value);
1100
+ if (!matchResult) continue;
1101
+ const directiveType = matchResult[1];
1102
+ const scope = matchResult[2];
1103
+ const mutators = matchResult[3];
1104
+ const optionalReason = matchResult[4];
1105
+ if (directiveType === void 0 || mutators === void 0) throw new Error("Stryker directive without directive type or mutators");
1106
+ let mutatorNames = mutators.split(",").map((mutator) => mutator.trim());
1107
+ this.warnAboutUnusedDirective(mutatorNames, directiveType, scope, comment);
1108
+ mutatorNames = mutatorNames.map((mutator) => mutator.toLowerCase());
1109
+ const reason = (optionalReason ?? DEFAULT_REASON).trim();
1110
+ this.applyDirective(directiveType, scope, mutatorNames, reason, loc);
1111
+ }
1112
+ }
1113
+ applyDirective(directiveType, scope, mutatorNames, reason, loc) {
1114
+ switch (directiveType) {
1115
+ case "disable":
1116
+ this.applyDisable(scope, mutatorNames, reason, loc);
1117
+ break;
1118
+ case "restore": this.applyRestore(scope, mutatorNames, loc);
1119
+ }
1120
+ }
1121
+ applyDisable(scope, mutatorNames, reason, loc) {
1122
+ switch (scope) {
1123
+ case "next-line":
1124
+ this.currentIgnoreRule = new IgnoreRule(mutatorNames, this.getLine(loc), reason, this.currentIgnoreRule);
1125
+ break;
1126
+ case void 0:
1127
+ default: this.currentIgnoreRule = new IgnoreRule(mutatorNames, void 0, reason, this.currentIgnoreRule);
1128
+ }
1129
+ }
1130
+ applyRestore(scope, mutatorNames, loc) {
1131
+ switch (scope) {
1132
+ case "next-line":
1133
+ this.currentIgnoreRule = new RestoreRule(mutatorNames, this.getLine(loc), this.currentIgnoreRule);
1134
+ break;
1135
+ case void 0:
1136
+ default: this.currentIgnoreRule = new RestoreRule(mutatorNames, void 0, this.currentIgnoreRule);
1137
+ }
1138
+ }
1139
+ getLine(loc) {
1140
+ if (loc === void 0 || loc === null || loc.start === null || loc.start === void 0) throw new Error("Babel node without location");
1141
+ return loc.start.line;
1142
+ }
1143
+ findIgnoreReason(line, mutatorName) {
1144
+ mutatorName = mutatorName.toLowerCase();
1145
+ return this.currentIgnoreRule.findIgnoreReason(mutatorName, line);
1146
+ }
1147
+ warnAboutUnusedDirective(mutators, directiveType, scope, comment) {
1148
+ for (const mutator of mutators) {
1149
+ if (mutator === WILDCARD) continue;
1150
+ if (!this.allMutatorNames.includes(mutator.toLowerCase())) {
1151
+ const commentLoc = comment.loc;
1152
+ if (commentLoc === void 0 || commentLoc === null || commentLoc.start === null || commentLoc.start === void 0) throw new Error("Comment without location");
1153
+ this.logger.warn(`Unused 'Stryker ${scope ? directiveType + " " + scope : directiveType}' directive. Mutator with name '${mutator}' not found. Directive found at: ${this.originFileName}:${commentLoc.start.line}:${commentLoc.start.column}.`);
1154
+ }
1155
+ }
1156
+ }
1157
+ };
1158
+ //#endregion
1159
+ //#region src/transformers/ignorer-bookkeeper.ts
1160
+ /**
1161
+ * Responsible for keeping track of the active ignore message and node using the configured ignore-plugins.
1162
+ */
1163
+ var IgnorerBookkeeper = class {
1164
+ ignorers;
1165
+ activeIgnored;
1166
+ get currentIgnoreMessage() {
1167
+ return this.activeIgnored?.message;
1168
+ }
1169
+ constructor(ignorers) {
1170
+ this.ignorers = ignorers;
1171
+ }
1172
+ enterNode(path) {
1173
+ if (!this.activeIgnored) this.ignorers.forEach((ignorer) => {
1174
+ const message = ignorer.shouldIgnore(path);
1175
+ if (message) this.activeIgnored = {
1176
+ node: path.node,
1177
+ message
1178
+ };
1179
+ });
1180
+ }
1181
+ leaveNode(path) {
1182
+ if (this.activeIgnored?.node === path.node) this.activeIgnored = void 0;
1183
+ }
1184
+ };
1185
+ //#endregion
1186
+ //#region src/transformers/babel-transformer.ts
1187
+ const { traverse: traverse$1 } = babel;
1188
+ const transformBabel = ({ root, originFileName, rawContent, offset }, mutantCollector, { options, mutateDescription, logger }, mutators = allMutators, mutantPlacers = allMutantPlacers) => {
1189
+ const file = createBabelFile(originFileName, rawContent, root);
1190
+ const placementMap = /* @__PURE__ */ new Map();
1191
+ const directiveBookkeeper = new DirectiveBookkeeper(logger, mutators, originFileName);
1192
+ const ignorerBookkeeper = new IgnorerBookkeeper(options.ignorers);
1193
+ traverse$1(file.ast, {
1194
+ enter(path) {
1195
+ directiveBookkeeper.processStrykerDirectives(path.node);
1196
+ if (shouldSkip(path)) path.skip();
1197
+ else {
1198
+ ignorerBookkeeper.enterNode(path);
1199
+ addToPlacementMapIfPossible(path);
1200
+ if (shouldMutate(path)) {
1201
+ const mutantsToPlace = collectMutants(path);
1202
+ if (mutantsToPlace.length) {
1203
+ const placementPath = path.find((ancestor) => placementMap.has(ancestor.node));
1204
+ if (placementPath) {
1205
+ const placement = placementMap.get(placementPath.node);
1206
+ if (placement === void 0) throw new Error("Placement not found for node");
1207
+ const { appliedMutants } = placement;
1208
+ mutantsToPlace.forEach((mutant) => appliedMutants.set(mutant, mutant.applied(placementPath.node)));
1209
+ } else throw new Error(`Mutants cannot be placed. This shouldn't happen! Unplaced mutants: ${JSON.stringify(mutantsToPlace, null, 2)}`);
1210
+ }
1211
+ }
1212
+ }
1213
+ },
1214
+ exit(path) {
1215
+ placeMutantsIfNeeded(path);
1216
+ ignorerBookkeeper.leaveNode(path);
1217
+ }
1218
+ });
1219
+ placeHeaderIfNeeded(mutantCollector, originFileName, options, root);
1220
+ /**
1221
+ * If this node can be used to place mutants on, add to the placement map
1222
+ */
1223
+ function addToPlacementMapIfPossible(path) {
1224
+ const placer = mutantPlacers.find((p) => p.canPlace(path));
1225
+ if (placer) placementMap.set(path.node, {
1226
+ appliedMutants: /* @__PURE__ */ new Map(),
1227
+ placer
1228
+ });
1229
+ }
1230
+ /**
1231
+ * Don't traverse import declarations, decorators and nodes that don't have overlap with the selected mutation ranges
1232
+ */
1233
+ function shouldSkip(path) {
1234
+ return isTypeNode(path) || isImportDeclaration(path) || path.isDecorator() || !mutateDescription || Array.isArray(mutateDescription) && mutateDescription.every((range) => !locationOverlaps(range, getNodeLocation(path.node)));
1235
+ }
1236
+ function shouldMutate(path) {
1237
+ return mutateDescription === true || Array.isArray(mutateDescription) && mutateDescription.some((range) => locationIncluded(range, getNodeLocation(path.node)));
1238
+ }
1239
+ /**
1240
+ * Place mutants that are assigned to the current node path (on exit)
1241
+ */
1242
+ function placeMutantsIfNeeded(path) {
1243
+ const mutantsPlacement = placementMap.get(path.node);
1244
+ if (mutantsPlacement?.appliedMutants.size) try {
1245
+ mutantsPlacement.placer.place(path, mutantsPlacement.appliedMutants);
1246
+ path.skip();
1247
+ } catch (error) {
1248
+ throwPlacementError(toError(error), path, mutantsPlacement.placer, [...mutantsPlacement.appliedMutants.keys()], originFileName);
1249
+ }
1250
+ }
1251
+ /**
1252
+ * Collect the mutants for the current node and return the non-ignored.
1253
+ */
1254
+ function collectMutants(path) {
1255
+ return [...mutate(path)].map((mutable) => mutantCollector.collect(originFileName, path.node, mutable, offset)).filter((mutant) => !mutant.ignoreReason);
1256
+ }
1257
+ /**
1258
+ * Generate mutants for the current node.
1259
+ * @yields {Mutable} A mutable describing the mutant to be placed
1260
+ */
1261
+ function* mutate(node) {
1262
+ for (const mutator of mutators) for (const replacement of mutator.mutate(node)) {
1263
+ const ignoreReason = directiveBookkeeper.findIgnoreReason(getNodeLocation(node.node).start.line, mutator.name) ?? findExcludedMutatorIgnoreReason(mutator.name) ?? ignorerBookkeeper.currentIgnoreMessage;
1264
+ yield {
1265
+ replacement,
1266
+ mutatorName: mutator.name,
1267
+ ...ignoreReason === void 0 ? {} : { ignoreReason }
1268
+ };
1269
+ }
1270
+ function findExcludedMutatorIgnoreReason(mutatorName) {
1271
+ if (options.excludedMutations.includes(mutatorName)) return `Ignored because of excluded mutation "${mutatorName}"`;
1272
+ else return;
1273
+ }
1274
+ }
1275
+ };
1276
+ function getNodeLocation(node) {
1277
+ const loc = node.loc;
1278
+ if (loc === void 0 || loc === null || loc.start === null || loc.start === void 0 || loc.end === null || loc.end === void 0) throw new Error("Babel node without location");
1279
+ return loc;
1280
+ }
1281
+ function toError(value) {
1282
+ if (value instanceof Error) return value;
1283
+ return new Error("Unexpected error", { cause: value });
1284
+ }
1285
+ //#endregion
1286
+ //#region src/transformers/html-transformer.ts
1287
+ const transformHtml = ({ root }, mutantCollector, context) => {
1288
+ root.scripts.forEach((ast) => {
1289
+ context.transform(ast, mutantCollector, context);
1290
+ });
1291
+ };
1292
+ //#endregion
1293
+ //#region src/transformers/svelte-transformer.ts
1294
+ const moduleScript = `<script context="module">
1295
+ \n<\/script>\n`;
1296
+ const transformSvelte = (svelte, mutantCollector, context) => {
1297
+ const { root, originFileName } = svelte;
1298
+ [root.moduleScript, ...root.additionalScripts].filter(notEmpty).forEach((script) => {
1299
+ context.transform(script.ast, mutantCollector, {
1300
+ ...context,
1301
+ options: {
1302
+ ...context.options,
1303
+ noHeader: true
1304
+ }
1305
+ });
1306
+ });
1307
+ if (mutantCollector.hasPlacedMutants(originFileName)) {
1308
+ if (!root.moduleScript) {
1309
+ root.moduleScript = {
1310
+ ast: {
1311
+ format: "js",
1312
+ root: types.file(types.program([])),
1313
+ rawContent: "",
1314
+ originFileName
1315
+ },
1316
+ range: {
1317
+ start: 26,
1318
+ end: 26
1319
+ },
1320
+ isExpression: false
1321
+ };
1322
+ svelte.rawContent = `${moduleScript}${svelte.rawContent}`;
1323
+ svelte.root.additionalScripts.forEach((script) => {
1324
+ script.range.start += moduleScript.length;
1325
+ script.range.end += moduleScript.length;
1326
+ });
1327
+ }
1328
+ placeHeader(root.moduleScript.ast.root);
1329
+ }
1330
+ };
1331
+ //#endregion
1332
+ //#region src/transformers/transformer.ts
1333
+ /**
1334
+ * Transform the AST by generating mutants and placing them in the AST.
1335
+ * Supports all AST formats supported by Stryker.
1336
+ * @param ast The Abstract Syntax Tree
1337
+ * @param mutantCollector the mutant collector that will be used to register and administer mutants
1338
+ * @param transformerContext the options used during transforming
1339
+ */
1340
+ function transform(ast, mutantCollector, transformerContext) {
1341
+ const context = {
1342
+ ...transformerContext,
1343
+ transform
1344
+ };
1345
+ switch (ast.format) {
1346
+ case "html":
1347
+ transformHtml(ast, mutantCollector, context);
1348
+ break;
1349
+ case "js":
1350
+ case "ts":
1351
+ case "tsx":
1352
+ transformBabel(ast, mutantCollector, context);
1353
+ break;
1354
+ case "svelte": transformSvelte(ast, mutantCollector, context);
1355
+ }
1356
+ }
1357
+ //#endregion
1358
+ //#region src/util/syntax-helpers.ts
1359
+ const STRYKER_NAMESPACE_HELPER = "stryNS_9fa48";
1360
+ const COVER_MUTANT_HELPER = "stryCov_9fa48";
1361
+ const IS_MUTANT_ACTIVE_HELPER = "stryMutAct_9fa48";
1362
+ const { types: types$2, traverse } = babel;
1363
+ /**
1364
+ * Returns syntax for the header if JS/TS files
1365
+ */
1366
+ const parsedInstrumentationHeader = babel.parse(`function ${STRYKER_NAMESPACE_HELPER}(){
1367
+ var g = typeof globalThis === 'object' && globalThis && globalThis.Math === Math && globalThis || new Function("return this")();
1368
+ var ns = g.${ID.NAMESPACE} || (g.${ID.NAMESPACE} = {});
1369
+ if (ns.${ID.ACTIVE_MUTANT} === undefined && g.process && g.process.env && g.process.env.${ID.ACTIVE_MUTANT_ENV_VARIABLE}) {
1370
+ ns.${ID.ACTIVE_MUTANT} = g.process.env.${ID.ACTIVE_MUTANT_ENV_VARIABLE};
1371
+ }
1372
+ function retrieveNS(){
1373
+ return ns;
1374
+ }
1375
+ ${STRYKER_NAMESPACE_HELPER} = retrieveNS;
1376
+ return retrieveNS();
1377
+ }
1378
+ ${STRYKER_NAMESPACE_HELPER}();
1379
+
1380
+ function ${COVER_MUTANT_HELPER}() {
1381
+ var ns = ${STRYKER_NAMESPACE_HELPER}();
1382
+ var cov = ns.${ID.MUTATION_COVERAGE_OBJECT} || (ns.${ID.MUTATION_COVERAGE_OBJECT} = { static: {}, perTest: {} });
1383
+ function cover() {
1384
+ var c = cov.static;
1385
+ if (ns.${ID.CURRENT_TEST_ID}) {
1386
+ c = cov.perTest[ns.${ID.CURRENT_TEST_ID}] = cov.perTest[ns.${ID.CURRENT_TEST_ID}] || {};
1387
+ }
1388
+ var a = arguments;
1389
+ for(var i=0; i < a.length; i++){
1390
+ c[a[i]] = (c[a[i]] || 0) + 1;
1391
+ }
1392
+ }
1393
+ ${COVER_MUTANT_HELPER} = cover;
1394
+ cover.apply(null, arguments);
1395
+ }
1396
+ function ${IS_MUTANT_ACTIVE_HELPER}(id) {
1397
+ var ns = ${STRYKER_NAMESPACE_HELPER}();
1398
+ function isActive(id) {
1399
+ if (ns.${ID.ACTIVE_MUTANT} === id) {
1400
+ if (ns.${ID.HIT_COUNT} !== void 0 && ++ns.${ID.HIT_COUNT} > ns.${ID.HIT_LIMIT}) {
1401
+ throw new Error('Stryker: Hit count limit reached (' + ns.${ID.HIT_COUNT} + ')');
1402
+ }
1403
+ return true;
1404
+ }
1405
+ return false;
1406
+ }
1407
+ ${IS_MUTANT_ACTIVE_HELPER} = isActive;
1408
+ return isActive(id);
1409
+ }`, {
1410
+ configFile: false,
1411
+ browserslistConfigFile: false,
1412
+ env: { targets: {} }
1413
+ });
1414
+ if (!types$2.isFile(parsedInstrumentationHeader)) throw new Error("Instrumentation header parsed as non-File");
1415
+ const instrumentationBabelHeader = parsedInstrumentationHeader.program.body;
1416
+ deepFreeze(instrumentationBabelHeader);
1417
+ /**
1418
+ * returns syntax for `global.activeMutant === $mutantId`
1419
+ * @param mutantId The id of the mutant to switch
1420
+ */
1421
+ function mutantTestExpression(mutantId) {
1422
+ return types$2.callExpression(types$2.identifier(IS_MUTANT_ACTIVE_HELPER), [types$2.stringLiteral(mutantId)]);
1423
+ }
1424
+ function eqLocation(a, b) {
1425
+ function eqPosition(start, end) {
1426
+ return start.column === end.column && start.line === end.line;
1427
+ }
1428
+ return eqPosition(a.start, b.start) && eqPosition(a.end, b.end);
1429
+ }
1430
+ function eqNode(a, b) {
1431
+ return a.type === b.type && !!a.loc && !!b.loc && eqLocation(a.loc, b.loc);
1432
+ }
1433
+ /**
1434
+ * Returns a sequence of mutation coverage counters with an optional last expression.
1435
+ *
1436
+ * @example (global.__coverMutant__(0, 1), 40 + 2)
1437
+ * @param mutants The mutants for which covering syntax needs to be generated
1438
+ * @param targetExpression The original expression
1439
+ */
1440
+ function mutationCoverageSequenceExpression(mutants, targetExpression) {
1441
+ const mutantIds = [...mutants].map((mutant) => types$2.stringLiteral(mutant.id));
1442
+ const sequence = [types$2.callExpression(types$2.identifier(COVER_MUTANT_HELPER), mutantIds)];
1443
+ if (targetExpression) sequence.push(targetExpression);
1444
+ return types$2.sequenceExpression(sequence);
1445
+ }
1446
+ function isTypeNode(path) {
1447
+ return path.isTypeAnnotation() || flowTypeAnnotationNodeTypes.includes(path.node.type) || tsTypeAnnotationNodeTypes.includes(path.node.type) || isDeclareVariableStatement(path) || isDeclareModule(path);
1448
+ }
1449
+ /**
1450
+ * Determines whether or not it is a declare variable statement node.
1451
+ * @example
1452
+ * declare const foo: 'foo';
1453
+ */
1454
+ function isDeclareVariableStatement(path) {
1455
+ return path.isVariableDeclaration() && path.node.declare === true;
1456
+ }
1457
+ /**
1458
+ * Determines whether or not a node is a string literal that is the name of a module.
1459
+ * @example
1460
+ * declare module "express" {};
1461
+ */
1462
+ function isDeclareModule(path) {
1463
+ return path.isTSModuleDeclaration() && (path.node.declare ?? false);
1464
+ }
1465
+ const tsTypeAnnotationNodeTypes = Object.freeze([
1466
+ "TSAsExpression",
1467
+ "TSInterfaceDeclaration",
1468
+ "TSTypeAnnotation",
1469
+ "TSTypeAliasDeclaration",
1470
+ "TSEnumDeclaration",
1471
+ "TSDeclareFunction",
1472
+ "TSTypeParameterInstantiation",
1473
+ "TSTypeParameterDeclaration"
1474
+ ]);
1475
+ const flowTypeAnnotationNodeTypes = Object.freeze([
1476
+ "DeclareClass",
1477
+ "DeclareFunction",
1478
+ "DeclareInterface",
1479
+ "DeclareModule",
1480
+ "DeclareModuleExports",
1481
+ "DeclareTypeAlias",
1482
+ "DeclareOpaqueType",
1483
+ "DeclareVariable",
1484
+ "DeclareExportDeclaration",
1485
+ "DeclareExportAllDeclaration",
1486
+ "InterfaceDeclaration",
1487
+ "OpaqueType",
1488
+ "TypeAlias",
1489
+ "InterfaceDeclaration"
1490
+ ]);
1491
+ function isImportDeclaration(path) {
1492
+ return types$2.isTSImportEqualsDeclaration(path.node) || path.isImportDeclaration();
1493
+ }
1494
+ /**
1495
+ * Determines if a location (needle) is included in an other location (haystack)
1496
+ * @param haystack The range to look in
1497
+ * @param needle the range to search for
1498
+ */
1499
+ function locationIncluded(haystack, needle) {
1500
+ const startIncluded = haystack.start.line < needle.start.line || haystack.start.line === needle.start.line && haystack.start.column <= needle.start.column;
1501
+ const endIncluded = haystack.end.line > needle.end.line || haystack.end.line === needle.end.line && haystack.end.column >= needle.end.column;
1502
+ return startIncluded && endIncluded;
1503
+ }
1504
+ /**
1505
+ * Determines if two locations overlap with each other
1506
+ */
1507
+ function locationOverlaps(a, b) {
1508
+ const startIncluded = a.start.line < b.end.line || a.start.line === b.end.line && a.start.column <= b.end.column;
1509
+ const endIncluded = a.end.line > b.start.line || a.end.line === b.start.line && a.end.column >= b.start.column;
1510
+ return startIncluded && endIncluded;
1511
+ }
1512
+ /**
1513
+ * Helper for `types.cloneNode(node, deep: true, withoutLocations: false);`
1514
+ */
1515
+ function deepCloneNode(node) {
1516
+ return types$2.cloneNode(node, true, false);
1517
+ }
1518
+ function placeHeaderIfNeeded(mutantCollector, originFileName, options, root) {
1519
+ if (mutantCollector.hasPlacedMutants(originFileName) && !options.noHeader) placeHeader(root);
1520
+ }
1521
+ function placeHeader(root) {
1522
+ let header = instrumentationBabelHeader;
1523
+ const leadingComments = root.program.body[0]?.leadingComments;
1524
+ if (Array.isArray(leadingComments)) {
1525
+ const firstHeader = instrumentationBabelHeader[0];
1526
+ if (firstHeader === void 0) throw new Error("Instrumentation header is empty");
1527
+ const cloned = types$2.cloneNode(firstHeader, true, false);
1528
+ cloned.leadingComments = leadingComments;
1529
+ header = [cloned, ...instrumentationBabelHeader.slice(1)];
1530
+ }
1531
+ root.program.body.unshift(...header);
1532
+ }
1533
+ //#endregion
1534
+ //#region src/parsers/svelte-parser.ts
1535
+ const MIN_SVELTE_VERSION = ">=3.30";
1536
+ function isPlainRecord(value) {
1537
+ return typeof value === "object" && value !== null;
1538
+ }
1539
+ function isUnknownArray(value) {
1540
+ return Array.isArray(value);
1541
+ }
1542
+ function isRangedProgram(value) {
1543
+ return isPlainRecord(value) && typeof value["start"] === "number" && typeof value["end"] === "number";
1544
+ }
1545
+ function isWalkFunction(value) {
1546
+ return typeof value === "function";
1547
+ }
1548
+ function isRangedBaseNode(value) {
1549
+ return isPlainRecord(value) && typeof value["type"] === "string" && typeof value["start"] === "number" && typeof value["end"] === "number";
1550
+ }
1551
+ function isTemplateExpressionType(type) {
1552
+ return type === "MustacheTag" || type === "RawMustacheTag" || type === "IfBlock" || type === "ConstTag" || type === "EachBlock" || type === "AwaitBlock" || type === "KeyBlock" || type === "EventHandler";
1553
+ }
1554
+ function tryGetScriptRangeFromElement(node) {
1555
+ if (!isPlainRecord(node) || node["type"] !== "Element" || node["name"] !== "script") return;
1556
+ const children = node["children"];
1557
+ if (!isUnknownArray(children) || children.length === 0) return;
1558
+ const firstChild = children[0];
1559
+ if (!isPlainRecord(firstChild) || firstChild["type"] !== "Text" || typeof firstChild["start"] !== "number" || typeof firstChild["end"] !== "number") return;
1560
+ return {
1561
+ start: firstChild["start"],
1562
+ end: firstChild["end"],
1563
+ isExpression: false
1564
+ };
1565
+ }
1566
+ async function parse$1(text, fileName, context) {
1567
+ const { parse: svelteParse, preprocess, VERSION } = await import("./compiler-Ck-td5Ds.mjs");
1568
+ let walk;
1569
+ if (!satisfies(VERSION, MIN_SVELTE_VERSION)) throw new Error(`Svelte version ${VERSION} not supported. Expected: ${MIN_SVELTE_VERSION} (processing file ${fileName})`);
1570
+ if (satisfies(VERSION, ">=5")) {
1571
+ const walkerModule = await import(import.meta.resolve("estree-walker", import.meta.resolve("svelte")));
1572
+ if (!isPlainRecord(walkerModule) || !isWalkFunction(walkerModule["walk"])) throw new Error("estree-walker module without walk export");
1573
+ walk = walkerModule["walk"];
1574
+ } else {
1575
+ const svelteCompilerModule = await import("./compiler-Ck-td5Ds.mjs");
1576
+ if (!isPlainRecord(svelteCompilerModule) || !isWalkFunction(svelteCompilerModule["walk"])) throw new Error("svelte/compiler module without walk export");
1577
+ walk = svelteCompilerModule["walk"];
1578
+ }
1579
+ const positionConverter = new PositionConverter(text);
1580
+ const { replacedCode, scriptMap } = await replaceScripts(text);
1581
+ const svelteAst = svelteParse(replacedCode, { filename: fileName });
1582
+ const { remappedModuleScriptRange, remappedScriptRanges } = remapScriptLocations(replacedCode, scriptMap, getModuleScriptRange(svelteAst), getTemplateScriptRanges(svelteAst, walk));
1583
+ const [moduleScript, ...additionalScripts] = await Promise.all([parseTemplateScriptIfDefined(remappedModuleScriptRange), ...remappedScriptRanges.map(parseTemplateScript)]);
1584
+ return {
1585
+ originFileName: fileName,
1586
+ rawContent: text,
1587
+ format: "svelte",
1588
+ root: {
1589
+ ...moduleScript === void 0 ? {} : { moduleScript },
1590
+ additionalScripts
1591
+ }
1592
+ };
1593
+ /**
1594
+ * Replaces script tags with placeholders.
1595
+ * This is needed, because svelte's `parse` doesn't support `lang="ts"`.
1596
+ */
1597
+ async function replaceScripts(code) {
1598
+ const map = /* @__PURE__ */ new Map();
1599
+ let scriptIndex = 0;
1600
+ return {
1601
+ replacedCode: (await preprocess(code, { script(script) {
1602
+ const scriptName = `script${scriptIndex++}`;
1603
+ map.set(scriptName, script);
1604
+ return { code: scriptName };
1605
+ } })).code,
1606
+ scriptMap: map
1607
+ };
1608
+ }
1609
+ function getTemplateScriptRanges(ast, walker) {
1610
+ const ranges = [];
1611
+ if (isPlainRecord(ast) && ast["instance"] !== null && ast["instance"] !== void 0) {
1612
+ const instance = ast["instance"];
1613
+ if (isPlainRecord(instance) && "content" in instance) {
1614
+ const content = instance["content"];
1615
+ if (isRangedProgram(content)) ranges.push({
1616
+ start: content.start,
1617
+ end: content.end,
1618
+ isExpression: false
1619
+ });
1620
+ else throw new Error("Svelte instance script without a source range");
1621
+ }
1622
+ }
1623
+ if (!isPlainRecord(ast) || !("html" in ast)) throw new Error("Svelte AST without html");
1624
+ const html = ast["html"];
1625
+ walker(html, { enter(node) {
1626
+ const scriptRange = tryGetScriptRangeFromElement(node);
1627
+ if (scriptRange) ranges.push(scriptRange);
1628
+ const templateExpression = collectTemplateExpression(node);
1629
+ if (templateExpression) {
1630
+ const { start, end } = templateExpression;
1631
+ ranges.push({
1632
+ start,
1633
+ end,
1634
+ isExpression: true
1635
+ });
1636
+ }
1637
+ } });
1638
+ return ranges;
1639
+ }
1640
+ async function parseTemplateScriptIfDefined(range) {
1641
+ if (range) return parseTemplateScript(range);
1642
+ }
1643
+ async function parseTemplateScript({ start, end, isExpression, format }) {
1644
+ const scriptText = text.slice(start, end);
1645
+ return {
1646
+ ast: {
1647
+ ...await context.parse(scriptText, fileName, format),
1648
+ offset: positionConverter.positionFromOffset(start)
1649
+ },
1650
+ range: {
1651
+ start,
1652
+ end
1653
+ },
1654
+ isExpression
1655
+ };
1656
+ }
1657
+ }
1658
+ function getModuleScriptRange(svelteAst) {
1659
+ if (!isPlainRecord(svelteAst) || !("module" in svelteAst) || svelteAst["module"] === null || svelteAst["module"] === void 0) return;
1660
+ const mod = svelteAst["module"];
1661
+ if (!isPlainRecord(mod) || !("content" in mod)) throw new Error("Svelte module script without a source range");
1662
+ const content = mod["content"];
1663
+ if (!isRangedProgram(content)) throw new Error("Svelte module script without a source range");
1664
+ return {
1665
+ start: content.start,
1666
+ end: content.end,
1667
+ isExpression: false
1668
+ };
1669
+ }
1670
+ /**
1671
+ * Remaps script locations back to the original places using the script map
1672
+ */
1673
+ function remapScriptLocations(code, scriptMap, moduleScriptRange, templateRanges) {
1674
+ const scriptRanges = [moduleScriptRange, ...templateRanges].filter(notEmpty).sort((a, b) => a.start - b.start);
1675
+ let offset = 0;
1676
+ let newModuleScriptRange;
1677
+ const newScriptRanges = scriptRanges.map((range) => {
1678
+ const script = code.substring(range.start, range.end);
1679
+ const actualScript = scriptMap.get(script);
1680
+ const start = range.start + offset;
1681
+ if (actualScript) {
1682
+ const scriptRange = {
1683
+ start,
1684
+ end: start + actualScript.content.length,
1685
+ isExpression: range.isExpression,
1686
+ format: actualScript.attributes["lang"] === "ts" ? "ts" : "js"
1687
+ };
1688
+ offset += actualScript.content.length - script.length;
1689
+ if (range === moduleScriptRange) newModuleScriptRange = scriptRange;
1690
+ return scriptRange;
1691
+ } else return {
1692
+ start,
1693
+ end: start + script.length,
1694
+ isExpression: range.isExpression,
1695
+ format: "js"
1696
+ };
1697
+ });
1698
+ return {
1699
+ remappedModuleScriptRange: newModuleScriptRange,
1700
+ remappedScriptRanges: newScriptRanges.filter((range) => range !== newModuleScriptRange)
1701
+ };
1702
+ }
1703
+ function collectTemplateExpression(node) {
1704
+ if (!isPlainRecord(node) || typeof node["type"] !== "string") return;
1705
+ if (!isTemplateExpressionType(node["type"])) return;
1706
+ if (!("expression" in node)) return;
1707
+ const expression = node["expression"];
1708
+ if (isRangedBaseNode(expression)) return expression;
1709
+ }
1710
+ //#endregion
1711
+ //#region src/parsers/ts-parser.ts
1712
+ const { types: types$1, parseAsync } = babel;
1713
+ const require = createRequire(import.meta.url);
1714
+ /**
1715
+ * See https://babeljs.io/docs/en/babel-preset-typescript
1716
+ * @param text The text to parse
1717
+ * @param fileName The name of the file
1718
+ */
1719
+ async function parseTS(text, fileName) {
1720
+ return {
1721
+ originFileName: fileName,
1722
+ rawContent: text,
1723
+ format: "ts",
1724
+ root: await parse(text, fileName, false)
1725
+ };
1726
+ }
1727
+ async function parseTsx(text, fileName) {
1728
+ return {
1729
+ root: await parse(text, fileName, true),
1730
+ format: "tsx",
1731
+ originFileName: fileName,
1732
+ rawContent: text
1733
+ };
1734
+ }
1735
+ async function parse(text, fileName, isTSX) {
1736
+ const ast = await parseAsync(text, {
1737
+ filename: fileName,
1738
+ parserOpts: { ranges: true },
1739
+ configFile: false,
1740
+ babelrc: false,
1741
+ presets: [[require.resolve("@babel/preset-typescript"), {
1742
+ isTSX,
1743
+ allExtensions: true
1744
+ }]],
1745
+ plugins: [[require.resolve("@babel/plugin-proposal-decorators"), { legacy: true }], [require.resolve("@babel/plugin-transform-explicit-resource-management")]]
1746
+ });
1747
+ if (ast === null) throw new Error(`Expected ${fileName} to contain a babel.types.file, but it yielded null`);
1748
+ if (types$1.isProgram(ast)) throw new Error(`Expected ${fileName} to contain a babel.types.file, but was a program`);
1749
+ return ast;
1750
+ }
1751
+ //#endregion
1752
+ //#region src/parsers/create-parser.ts
1753
+ function createParser(parserOptions) {
1754
+ const jsParse = createParser$1(parserOptions);
1755
+ async function parse(code, fileName, formatOverride) {
1756
+ const format = getFormat(fileName, formatOverride);
1757
+ if (!format) {
1758
+ const ext = path.extname(fileName).toLowerCase();
1759
+ throw new Error(`Unable to parse ${fileName}. No parser registered for ${ext}!`);
1760
+ }
1761
+ switch (format) {
1762
+ case "js": return jsParse(code, fileName);
1763
+ case "tsx": return parseTsx(code, fileName);
1764
+ case "ts": return parseTS(code, fileName);
1765
+ case "html": return parse$2(code, fileName, { parse });
1766
+ case "svelte": return parse$1(code, fileName, { parse });
1767
+ default: throw new Error(`Unsupported format: ${String(format)}`);
1768
+ }
1769
+ }
1770
+ return parse;
1771
+ }
1772
+ function getFormat(fileName, override) {
1773
+ if (override) return override;
1774
+ else switch (path.extname(fileName).toLowerCase()) {
1775
+ case ".js":
1776
+ case ".jsx":
1777
+ case ".mjs":
1778
+ case ".cjs": return "js";
1779
+ case ".mts":
1780
+ case ".cts":
1781
+ case ".ts": return "ts";
1782
+ case ".tsx": return "tsx";
1783
+ case ".vue":
1784
+ case ".html":
1785
+ case ".htm": return "html";
1786
+ case ".svelte": return "svelte";
1787
+ default: return;
1788
+ }
1789
+ }
1790
+ //#endregion
1791
+ //#region src/printers/html-printer.ts
1792
+ function getScriptStart$1(script) {
1793
+ const start = script.root.start;
1794
+ if (start === void 0 || start === null) throw new Error("Script AST root without start");
1795
+ return start;
1796
+ }
1797
+ function getScriptEnd$1(script) {
1798
+ const end = script.root.end;
1799
+ if (end === void 0 || end === null) throw new Error("Script AST root without end");
1800
+ return end;
1801
+ }
1802
+ const print$4 = (ast, context) => {
1803
+ const sortedScripts = [...ast.root.scripts].sort((a, b) => getScriptStart$1(a) - getScriptStart$1(b));
1804
+ let currentIndex = 0;
1805
+ let html = "";
1806
+ for (const script of sortedScripts) {
1807
+ html += ast.rawContent.substring(currentIndex, getScriptStart$1(script));
1808
+ html += "\n";
1809
+ html += context.print(script, context);
1810
+ html += "\n";
1811
+ currentIndex = getScriptEnd$1(script);
1812
+ }
1813
+ html += ast.rawContent.substr(currentIndex);
1814
+ return html;
1815
+ };
1816
+ //#endregion
1817
+ //#region src/printers/js-printer.ts
1818
+ const print$3 = (file) => {
1819
+ return generate(file.root, { sourceMaps: false }).code;
1820
+ };
1821
+ //#endregion
1822
+ //#region src/printers/svelte-printer.ts
1823
+ const print$2 = ({ root, rawContent }, context) => {
1824
+ let currentIndex = 0;
1825
+ let outputText = "";
1826
+ const sortedScripts = [root.moduleScript, ...root.additionalScripts].filter(notEmpty).sort((a, b) => a.range.start - b.range.start);
1827
+ for (const script of sortedScripts) if (script.isExpression) {
1828
+ const codeWithoutSemicolon = context.print(script.ast, context).slice(0, -1);
1829
+ outputText += rawContent.substring(currentIndex, script.range.start) + codeWithoutSemicolon;
1830
+ currentIndex = script.range.end;
1831
+ } else {
1832
+ outputText += rawContent.substring(currentIndex, script.range.start);
1833
+ outputText += "\n";
1834
+ outputText += context.print(script.ast, context);
1835
+ outputText += "\n";
1836
+ currentIndex = script.range.end;
1837
+ }
1838
+ outputText += rawContent.substring(currentIndex);
1839
+ return outputText;
1840
+ };
1841
+ //#endregion
1842
+ //#region src/printers/ts-printer.ts
1843
+ const print$1 = (file) => {
1844
+ return generate(file.root, {
1845
+ decoratorsBeforeExport: true,
1846
+ sourceMaps: false
1847
+ }).code;
1848
+ };
1849
+ //#endregion
1850
+ //#region src/printers/index.ts
1851
+ function print(file) {
1852
+ const context = { print };
1853
+ switch (file.format) {
1854
+ case "js": return print$3(file, context);
1855
+ case "ts": return print$1(file, context);
1856
+ case "tsx": return print$1(file, context);
1857
+ case "html": return print$4(file, context);
1858
+ case "svelte": return print$2(file, context);
1859
+ }
1860
+ }
1861
+ //#endregion
1862
+ //#region src/create-instrumenter.ts
1863
+ createInstrumenter.inject = tokens(commonTokens.injector);
1864
+ function createInstrumenter(injector) {
1865
+ return injector.provideValue(instrumenterTokens.print, print).provideValue(instrumenterTokens.createParser, createParser).provideValue(instrumenterTokens.transform, transform).injectClass(Instrumenter);
1866
+ }
1867
+ //#endregion
1868
+ //#region src/disable-type-checks.ts
1869
+ const commentDirectiveRegEx = /^(\s*)@(ts-[a-z-]+).*$/;
1870
+ const tsDirectiveLikeRegEx = /@(ts-[a-z-]+)/;
1871
+ const startingCommentRegex = /(^\s*\/\*.*?\*\/)/gs;
1872
+ /**
1873
+ * Disables TypeScript type checking for a single file by inserting `// @ts-nocheck` commands.
1874
+ * It also does this for *.js files, as they can be type checked by typescript as well.
1875
+ * Other file types are silently ignored
1876
+ *
1877
+ * @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#-ts-nocheck-in-typescript-files
1878
+ */
1879
+ async function disableTypeChecks(file, options) {
1880
+ const format = getFormat(file.name);
1881
+ if (!format) return file;
1882
+ if (isJSFileWithoutTSDirectives(file, format)) return {
1883
+ ...file,
1884
+ content: prefixWithNoCheck(file.content)
1885
+ };
1886
+ const ast = await createParser(options)(file.content, file.name);
1887
+ switch (ast.format) {
1888
+ case "js":
1889
+ case "ts":
1890
+ case "tsx": return {
1891
+ ...file,
1892
+ content: disableTypeCheckingInBabelAst(ast)
1893
+ };
1894
+ case "html": return {
1895
+ ...file,
1896
+ content: disableTypeCheckingInHtml(ast)
1897
+ };
1898
+ case "svelte": return {
1899
+ ...file,
1900
+ content: disableTypeCheckingInSvelte(ast)
1901
+ };
1902
+ }
1903
+ }
1904
+ function isJSFileWithoutTSDirectives(file, format) {
1905
+ return (format === "ts" || format === "js") && !tsDirectiveLikeRegEx.test(file.content);
1906
+ }
1907
+ function disableTypeCheckingInBabelAst(ast) {
1908
+ return prefixWithNoCheck(removeTSDirectives(ast.rawContent, ast.root.comments));
1909
+ }
1910
+ function prefixWithNoCheck(code) {
1911
+ if (code.startsWith("#")) {
1912
+ const newLineIndex = code.indexOf("\n");
1913
+ if (newLineIndex > 0) return `${code.substring(0, newLineIndex)}\n// @ts-nocheck\n${code.substring(newLineIndex + 1)}`;
1914
+ else return code;
1915
+ } else {
1916
+ startingCommentRegex.lastIndex = 0;
1917
+ const leadingComment = startingCommentRegex.exec(code)?.[1];
1918
+ if (leadingComment === void 0) return `// @ts-nocheck\n${code}`;
1919
+ return `${leadingComment.concat("\n")}// @ts-nocheck\n${code.substring(leadingComment.length)}`;
1920
+ }
1921
+ }
1922
+ function getScriptStart(script) {
1923
+ const start = script.root.start;
1924
+ if (start === void 0 || start === null) throw new Error("Script AST root without start");
1925
+ return start;
1926
+ }
1927
+ function getScriptEnd(script) {
1928
+ const end = script.root.end;
1929
+ if (end === void 0 || end === null) throw new Error("Script AST root without end");
1930
+ return end;
1931
+ }
1932
+ function disableTypeCheckingInHtml(ast) {
1933
+ const sortedScripts = [...ast.root.scripts].sort((a, b) => getScriptStart(a) - getScriptStart(b));
1934
+ let currentIndex = 0;
1935
+ let html = "";
1936
+ for (const script of sortedScripts) {
1937
+ html += ast.rawContent.substring(currentIndex, getScriptStart(script));
1938
+ html += "\n";
1939
+ html += prefixWithNoCheck(removeTSDirectives(script.rawContent, script.root.comments));
1940
+ html += "\n";
1941
+ currentIndex = getScriptEnd(script);
1942
+ }
1943
+ html += ast.rawContent.substring(currentIndex);
1944
+ return html;
1945
+ }
1946
+ function disableTypeCheckingInSvelte(ast) {
1947
+ const sortedScripts = [ast.root.moduleScript, ...ast.root.additionalScripts].filter(notEmpty).sort((a, b) => a.range.start - b.range.start);
1948
+ let currentIndex = 0;
1949
+ let html = "";
1950
+ for (const script of sortedScripts) {
1951
+ html += ast.rawContent.substring(currentIndex, script.range.start);
1952
+ html += "\n";
1953
+ html += prefixWithNoCheck(removeTSDirectives(script.ast.rawContent, script.ast.root.comments));
1954
+ html += "\n";
1955
+ currentIndex = script.range.end;
1956
+ }
1957
+ html += ast.rawContent.substring(currentIndex);
1958
+ return html;
1959
+ }
1960
+ function removeTSDirectives(text, comments) {
1961
+ const directiveRanges = comments?.map(tryParseTSDirective).filter(notEmpty).sort((a, b) => a.startPos - b.startPos);
1962
+ if (directiveRanges) {
1963
+ let currentIndex = 0;
1964
+ let pruned = "";
1965
+ for (const directiveRange of directiveRanges) {
1966
+ pruned += text.substring(currentIndex, directiveRange.startPos);
1967
+ currentIndex = directiveRange.endPos;
1968
+ }
1969
+ pruned += text.substring(currentIndex);
1970
+ return pruned;
1971
+ } else return text;
1972
+ }
1973
+ function tryParseTSDirective(comment) {
1974
+ const match = commentDirectiveRegEx.exec(comment.value);
1975
+ if (match) {
1976
+ const start = comment.start;
1977
+ if (start === void 0 || start === null) throw new Error("Comment without start");
1978
+ const directivePrefix = match[1];
1979
+ if (directivePrefix === void 0) throw new Error("TS directive match without prefix");
1980
+ const directiveName = match[2];
1981
+ if (directiveName === void 0) throw new Error("TS directive match without directive name");
1982
+ const directiveStartPos = start + directivePrefix.length + 2;
1983
+ return {
1984
+ startPos: directiveStartPos,
1985
+ endPos: directiveStartPos + directiveName.length + 1
1986
+ };
1987
+ }
1988
+ }
1989
+ //#endregion
1990
+ //#region src/frameworks/angular-ignorer.ts
1991
+ const ANGULAR_SIGNAL_IO_FUNCTIONS = Object.freeze([
1992
+ "input",
1993
+ "model",
1994
+ "output"
1995
+ ]);
1996
+ const ANGULAR_SIGNAL_QUERY_FUNCTIONS = Object.freeze([
1997
+ "contentChild",
1998
+ "contentChildren",
1999
+ "viewChild",
2000
+ "viewChildren"
2001
+ ]);
2002
+ const INPUT_MODEL_OUTPUT_CONFIG_MSG = "Angular signal based input, model and output functions configuration object cannot be mutated as that causes issues with the Angular compiler.";
2003
+ const SIGNAL_QUERY_OPTIONS_MSG = "Angular signal query options object cannot be mutated as that causes issues with the Angular compiler.";
2004
+ var AngularIgnorer = class {
2005
+ shouldIgnore(path) {
2006
+ if (this.isInputModelOrOutputConfigurationObject(path)) return INPUT_MODEL_OUTPUT_CONFIG_MSG;
2007
+ if (this.isSignalQueryOptionsObject(path)) return SIGNAL_QUERY_OPTIONS_MSG;
2008
+ }
2009
+ #isClassFieldLike(path) {
2010
+ return path.isClassProperty() || path.isClassPrivateProperty() || path.isClassAccessorProperty();
2011
+ }
2012
+ /**
2013
+ * Determines if the given path is a configuration object for an Angular input, model or output function.
2014
+ * This solves the "Argument needs to be statically analyzable." error
2015
+ */
2016
+ isInputModelOrOutputConfigurationObject(path) {
2017
+ if (!path.isObjectExpression() || !path.parentPath.isCallExpression() || !path.parentPath.parentPath.isClassProperty()) return false;
2018
+ const callExpression = path.parentPath;
2019
+ const objectExpression = path;
2020
+ const isRequiredSignalIOFunction = callExpression.node.callee.type === "MemberExpression" && callExpression.node.callee.object.type === "Identifier" && ANGULAR_SIGNAL_IO_FUNCTIONS.includes(callExpression.node.callee.object.name) && callExpression.node.callee.property.type === "Identifier" && callExpression.node.callee.property.name === "required";
2021
+ const isSignalIOFunction = callExpression.node.callee.type === "Identifier" && ANGULAR_SIGNAL_IO_FUNCTIONS.includes(callExpression.node.callee.name);
2022
+ const isOutput = callExpression.node.callee.type === "Identifier" && callExpression.node.callee.name === "output";
2023
+ if (isRequiredSignalIOFunction || isOutput) return callExpression.node.arguments.length >= 1 && callExpression.node.arguments[0] === objectExpression.node;
2024
+ if (isSignalIOFunction) return callExpression.node.arguments.length >= 2 && callExpression.node.arguments[1] === objectExpression.node;
2025
+ return false;
2026
+ }
2027
+ /**
2028
+ * Determines if the given path is a configuration object for an Angular signal query function.
2029
+ * This solves the "Argument needs to be statically analyzable." error
2030
+ */
2031
+ isSignalQueryOptionsObject(path) {
2032
+ if (!path.isObjectExpression() || !path.parentPath.isCallExpression() || !this.#isClassFieldLike(path.parentPath.parentPath)) return false;
2033
+ const callExpression = path.parentPath;
2034
+ const objectExpression = path;
2035
+ const callee = callExpression.node.callee;
2036
+ const isQueryFn = callee.type === "Identifier" && ANGULAR_SIGNAL_QUERY_FUNCTIONS.includes(callee.name);
2037
+ const isRequiredQueryFn = callee.type === "MemberExpression" && callee.object.type === "Identifier" && ANGULAR_SIGNAL_QUERY_FUNCTIONS.includes(callee.object.name) && callee.property.type === "Identifier" && callee.property.name === "required";
2038
+ if (!isQueryFn && !isRequiredQueryFn) return false;
2039
+ return callExpression.node.arguments.length >= 2 && callExpression.node.arguments[1] === objectExpression.node;
2040
+ }
2041
+ };
2042
+ //#endregion
2043
+ //#region src/frameworks/index.ts
2044
+ const strykerPlugins = [declareClassPlugin(PluginKind.Ignore, "angular", AngularIgnorer)];
2045
+ const frameworkPluginsFileUrl = import.meta.url;
2046
+ //#endregion
2047
+ //#region src/instrumenter.ts
2048
+ /**
2049
+ * The instrumenter is responsible for
2050
+ * * Generating mutants based on source files
2051
+ * * Instrumenting the source code with the mutants placed in `mutant switches`.
2052
+ * * Adding mutant coverage expressions in the source code.
2053
+ * @see https://github.com/stryker-mutator/stryker-js/issues/1514
2054
+ */
2055
+ var Instrumenter = class {
2056
+ logger;
2057
+ _createParser;
2058
+ _print;
2059
+ _transform;
2060
+ static inject = tokens(commonTokens.logger, instrumenterTokens.createParser, instrumenterTokens.print, instrumenterTokens.transform);
2061
+ constructor(logger, _createParser = createParser, _print = print, _transform = transform) {
2062
+ this.logger = logger;
2063
+ this._createParser = _createParser;
2064
+ this._print = _print;
2065
+ this._transform = _transform;
2066
+ }
2067
+ async instrument(files, options) {
2068
+ this.logger.debug("Instrumenting %d source files with mutants", files.length);
2069
+ const mutantCollector = new MutantCollector();
2070
+ const outFiles = [];
2071
+ let mutantCount = 0;
2072
+ const parse = this._createParser(options);
2073
+ for (const { name, mutate, content } of files) {
2074
+ const ast = await parse(content, name);
2075
+ this._transform(ast, mutantCollector, {
2076
+ options,
2077
+ mutateDescription: toBabelLineNumber(mutate),
2078
+ logger: this.logger
2079
+ });
2080
+ const mutatedContent = this._print(ast);
2081
+ outFiles.push({
2082
+ name,
2083
+ mutate,
2084
+ content: mutatedContent
2085
+ });
2086
+ if (this.logger.isDebugEnabled()) {
2087
+ const nrOfMutantsInFile = mutantCollector.mutants.length - mutantCount;
2088
+ mutantCount = mutantCollector.mutants.length;
2089
+ this.logger.debug(`Instrumented ${path.relative(process.cwd(), name)} (${nrOfMutantsInFile} mutant(s))`);
2090
+ }
2091
+ }
2092
+ const mutants = mutantCollector.mutants.map((mutant) => mutant.toApiMutant());
2093
+ this.logger.info("Instrumented %d source file(s) with %d mutant(s)", files.length, mutants.length);
2094
+ return {
2095
+ files: outFiles,
2096
+ mutants
2097
+ };
2098
+ }
2099
+ };
2100
+ function toBabelLineNumber(range) {
2101
+ if (typeof range === "boolean") return range;
2102
+ else return range.map(({ start, end }) => ({
2103
+ start: {
2104
+ column: start.column,
2105
+ line: start.line + 1
2106
+ },
2107
+ end: {
2108
+ column: end.column,
2109
+ line: end.line + 1
2110
+ }
2111
+ }));
2112
+ }
2113
+ //#endregion
2114
+ export { Instrumenter, createInstrumenter, disableTypeChecks, frameworkPluginsFileUrl, strykerPlugins };