@systemfsoftware/stryker-js-instrumenter 9.0.1 → 10.0.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/CHANGELOG.md +30 -0
- package/dist/index.d.mts +338 -168
- package/dist/index.mjs +833 -795
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -8,18 +8,19 @@ import * as S from "effect/Schema";
|
|
|
8
8
|
import * as Effect from "effect/Effect";
|
|
9
9
|
import { dual } from "effect/Function";
|
|
10
10
|
import { Handle, Sandwich, Workflow } from "@systemfsoftware/effect-cell-types";
|
|
11
|
-
import * as
|
|
12
|
-
import * as
|
|
11
|
+
import * as Order from "effect/Order";
|
|
12
|
+
import * as SchemaGetter from "effect/SchemaGetter";
|
|
13
13
|
import { RegExpParser, visitRegExpAST } from "@eslint-community/regexpp";
|
|
14
|
+
import * as Arr from "effect/Array";
|
|
14
15
|
//#region src/ErrorText.schema.ts
|
|
15
|
-
var ErrorText = class
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
static fromCause = (cause) => Option.flatMap(nonEmptyText(causeChainTextOf(cause)), (text) => CauseText.makeOption({ text }));
|
|
20
|
-
};
|
|
16
|
+
var ErrorText = class extends S.Class("ErrorText")({ text: S.NonEmptyString }) {};
|
|
17
|
+
var CauseText = class extends S.Class("CauseText")({ text: S.NonEmptyString }) {};
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/error-text.ts
|
|
21
20
|
const nonEmptyText = Option.liftPredicate(S.is(S.NonEmptyString));
|
|
22
|
-
|
|
21
|
+
const errorTextOf$2 = (cause) => Option.flatMap(nonEmptyText(errorTextValueOf(cause)), (text) => ErrorText.makeOption({ text }));
|
|
22
|
+
const causeTextOf = (cause) => Option.flatMap(nonEmptyText(causeChainTextOf(cause)), (text) => CauseText.makeOption({ text }));
|
|
23
|
+
function errorTextValueOf(error) {
|
|
23
24
|
return Match.value(error).pipe(Match.when(isEmptyError, () => ""), Match.when(Match.instanceOf(Error), errorText), Match.orElse(() => stringifyNonError(error)));
|
|
24
25
|
}
|
|
25
26
|
function causeChainTextOf(cause) {
|
|
@@ -87,10 +88,10 @@ function causeText(cause, depth) {
|
|
|
87
88
|
function missingCauseTextOf(cause, depth) {
|
|
88
89
|
return Boolean$1.match(isMissingCause(cause), {
|
|
89
90
|
onTrue: () => void 0,
|
|
90
|
-
onFalse: () =>
|
|
91
|
+
onFalse: () => causeNodeTextOf(cause, depth)
|
|
91
92
|
});
|
|
92
93
|
}
|
|
93
|
-
function
|
|
94
|
+
function causeNodeTextOf(cause, depth) {
|
|
94
95
|
return Match.value(cause).pipe(Match.when(Match.string, (text) => textIfNonEmpty(text)), Match.orElse((value) => objectCauseTextOf(value, depth)));
|
|
95
96
|
}
|
|
96
97
|
function objectCauseTextOf(cause, depth) {
|
|
@@ -100,93 +101,92 @@ function objectCauseTextOf(cause, depth) {
|
|
|
100
101
|
//#region src/ErrorText/mod.ts
|
|
101
102
|
var ErrorText_exports = /* @__PURE__ */ __exportAll({
|
|
102
103
|
CauseText: () => CauseText,
|
|
103
|
-
ErrorText: () => ErrorText
|
|
104
|
+
ErrorText: () => ErrorText,
|
|
105
|
+
causeTextOf: () => causeTextOf,
|
|
106
|
+
errorTextOf: () => errorTextOf$2
|
|
104
107
|
});
|
|
105
108
|
//#endregion
|
|
106
109
|
//#region src/Location.schema.ts
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
110
|
+
const Offset = S.Int.check(S.isGreaterThanOrEqualTo(0));
|
|
111
|
+
/** A 1-based line: the first line of a file is line 1. */
|
|
112
|
+
const Line = S.Int.check(S.isGreaterThanOrEqualTo(1));
|
|
113
|
+
/** A 1-based column: the first character of a line is column 1. */
|
|
114
|
+
const Column = S.Int.check(S.isGreaterThanOrEqualTo(1));
|
|
115
|
+
const Position = S.Struct({
|
|
116
|
+
line: Line,
|
|
117
|
+
column: Column
|
|
118
|
+
});
|
|
119
|
+
const positionOrder = Order.combine(Order.mapInput(Order.Number, (position) => position.line), Order.mapInput(Order.Number, (position) => position.column));
|
|
120
|
+
const inOrder = (order) => (ends) => ({
|
|
121
|
+
start: Order.min(order)(ends.start, ends.end),
|
|
122
|
+
end: Order.max(order)(ends.start, ends.end)
|
|
123
|
+
});
|
|
124
|
+
const notReversed = (order) => (ends) => Order.isLessThanOrEqualTo(order)(ends.start, ends.end);
|
|
125
|
+
const PositionEnds = S.Struct({
|
|
126
|
+
start: Position,
|
|
127
|
+
end: Position
|
|
128
|
+
});
|
|
129
|
+
const Location = S.declare((value) => S.is(PositionEnds)(value) && notReversed(positionOrder)(value), {
|
|
130
|
+
expected: "a location whose end is not before its start",
|
|
131
|
+
toCodecArbitrary: () => S.link()(PositionEnds, {
|
|
132
|
+
decode: SchemaGetter.transform(inOrder(positionOrder)),
|
|
133
|
+
encode: SchemaGetter.transform((ends) => ends)
|
|
134
|
+
})
|
|
121
135
|
});
|
|
122
|
-
const
|
|
123
|
-
start:
|
|
124
|
-
end: S.optional(
|
|
136
|
+
const OpenEnds = S.Struct({
|
|
137
|
+
start: Position,
|
|
138
|
+
end: S.optional(Position)
|
|
125
139
|
});
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
*/
|
|
130
|
-
const ReportLocationFromMutant = LocationSchema$1;
|
|
131
|
-
const NonNegativeInt = S.Int.pipe(S.check(S.isGreaterThanOrEqualTo(0)));
|
|
132
|
-
var LineTable = class extends S.Class("LineTable")({ lineStarts: S.NonEmptyArray(NonNegativeInt) }) {
|
|
133
|
-
positionAt(offset) {
|
|
134
|
-
return positionOf(this.lineStarts, offset);
|
|
135
|
-
}
|
|
136
|
-
locationAt(span) {
|
|
137
|
-
return {
|
|
138
|
-
start: this.positionAt(span.start),
|
|
139
|
-
end: this.positionAt(span.end)
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
zeroBasedPositionAt(offset) {
|
|
143
|
-
return zeroBasedPositionOf(this.lineStarts, offset);
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
const LINE_TERMINATOR = /\r\n|[\n\r\u2028\u2029]/g;
|
|
147
|
-
const lineStartsOf = (text) => ({ lineStarts: [0, ...[...text.matchAll(LINE_TERMINATOR)].map((match) => endOfMatch(match))] });
|
|
148
|
-
const endOfMatch = (match) => (match.index ?? 0) + match[0].length;
|
|
149
|
-
const canonicalTextOf = (table) => Boolean$1.match(table.lineStarts.length === 1, {
|
|
150
|
-
onTrue: () => "",
|
|
151
|
-
onFalse: () => `${Arr.zip(table.lineStarts.slice(0, -1), table.lineStarts.slice(1)).map(([start, next]) => " ".repeat(next - start - 1)).join("\n")}\n`
|
|
152
|
-
});
|
|
153
|
-
const LineTableFromText = S.String.pipe(S.decodeTo(LineTable, {
|
|
154
|
-
decode: SGetter.transform(lineStartsOf),
|
|
155
|
-
encode: SGetter.transform(canonicalTextOf)
|
|
140
|
+
const closedEnds = (ends) => Option.map(Option.fromUndefinedOr(ends.end), (end) => ({
|
|
141
|
+
start: ends.start,
|
|
142
|
+
end
|
|
156
143
|
}));
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
144
|
+
const OpenEndLocation = S.declare((value) => S.is(OpenEnds)(value) && Option.match(closedEnds(value), {
|
|
145
|
+
onNone: () => true,
|
|
146
|
+
onSome: notReversed(positionOrder)
|
|
147
|
+
}), {
|
|
148
|
+
expected: "a location whose end, when present, is not before its start",
|
|
149
|
+
toCodecArbitrary: () => S.link()(OpenEnds, {
|
|
150
|
+
decode: SchemaGetter.transform((ends) => Option.getOrElse(Option.map(closedEnds(ends), inOrder(positionOrder)), () => ends)),
|
|
151
|
+
encode: SchemaGetter.transform((ends) => ends)
|
|
152
|
+
})
|
|
153
|
+
});
|
|
154
|
+
const OffsetEnds = S.Struct({
|
|
155
|
+
start: Offset,
|
|
156
|
+
end: Offset
|
|
157
|
+
});
|
|
158
|
+
const Span = S.declare((value) => S.is(OffsetEnds)(value) && notReversed(Order.Number)(value), {
|
|
159
|
+
expected: "a span whose end is not before its start",
|
|
160
|
+
toCodecArbitrary: () => S.link()(OffsetEnds, {
|
|
161
|
+
decode: SchemaGetter.transform(inOrder(Order.Number)),
|
|
162
|
+
encode: SchemaGetter.transform((ends) => ends)
|
|
163
|
+
})
|
|
164
|
+
});
|
|
165
|
+
const ScriptOrigin = S.Struct({
|
|
166
|
+
line: Line,
|
|
167
|
+
columnShift: Offset
|
|
168
|
+
});
|
|
169
|
+
S.is(Offset), S.is(Line), S.is(Column), S.is(Location), S.is(Span);
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/directives/directive.schema.ts
|
|
172
|
+
const MutatorNameSchema = S.String.pipe(S.check(S.isPattern(/^[a-zA-Z]+(?: [a-zA-Z]+)*$/)));
|
|
173
|
+
const DirectiveReasonSchema = S.String.pipe(S.check(S.isPattern(/^\S(?:[^\r\n\u2028\u2029]*\S)?$/)));
|
|
174
|
+
const DirectiveSchema = S.Struct({
|
|
175
|
+
action: S.Literals(["disable", "restore"]),
|
|
176
|
+
scope: S.Literals(["next-line", "block"]),
|
|
177
|
+
mutatorNames: S.Array(MutatorNameSchema).check(S.isMinLength(1)),
|
|
178
|
+
reason: DirectiveReasonSchema
|
|
179
|
+
});
|
|
180
|
+
const LocatedDirectiveSchema = S.Struct({
|
|
181
|
+
directive: DirectiveSchema,
|
|
182
|
+
at: Position,
|
|
183
|
+
governedLine: Line
|
|
184
|
+
});
|
|
185
|
+
S.Struct({
|
|
186
|
+
directive: DirectiveSchema,
|
|
187
|
+
at: Position,
|
|
188
|
+
mutatorName: MutatorNameSchema
|
|
189
|
+
});
|
|
190
190
|
//#endregion
|
|
191
191
|
//#region src/Mutant.schema.ts
|
|
192
192
|
const MutantStatusSchema = S.Literals([
|
|
@@ -199,11 +199,30 @@ const MutantStatusSchema = S.Literals([
|
|
|
199
199
|
"Ignored",
|
|
200
200
|
"Pending"
|
|
201
201
|
]);
|
|
202
|
-
const
|
|
202
|
+
const SurvivorStatusSchema = S.Literals(["Survived", "NoCoverage"]);
|
|
203
|
+
const RememberedStatusSchema = S.Literals([
|
|
204
|
+
"Survived",
|
|
205
|
+
"Killed",
|
|
206
|
+
"Timeout",
|
|
207
|
+
"NoCoverage",
|
|
208
|
+
"Ignored"
|
|
209
|
+
]);
|
|
210
|
+
const EphemeralStatusSchema = S.Literals([
|
|
211
|
+
"CompileError",
|
|
212
|
+
"RuntimeError",
|
|
213
|
+
"Pending"
|
|
214
|
+
]);
|
|
215
|
+
const ActionableStatusSchema = S.Literals([
|
|
216
|
+
"Survived",
|
|
217
|
+
"NoCoverage",
|
|
218
|
+
"Timeout",
|
|
219
|
+
"RuntimeError"
|
|
220
|
+
]);
|
|
221
|
+
const MutantId = S.String.check(S.isPattern(/^(0|[1-9][0-9]*)$/)).pipe(S.brand("MutantId"));
|
|
203
222
|
const MutatorName = S.NonEmptyString.pipe(S.brand("MutatorName"));
|
|
204
223
|
const CanonicalFileName = S.String.pipe(S.decodeTo(S.String.pipe(S.check(S.isPattern(/^[^\\]+$/)), S.brand("CanonicalFileName")), {
|
|
205
|
-
decode:
|
|
206
|
-
encode:
|
|
224
|
+
decode: SchemaGetter.transform((fileName) => fileName.replace(/\\/g, "/")),
|
|
225
|
+
encode: SchemaGetter.transform((canonical) => canonical)
|
|
207
226
|
}));
|
|
208
227
|
/**
|
|
209
228
|
* A mutant's file location in the mutation-testing-report-schema contract:
|
|
@@ -211,19 +230,19 @@ const CanonicalFileName = S.String.pipe(S.decodeTo(S.String.pipe(S.check(S.isPat
|
|
|
211
230
|
* machine stream emit. Every producer on the instrument path (node spans,
|
|
212
231
|
* embedded-region shifts) targets this base; no downstream layer converts.
|
|
213
232
|
*/
|
|
214
|
-
|
|
233
|
+
const Mutant = S.TaggedStruct("Mutant", {
|
|
215
234
|
id: MutantId,
|
|
216
235
|
fileName: CanonicalFileName,
|
|
217
236
|
mutatorName: MutatorName,
|
|
218
237
|
replacement: S.String,
|
|
219
|
-
location:
|
|
238
|
+
location: Location,
|
|
220
239
|
status: S.optional(MutantStatusSchema),
|
|
221
240
|
statusReason: S.optional(S.String),
|
|
222
241
|
coveredBy: S.String.pipe(S.Array, S.optional),
|
|
223
242
|
static: S.optional(S.Boolean),
|
|
224
243
|
testsCompleted: S.optional(S.Finite),
|
|
225
244
|
description: S.optional(S.String)
|
|
226
|
-
}) {};
|
|
245
|
+
}).check(S.makeFilter((mutant) => mutant.statusReason === void 0 || mutant.status !== void 0, { message: "a mutant carries a status reason only together with a status" }));
|
|
227
246
|
const MutantFromUnknown = S.Unknown.pipe(S.decodeTo(Mutant));
|
|
228
247
|
const RunOptionsFields = {
|
|
229
248
|
timeout: S.Finite.pipe(S.check(S.isGreaterThanOrEqualTo(0))),
|
|
@@ -241,15 +260,15 @@ const MutantRunOptionsSchema = S.Struct({
|
|
|
241
260
|
hitLimit: S.optionalKey(HitCount)
|
|
242
261
|
});
|
|
243
262
|
const MutantCoverageSchema = S.Struct({
|
|
244
|
-
perTest: S.Record(S.String, S.Record(
|
|
245
|
-
static: S.Record(
|
|
263
|
+
perTest: S.Record(S.String, S.Record(MutantId, HitCount)),
|
|
264
|
+
static: S.Record(MutantId, HitCount)
|
|
246
265
|
});
|
|
247
266
|
var InstrumenterContext = class extends S.Class("InstrumenterContext")({
|
|
248
267
|
activeMutant: S.optional(S.String),
|
|
249
268
|
currentTestId: S.optional(S.String),
|
|
250
269
|
mutantCoverage: S.optional(MutantCoverageSchema),
|
|
251
|
-
hitCount: S.optional(
|
|
252
|
-
hitLimit: S.optional(
|
|
270
|
+
hitCount: S.optional(HitCount),
|
|
271
|
+
hitLimit: S.optional(HitCount)
|
|
253
272
|
}) {
|
|
254
273
|
static NAMESPACE = "__stryker__";
|
|
255
274
|
static MUTATION_COVERAGE_OBJECT = "mutantCoverage";
|
|
@@ -272,21 +291,15 @@ var InstrumentError = class extends S.TaggedError("@systemfsoftware/stryker-js-i
|
|
|
272
291
|
});
|
|
273
292
|
}
|
|
274
293
|
};
|
|
275
|
-
const MutateDescriptionSchema = S.Union([S.Boolean, S.Array(
|
|
276
|
-
const SourceLineSchema = S.Int.pipe(S.check(S.isGreaterThanOrEqualTo(1)));
|
|
277
|
-
const SourceColumnSchema = S.Int.pipe(S.check(S.isGreaterThanOrEqualTo(0)));
|
|
278
|
-
const NodePositionSchema = S.Struct({
|
|
279
|
-
line: S.Int,
|
|
280
|
-
column: S.Int
|
|
281
|
-
});
|
|
294
|
+
const MutateDescriptionSchema = S.Union([S.Boolean, S.Array(Location)]);
|
|
282
295
|
const FileSchema = S.Struct({
|
|
283
|
-
name:
|
|
296
|
+
name: CanonicalFileName,
|
|
284
297
|
content: S.String,
|
|
285
298
|
mutate: MutateDescriptionSchema
|
|
286
299
|
});
|
|
287
300
|
const IgnorerSchema = S.Unknown;
|
|
288
301
|
S.Struct({
|
|
289
|
-
excludedMutations: S.Array(
|
|
302
|
+
excludedMutations: S.Array(MutatorNameSchema),
|
|
290
303
|
ignorers: S.Array(IgnorerSchema),
|
|
291
304
|
noHeader: S.optional(S.Boolean),
|
|
292
305
|
optInMutations: S.String.pipe(S.Array, S.optional)
|
|
@@ -316,13 +329,65 @@ const PlacerNameSchema = S.Literals([
|
|
|
316
329
|
var MutantsUnapplied = class extends S.TaggedError()("MutantsUnapplied", {
|
|
317
330
|
fileName: S.String,
|
|
318
331
|
placer: PlacerNameSchema,
|
|
319
|
-
mutatorNames: S.Array(
|
|
332
|
+
mutatorNames: S.Array(MutatorNameSchema),
|
|
320
333
|
cause: S.Defect()
|
|
321
334
|
}) {};
|
|
322
335
|
var MutantNotApplied = class extends S.TaggedError()("MutantNotApplied", {
|
|
323
336
|
fileName: S.String,
|
|
324
|
-
mutatorName:
|
|
337
|
+
mutatorName: MutatorNameSchema
|
|
325
338
|
}) {};
|
|
339
|
+
var NodeWithoutSpan = class extends S.TaggedError()("NodeWithoutSpan", { fileName: S.String }) {};
|
|
340
|
+
var MutantsUnplaced = class extends S.TaggedError()("MutantsUnplaced", {
|
|
341
|
+
fileName: S.String,
|
|
342
|
+
detail: S.String
|
|
343
|
+
}) {};
|
|
344
|
+
var PlacementRefused = class extends S.TaggedError()("PlacementRefused", { message: S.String }) {};
|
|
345
|
+
//#endregion
|
|
346
|
+
//#region src/Location.ts
|
|
347
|
+
const LINE_TERMINATOR = /\r\n|[\n\r\u2028\u2029]/g;
|
|
348
|
+
const lineStartsOf = (text) => [0, ...[...text.matchAll(LINE_TERMINATOR)].map((match) => match.index + match[0].length)];
|
|
349
|
+
const middleIndex = (low, high) => low + (high - low >> 1);
|
|
350
|
+
const positionAtOffset = (lineStarts, offset, low, high, start) => Boolean$1.match(low > high, {
|
|
351
|
+
onTrue: () => ({
|
|
352
|
+
line: low,
|
|
353
|
+
column: offset - start + 1
|
|
354
|
+
}),
|
|
355
|
+
onFalse: () => Option.match(Arr.get(lineStarts, middleIndex(low, high)), {
|
|
356
|
+
onNone: () => ({
|
|
357
|
+
line: low,
|
|
358
|
+
column: offset - start + 1
|
|
359
|
+
}),
|
|
360
|
+
onSome: (found) => Boolean$1.match(found === offset, {
|
|
361
|
+
onTrue: () => ({
|
|
362
|
+
line: middleIndex(low, high) + 1,
|
|
363
|
+
column: 1
|
|
364
|
+
}),
|
|
365
|
+
onFalse: () => Boolean$1.match(found < offset, {
|
|
366
|
+
onTrue: () => positionAtOffset(lineStarts, offset, middleIndex(low, high) + 1, high, found),
|
|
367
|
+
onFalse: () => positionAtOffset(lineStarts, offset, low, middleIndex(low, high) - 1, start)
|
|
368
|
+
})
|
|
369
|
+
})
|
|
370
|
+
})
|
|
371
|
+
});
|
|
372
|
+
const positionAt = dual(2, (lineStarts, offset) => positionAtOffset(lineStarts, offset, 0, lineStarts.length - 1, 0));
|
|
373
|
+
const locationOf = dual(2, (lineStarts, span) => Location.make({
|
|
374
|
+
start: positionAt(lineStarts, span.start),
|
|
375
|
+
end: positionAt(lineStarts, span.end)
|
|
376
|
+
}));
|
|
377
|
+
const originAt = dual(2, (lineStarts, offset) => {
|
|
378
|
+
const position = positionAt(lineStarts, offset);
|
|
379
|
+
return ScriptOrigin.make({
|
|
380
|
+
line: position.line,
|
|
381
|
+
columnShift: position.column - 1
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
const offsetAt = dual(2, (lineStarts, position) => Option.flatMap(Arr.get(lineStarts, position.line - 1), (start) => {
|
|
385
|
+
const offset = start + position.column - 1;
|
|
386
|
+
return Option.match(Arr.get(lineStarts, position.line), {
|
|
387
|
+
onNone: () => Option.some(offset),
|
|
388
|
+
onSome: (next) => Option.liftPredicate(offset, (candidate) => candidate < next)
|
|
389
|
+
});
|
|
390
|
+
}));
|
|
326
391
|
//#endregion
|
|
327
392
|
//#region src/Ast.handle.ts
|
|
328
393
|
const formatKeyOf = (ast) => ast.format === "embedded" ? ast.formatId : ast.format;
|
|
@@ -560,14 +625,14 @@ const switchCase = dual((args) => isDataFirstArity(args, 2), (test, consequent,
|
|
|
560
625
|
consequent: [...consequent]
|
|
561
626
|
}, loc));
|
|
562
627
|
const cloneNode = (node) => structuredClone(node);
|
|
563
|
-
const attachComments = dual((args) => args.length >= 3, (self, comments,
|
|
628
|
+
const attachComments = dual((args) => args.length >= 3, (self, comments, lineStarts) => Boolean$1.match(comments.length === 0, {
|
|
564
629
|
onTrue: () => void 0,
|
|
565
630
|
onFalse: () => {
|
|
566
631
|
const nodes = collectNodes(self.root).filter((entry) => entry.node !== self.root);
|
|
567
632
|
nodes.sort((a, b) => a.start - b.start);
|
|
568
633
|
const groups = groupComments(nodes, comments);
|
|
569
|
-
assignComments(groups.leading,
|
|
570
|
-
assignComments(groups.trailing,
|
|
634
|
+
assignComments(groups.leading, lineStarts, "leadingComments");
|
|
635
|
+
assignComments(groups.trailing, lineStarts, "trailingComments");
|
|
571
636
|
}
|
|
572
637
|
}));
|
|
573
638
|
const groupComments = (nodes, comments) => {
|
|
@@ -593,12 +658,12 @@ const commentHost = (nodes, comment) => {
|
|
|
593
658
|
};
|
|
594
659
|
const followingNode = (nodes, comment) => Option.getOrUndefined(Option.map(Option.fromNullishOr(nodes.find((candidate) => candidate.start >= comment.end)), (entry) => entry.node));
|
|
595
660
|
const precedingStatement = (nodes, comment) => Option.getOrUndefined(Option.map(Option.fromNullishOr(nodes.findLast((candidate) => candidate.end <= comment.start && isStatementKind(candidate.node))), (entry) => entry.node));
|
|
596
|
-
const assignComments = (map,
|
|
661
|
+
const assignComments = (map, lineStarts, field) => map.forEach((list, node) => Object.assign(node, { [field]: list.map((comment) => ({
|
|
597
662
|
...comment,
|
|
598
|
-
loc: {
|
|
599
|
-
start:
|
|
600
|
-
end:
|
|
601
|
-
}
|
|
663
|
+
loc: locationOf(lineStarts, {
|
|
664
|
+
start: comment.start,
|
|
665
|
+
end: comment.end
|
|
666
|
+
})
|
|
602
667
|
})) }));
|
|
603
668
|
const pushComment = (map, node, comment) => {
|
|
604
669
|
Option.match(Option.fromNullishOr(map.get(node)), {
|
|
@@ -615,72 +680,41 @@ const WALK_SKIPPED_KEYS = {
|
|
|
615
680
|
start: true,
|
|
616
681
|
end: true
|
|
617
682
|
};
|
|
618
|
-
const walkNodes = (root, visitors) => visitIfNode(root, {
|
|
619
|
-
key: null,
|
|
620
|
-
index: null
|
|
621
|
-
}, visitors);
|
|
622
|
-
const visitNode = (node, context, visitors) => {
|
|
623
|
-
const state = { skipped: false };
|
|
624
|
-
visitors.enter(node, context, { skip: () => {
|
|
625
|
-
state.skipped = true;
|
|
626
|
-
} });
|
|
627
|
-
Boolean$1.match(state.skipped, {
|
|
628
|
-
onTrue: () => void 0,
|
|
629
|
-
onFalse: () => visitChildren(node, visitors)
|
|
630
|
-
});
|
|
631
|
-
visitors.leave(node, context);
|
|
632
|
-
};
|
|
633
|
-
const visitChildren = (node, visitors) => Object.keys(node).forEach((key) => Boolean$1.match(WALK_SKIPPED_KEYS[key] === true, {
|
|
634
|
-
onTrue: () => void 0,
|
|
635
|
-
onFalse: () => visitSlot(node[key], key, visitors)
|
|
636
|
-
}));
|
|
637
683
|
const isChildList = (value) => Array.isArray(value);
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
684
|
+
const childSlotsOf = (node) => Object.keys(node).flatMap((key) => WALK_SKIPPED_KEYS[key] === true ? [] : childSlotsAt(key, node[key]));
|
|
685
|
+
const childSlotsAt = (key, value) => isChildList(value) ? listSlotsAt(key, value) : singleSlotAt(key, value);
|
|
686
|
+
const listSlotsAt = (key, value) => value.flatMap((item, index) => isAstNode(item) ? [{
|
|
687
|
+
key,
|
|
688
|
+
index,
|
|
689
|
+
node: item
|
|
690
|
+
}] : []);
|
|
691
|
+
const singleSlotAt = (key, value) => Option.match(Option.filter(Option.some(value), isAstNode), {
|
|
692
|
+
onNone: () => [],
|
|
693
|
+
onSome: (node) => [{
|
|
644
694
|
key,
|
|
645
|
-
index: null
|
|
646
|
-
|
|
695
|
+
index: null,
|
|
696
|
+
node
|
|
697
|
+
}]
|
|
698
|
+
});
|
|
699
|
+
const asWalkedNode = (value) => Option.filter(Option.some(value), isAstNode);
|
|
700
|
+
const childNodes = (node) => Option.match(asWalkedNode(node), {
|
|
701
|
+
onNone: () => [],
|
|
702
|
+
onSome: (walked) => childSlotsOf(walked).filter((slot) => !isCommentKey(slot.key)).map((slot) => ({
|
|
703
|
+
key: slot.key,
|
|
704
|
+
index: slot.index,
|
|
705
|
+
node: slot.node
|
|
706
|
+
}))
|
|
647
707
|
});
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
},
|
|
659
|
-
leave(node) {
|
|
660
|
-
ancestors.pop();
|
|
661
|
-
visitors.leave?.(node, [...ancestors]);
|
|
662
|
-
}
|
|
663
|
-
});
|
|
664
|
-
};
|
|
665
|
-
const collectNodes = (root) => {
|
|
666
|
-
const out = [];
|
|
667
|
-
walker(root, { enter(node) {
|
|
668
|
-
appendEntry(node, out);
|
|
669
|
-
} });
|
|
670
|
-
return out;
|
|
671
|
-
};
|
|
672
|
-
const appendEntry = (node, out) => {
|
|
673
|
-
Option.match(Option.fromNullishOr(spanOf(node)), {
|
|
674
|
-
onNone: () => void 0,
|
|
675
|
-
onSome: (span) => {
|
|
676
|
-
out.push({
|
|
677
|
-
node,
|
|
678
|
-
start: span.start,
|
|
679
|
-
end: span.end
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
});
|
|
683
|
-
};
|
|
708
|
+
const collectNodes = (root) => Option.toArray(nodeEntryOf(root)).concat(childEntriesOf(root));
|
|
709
|
+
const childEntriesOf = (root) => Option.match(asWalkedNode(root), {
|
|
710
|
+
onNone: () => [],
|
|
711
|
+
onSome: (node) => childSlotsOf(node).flatMap((slot) => collectNodes(slot.node))
|
|
712
|
+
});
|
|
713
|
+
const nodeEntryOf = (node) => Option.map(Option.fromNullishOr(spanOf(node)), (span) => ({
|
|
714
|
+
node,
|
|
715
|
+
start: span.start,
|
|
716
|
+
end: span.end
|
|
717
|
+
}));
|
|
684
718
|
function isAstNode(value) {
|
|
685
719
|
return Predicate.isObject(value) && typeof value["type"] === "string";
|
|
686
720
|
}
|
|
@@ -689,32 +723,41 @@ const COMMENT_KEYS = {
|
|
|
689
723
|
trailingComments: true
|
|
690
724
|
};
|
|
691
725
|
const isCommentKey = (key) => typeof key === "string" && COMMENT_KEYS[key] === true;
|
|
692
|
-
const walkTraverse = (root,
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
726
|
+
const walkTraverse = (root, visitors) => Option.match(asWalkedNode(root), {
|
|
727
|
+
onNone: () => void 0,
|
|
728
|
+
onSome: (node) => traverseNode(node, null, {
|
|
729
|
+
key: null,
|
|
730
|
+
index: null
|
|
731
|
+
}, visitors)
|
|
732
|
+
});
|
|
733
|
+
const skipControls = () => {
|
|
734
|
+
let skipped = false;
|
|
735
|
+
return {
|
|
736
|
+
skipped: () => skipped,
|
|
737
|
+
skip: () => {
|
|
738
|
+
skipped = true;
|
|
699
739
|
}
|
|
700
|
-
}
|
|
740
|
+
};
|
|
701
741
|
};
|
|
702
|
-
const
|
|
703
|
-
const readPath = (stack, node, controls, context, visitors) => {
|
|
742
|
+
const traverseNode = (node, parentPath, context, visitors) => {
|
|
704
743
|
Boolean$1.match(isCommentKey(context.key), {
|
|
705
744
|
onTrue: () => void 0,
|
|
706
745
|
onFalse: () => {
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
notify(visitors.enter,
|
|
746
|
+
const controls = skipControls();
|
|
747
|
+
const path = createPath(node, parentPath, controls, context);
|
|
748
|
+
notify(visitors.enter, path);
|
|
749
|
+
Boolean$1.match(controls.skipped(), {
|
|
750
|
+
onTrue: () => void 0,
|
|
751
|
+
onFalse: () => childSlotsOf(node).forEach((slot) => traverseNode(slot.node, path, {
|
|
752
|
+
key: slot.key,
|
|
753
|
+
index: slot.index
|
|
754
|
+
}, visitors))
|
|
755
|
+
});
|
|
756
|
+
notify(visitors.exit, path);
|
|
710
757
|
}
|
|
711
758
|
});
|
|
712
759
|
};
|
|
713
|
-
const
|
|
714
|
-
onTrue: () => void 0,
|
|
715
|
-
onFalse: () => notify(visitors.exit, stack.pop())
|
|
716
|
-
});
|
|
717
|
-
const parentOf = (stack) => Option.getOrNull(Arr.last(stack));
|
|
760
|
+
const traverse = dual(2, (self, visitors) => walkTraverse(self.root, visitors));
|
|
718
761
|
const notify = (visitor, path) => Option.match(Option.all({
|
|
719
762
|
visitor: Option.fromNullishOr(visitor),
|
|
720
763
|
path: Option.fromNullishOr(path)
|
|
@@ -877,13 +920,6 @@ const EMPTY_IMPORT_TABLE = Object.freeze({
|
|
|
877
920
|
bareFunctions: /* @__PURE__ */ new Map(),
|
|
878
921
|
pipeBindings: /* @__PURE__ */ new Set()
|
|
879
922
|
});
|
|
880
|
-
const importTables = /* @__PURE__ */ new WeakMap();
|
|
881
|
-
const importTableFor = (program) => Option.getOrElse(Option.fromNullishOr(importTables.get(program)), () => rememberImportTable(program));
|
|
882
|
-
function rememberImportTable(program) {
|
|
883
|
-
const table = buildImportTable(program);
|
|
884
|
-
importTables.set(program, table);
|
|
885
|
-
return table;
|
|
886
|
-
}
|
|
887
923
|
const buildImportTable = (program) => Arr.reduce(Arr.flatMap(Arr.filter(program.body, isImportDeclaration$1), importContributions), EMPTY_IMPORT_TABLE, applyContribution);
|
|
888
924
|
const withEntry = (entries, key, value) => new Map([...entries, [key, value]]);
|
|
889
925
|
const withMember = (members, value) => /* @__PURE__ */ new Set([...members, value]);
|
|
@@ -935,7 +971,7 @@ const namespaceBindingFor = (source) => Match.value(source).pipe(Match.when((can
|
|
|
935
971
|
}))));
|
|
936
972
|
const moduleNameFromName = (name) => Arr.findFirst(MODULE_NAMES, (module) => module === name);
|
|
937
973
|
const moduleNameFromSource = (source) => Arr.findFirst(MODULE_NAMES, (module) => source === `${EFFECT_SOURCE}/${module}`);
|
|
938
|
-
const resolveEffectCallDataFirst = (node, context) => Option.flatMap(programOf(context), (program) => resolveIn(node, context,
|
|
974
|
+
const resolveEffectCallDataFirst = (node, context) => Option.flatMap(programOf(context), (program) => resolveIn(node, context, buildImportTable(program)));
|
|
939
975
|
const resolveEffectCall = dual((args) => args.length >= 2, resolveEffectCallDataFirst);
|
|
940
976
|
const programOf = (context) => Arr.findLast(context.ancestors, isProgram);
|
|
941
977
|
const resolveIn = (node, context, table) => Match.value(node).pipe(Match.when(isCallExpression$1, (call) => resolveCall(call, context, table)), Match.when(isMemberExpression, (member) => resolveReference(member, context, table)), Match.when(isIdentifier$1, (reference) => resolveReference(reference, context, table)), Match.orElse(() => Option.none()));
|
|
@@ -1341,9 +1377,10 @@ function eqNode(a, b) {
|
|
|
1341
1377
|
return identity !== void 0 && identity === nodeIdentity(b);
|
|
1342
1378
|
}
|
|
1343
1379
|
function nodeIdentity(node) {
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1380
|
+
return Option.match(Option.fromNullishOr(spanOf(node)), {
|
|
1381
|
+
onNone: () => void 0,
|
|
1382
|
+
onSome: (span) => `${node.type}:${span.start}:${span.end}`
|
|
1383
|
+
});
|
|
1347
1384
|
}
|
|
1348
1385
|
function orDefault(value, fallback) {
|
|
1349
1386
|
return value ?? fallback;
|
|
@@ -1391,14 +1428,15 @@ function cloneWithReplacement(mutant, originalTree) {
|
|
|
1391
1428
|
function hasReplaced(root, original, replacement) {
|
|
1392
1429
|
let applied = false;
|
|
1393
1430
|
traverse(make(root), { enter(path) {
|
|
1394
|
-
|
|
1431
|
+
applied = applied || replaceFirstMatch(path, original, replacement);
|
|
1395
1432
|
} });
|
|
1396
1433
|
return applied;
|
|
1397
1434
|
}
|
|
1398
1435
|
function replaceFirstMatch(path, original, replacement) {
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1436
|
+
return Match.value(eqNode(path.node, original)).pipe(Match.when(false, () => false), Match.orElse(() => {
|
|
1437
|
+
path.replaceWith(replacement);
|
|
1438
|
+
return true;
|
|
1439
|
+
}));
|
|
1402
1440
|
}
|
|
1403
1441
|
/**
|
|
1404
1442
|
* The mutations of a regular expression pattern.
|
|
@@ -1427,17 +1465,16 @@ function replaceFirstMatch(path, original, replacement) {
|
|
|
1427
1465
|
* quantifier removal ahead of class negation.
|
|
1428
1466
|
*/
|
|
1429
1467
|
function mutateRegexPattern(pattern, flags) {
|
|
1430
|
-
|
|
1431
|
-
return parseRegexMutants(pattern, orDefault(flags, ""));
|
|
1468
|
+
return pattern.length === 0 ? [] : parseRegexMutants(pattern, orDefault(flags, ""));
|
|
1432
1469
|
}
|
|
1433
1470
|
function parseRegexMutants(pattern, flags) {
|
|
1434
1471
|
try {
|
|
1435
1472
|
const groups = collectSplices(pattern, flags);
|
|
1436
|
-
groups.rest.
|
|
1473
|
+
const rest = groups.rest.toSorted((a, b) => a.start - b.start || a.priority - b.priority);
|
|
1437
1474
|
return [
|
|
1438
1475
|
...groups.bol,
|
|
1439
1476
|
...groups.eol,
|
|
1440
|
-
...
|
|
1477
|
+
...rest
|
|
1441
1478
|
].map((splice) => spliceText(pattern, splice));
|
|
1442
1479
|
} catch {
|
|
1443
1480
|
return [];
|
|
@@ -1500,18 +1537,17 @@ function negationMarker(negate) {
|
|
|
1500
1537
|
}
|
|
1501
1538
|
function collectCharacterClass(characterClass, groups) {
|
|
1502
1539
|
const pos = characterClass.start + 1;
|
|
1503
|
-
|
|
1540
|
+
groups.rest.push(Match.value(characterClass.negate).pipe(Match.when(true, () => ({
|
|
1504
1541
|
start: pos,
|
|
1505
1542
|
end: pos + 1,
|
|
1506
1543
|
text: "",
|
|
1507
1544
|
priority: 1
|
|
1508
|
-
})
|
|
1509
|
-
else groups.rest.push({
|
|
1545
|
+
})), Match.orElse(() => ({
|
|
1510
1546
|
start: pos,
|
|
1511
1547
|
end: pos,
|
|
1512
1548
|
text: "^",
|
|
1513
1549
|
priority: 1
|
|
1514
|
-
});
|
|
1550
|
+
}))));
|
|
1515
1551
|
}
|
|
1516
1552
|
function collectCharacterSet(characterSet, groups) {
|
|
1517
1553
|
pushWhen(groups.rest, characterSetSplice(characterSet));
|
|
@@ -1541,12 +1577,10 @@ function classSplice(characterSet) {
|
|
|
1541
1577
|
/** `\d`-style sets invert by letter case, `\p{}` by `p`/`P`. */
|
|
1542
1578
|
function invertedMarker(marker) {
|
|
1543
1579
|
const propertyMarker = PROPERTY_MARKERS[marker];
|
|
1544
|
-
|
|
1545
|
-
return invertLetterCase(marker);
|
|
1580
|
+
return propertyMarker === void 0 ? invertLetterCase(marker) : propertyMarker;
|
|
1546
1581
|
}
|
|
1547
1582
|
function invertLetterCase(letter) {
|
|
1548
|
-
|
|
1549
|
-
return letter.toUpperCase();
|
|
1583
|
+
return letter === letter.toUpperCase() ? letter.toLowerCase() : letter.toUpperCase();
|
|
1550
1584
|
}
|
|
1551
1585
|
function collectQuantifier(quantifier, groups) {
|
|
1552
1586
|
groups.rest.push({
|
|
@@ -1562,11 +1596,15 @@ function anchorRemoval(assertion, pattern) {
|
|
|
1562
1596
|
end: assertion.end,
|
|
1563
1597
|
text: ""
|
|
1564
1598
|
};
|
|
1565
|
-
|
|
1566
|
-
return splice;
|
|
1599
|
+
return spliceText(pattern, splice).length === 0 ? void 0 : splice;
|
|
1567
1600
|
}
|
|
1568
1601
|
function pushWhen(list, splice) {
|
|
1569
|
-
|
|
1602
|
+
Option.match(Option.fromNullishOr(splice), {
|
|
1603
|
+
onNone: () => void 0,
|
|
1604
|
+
onSome: (present) => {
|
|
1605
|
+
list.push(present);
|
|
1606
|
+
}
|
|
1607
|
+
});
|
|
1570
1608
|
}
|
|
1571
1609
|
function spliceText(pattern, splice) {
|
|
1572
1610
|
return pattern.slice(0, splice.start) + splice.text + pattern.slice(splice.end);
|
|
@@ -1620,16 +1658,14 @@ function isStringConcatenation(node) {
|
|
|
1620
1658
|
}
|
|
1621
1659
|
/** A chained `a + b + c` carries its value on the innermost left operand's right side. */
|
|
1622
1660
|
function outerLeftOperand(node) {
|
|
1623
|
-
|
|
1624
|
-
return node.left;
|
|
1661
|
+
return node.left.type === "BinaryExpression" ? node.left.right : node.left;
|
|
1625
1662
|
}
|
|
1626
1663
|
const arrayDeclarationMutator = (node) => Match.value(node).pipe(Match.when(isArrayExpression, (array) => [arrayDeclarationReplacement(array)]), Match.when(isArrayConstructorCall, (construct) => [arrayConstructorReplacement(construct)]), Match.orElse(() => NO_MUTANTS));
|
|
1627
1664
|
function isArrayExpression(node) {
|
|
1628
1665
|
return node.type === "ArrayExpression";
|
|
1629
1666
|
}
|
|
1630
1667
|
function arrayDeclarationReplacement(array) {
|
|
1631
|
-
|
|
1632
|
-
return arrayExpression([stringLiteral("Stryker was here")]);
|
|
1668
|
+
return array.elements.length > 0 ? arrayExpression([]) : arrayExpression([stringLiteral("Stryker was here")]);
|
|
1633
1669
|
}
|
|
1634
1670
|
function isArrayConstructorCall(node) {
|
|
1635
1671
|
return isCallOrNewExpression(node) && isArrayIdentifier(node.callee);
|
|
@@ -1642,12 +1678,11 @@ function isArrayIdentifier(node) {
|
|
|
1642
1678
|
}
|
|
1643
1679
|
function arrayConstructorReplacement(construct) {
|
|
1644
1680
|
const mutatedCallArgs = constructorArguments(construct.arguments);
|
|
1645
|
-
|
|
1646
|
-
return
|
|
1681
|
+
const callee = cloneNode(construct.callee);
|
|
1682
|
+
return construct.type === "NewExpression" ? newExpression(callee, mutatedCallArgs) : callExpression(callee, mutatedCallArgs);
|
|
1647
1683
|
}
|
|
1648
1684
|
function constructorArguments(args) {
|
|
1649
|
-
|
|
1650
|
-
return [arrayExpression([])];
|
|
1685
|
+
return args.length > 0 ? [] : [arrayExpression([])];
|
|
1651
1686
|
}
|
|
1652
1687
|
const arrowFunctionMutator = (node) => Match.value(node).pipe(Match.when(isExpressionBodiedArrow, () => [arrowFunctionExpression([], identifier("undefined"))]), Match.orElse(() => NO_MUTANTS));
|
|
1653
1688
|
function isExpressionBodiedArrow(node) {
|
|
@@ -1683,8 +1718,7 @@ function isStringLiteral(value) {
|
|
|
1683
1718
|
return nodeType(value) === "Literal" && hasStringValue(value);
|
|
1684
1719
|
}
|
|
1685
1720
|
function hasStringValue(value) {
|
|
1686
|
-
|
|
1687
|
-
return typeof value["value"] === "string";
|
|
1721
|
+
return Predicate.hasProperty(value, "value") && typeof value["value"] === "string";
|
|
1688
1722
|
}
|
|
1689
1723
|
const stringAssignmentTypes = Object.freeze([
|
|
1690
1724
|
"&&=",
|
|
@@ -1767,8 +1801,7 @@ function hasSuperInChildren(node) {
|
|
|
1767
1801
|
return Object.keys(node).some((key) => containsSuperInValue(propertyOf(node, key)));
|
|
1768
1802
|
}
|
|
1769
1803
|
function containsSuperInValue(value) {
|
|
1770
|
-
|
|
1771
|
-
return containsSuperCall(value);
|
|
1804
|
+
return Array.isArray(value) ? value.some(containsSuperCall) : containsSuperCall(value);
|
|
1772
1805
|
}
|
|
1773
1806
|
const booleanLiteralMutator = (node) => Match.value(node).pipe(Match.when(isBooleanLiteral, (literal) => [booleanLiteral(!literal.value)]), Match.when(isNegatedPrefix, (unary) => [cloneNode(unary.argument)]), Match.orElse(() => NO_MUTANTS));
|
|
1774
1807
|
function isBooleanLiteral(node) {
|
|
@@ -1919,8 +1952,12 @@ const baseReplacements = {
|
|
|
1919
1952
|
setUTCSeconds: "setUTCMilliseconds"
|
|
1920
1953
|
};
|
|
1921
1954
|
const noReverseReplacements = ["getUTCDate", "setUTCDate"];
|
|
1922
|
-
const
|
|
1923
|
-
|
|
1955
|
+
const isReversible = (key, value) => value !== null && !noReverseReplacements.includes(key);
|
|
1956
|
+
const reverseReplacements = Object.fromEntries(Object.entries(baseReplacements).flatMap(([key, value]) => isReversible(key, value) ? [[value, key]] : []));
|
|
1957
|
+
const replacements = {
|
|
1958
|
+
...baseReplacements,
|
|
1959
|
+
...reverseReplacements
|
|
1960
|
+
};
|
|
1924
1961
|
const methodExpressionMutator = (node) => Match.value(node).pipe(Match.when(isCallExpression, (call) => methodCallMutants(call)), Match.orElse(() => NO_MUTANTS));
|
|
1925
1962
|
function methodCallMutants(call) {
|
|
1926
1963
|
return Match.value(methodMutation(call)).pipe(Match.when(isMethodMutation, (mutation) => [methodExpressionReplacement(mutation)]), Match.orElse(() => NO_MUTANTS));
|
|
@@ -1934,7 +1971,9 @@ function methodMutation(call) {
|
|
|
1934
1971
|
return Match.value(callee).pipe(Match.when(void 0, () => void 0), Match.orElse((member) => mutationFor(call, member)));
|
|
1935
1972
|
}
|
|
1936
1973
|
function mutationFor(call, callee) {
|
|
1937
|
-
|
|
1974
|
+
const methodName = callee.property.name;
|
|
1975
|
+
const replacement = Object.hasOwn(replacements, methodName) ? replacements[methodName] : void 0;
|
|
1976
|
+
return Match.value(replacement).pipe(Match.when(void 0, () => void 0), Match.orElse((newName) => ({
|
|
1938
1977
|
call,
|
|
1939
1978
|
callee,
|
|
1940
1979
|
newName
|
|
@@ -2017,16 +2056,17 @@ const PLACEHOLDER = "Stryker was here!";
|
|
|
2017
2056
|
const stringLiteralMutator = (node, context) => Match.value(node).pipe(Match.when(isTemplateLiteral, (template) => templateMutants(template)), Match.when(isStringLiteral, (literal) => mutantsWhen(isValidParent(literal, context), () => [stringLiteral(replacementText(literal.value.length === 0))])), Match.orElse(() => NO_MUTANTS));
|
|
2018
2057
|
function templateMutants(template) {
|
|
2019
2058
|
const first = template.quasis[0];
|
|
2020
|
-
|
|
2021
|
-
|
|
2059
|
+
return Option.match(Option.fromNullishOr(first), {
|
|
2060
|
+
onNone: () => NO_MUTANTS,
|
|
2061
|
+
onSome: (present) => [emptyOrPlaceholderTemplate(template, present)]
|
|
2062
|
+
});
|
|
2022
2063
|
}
|
|
2023
2064
|
function emptyOrPlaceholderTemplate(template, first) {
|
|
2024
2065
|
const isEmptyTemplate = [template.quasis.length === 1, first.value.raw.length === 0].every(Boolean);
|
|
2025
2066
|
return templateLiteral([templateElement(replacementText(isEmptyTemplate))], []);
|
|
2026
2067
|
}
|
|
2027
2068
|
function replacementText(isEmpty) {
|
|
2028
|
-
|
|
2029
|
-
return "";
|
|
2069
|
+
return isEmpty ? PLACEHOLDER : "";
|
|
2030
2070
|
}
|
|
2031
2071
|
function isValidParent(child, context) {
|
|
2032
2072
|
const parent = context.parent;
|
|
@@ -2181,10 +2221,7 @@ const selectMutators = dual((args) => args.length >= 2, selectMutatorsDataFirst)
|
|
|
2181
2221
|
var ParseFailed = class extends S.TaggedError("@systemfsoftware/stryker-js-instrumenter/Parser.schema/ParseFailed")("ParseFailed", {
|
|
2182
2222
|
fileName: S.String,
|
|
2183
2223
|
message: S.String,
|
|
2184
|
-
location:
|
|
2185
|
-
line: S.Finite,
|
|
2186
|
-
column: S.Finite
|
|
2187
|
-
}),
|
|
2224
|
+
location: Position,
|
|
2188
2225
|
cause: S.Defect()
|
|
2189
2226
|
}) {
|
|
2190
2227
|
get message() {
|
|
@@ -2195,61 +2232,70 @@ var ParseFailed = class extends S.TaggedError("@systemfsoftware/stryker-js-instr
|
|
|
2195
2232
|
//#region src/Parser.service.ts
|
|
2196
2233
|
const oxcModule = Effect.cached(Effect.promise(() => import("oxc-parser")));
|
|
2197
2234
|
const loadOxc = Effect.flatMap(oxcModule, (load) => load);
|
|
2198
|
-
const parseWithOxcDataFirst = (text, fileName, lang)
|
|
2235
|
+
const parseWithOxcDataFirst = Effect.fn("stryker.instrument.parser.parseWithOxc")(function* (text, fileName, lang) {
|
|
2199
2236
|
const result = (yield* loadOxc).parseSync(fileName, text, {
|
|
2200
2237
|
lang,
|
|
2201
2238
|
range: true
|
|
2202
2239
|
});
|
|
2203
|
-
const
|
|
2204
|
-
const failure = oxcParseFailure(result.errors, fileName,
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2240
|
+
const lineStarts = lineStartsOf(text);
|
|
2241
|
+
const failure = oxcParseFailure(result.errors, fileName, lineStarts);
|
|
2242
|
+
return yield* Option.match(Option.fromNullishOr(failure), {
|
|
2243
|
+
onNone: () => Effect.succeed({
|
|
2244
|
+
root: result.program,
|
|
2245
|
+
comments: result.comments
|
|
2246
|
+
}),
|
|
2247
|
+
onSome: (failed) => Effect.fail(failed)
|
|
2248
|
+
});
|
|
2210
2249
|
});
|
|
2211
2250
|
const parseWithOxc = dual((args) => args.length >= 3, parseWithOxcDataFirst);
|
|
2212
|
-
const oxcParseFailure = (errors, fileName,
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
return ParseFailed.make({
|
|
2251
|
+
const oxcParseFailure = (errors, fileName, lineStarts) => Option.match(Option.fromNullishOr(errors.at(0)), {
|
|
2252
|
+
onNone: () => void 0,
|
|
2253
|
+
onSome: (first) => ParseFailed.make({
|
|
2216
2254
|
fileName,
|
|
2217
2255
|
message: first.message,
|
|
2218
|
-
location:
|
|
2256
|
+
location: positionAt(lineStarts, oxcErrorLabelStart(first)),
|
|
2219
2257
|
cause: errors.map((reported) => reported.message)
|
|
2220
|
-
})
|
|
2221
|
-
};
|
|
2222
|
-
const oxcErrorLabelStart = (error) =>
|
|
2223
|
-
const label = error.labels.at(0);
|
|
2224
|
-
if (label === void 0) return 0;
|
|
2225
|
-
return label.start;
|
|
2226
|
-
};
|
|
2258
|
+
})
|
|
2259
|
+
});
|
|
2260
|
+
const oxcErrorLabelStart = (error) => Option.getOrElse(Option.map(Option.fromNullishOr(error.labels.at(0)), (label) => label.start), () => 0);
|
|
2227
2261
|
const createParser = () => ({ parse: (code, fileName, scriptFormat) => Match.value(scriptFormat).pipe(Match.when("js", () => parseJS(code, fileName)), Match.when("ts", () => parseTS(code, fileName)), Match.when("tsx", () => parseTsx(code, fileName)), Match.exhaustive) });
|
|
2228
|
-
const parseJSDataFirst =
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2262
|
+
const parseJSDataFirst = Effect.fn("stryker.instrument.parser.parseJS")(function* (text, fileName) {
|
|
2263
|
+
const parsed = yield* parseWithOxc(text, fileName, "js");
|
|
2264
|
+
return {
|
|
2265
|
+
originFileName: fileName,
|
|
2266
|
+
rawContent: text,
|
|
2267
|
+
format: "js",
|
|
2268
|
+
root: parsed.root,
|
|
2269
|
+
comments: parsed.comments
|
|
2270
|
+
};
|
|
2271
|
+
});
|
|
2235
2272
|
const parseJS = dual((args) => args.length >= 2, parseJSDataFirst);
|
|
2236
|
-
const parseTSDataFirst =
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2273
|
+
const parseTSDataFirst = Effect.fn("stryker.instrument.parser.parseTS")(function* (text, fileName) {
|
|
2274
|
+
const parsed = yield* parseWithOxc(text, fileName, "ts");
|
|
2275
|
+
return {
|
|
2276
|
+
originFileName: fileName,
|
|
2277
|
+
rawContent: text,
|
|
2278
|
+
format: "ts",
|
|
2279
|
+
root: parsed.root,
|
|
2280
|
+
comments: parsed.comments
|
|
2281
|
+
};
|
|
2282
|
+
});
|
|
2243
2283
|
const parseTS = dual((args) => args.length >= 2, parseTSDataFirst);
|
|
2244
|
-
const parseTsxDataFirst =
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2284
|
+
const parseTsxDataFirst = Effect.fn("stryker.instrument.parser.parseTsx")(function* (text, fileName) {
|
|
2285
|
+
const parsed = yield* parseWithOxc(text, fileName, "tsx");
|
|
2286
|
+
return {
|
|
2287
|
+
root: parsed.root,
|
|
2288
|
+
comments: parsed.comments,
|
|
2289
|
+
format: "tsx",
|
|
2290
|
+
originFileName: fileName,
|
|
2291
|
+
rawContent: text
|
|
2292
|
+
};
|
|
2293
|
+
});
|
|
2251
2294
|
const parseTsx = dual((args) => args.length >= 2, parseTsxDataFirst);
|
|
2252
2295
|
//#endregion
|
|
2296
|
+
//#region src/print/PrintFailed.schema.ts
|
|
2297
|
+
var PrintFailed = class extends S.TaggedError("@systemfsoftware/stryker-js-instrumenter/print/PrintFailed.schema/PrintFailed")("PrintFailed", { message: S.String }) {};
|
|
2298
|
+
//#endregion
|
|
2253
2299
|
//#region src/print/SourceText.ts
|
|
2254
2300
|
const printProgramDataFirst = (program, opts = {}) => programText(opts, program);
|
|
2255
2301
|
const printProgram = dual((args) => Predicate.hasProperty(args[0], "type"), printProgramDataFirst);
|
|
@@ -3239,28 +3285,19 @@ const externalModuleArgumentText = (value) => Boolean$1.match(Predicate.isTruthy
|
|
|
3239
3285
|
function printDataFirst(file, entryForFormat) {
|
|
3240
3286
|
const context = { print: (inner) => print(inner, entryForFormat) };
|
|
3241
3287
|
return Option.match(entryForFormat(formatKeyOf(file)), {
|
|
3242
|
-
onNone: () =>
|
|
3243
|
-
onSome: (entry) => entry.print(file, context)
|
|
3288
|
+
onNone: () => Result.fail(PrintFailed.make({ message: `No registered format renders the "${formatKeyOf(file)}" AST` })),
|
|
3289
|
+
onSome: (entry) => Result.succeed(entry.print(file, context))
|
|
3244
3290
|
});
|
|
3245
3291
|
}
|
|
3246
3292
|
const print = dual((args) => args.length >= 2, printDataFirst);
|
|
3247
|
-
|
|
3248
|
-
throw new Error(`No registered format renders the "${formatKeyOf(file)}" AST`);
|
|
3249
|
-
}
|
|
3250
|
-
function hashbangOf(root) {
|
|
3251
|
-
if (!hasHashbang(root)) return null;
|
|
3252
|
-
return printHashbang(root.hashbang);
|
|
3253
|
-
}
|
|
3293
|
+
const hashbangOf = (root) => hasHashbang(root) ? printHashbang(root.hashbang) : null;
|
|
3254
3294
|
const hasHashbang = (root) => Predicate.hasProperty(root, "hashbang") && Predicate.isObject(root.hashbang);
|
|
3255
3295
|
const printHashbang = (field) => ({
|
|
3256
3296
|
type: "Hashbang",
|
|
3257
3297
|
value: hashbangValue(field),
|
|
3258
3298
|
start: field.start
|
|
3259
3299
|
});
|
|
3260
|
-
|
|
3261
|
-
if (typeof field.value !== "string") return "";
|
|
3262
|
-
return field.value;
|
|
3263
|
-
}
|
|
3300
|
+
const hashbangValue = (field) => typeof field.value === "string" ? field.value : "";
|
|
3264
3301
|
const printScriptAst$1 = (root) => printProgram(root, { hashbang: hashbangOf(root) });
|
|
3265
3302
|
const jsPrintDataFirst = (file) => printScriptAst$1(file.root);
|
|
3266
3303
|
const jsPrint = dual((args) => args.length >= 2, jsPrintDataFirst);
|
|
@@ -3344,51 +3381,10 @@ const resolveFormat = Workflow.make({
|
|
|
3344
3381
|
decide: (command) => Match.value(selectionFor(command)).pipe(Match.tag("ClaimSelected", ({ claim }) => Result.succeed(assignedFrom(claim, command))), Match.tag("PinUnclaimed", ({ formatId }) => Result.fail(overrideUnclaimedFrom(command, formatId))), Match.tag("ExtensionUnclaimed", () => Result.succeed(skippedFrom(command))), Match.exhaustive)
|
|
3345
3382
|
});
|
|
3346
3383
|
//#endregion
|
|
3347
|
-
//#region src/directives/directive.schema.ts
|
|
3348
|
-
const MutatorNameSchema = S.String.pipe(S.check(S.isPattern(/^[a-zA-Z]+(?: [a-zA-Z]+)*$/)));
|
|
3349
|
-
const DirectiveReasonSchema = S.String.pipe(S.check(S.isPattern(/^\S(?:[\s\S]*\S)?$/)));
|
|
3350
|
-
const DirectiveSchema = S.Struct({
|
|
3351
|
-
action: S.Literals(["disable", "restore"]),
|
|
3352
|
-
scope: S.Literals(["next-line", "block"]),
|
|
3353
|
-
mutatorNames: S.Array(MutatorNameSchema).check(S.isMinLength(1)),
|
|
3354
|
-
reason: DirectiveReasonSchema
|
|
3355
|
-
});
|
|
3356
|
-
const LocatedPositionSchema = S.Struct({
|
|
3357
|
-
line: SourceLineSchema,
|
|
3358
|
-
column: SourceColumnSchema
|
|
3359
|
-
});
|
|
3360
|
-
const LocatedDirectiveSchema = S.Struct({
|
|
3361
|
-
directive: DirectiveSchema,
|
|
3362
|
-
at: LocatedPositionSchema,
|
|
3363
|
-
governedLine: SourceLineSchema
|
|
3364
|
-
});
|
|
3365
|
-
S.Struct({
|
|
3366
|
-
directive: DirectiveSchema,
|
|
3367
|
-
at: LocatedPositionSchema,
|
|
3368
|
-
mutatorName: S.String
|
|
3369
|
-
});
|
|
3370
|
-
//#endregion
|
|
3371
3384
|
//#region src/directives/decode-directive.workflow.ts
|
|
3372
3385
|
const DIRECTIVE_PATTERN = /^\s?Stryker (disable|restore)(?: (next-line))? ([a-zA-Z, ]+)(?::(.+)?)?/;
|
|
3373
3386
|
const DEFAULT_REASON = "Ignored using a comment";
|
|
3374
3387
|
const NEXT_LINE$1 = "next-line";
|
|
3375
|
-
S.Struct({
|
|
3376
|
-
clause: S.Literals([
|
|
3377
|
-
" Stryker disable",
|
|
3378
|
-
" Stryker restore",
|
|
3379
|
-
" Stryker disable next-line",
|
|
3380
|
-
" Stryker restore next-line",
|
|
3381
|
-
" Stryker enable",
|
|
3382
|
-
" nothing"
|
|
3383
|
-
]),
|
|
3384
|
-
nameLetters: S.Array(S.Literals([
|
|
3385
|
-
"a",
|
|
3386
|
-
"b",
|
|
3387
|
-
"Z",
|
|
3388
|
-
",",
|
|
3389
|
-
" "
|
|
3390
|
-
])).check(S.isMinLength(1))
|
|
3391
|
-
});
|
|
3392
3388
|
var DecodeDirectiveCommand = class extends S.TaggedClass()("DecodeDirectiveCommand", { commentText: S.String }) {
|
|
3393
3389
|
static [Workflow.InstrumentationBrand] = {};
|
|
3394
3390
|
};
|
|
@@ -3496,27 +3492,34 @@ const loadHeader = Effect.map(parseWithOxc(INSTRUMENTATION_HEADER_SOURCE, "instr
|
|
|
3496
3492
|
instrumentationHeaderValue = parsed.root.body;
|
|
3497
3493
|
return parsed.root.body;
|
|
3498
3494
|
});
|
|
3499
|
-
const instrumentationHeader =
|
|
3495
|
+
const instrumentationHeader = Option.match(Option.fromNullishOr(instrumentationHeaderValue), {
|
|
3496
|
+
onNone: () => loadHeader,
|
|
3497
|
+
onSome: (header) => Effect.succeed(header)
|
|
3498
|
+
});
|
|
3500
3499
|
const placeHeader = (root) => Effect.map(headerFor(root), (header) => {
|
|
3501
3500
|
root.body.unshift(...header);
|
|
3502
3501
|
});
|
|
3503
|
-
const headerFor = (root) => Effect.map(instrumentationHeader, (header) => Option.match(leadingCommentsOf(root), {
|
|
3502
|
+
const headerFor = (root) => Effect.map(instrumentationHeader, (header) => Option.match(Option.all([Option.fromNullishOr(header[0]), leadingCommentsOf(root)]), {
|
|
3504
3503
|
onNone: () => header,
|
|
3505
|
-
onSome: (leadingComments) =>
|
|
3504
|
+
onSome: ([firstHeader, leadingComments]) => commentedHeader(firstHeader, leadingComments, header.slice(1))
|
|
3506
3505
|
}));
|
|
3507
|
-
const placeHeaderIfNeededDataFirst = (placed, options, root) => placed
|
|
3506
|
+
const placeHeaderIfNeededDataFirst = (placed, options, root) => Boolean$1.match(placed, {
|
|
3507
|
+
onTrue: () => placeHeaderWhenWanted(options, root),
|
|
3508
|
+
onFalse: () => Effect.void
|
|
3509
|
+
});
|
|
3508
3510
|
const placeHeaderIfNeeded = dual((args) => args.length >= 3, placeHeaderIfNeededDataFirst);
|
|
3509
|
-
const placeHeaderWhenWanted = (options, root) => options.noHeader === true
|
|
3511
|
+
const placeHeaderWhenWanted = (options, root) => Boolean$1.match(options.noHeader === true, {
|
|
3512
|
+
onTrue: () => Effect.void,
|
|
3513
|
+
onFalse: () => placeHeader(root)
|
|
3514
|
+
});
|
|
3510
3515
|
function leadingCommentsOf(root) {
|
|
3511
3516
|
const first = root.body[0];
|
|
3512
|
-
|
|
3513
|
-
return Option.some(first.leadingComments);
|
|
3517
|
+
return Option.map(Option.liftPredicate(first, hasLeadingComments$1), (value) => value.leadingComments);
|
|
3514
3518
|
}
|
|
3515
|
-
function commentedHeader(leadingComments,
|
|
3516
|
-
const firstHeader = Option.getOrThrowWith(Option.fromNullishOr(header[0]), () => /* @__PURE__ */ new Error("Instrumentation header is empty"));
|
|
3519
|
+
function commentedHeader(firstHeader, leadingComments, rest) {
|
|
3517
3520
|
const cloned = cloneNode(firstHeader);
|
|
3518
3521
|
Object.assign(cloned, { leadingComments });
|
|
3519
|
-
return cloned;
|
|
3522
|
+
return [cloned, ...rest];
|
|
3520
3523
|
}
|
|
3521
3524
|
//#endregion
|
|
3522
3525
|
//#region src/place-mutants.workflow.ts
|
|
@@ -3532,8 +3535,8 @@ const ReplacementFactsSchema = S.Struct({
|
|
|
3532
3535
|
isSwitchCase: S.Boolean
|
|
3533
3536
|
});
|
|
3534
3537
|
const PlacedMutantSchema = S.Struct({
|
|
3535
|
-
id:
|
|
3536
|
-
mutatorName:
|
|
3538
|
+
id: MutantId,
|
|
3539
|
+
mutatorName: MutatorNameSchema,
|
|
3537
3540
|
replacement: ReplacementFactsSchema
|
|
3538
3541
|
});
|
|
3539
3542
|
var PlaceMutantsCommand = class extends S.TaggedClass()("PlaceMutantsCommand", {
|
|
@@ -3546,19 +3549,19 @@ var PlaceMutantsCommand = class extends S.TaggedClass()("PlaceMutantsCommand", {
|
|
|
3546
3549
|
const PlacementDecisionTypeId = Symbol.for("@systemfsoftware/stryker-js-instrumenter/PlacementDecision");
|
|
3547
3550
|
var ExpressionSite = class extends S.TaggedClass()("ExpressionSite", {
|
|
3548
3551
|
fileName: S.String,
|
|
3549
|
-
mutantIds: S.Array(
|
|
3552
|
+
mutantIds: S.Array(MutantId)
|
|
3550
3553
|
}) {
|
|
3551
3554
|
[PlacementDecisionTypeId] = PlacementDecisionTypeId;
|
|
3552
3555
|
};
|
|
3553
3556
|
var StatementSite = class extends S.TaggedClass()("StatementSite", {
|
|
3554
3557
|
fileName: S.String,
|
|
3555
|
-
mutantIds: S.Array(
|
|
3558
|
+
mutantIds: S.Array(MutantId)
|
|
3556
3559
|
}) {
|
|
3557
3560
|
[PlacementDecisionTypeId] = PlacementDecisionTypeId;
|
|
3558
3561
|
};
|
|
3559
3562
|
var SwitchCaseSite = class extends S.TaggedClass()("SwitchCaseSite", {
|
|
3560
3563
|
fileName: S.String,
|
|
3561
|
-
mutantIds: S.Array(
|
|
3564
|
+
mutantIds: S.Array(MutantId)
|
|
3562
3565
|
}) {
|
|
3563
3566
|
[PlacementDecisionTypeId] = PlacementDecisionTypeId;
|
|
3564
3567
|
};
|
|
@@ -3570,8 +3573,8 @@ const ExpectedKindSchema = S.Literals([
|
|
|
3570
3573
|
var MutantKindMismatch = class extends S.TaggedError()("MutantKindMismatch", {
|
|
3571
3574
|
fileName: S.String,
|
|
3572
3575
|
placer: PlacerNameSchema,
|
|
3573
|
-
mutantId:
|
|
3574
|
-
mutatorName:
|
|
3576
|
+
mutantId: MutantId,
|
|
3577
|
+
mutatorName: MutatorNameSchema,
|
|
3575
3578
|
expected: ExpectedKindSchema
|
|
3576
3579
|
}) {};
|
|
3577
3580
|
var NoPlacerClaimsNode = class extends S.TaggedError()("NoPlacerClaimsNode", { fileName: S.String }) {};
|
|
@@ -3624,23 +3627,19 @@ const placeMutants = Workflow.make({
|
|
|
3624
3627
|
});
|
|
3625
3628
|
//#endregion
|
|
3626
3629
|
//#region src/plan-mutants.workflow.ts
|
|
3627
|
-
const LocationSchema = S.Struct({
|
|
3628
|
-
start: NodePositionSchema,
|
|
3629
|
-
end: NodePositionSchema
|
|
3630
|
-
});
|
|
3631
3630
|
const WILDCARD = "all";
|
|
3632
3631
|
const NEXT_LINE = "next-line";
|
|
3633
3632
|
const MutantCandidateSchema = S.Struct({
|
|
3634
|
-
mutatorName:
|
|
3633
|
+
mutatorName: MutatorNameSchema,
|
|
3635
3634
|
replacementCode: S.String,
|
|
3636
|
-
location: S.optional(
|
|
3635
|
+
location: S.optional(Location),
|
|
3637
3636
|
ignorerReason: S.optional(S.String)
|
|
3638
3637
|
});
|
|
3639
3638
|
const PlannedMutantSchema = S.Struct({
|
|
3640
|
-
id:
|
|
3641
|
-
mutatorName:
|
|
3639
|
+
id: MutantId,
|
|
3640
|
+
mutatorName: MutatorNameSchema,
|
|
3642
3641
|
replacementCode: S.String,
|
|
3643
|
-
location:
|
|
3642
|
+
location: Location,
|
|
3644
3643
|
ignoreReason: S.optional(S.String)
|
|
3645
3644
|
});
|
|
3646
3645
|
const MutantCounterSchema = S.Int.pipe(S.check(S.isBetween({
|
|
@@ -3651,10 +3650,10 @@ const MutantCountSchema = S.Int.pipe(S.check(S.isGreaterThanOrEqualTo(0)));
|
|
|
3651
3650
|
var PlanMutantsCommand = class extends S.TaggedClass()("PlanMutantsCommand", {
|
|
3652
3651
|
fileName: S.String,
|
|
3653
3652
|
firstIndex: MutantCounterSchema,
|
|
3654
|
-
offset:
|
|
3655
|
-
line:
|
|
3656
|
-
mutatorNames: S.Array(
|
|
3657
|
-
excludedMutations: S.Array(
|
|
3653
|
+
offset: ScriptOrigin,
|
|
3654
|
+
line: Line,
|
|
3655
|
+
mutatorNames: S.Array(MutatorNameSchema),
|
|
3656
|
+
excludedMutations: S.Array(MutatorNameSchema),
|
|
3658
3657
|
rule: S.Array(LocatedDirectiveSchema),
|
|
3659
3658
|
directives: S.Array(LocatedDirectiveSchema),
|
|
3660
3659
|
candidates: S.Array(MutantCandidateSchema)
|
|
@@ -3679,7 +3678,7 @@ var MutantsFullyIgnored = class extends S.TaggedClass()("MutantsFullyIgnored", {
|
|
|
3679
3678
|
};
|
|
3680
3679
|
var MutantWithoutLocation = class extends S.TaggedError()("MutantWithoutLocation", {
|
|
3681
3680
|
fileName: S.String,
|
|
3682
|
-
mutatorName:
|
|
3681
|
+
mutatorName: MutatorNameSchema
|
|
3683
3682
|
}) {};
|
|
3684
3683
|
const reachedLine = (located, line) => Match.value(located.directive.scope).pipe(Match.when(NEXT_LINE, () => located.governedLine === line), Match.when("block", () => true), Match.exhaustive);
|
|
3685
3684
|
const namesMutator = (located, lowerMutatorName) => located.directive.mutatorNames.some((name) => [name === WILDCARD, name.toLowerCase() === lowerMutatorName].some(Boolean));
|
|
@@ -3697,12 +3696,13 @@ const unusedDirectiveWarning = (unused, fileName) => {
|
|
|
3697
3696
|
};
|
|
3698
3697
|
const warningsOf = (command) => unusedDirectives(command.directives, command.mutatorNames).map((unused) => unusedDirectiveWarning(unused, command.fileName));
|
|
3699
3698
|
/**
|
|
3700
|
-
*
|
|
3701
|
-
*
|
|
3702
|
-
*
|
|
3703
|
-
*
|
|
3699
|
+
* The node span (`source`) is a 1-based position inside the embedded script;
|
|
3700
|
+
* the region origin (`offset`) is where that script begins in its host file.
|
|
3701
|
+
* The region's first line carries its column shift, later lines start at
|
|
3702
|
+
* column 1, so the shift adds `offset.line - 1` lines and — only when the node
|
|
3703
|
+
* sits on the region's first line — `offset.columnShift`.
|
|
3704
3704
|
*/
|
|
3705
|
-
const columnOffsetOf = (source, offset) => Match.value(source.line === 1).pipe(Match.when(true, () => offset.
|
|
3705
|
+
const columnOffsetOf = (source, offset) => Match.value(source.line === 1).pipe(Match.when(true, () => offset.columnShift), Match.when(false, () => 0), Match.exhaustive);
|
|
3706
3706
|
const shiftedPosition = (source, offset) => ({
|
|
3707
3707
|
column: source.column + columnOffsetOf(source, offset),
|
|
3708
3708
|
line: source.line + offset.line - 1
|
|
@@ -3717,7 +3717,7 @@ const plannedMutant = (command, candidate, index) => Option.match(Option.fromNul
|
|
|
3717
3717
|
mutatorName: candidate.mutatorName
|
|
3718
3718
|
})),
|
|
3719
3719
|
onSome: (location) => Result.succeed({
|
|
3720
|
-
id: `${command.firstIndex + index}
|
|
3720
|
+
id: PlannedMutantSchema.fields.id.make(`${command.firstIndex + index}`),
|
|
3721
3721
|
mutatorName: candidate.mutatorName,
|
|
3722
3722
|
replacementCode: candidate.replacementCode,
|
|
3723
3723
|
location: shiftedLocation(location, command.offset),
|
|
@@ -3753,7 +3753,11 @@ const comparePositions = (a, b) => {
|
|
|
3753
3753
|
};
|
|
3754
3754
|
const locationIncluded = (haystack, needle) => comparePositions(haystack.start, needle.start) <= 0 && comparePositions(haystack.end, needle.end) >= 0;
|
|
3755
3755
|
const locationOverlaps = (a, b) => comparePositions(a.start, b.end) <= 0 && comparePositions(a.end, b.start) >= 0;
|
|
3756
|
-
const errorTextOf$1 = (cause) => Option.getOrElse(Option.map(
|
|
3756
|
+
const errorTextOf$1 = (cause) => Option.getOrElse(Option.map(errorTextOf$2(cause), (rendered) => rendered.text), () => "");
|
|
3757
|
+
const traversalFailure = (cause) => InstrumentError.make({
|
|
3758
|
+
message: cause instanceof Error ? cause.message : errorTextOf$1(cause),
|
|
3759
|
+
cause
|
|
3760
|
+
});
|
|
3757
3761
|
const DEFAULT_MUTATOR_REGISTRY = {
|
|
3758
3762
|
defaults: defaultMutators,
|
|
3759
3763
|
optIn: optInMutators
|
|
@@ -3776,28 +3780,20 @@ const locatedDirective = (comment, governedLine) => Option.flatMap(Option.fromNu
|
|
|
3776
3780
|
at: loc.start,
|
|
3777
3781
|
governedLine
|
|
3778
3782
|
})));
|
|
3779
|
-
const directivesOf = (node,
|
|
3783
|
+
const directivesOf = (node, governedLine) => attachedComments(node).flatMap((comment) => Option.toArray(locatedDirective(comment, governedLine)));
|
|
3780
3784
|
const foldInto = (rule, directive) => Match.value(foldRule(FoldRuleCommand.make({
|
|
3781
3785
|
rule,
|
|
3782
3786
|
directive
|
|
3783
3787
|
}))).pipe(Match.when(Result.isSuccess, (folded) => folded.success.rule), Match.orElse(() => rule));
|
|
3784
3788
|
const NO_COMMENTS = [];
|
|
3785
|
-
|
|
3786
|
-
if (!hasLeadingComments(node)) return NO_COMMENTS;
|
|
3787
|
-
return node.leadingComments;
|
|
3788
|
-
}
|
|
3789
|
+
const attachedComments = (node) => hasLeadingComments(node) ? node.leadingComments : NO_COMMENTS;
|
|
3789
3790
|
const hasLeadingComments = (value) => Predicate.hasProperty(value, "leadingComments") && Array.isArray(value.leadingComments);
|
|
3790
|
-
function
|
|
3791
|
-
const ancestors = [];
|
|
3792
|
-
for (let current = path.parentPath; current !== null; current = current.parentPath) ancestors.push(current.node);
|
|
3793
|
-
return ancestors;
|
|
3794
|
-
}
|
|
3795
|
-
function isTypeNode(path) {
|
|
3791
|
+
function isTypeNode(node) {
|
|
3796
3792
|
return [
|
|
3797
|
-
tsTypeAnnotationNodeTypes.includes(
|
|
3798
|
-
flowTypeAnnotationNodeTypes.includes(
|
|
3799
|
-
isDeclareVariableStatement(
|
|
3800
|
-
isDeclareModule(
|
|
3793
|
+
tsTypeAnnotationNodeTypes.includes(node.type),
|
|
3794
|
+
flowTypeAnnotationNodeTypes.includes(node.type),
|
|
3795
|
+
isDeclareVariableStatement(node),
|
|
3796
|
+
isDeclareModule(node)
|
|
3801
3797
|
].some((isType) => isType);
|
|
3802
3798
|
}
|
|
3803
3799
|
function isDeclareVariableStatement(node) {
|
|
@@ -3833,8 +3829,8 @@ const flowTypeAnnotationNodeTypes = Object.freeze([
|
|
|
3833
3829
|
"OpaqueType",
|
|
3834
3830
|
"TypeAlias"
|
|
3835
3831
|
]);
|
|
3836
|
-
function isImportDeclaration(
|
|
3837
|
-
return nodeType(
|
|
3832
|
+
function isImportDeclaration(node) {
|
|
3833
|
+
return nodeType(node) === "TSImportEqualsDeclaration" || node.type === "ImportDeclaration";
|
|
3838
3834
|
}
|
|
3839
3835
|
function mutantTestExpression(mutantId) {
|
|
3840
3836
|
return callExpression(identifier(IS_MUTANT_ACTIVE_HELPER), [stringLiteral(mutantId)]);
|
|
@@ -3842,41 +3838,33 @@ function mutantTestExpression(mutantId) {
|
|
|
3842
3838
|
function mutationCoverageSequenceExpression(mutants, targetExpression) {
|
|
3843
3839
|
const mutantIds = [...mutants].map((mutant) => stringLiteral(mutant.id));
|
|
3844
3840
|
const sequence = [callExpression(identifier(COVER_MUTANT_HELPER), mutantIds)];
|
|
3845
|
-
|
|
3846
|
-
return sequenceExpression(sequence);
|
|
3841
|
+
return sequenceExpression(targetExpression === void 0 ? sequence : [...sequence, targetExpression]);
|
|
3847
3842
|
}
|
|
3848
3843
|
const refusalPlacer = (refusal) => Match.value(refusal).pipe(Match.tag("MutantKindMismatch", (mismatch) => mismatch.placer), Match.tag("MutantsUnapplied", (unapplied) => unapplied.placer), Match.tag("NoPlacerClaimsNode", () => "no placer"), Match.tag("MutantNotApplied", () => "no placer"), Match.exhaustive);
|
|
3849
3844
|
const refusalDetail = (refusal) => Match.value(refusal).pipe(Match.tag("MutantKindMismatch", (mismatch) => `Expected ${mismatch.expected} for mutant ${mismatch.mutantId}`), Match.tag("MutantsUnapplied", (unapplied) => `Failed to apply ${unapplied.mutatorNames.join(", ")}`), Match.tag("NoPlacerClaimsNode", () => "No placer claims the node"), Match.tag("MutantNotApplied", (unapplied) => `Could not apply the ${unapplied.mutatorName} mutant`), Match.exhaustive);
|
|
3850
|
-
const
|
|
3845
|
+
const placementFailureMessage = (refusal, node, mutants, fileName, lineStarts, basePath) => {
|
|
3851
3846
|
const message = `${refusalPlacer(refusal)} could not place mutants with type(s): "${placementListFormat.format(mutants.map((mutant) => mutant.mutatorName))}"`;
|
|
3852
|
-
return
|
|
3847
|
+
return `${placementLocation(node, fileName, lineStarts, basePath)} ${message}. Either remove this file from the list of files to be mutated, or exclude the mutator (using \`mutator.excludedMutations\`). Original error: ${refusalDetail(refusal)}`;
|
|
3853
3848
|
};
|
|
3854
3849
|
function nodeOfKind(mutant, node, isKind, kind) {
|
|
3855
|
-
return
|
|
3856
|
-
}
|
|
3857
|
-
function narrowNode(node, isKind, message) {
|
|
3858
|
-
if (isKind(node)) return node;
|
|
3859
|
-
throw new Error(message);
|
|
3850
|
+
return isKind(node) ? Result.succeed(node) : Result.fail(/* @__PURE__ */ new Error(`Cannot place mutant ${mutant.id}: expected ${kind}, got ${node.type}`));
|
|
3860
3851
|
}
|
|
3861
3852
|
function expressionOf(node) {
|
|
3862
|
-
return
|
|
3853
|
+
return isExpressionKind(node) ? Result.succeed(node) : Result.fail(/* @__PURE__ */ new Error(`Expected an expression, got ${node.type}`));
|
|
3863
3854
|
}
|
|
3864
3855
|
function statementOf(node) {
|
|
3865
|
-
return
|
|
3856
|
+
return isStatementKind(node) ? Result.succeed(node) : Result.fail(/* @__PURE__ */ new Error(`Expected a statement, got ${node.type}`));
|
|
3866
3857
|
}
|
|
3867
3858
|
function isSwitchCase(node) {
|
|
3868
3859
|
return nodeType(node) === "SwitchCase";
|
|
3869
3860
|
}
|
|
3870
3861
|
function switchCaseOf(node) {
|
|
3871
|
-
return
|
|
3862
|
+
return isSwitchCase(node) ? Result.succeed(node) : Result.fail(/* @__PURE__ */ new Error(`Expected a switch case, got ${node.type}`));
|
|
3872
3863
|
}
|
|
3873
|
-
const fileNameWithin = (basePath, fileName) =>
|
|
3874
|
-
|
|
3875
|
-
return relativeTo(basePath, fileName);
|
|
3876
|
-
};
|
|
3877
|
-
function placementLocation(node, fileName, lineTable, basePath) {
|
|
3864
|
+
const fileNameWithin = (basePath, fileName) => basePath === void 0 ? fileName : relativeTo(basePath, fileName);
|
|
3865
|
+
function placementLocation(node, fileName, lineStarts, basePath) {
|
|
3878
3866
|
const relativeFile = fileNameWithin(basePath, fileName);
|
|
3879
|
-
const position = Option.map(Option.fromNullishOr(spanOf(node)), (span) =>
|
|
3867
|
+
const position = Option.map(Option.fromNullishOr(spanOf(node)), (span) => positionAt(lineStarts, span.start));
|
|
3880
3868
|
return Option.match(position, {
|
|
3881
3869
|
onNone: () => `${relativeFile}:undefined:undefined`,
|
|
3882
3870
|
onSome: (at) => `${relativeFile}:${at.line}:${at.column}`
|
|
@@ -3886,27 +3874,25 @@ const placementListFormat = new Intl.ListFormat("en");
|
|
|
3886
3874
|
const normalizeSeparators = (value) => value.replace(/\\/g, "/");
|
|
3887
3875
|
const withTrailingSlash = (basePath) => {
|
|
3888
3876
|
const normalized = normalizeSeparators(basePath);
|
|
3889
|
-
|
|
3890
|
-
return `${normalized}/`;
|
|
3877
|
+
return normalized.endsWith("/") ? normalized : `${normalized}/`;
|
|
3891
3878
|
};
|
|
3892
3879
|
const relativeTo = (basePath, fileName) => {
|
|
3893
3880
|
const prefix = withTrailingSlash(basePath);
|
|
3894
3881
|
const normalizedFile = normalizeSeparators(fileName);
|
|
3895
|
-
|
|
3896
|
-
return normalizedFile.slice(prefix.length);
|
|
3882
|
+
return normalizedFile.startsWith(prefix) ? normalizedFile.slice(prefix.length) : fileName;
|
|
3897
3883
|
};
|
|
3898
3884
|
function classOrFunctionExpressionNamedIfNeeded(path) {
|
|
3899
|
-
return
|
|
3885
|
+
return Option.flatMap(Option.filter(Option.some(path.node), isAnonymousFunctionOrClass), (node) => nameFromParent(path, node));
|
|
3900
3886
|
}
|
|
3901
3887
|
function nameFromParent(path, node) {
|
|
3902
3888
|
return Match.value(path.parentPath?.node).pipe(Match.when(isVariableDeclarator, (declarator) => adoptDeclaredName(node, declarator)), Match.when({
|
|
3903
3889
|
type: "Property",
|
|
3904
3890
|
key: { type: "Identifier" }
|
|
3905
|
-
}, () => namedPropertyValue(path, node)), Match.orElse(() =>
|
|
3891
|
+
}, () => namedPropertyValue(path, node)), Match.orElse(() => Option.none()));
|
|
3906
3892
|
}
|
|
3907
3893
|
/** A property value only survives by name when the declaration above it carries one. */
|
|
3908
3894
|
function namedPropertyValue(path, node) {
|
|
3909
|
-
return Match.value(path.getStatementParent()?.node.type).pipe(Match.when("VariableDeclaration", () => node), Match.orElse(() =>
|
|
3895
|
+
return Match.value(path.getStatementParent()?.node.type).pipe(Match.when("VariableDeclaration", () => Option.some(node)), Match.orElse(() => Option.none()));
|
|
3910
3896
|
}
|
|
3911
3897
|
function adoptIdentifier(node, identifier) {
|
|
3912
3898
|
node.id = identifier;
|
|
@@ -3919,14 +3905,11 @@ function isFunctionOrClassExpression(node) {
|
|
|
3919
3905
|
return node.type === "FunctionExpression" || node.type === "ClassExpression";
|
|
3920
3906
|
}
|
|
3921
3907
|
function arrowFunctionExpressionNamedIfNeeded(path) {
|
|
3922
|
-
return Match.value(path.node).pipe(Match.when({ type: "ArrowFunctionExpression" }, (node) => arrowNamedByDeclarator(node, path.parentPath)), Match.orElse(() =>
|
|
3908
|
+
return Match.value(path.node).pipe(Match.when({ type: "ArrowFunctionExpression" }, (node) => arrowNamedByDeclarator(node, path.parentPath)), Match.orElse(() => Option.none()));
|
|
3923
3909
|
}
|
|
3924
3910
|
/** An arrow bound to a named declaration is re-emitted as a named function. */
|
|
3925
3911
|
function arrowNamedByDeclarator(node, parentPath) {
|
|
3926
|
-
return Option.
|
|
3927
|
-
onNone: () => void 0,
|
|
3928
|
-
onSome: (identifier) => namedArrowExpression(node, identifier)
|
|
3929
|
-
});
|
|
3912
|
+
return Option.map(declaratorIdentifier(parentPath), (identifier) => namedArrowExpression(node, identifier));
|
|
3930
3913
|
}
|
|
3931
3914
|
function namedArrowExpression(node, identifier) {
|
|
3932
3915
|
const declaration = variableDeclaration("const", [variableDeclarator(identifier, node)]);
|
|
@@ -3939,10 +3922,7 @@ function isVariableDeclarator(node) {
|
|
|
3939
3922
|
return nodeType(node) === "VariableDeclarator";
|
|
3940
3923
|
}
|
|
3941
3924
|
function adoptDeclaredName(node, declarator) {
|
|
3942
|
-
return Option.
|
|
3943
|
-
onNone: () => void 0,
|
|
3944
|
-
onSome: (identifier) => adoptIdentifier(node, identifier)
|
|
3945
|
-
});
|
|
3925
|
+
return Option.map(declaredName(declarator), (identifier) => adoptIdentifier(node, identifier));
|
|
3946
3926
|
}
|
|
3947
3927
|
function declaredName(declarator) {
|
|
3948
3928
|
return Match.value(declarator.id).pipe(Match.when(isIdentifierReference, (identifier) => Option.some(identifier)), Match.orElse(() => Option.none()));
|
|
@@ -3951,10 +3931,10 @@ function isIdentifierReference(node) {
|
|
|
3951
3931
|
return nodeType(node) === "Identifier";
|
|
3952
3932
|
}
|
|
3953
3933
|
function nameIfAnonymous(path) {
|
|
3954
|
-
return classOrFunctionExpressionNamedIfNeeded(path)
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3934
|
+
return Option.match(Option.orElse(classOrFunctionExpressionNamedIfNeeded(path), () => arrowFunctionExpressionNamedIfNeeded(path)), {
|
|
3935
|
+
onNone: () => expressionOf(path.node),
|
|
3936
|
+
onSome: (named) => Result.succeed(named)
|
|
3937
|
+
});
|
|
3958
3938
|
}
|
|
3959
3939
|
function isChainLink(node) {
|
|
3960
3940
|
return [
|
|
@@ -3972,51 +3952,45 @@ function isCallExpressionNode(node) {
|
|
|
3972
3952
|
function isNonNullExpression(node) {
|
|
3973
3953
|
return nodeType(node) === "TSNonNullExpression";
|
|
3974
3954
|
}
|
|
3975
|
-
function isValidExpression(
|
|
3976
|
-
|
|
3977
|
-
return parent === null || !isUnmutatableContext(path, parent);
|
|
3955
|
+
function isValidExpression(node, parent) {
|
|
3956
|
+
return parent === null || !isUnmutatableContext(node, parent);
|
|
3978
3957
|
}
|
|
3979
|
-
function isUnmutatableContext(
|
|
3958
|
+
function isUnmutatableContext(node, parent) {
|
|
3980
3959
|
return [
|
|
3981
|
-
isObjectPropertyKey(
|
|
3982
|
-
isPartOfChain(
|
|
3960
|
+
isObjectPropertyKey(node, parent),
|
|
3961
|
+
isPartOfChain(node, parent),
|
|
3983
3962
|
isTaggedTemplateTag(parent),
|
|
3984
|
-
isDeletedOperand(
|
|
3985
|
-
isAssignedTarget(
|
|
3963
|
+
isDeletedOperand(node, parent),
|
|
3964
|
+
isAssignedTarget(node, parent)
|
|
3986
3965
|
].some((invalid) => invalid);
|
|
3987
3966
|
}
|
|
3988
|
-
function isObjectPropertyKey(
|
|
3989
|
-
|
|
3990
|
-
return parentNode.type === "Property" && parentNode.key === path.node;
|
|
3967
|
+
function isObjectPropertyKey(node, parent) {
|
|
3968
|
+
return parent.type === "Property" && parent.key === node;
|
|
3991
3969
|
}
|
|
3992
3970
|
function isTaggedTemplateTag(parent) {
|
|
3993
|
-
return parent.
|
|
3971
|
+
return parent.type === "TaggedTemplateExpression";
|
|
3994
3972
|
}
|
|
3995
|
-
function isDeletedOperand(
|
|
3996
|
-
|
|
3997
|
-
return parentNode.type === "UnaryExpression" && parentNode.operator === "delete";
|
|
3973
|
+
function isDeletedOperand(node, parent) {
|
|
3974
|
+
return parent.type === "UnaryExpression" && parent.operator === "delete";
|
|
3998
3975
|
}
|
|
3999
|
-
function isAssignedTarget(
|
|
4000
|
-
|
|
4001
|
-
return parentNode.type === "AssignmentExpression" && parentNode.left === path.node;
|
|
3976
|
+
function isAssignedTarget(node, parent) {
|
|
3977
|
+
return parent.type === "AssignmentExpression" && parent.left === node;
|
|
4002
3978
|
}
|
|
4003
|
-
function isPartOfChain(
|
|
4004
|
-
return isChainLink(
|
|
3979
|
+
function isPartOfChain(node, parent) {
|
|
3980
|
+
return isChainLink(node) && chainContinuesIn(node, parent);
|
|
4005
3981
|
}
|
|
4006
|
-
function chainContinuesIn(
|
|
3982
|
+
function chainContinuesIn(node, parent) {
|
|
4007
3983
|
return [
|
|
4008
|
-
isMemberAccessParent(
|
|
4009
|
-
isNonNullExpression(parent
|
|
4010
|
-
isCalleeParent(
|
|
3984
|
+
isMemberAccessParent(node, parent),
|
|
3985
|
+
isNonNullExpression(parent),
|
|
3986
|
+
isCalleeParent(node, parent)
|
|
4011
3987
|
].some((continues) => continues);
|
|
4012
3988
|
}
|
|
4013
|
-
function isMemberAccessParent(
|
|
4014
|
-
|
|
4015
|
-
return parentNode.type === "MemberExpression" && isNotACallOnTheNode(parentNode, path.node);
|
|
3989
|
+
function isMemberAccessParent(node, parent) {
|
|
3990
|
+
return parent.type === "MemberExpression" && isNotACallOnTheNode(parent, node);
|
|
4016
3991
|
}
|
|
4017
|
-
function isCalleeParent(
|
|
4018
|
-
|
|
4019
|
-
return parentNode.type === "CallExpression" && parentNode.callee === path.node;
|
|
3992
|
+
function isCalleeParent(node, parent) {
|
|
3993
|
+
return parent.type === "CallExpression" && parent.callee === node;
|
|
4020
3994
|
}
|
|
4021
3995
|
function isNotACallOnTheNode(member, node) {
|
|
4022
3996
|
return !(member.computed && member.property === node);
|
|
@@ -4033,27 +4007,31 @@ function isParenthesizedWrapper(value) {
|
|
|
4033
4007
|
const expressionMutantPlacer = {
|
|
4034
4008
|
name: "expression",
|
|
4035
4009
|
place(path, appliedMutants) {
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4010
|
+
return Result.gen(function* () {
|
|
4011
|
+
const base = yield* nameIfAnonymous(path);
|
|
4012
|
+
const sequenced = mutationCoverageSequenceExpression(appliedMutants.keys(), base);
|
|
4013
|
+
const expression = yield* [...appliedMutants].reduce((accumulated, [mutant, appliedMutant]) => Result.flatMap(accumulated, (current) => Result.map(nodeOfKind(mutant, unwrapParenthesizedExpression(appliedMutant), isExpressionKind, "an expression"), (replacement) => conditionalExpression(mutantTestExpression(mutant.id), replacement, current))), Result.succeed(sequenced));
|
|
4014
|
+
path.replaceWith(expression);
|
|
4015
|
+
});
|
|
4040
4016
|
}
|
|
4041
4017
|
};
|
|
4042
4018
|
const statementMutantPlacer = {
|
|
4043
4019
|
name: "statement",
|
|
4044
4020
|
place(path, appliedMutants) {
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4021
|
+
return Result.gen(function* () {
|
|
4022
|
+
const statements = yield* statementsOf(path);
|
|
4023
|
+
const body = [expressionStatement(mutationCoverageSequenceExpression(appliedMutants.keys())), ...statements];
|
|
4024
|
+
const statement = yield* [...appliedMutants].reduce((accumulated, entry) => Result.flatMap(accumulated, (current) => guardedStatement(current, entry)), Result.succeed(blockStatement(body)));
|
|
4025
|
+
path.replaceWith(wrappedStatement(path, statement));
|
|
4026
|
+
});
|
|
4048
4027
|
}
|
|
4049
4028
|
};
|
|
4050
|
-
|
|
4029
|
+
const statementsOf = (path) => {
|
|
4051
4030
|
const node = path.node;
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
}
|
|
4031
|
+
return node.type === "BlockStatement" ? Result.succeed(node.body) : Result.map(statementOf(node), (statement) => [statement]);
|
|
4032
|
+
};
|
|
4055
4033
|
function guardedStatement(statement, entry) {
|
|
4056
|
-
return
|
|
4034
|
+
return Result.map(nodeOfKind(entry[0], entry[1], isStatementKind, "a statement"), (narrowed) => ifStatement(mutantTestExpression(entry[0].id), blockStatement([narrowed]), statement));
|
|
4057
4035
|
}
|
|
4058
4036
|
function wrappedStatement(path, statement) {
|
|
4059
4037
|
return Match.value(nodeType(path.node)).pipe(Match.when("BlockStatement", () => blockStatement([statement])), Match.orElse(() => statement));
|
|
@@ -4064,13 +4042,11 @@ const placerBuilders = Object.freeze({
|
|
|
4064
4042
|
"switch-case": {
|
|
4065
4043
|
name: "switch-case",
|
|
4066
4044
|
place(path, appliedMutants) {
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
}
|
|
4073
|
-
path.replaceWith(switchCase(currentCase.test, [consequence]));
|
|
4045
|
+
return Result.gen(function* () {
|
|
4046
|
+
const currentCase = yield* switchCaseOf(path.node);
|
|
4047
|
+
const consequence = yield* [...appliedMutants].reduce((accumulated, [mutant, appliedMutant]) => Result.flatMap(accumulated, (current) => Result.map(nodeOfKind(mutant, appliedMutant, isSwitchCase, "a switch case"), (appliedCase) => ifStatement(mutantTestExpression(mutant.id), blockStatement(appliedCase.consequent), current))), Result.succeed(blockStatement([expressionStatement(mutationCoverageSequenceExpression(appliedMutants.keys())), ...currentCase.consequent])));
|
|
4048
|
+
path.replaceWith(switchCase(currentCase.test, [consequence]));
|
|
4049
|
+
});
|
|
4074
4050
|
}
|
|
4075
4051
|
}
|
|
4076
4052
|
});
|
|
@@ -4086,231 +4062,281 @@ const transformDataFirst = (ast, mutantCollector, transformerContext) => {
|
|
|
4086
4062
|
}))));
|
|
4087
4063
|
};
|
|
4088
4064
|
const transform = dual((args) => args.length >= 3, transformDataFirst);
|
|
4089
|
-
const emptyAppliedMutants = () => /* @__PURE__ */ new Map();
|
|
4090
|
-
const traversalFailure = (cause) => InstrumentError.make({
|
|
4091
|
-
message: cause instanceof Error ? cause.message : errorTextOf$1(cause),
|
|
4092
|
-
cause
|
|
4093
|
-
});
|
|
4094
4065
|
function isMutateRangeList(value) {
|
|
4095
4066
|
return Array.isArray(value);
|
|
4096
4067
|
}
|
|
4097
|
-
const
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
isSwitchCase: nodeType(path.node) === "SwitchCase",
|
|
4137
|
-
expressionIsValid: isValidExpression(path)
|
|
4138
|
-
};
|
|
4139
|
-
}
|
|
4140
|
-
function placerNameOf(site) {
|
|
4141
|
-
return Match.value(site).pipe(Match.tag("ExpressionSite", () => "expression"), Match.tag("StatementSite", () => "statement"), Match.tag("SwitchCaseSite", () => "switch-case"), Match.exhaustive);
|
|
4142
|
-
}
|
|
4143
|
-
function addToPlacementMapIfPossible(path) {
|
|
4144
|
-
const facts = placementFacts(path);
|
|
4145
|
-
const claimed = placeMutants(PlaceMutantsCommand.make({
|
|
4146
|
-
fileName: originFileName,
|
|
4147
|
-
facts,
|
|
4148
|
-
mutants: []
|
|
4149
|
-
}));
|
|
4150
|
-
Match.value(claimed).pipe(Match.when(Result.isSuccess, () => placementMap.set(path.node, {
|
|
4151
|
-
appliedMutants: emptyAppliedMutants(),
|
|
4152
|
-
facts
|
|
4153
|
-
})), Match.orElse(() => void 0));
|
|
4154
|
-
}
|
|
4155
|
-
function hasAppliedMutants(placement) {
|
|
4156
|
-
return placement !== void 0 && placement.appliedMutants.size > 0;
|
|
4157
|
-
}
|
|
4158
|
-
function replacementRecord(mutant, applied) {
|
|
4159
|
-
const replacement = unwrapParenthesizedExpression(applied);
|
|
4160
|
-
return {
|
|
4161
|
-
id: mutant.id,
|
|
4162
|
-
mutatorName: mutant.mutatorName,
|
|
4163
|
-
replacement: {
|
|
4164
|
-
isExpression: isExpressionKind(replacement),
|
|
4165
|
-
isStatement: isStatementKind(replacement),
|
|
4166
|
-
isSwitchCase: nodeType(replacement) === "SwitchCase"
|
|
4167
|
-
}
|
|
4168
|
-
};
|
|
4068
|
+
const MUTATION_OFFSET = {
|
|
4069
|
+
line: 1,
|
|
4070
|
+
columnShift: 0
|
|
4071
|
+
};
|
|
4072
|
+
const initialFoldState = (firstIndex) => ({
|
|
4073
|
+
directiveRule: [],
|
|
4074
|
+
nextIndex: firstIndex,
|
|
4075
|
+
mutants: [],
|
|
4076
|
+
warnings: [],
|
|
4077
|
+
claims: [],
|
|
4078
|
+
placements: [],
|
|
4079
|
+
hasLiveMutants: false
|
|
4080
|
+
});
|
|
4081
|
+
const framesUpward = (frame) => [frame, ...framesAbove(frame)];
|
|
4082
|
+
const framesAbove = (frame) => Option.match(Option.fromNullishOr(frame.parent), {
|
|
4083
|
+
onNone: () => [],
|
|
4084
|
+
onSome: (parent) => framesUpward(parent)
|
|
4085
|
+
});
|
|
4086
|
+
const parentNodeOf = (frame) => Option.getOrNull(Option.map(Option.fromNullishOr(frame.parent), (parent) => parent.node));
|
|
4087
|
+
const ancestorsOfFrame = (frame) => Option.match(Option.fromNullishOr(frame.parent), {
|
|
4088
|
+
onNone: () => [],
|
|
4089
|
+
onSome: (parent) => [parent.node, ...ancestorsOfFrame(parent)]
|
|
4090
|
+
});
|
|
4091
|
+
const placementFactsOf = (frame) => ({
|
|
4092
|
+
isExpression: isExpressionKind(frame.node),
|
|
4093
|
+
isStatement: isStatementKind(frame.node),
|
|
4094
|
+
isSwitchCase: nodeType(frame.node) === "SwitchCase",
|
|
4095
|
+
expressionIsValid: isValidExpression(frame.node, parentNodeOf(frame))
|
|
4096
|
+
});
|
|
4097
|
+
const placerNameOf = (site) => Match.value(site).pipe(Match.tag("ExpressionSite", () => "expression"), Match.tag("StatementSite", () => "statement"), Match.tag("SwitchCaseSite", () => "switch-case"), Match.exhaustive);
|
|
4098
|
+
const replacementRecord = (mutant, applied) => {
|
|
4099
|
+
const replacement = unwrapParenthesizedExpression(applied);
|
|
4100
|
+
return {
|
|
4101
|
+
id: mutant.id,
|
|
4102
|
+
mutatorName: mutant.mutatorName,
|
|
4103
|
+
replacement: {
|
|
4104
|
+
isExpression: isExpressionKind(replacement),
|
|
4105
|
+
isStatement: isStatementKind(replacement),
|
|
4106
|
+
isSwitchCase: nodeType(replacement) === "SwitchCase"
|
|
4169
4107
|
}
|
|
4170
|
-
|
|
4171
|
-
|
|
4108
|
+
};
|
|
4109
|
+
};
|
|
4110
|
+
const plannedWithNodes = (candidates, planned, fileName) => candidates.flatMap((candidate, index) => Option.match(Option.fromNullishOr(planned[index]), {
|
|
4111
|
+
onNone: () => [],
|
|
4112
|
+
onSome: (mutant) => [createMutant(mutant, fileName, candidate.node, candidate.replacement)]
|
|
4113
|
+
}));
|
|
4114
|
+
const ignorersReasonFor = (node, ancestors, ignorers) => ignorers.reduce((reason, ignorer) => Option.orElse(reason, () => Option.fromNullishOr(ignorer.shouldIgnore(node, ancestors))), Option.none());
|
|
4115
|
+
const mutablesFor = (frame, location, context) => {
|
|
4116
|
+
const ancestors = ancestorsOfFrame(frame);
|
|
4117
|
+
const mutatorContext = toMutatorContext(ancestors);
|
|
4118
|
+
const replacements = context.mutatorEntries.flatMap(([mutatorName, mutate]) => [...mutate(frame.node, mutatorContext)].map((replacement) => ({
|
|
4119
|
+
mutatorName,
|
|
4120
|
+
replacement
|
|
4121
|
+
})));
|
|
4122
|
+
const ignorerReason = replacements.length === 0 ? void 0 : Option.getOrUndefined(ignorersReasonFor(frame.node, ancestors, context.ignorers));
|
|
4123
|
+
return replacements.map(({ mutatorName, replacement }) => ({
|
|
4124
|
+
node: frame.node,
|
|
4125
|
+
replacement,
|
|
4126
|
+
data: {
|
|
4127
|
+
mutatorName,
|
|
4128
|
+
replacementCode: printNode(replacement),
|
|
4129
|
+
location,
|
|
4130
|
+
ignorerReason
|
|
4172
4131
|
}
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4132
|
+
}));
|
|
4133
|
+
};
|
|
4134
|
+
const mutateRangesOf = (mutateDescription) => Option.filter(Option.some(mutateDescription), isMutateRangeList);
|
|
4135
|
+
const isOutsideMutateRanges = (location, mutateDescription) => Option.exists(mutateRangesOf(mutateDescription), (ranges) => ranges.every((range) => !locationOverlaps(range, location)));
|
|
4136
|
+
const isInsideMutateRanges = (location, mutateDescription) => Option.exists(mutateRangesOf(mutateDescription), (ranges) => ranges.some((range) => locationIncluded(range, location)));
|
|
4137
|
+
const shouldMutateAt = (location, mutateDescription) => mutateDescription === true || isInsideMutateRanges(location, mutateDescription);
|
|
4138
|
+
const shouldSkipNode = (frame, location, mutateDescription) => [
|
|
4139
|
+
isTypeNode(frame.node),
|
|
4140
|
+
isImportDeclaration(frame.node),
|
|
4141
|
+
nodeType(frame.node) === "Decorator",
|
|
4142
|
+
mutateDescription === false,
|
|
4143
|
+
isOutsideMutateRanges(location, mutateDescription)
|
|
4144
|
+
].some((skip) => skip);
|
|
4145
|
+
const locationOfNode = (frame, context) => Option.match(Option.map(Option.fromNullishOr(spanOf(frame.node)), (span) => locationOf(context.lineStarts, span)), {
|
|
4146
|
+
onNone: () => Result.fail(NodeWithoutSpan.make({ fileName: context.fileName })),
|
|
4147
|
+
onSome: Result.succeed
|
|
4148
|
+
});
|
|
4149
|
+
const refusedPlacement = (refusal, node, mutants, context) => PlacementRefused.make({ message: placementFailureMessage(refusal, node, mutants, context.fileName, context.lineStarts, context.basePath) });
|
|
4150
|
+
const applyMutantToClaim = (mutant, target, appliedSoFar, context) => Result.mapError(applyMutant(mutant, target), (failure) => refusedPlacement(failure, target, appliedSoFar, context));
|
|
4151
|
+
const applyToClaim = (mutants, target, state, context) => {
|
|
4152
|
+
const existing = state.claims.find((claim) => claim.node === target.node);
|
|
4153
|
+
const priorMutants = existing === void 0 ? [] : existing.applied.map(([mutant]) => mutant);
|
|
4154
|
+
const applied = mutants.reduce((accumulated, mutant) => Result.flatMap(accumulated, (entries) => Result.map(applyMutantToClaim(mutant, target.node, [...priorMutants, ...entries.map(([entry]) => entry)], context), (tree) => [...entries, [mutant, tree]])), Result.succeed([]));
|
|
4155
|
+
return Result.map(applied, (entries) => ({
|
|
4156
|
+
...state,
|
|
4157
|
+
claims: state.claims.map((claim) => claim.node === target.node ? {
|
|
4158
|
+
...claim,
|
|
4159
|
+
applied: [...claim.applied, ...entries]
|
|
4160
|
+
} : claim)
|
|
4161
|
+
}));
|
|
4162
|
+
};
|
|
4163
|
+
const attachPlaceable = (mutants, frame, state, context) => Match.value(mutants.length > 0).pipe(Match.when(false, () => Result.succeed(state)), Match.when(true, () => Option.match(Option.fromNullishOr(framesUpward(frame).find((candidate) => state.claims.some((claim) => claim.node === candidate.node))), {
|
|
4164
|
+
onNone: () => Result.fail(MutantsUnplaced.make({
|
|
4165
|
+
fileName: context.fileName,
|
|
4166
|
+
detail: JSON.stringify(mutants, null, 2)
|
|
4167
|
+
})),
|
|
4168
|
+
onSome: (target) => applyToClaim(mutants, target, state, context)
|
|
4169
|
+
})), Match.exhaustive);
|
|
4170
|
+
const collectPlan = (frame, candidates, plan, state, context) => {
|
|
4171
|
+
const collected = plannedWithNodes(candidates, plan.mutants, context.fileName);
|
|
4172
|
+
const nextState = {
|
|
4173
|
+
...state,
|
|
4174
|
+
mutants: [...state.mutants, ...collected],
|
|
4175
|
+
warnings: [...state.warnings, ...plan.warnings],
|
|
4176
|
+
nextIndex: plan.nextIndex
|
|
4177
|
+
};
|
|
4178
|
+
return Match.value(plan).pipe(Match.tag("MutantsPlanned", (planned) => attachPlaceable(plannedWithNodes(candidates, planned.placeable, context.fileName), frame, {
|
|
4179
|
+
...nextState,
|
|
4180
|
+
hasLiveMutants: true
|
|
4181
|
+
}, context)), Match.orElse(() => Result.succeed(nextState)));
|
|
4182
|
+
};
|
|
4183
|
+
const planMutantsAt = (frame, candidates, directives, location, state, context) => {
|
|
4184
|
+
const plan = planMutants(PlanMutantsCommand.make({
|
|
4185
|
+
fileName: context.fileName,
|
|
4186
|
+
firstIndex: state.nextIndex,
|
|
4187
|
+
offset: context.offset,
|
|
4188
|
+
line: location.start.line,
|
|
4189
|
+
mutatorNames: [...context.allMutatorNames],
|
|
4190
|
+
excludedMutations: [...context.excludedMutations],
|
|
4191
|
+
rule: [...state.directiveRule],
|
|
4192
|
+
directives: [...directives],
|
|
4193
|
+
candidates: candidates.map((candidate) => candidate.data)
|
|
4194
|
+
}));
|
|
4195
|
+
return Match.value(plan).pipe(Match.when(Result.isFailure, (failed) => Result.fail(failed.failure)), Match.orElse((succeeded) => collectPlan(frame, candidates, succeeded.success, state, context)));
|
|
4196
|
+
};
|
|
4197
|
+
const candidatesFor = (frame, location, context) => Match.value(shouldMutateAt(location, context.mutateDescription)).pipe(Match.when(true, () => mutablesFor(frame, location, context)), Match.orElse(() => []));
|
|
4198
|
+
const needsPlan = (candidates, directives) => [candidates.length > 0, directives.length > 0].some(Boolean);
|
|
4199
|
+
const planAtNode = (frame, directives, location, state, context) => {
|
|
4200
|
+
const candidates = candidatesFor(frame, location, context);
|
|
4201
|
+
return Match.value(needsPlan(candidates, directives)).pipe(Match.when(false, () => Result.succeed(state)), Match.when(true, () => planMutantsAt(frame, candidates, directives, location, state, context)), Match.exhaustive);
|
|
4202
|
+
};
|
|
4203
|
+
const claimSelf = (frame, facts, claims, context) => {
|
|
4204
|
+
const claimed = placeMutants(PlaceMutantsCommand.make({
|
|
4205
|
+
fileName: context.fileName,
|
|
4206
|
+
facts,
|
|
4207
|
+
mutants: []
|
|
4208
|
+
}));
|
|
4209
|
+
return Match.value(claimed.pipe(Result.isSuccess)).pipe(Match.when(true, () => [...claims, {
|
|
4210
|
+
node: frame.node,
|
|
4211
|
+
facts,
|
|
4212
|
+
applied: []
|
|
4213
|
+
}]), Match.orElse(() => claims));
|
|
4214
|
+
};
|
|
4215
|
+
const siteOfClaim = (claim, context) => {
|
|
4216
|
+
const mutants = claim.applied.map(([mutant]) => mutant);
|
|
4217
|
+
const decision = placeMutants(PlaceMutantsCommand.make({
|
|
4218
|
+
fileName: context.fileName,
|
|
4219
|
+
facts: claim.facts,
|
|
4220
|
+
mutants: claim.applied.map(([mutant, applied]) => replacementRecord(mutant, applied))
|
|
4221
|
+
}));
|
|
4222
|
+
return Match.value(decision).pipe(Match.when(Result.isFailure, (refused) => Result.fail(refusedPlacement(refused.failure, claim.node, mutants, context))), Match.orElse((decided) => Result.succeed(decided.success)));
|
|
4223
|
+
};
|
|
4224
|
+
const emitClaim = (claim, state, context) => Match.value(claim.applied.length > 0).pipe(Match.when(false, () => Result.succeed(state)), Match.when(true, () => Result.map(siteOfClaim(claim, context), (site) => ({
|
|
4225
|
+
...state,
|
|
4226
|
+
placements: [...state.placements, {
|
|
4227
|
+
node: claim.node,
|
|
4228
|
+
placer: placerNameOf(site),
|
|
4229
|
+
applied: claim.applied
|
|
4230
|
+
}]
|
|
4231
|
+
}))), Match.exhaustive);
|
|
4232
|
+
const emitPlacement = (frame, state, context) => Option.match(Option.fromNullishOr(state.claims.find((claim) => claim.node === frame.node)), {
|
|
4233
|
+
onNone: () => Result.succeed(state),
|
|
4234
|
+
onSome: (claim) => emitClaim(claim, state, context)
|
|
4235
|
+
});
|
|
4236
|
+
const foldChildren = (frame, state, context) => childNodes(frame.node).reduce((accumulated, child) => Result.flatMap(accumulated, (current) => foldPlacements({
|
|
4237
|
+
node: child.node,
|
|
4238
|
+
parent: frame
|
|
4239
|
+
}, current, context)), Result.succeed(state));
|
|
4240
|
+
const visitFrame = (frame, directives, location, state, context) => {
|
|
4241
|
+
const facts = placementFactsOf(frame);
|
|
4242
|
+
const claimed = claimSelf(frame, facts, state.claims, context);
|
|
4243
|
+
return Result.flatMap(planAtNode(frame, directives, location, {
|
|
4244
|
+
...state,
|
|
4245
|
+
claims: claimed
|
|
4246
|
+
}, context), (afterPlan) => Result.flatMap(foldChildren(frame, afterPlan, context), (afterChildren) => emitPlacement(frame, afterChildren, context)));
|
|
4247
|
+
};
|
|
4248
|
+
const foldPlacements = (frame, state, context) => Result.flatMap(locationOfNode(frame, context), (location) => {
|
|
4249
|
+
const directives = directivesOf(frame.node, location.start.line);
|
|
4250
|
+
const ruled = {
|
|
4251
|
+
...state,
|
|
4252
|
+
directiveRule: directives.reduce(foldInto, state.directiveRule)
|
|
4253
|
+
};
|
|
4254
|
+
return Match.value(shouldSkipNode(frame, location, context.mutateDescription)).pipe(Match.when(true, () => Result.succeed(ruled)), Match.when(false, () => visitFrame(frame, directives, location, ruled, context)), Match.exhaustive);
|
|
4255
|
+
});
|
|
4256
|
+
const planInstrumentation = (root, firstIndex, context) => Result.map(foldPlacements({
|
|
4257
|
+
node: root,
|
|
4258
|
+
parent: null
|
|
4259
|
+
}, initialFoldState(firstIndex), context), (state) => ({
|
|
4260
|
+
mutants: state.mutants,
|
|
4261
|
+
placements: state.placements,
|
|
4262
|
+
warnings: state.warnings,
|
|
4263
|
+
hasLiveMutants: state.hasLiveMutants
|
|
4264
|
+
}));
|
|
4265
|
+
const applyOnePlacement = (placement, path, previous, context) => {
|
|
4266
|
+
const result = placerBuilders[placement.placer].place(path, new Map(placement.applied));
|
|
4267
|
+
return Result.match(result, {
|
|
4268
|
+
onFailure: (cause) => new Error(placementFailureMessage(MutantsUnapplied.make({
|
|
4269
|
+
fileName: context.fileName,
|
|
4270
|
+
placer: placement.placer,
|
|
4271
|
+
mutatorNames: placement.applied.map(([mutant]) => mutant.mutatorName),
|
|
4272
|
+
cause: toError(cause)
|
|
4273
|
+
}), placement.node, placement.applied.map(([mutant]) => mutant), context.fileName, context.lineStarts, context.basePath)),
|
|
4274
|
+
onSuccess: () => previous
|
|
4275
|
+
});
|
|
4276
|
+
};
|
|
4277
|
+
const applyPlacementsToAst = (root, places, context) => {
|
|
4278
|
+
let failure;
|
|
4279
|
+
traverse(make(root), { exit(path) {
|
|
4280
|
+
Option.match(Option.fromNullishOr(places.get(path.node)), {
|
|
4281
|
+
onNone: () => void 0,
|
|
4282
|
+
onSome: (placement) => {
|
|
4283
|
+
failure = applyOnePlacement(placement, path, failure, context);
|
|
4184
4284
|
}
|
|
4185
|
-
}
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
}
|
|
4198
|
-
function candidateSteps(path) {
|
|
4199
|
-
return Match.value(shouldMutate(path)).pipe(Match.when(true, () => mutablesFor(path)), Match.when(false, () => []), Match.exhaustive);
|
|
4200
|
-
}
|
|
4201
|
-
function needsPlan(candidates, directives) {
|
|
4202
|
-
return [candidates.length > 0, directives.length > 0].some(Boolean);
|
|
4203
|
-
}
|
|
4204
|
-
function planAndPlace(path, candidates, directives) {
|
|
4205
|
-
Match.value(planFor(path, candidates, directives)).pipe(Match.when(Option.isSome, (toPlace) => placeOnPath(path, toPlace.value)), Match.orElse(() => void 0));
|
|
4206
|
-
}
|
|
4207
|
-
function planFor(path, candidates, directives) {
|
|
4208
|
-
const plan = planMutants(PlanMutantsCommand.make({
|
|
4209
|
-
fileName: originFileName,
|
|
4210
|
-
firstIndex: mutantCollector.nextIndex,
|
|
4211
|
-
offset: offset ?? {
|
|
4212
|
-
line: 1,
|
|
4213
|
-
column: 0
|
|
4214
|
-
},
|
|
4215
|
-
line: getNodeLocation(path.node).start.line,
|
|
4216
|
-
mutatorNames: allMutatorNames,
|
|
4217
|
-
excludedMutations: options.excludedMutations,
|
|
4218
|
-
rule: directiveRule,
|
|
4219
|
-
directives: [...directives],
|
|
4220
|
-
candidates: candidates.map((candidate) => candidate.data)
|
|
4221
|
-
}));
|
|
4222
|
-
return Match.value(plan).pipe(Match.when(Result.isFailure, (failed) => raisePlanFailure(failed.failure)), Match.orElse((succeeded) => collectPlanned(candidates, succeeded.success)));
|
|
4223
|
-
}
|
|
4224
|
-
function raisePlanFailure(failure) {
|
|
4225
|
-
throw new Error(`Mutant without a source location: ${failure.mutatorName} in ${failure.fileName}`);
|
|
4226
|
-
}
|
|
4227
|
-
function collectPlanned(candidates, plan) {
|
|
4228
|
-
mutantCollector.append(plannedWithNodes(candidates, plan.mutants));
|
|
4229
|
-
warnings.push(...plan.warnings);
|
|
4230
|
-
return Match.value(plan).pipe(Match.tag("MutantsPlanned", (planned) => {
|
|
4231
|
-
hasLiveMutants = true;
|
|
4232
|
-
return Option.some(plannedWithNodes(candidates, planned.placeable));
|
|
4233
|
-
}), Match.orElse(() => Option.none()));
|
|
4234
|
-
}
|
|
4235
|
-
function plannedWithNodes(candidates, planned) {
|
|
4236
|
-
return candidates.flatMap((candidate, index) => Option.match(Option.fromNullishOr(planned[index]), {
|
|
4237
|
-
onNone: () => [],
|
|
4238
|
-
onSome: (mutant) => [createMutant(mutant, originFileName, candidate.node, candidate.replacement)]
|
|
4239
|
-
}));
|
|
4240
|
-
}
|
|
4241
|
-
function placeOnPath(path, mutantsToPlace) {
|
|
4242
|
-
const placementPath = requiredPlacementPath(path, mutantsToPlace);
|
|
4243
|
-
const placement = requiredPlacement(placementPath.node);
|
|
4244
|
-
mutantsToPlace.forEach((mutant) => {
|
|
4245
|
-
Match.value(applyMutant(mutant, placementPath.node)).pipe(Match.when(Result.isFailure, (failed) => raisePlacementRefusal(failed.failure, placement, path)), Match.orElse((applied) => placement.appliedMutants.set(mutant, applied.success)));
|
|
4246
|
-
});
|
|
4247
|
-
}
|
|
4248
|
-
function requiredPlacementPath(path, mutantsToPlace) {
|
|
4249
|
-
return Option.getOrThrowWith(Option.fromNullishOr(path.find((ancestor) => placementMap.has(ancestor.node))), () => unplacedMutantsError(mutantsToPlace));
|
|
4250
|
-
}
|
|
4251
|
-
function unplacedMutantsError(mutantsToPlace) {
|
|
4252
|
-
return /* @__PURE__ */ new Error(`Mutants cannot be placed. This shouldn't happen! Unplaced mutants: ${JSON.stringify(mutantsToPlace, null, 2)}`);
|
|
4253
|
-
}
|
|
4254
|
-
function requiredPlacement(node) {
|
|
4255
|
-
return Option.getOrThrowWith(Option.fromNullishOr(placementMap.get(node)), () => /* @__PURE__ */ new Error("Placement not found for node"));
|
|
4256
|
-
}
|
|
4257
|
-
function shouldSkip(path) {
|
|
4258
|
-
return [
|
|
4259
|
-
isTypeNode(path),
|
|
4260
|
-
isImportDeclaration(path),
|
|
4261
|
-
nodeType(path.node) === "Decorator",
|
|
4262
|
-
mutateDescription === false,
|
|
4263
|
-
isOutsideMutateRanges(path)
|
|
4264
|
-
].some((skip) => skip);
|
|
4265
|
-
}
|
|
4266
|
-
function mutateRanges() {
|
|
4267
|
-
return Option.filter(Option.some(mutateDescription), isMutateRangeList);
|
|
4268
|
-
}
|
|
4269
|
-
function isOutsideMutateRanges(path) {
|
|
4270
|
-
return Option.exists(mutateRanges(), (ranges) => ranges.every((range) => !locationOverlaps(range, getNodeLocation(path.node))));
|
|
4271
|
-
}
|
|
4272
|
-
function shouldMutate(path) {
|
|
4273
|
-
return mutateDescription === true || isInsideMutateRanges(path);
|
|
4274
|
-
}
|
|
4275
|
-
function isInsideMutateRanges(path) {
|
|
4276
|
-
return Option.exists(mutateRanges(), (ranges) => ranges.some((range) => locationIncluded(range, getNodeLocation(path.node))));
|
|
4277
|
-
}
|
|
4278
|
-
function getNodeLocation(node) {
|
|
4279
|
-
const span = spanOf(node);
|
|
4280
|
-
if (span === void 0) throw new Error("Node without a span");
|
|
4281
|
-
return {
|
|
4282
|
-
start: lineTable.positionAt(span.start),
|
|
4283
|
-
end: lineTable.positionAt(span.end)
|
|
4284
|
-
};
|
|
4285
|
-
}
|
|
4286
|
-
function mutablesFor(path) {
|
|
4287
|
-
const ancestors = ancestorsOf(path);
|
|
4288
|
-
const context = toMutatorContext(ancestors);
|
|
4289
|
-
const location = Option.map(Option.fromNullishOr(spanOf(path.node)), (span) => ({
|
|
4290
|
-
start: lineTable.positionAt(span.start),
|
|
4291
|
-
end: lineTable.positionAt(span.end)
|
|
4292
|
-
}));
|
|
4293
|
-
const replacements = mutatorEntries.flatMap(([mutatorName, mutate]) => [...mutate(path.node, context)].map((replacement) => ({
|
|
4294
|
-
mutatorName,
|
|
4295
|
-
replacement
|
|
4296
|
-
})));
|
|
4297
|
-
const ignorerReason = replacements.length === 0 ? void 0 : Option.getOrUndefined(ignorersReason(path.node, ancestors));
|
|
4298
|
-
return replacements.map(({ mutatorName, replacement }) => ({
|
|
4299
|
-
node: path.node,
|
|
4300
|
-
replacement,
|
|
4301
|
-
data: {
|
|
4302
|
-
mutatorName,
|
|
4303
|
-
replacementCode: printNode(replacement),
|
|
4304
|
-
location: Option.getOrUndefined(location),
|
|
4305
|
-
ignorerReason
|
|
4306
|
-
}
|
|
4307
|
-
}));
|
|
4308
|
-
}
|
|
4309
|
-
function ignorersReason(node, ancestors) {
|
|
4310
|
-
return options.ignorers.reduce((reason, ignorer) => Option.orElse(reason, () => Option.fromNullishOr(ignorer.shouldIgnore(node, ancestors))), Option.none());
|
|
4311
|
-
}
|
|
4285
|
+
});
|
|
4286
|
+
} });
|
|
4287
|
+
return failure;
|
|
4288
|
+
};
|
|
4289
|
+
const applyPlan = (root, plan, context) => {
|
|
4290
|
+
const places = new Map(plan.placements.map((placement) => [placement.node, placement]));
|
|
4291
|
+
return Option.match(Option.fromNullishOr(applyPlacementsToAst(root, places, context)), {
|
|
4292
|
+
onNone: () => Effect.void,
|
|
4293
|
+
onSome: (error) => Effect.fail(InstrumentError.make({
|
|
4294
|
+
message: error.message,
|
|
4295
|
+
cause: error
|
|
4296
|
+
}))
|
|
4312
4297
|
});
|
|
4313
4298
|
};
|
|
4299
|
+
const refusalError = (refusal) => Match.value(refusal).pipe(Match.tag("NodeWithoutSpan", () => InstrumentError.make({
|
|
4300
|
+
message: "Node without a span",
|
|
4301
|
+
cause: void 0
|
|
4302
|
+
})), Match.tag("MutantsUnplaced", (unplaced) => InstrumentError.make({
|
|
4303
|
+
message: `Mutants cannot be placed. This shouldn't happen! Unplaced mutants: ${unplaced.detail}`,
|
|
4304
|
+
cause: void 0
|
|
4305
|
+
})), Match.tag("PlacementRefused", (refused) => InstrumentError.make({
|
|
4306
|
+
message: refused.message,
|
|
4307
|
+
cause: new Error(refused.message)
|
|
4308
|
+
})), Match.tag("MutantWithoutLocation", (failed) => InstrumentError.make({
|
|
4309
|
+
message: `Mutant without a source location: ${failed.mutatorName} in ${failed.fileName}`,
|
|
4310
|
+
cause: void 0
|
|
4311
|
+
})), Match.exhaustive);
|
|
4312
|
+
const transformScriptDataFirst = Effect.fn("stryker.instrument.transform.script")(function* ({ root, originFileName, rawContent, offset, comments }, mutantCollector, { options, mutateDescription, basePath }) {
|
|
4313
|
+
const lineStarts = lineStartsOf(rawContent);
|
|
4314
|
+
attachComments(make(root), comments, lineStarts);
|
|
4315
|
+
const selection = selectMutators(DEFAULT_MUTATOR_REGISTRY, options.optInMutations);
|
|
4316
|
+
const context = {
|
|
4317
|
+
fileName: originFileName,
|
|
4318
|
+
lineStarts,
|
|
4319
|
+
mutateDescription,
|
|
4320
|
+
offset: offset ?? MUTATION_OFFSET,
|
|
4321
|
+
basePath,
|
|
4322
|
+
mutatorEntries: selection.active,
|
|
4323
|
+
allMutatorNames: selection.known.map((name) => name.toLowerCase()),
|
|
4324
|
+
excludedMutations: options.excludedMutations,
|
|
4325
|
+
ignorers: options.ignorers
|
|
4326
|
+
};
|
|
4327
|
+
const planned = yield* Effect.try({
|
|
4328
|
+
try: () => planInstrumentation(root, mutantCollector.nextIndex, context),
|
|
4329
|
+
catch: traversalFailure
|
|
4330
|
+
});
|
|
4331
|
+
const plan = yield* Match.value(planned).pipe(Match.when(Result.isFailure, (refused) => {
|
|
4332
|
+
const error = refusalError(refused.failure);
|
|
4333
|
+
return Effect.fail(error);
|
|
4334
|
+
}), Match.orElse((succeeded) => Effect.succeed(succeeded.success)));
|
|
4335
|
+
mutantCollector.append(plan.mutants);
|
|
4336
|
+
yield* applyPlan(root, plan, context);
|
|
4337
|
+
yield* placeHeaderIfNeeded(plan.hasLiveMutants, options, root);
|
|
4338
|
+
return plan.warnings;
|
|
4339
|
+
});
|
|
4314
4340
|
const transformScript = dual((args) => args.length >= 3, transformScriptDataFirst);
|
|
4315
4341
|
function toMutatorContext(ancestors) {
|
|
4316
4342
|
return {
|
|
@@ -4319,10 +4345,7 @@ function toMutatorContext(ancestors) {
|
|
|
4319
4345
|
ancestors: [...ancestors]
|
|
4320
4346
|
};
|
|
4321
4347
|
}
|
|
4322
|
-
|
|
4323
|
-
if (value instanceof Error) return value;
|
|
4324
|
-
return new Error("Unexpected error", { cause: value });
|
|
4325
|
-
}
|
|
4348
|
+
const toError = (value) => value instanceof Error ? value : new Error("Unexpected error", { cause: value });
|
|
4326
4349
|
//#endregion
|
|
4327
4350
|
//#region src/TypeCheckDisablers.ts
|
|
4328
4351
|
const tsDirectiveLikeRegEx = /@(ts-[a-z-]+)/;
|
|
@@ -4332,18 +4355,15 @@ function disableTypeCheckingInScript(ast) {
|
|
|
4332
4355
|
return prefixWithNoCheck(removeTSDirectives(ast.rawContent, ast.comments));
|
|
4333
4356
|
}
|
|
4334
4357
|
function prefixWithNoCheck(code) {
|
|
4335
|
-
|
|
4336
|
-
return afterLeadingComment(code);
|
|
4358
|
+
return code.startsWith("#") ? afterHashbang(code) : afterLeadingComment(code);
|
|
4337
4359
|
}
|
|
4338
4360
|
function afterHashbang(code) {
|
|
4339
4361
|
const newLineIndex = code.indexOf("\n");
|
|
4340
|
-
|
|
4341
|
-
return `${code.substring(0, newLineIndex)}\n// @ts-nocheck\n${code.substring(newLineIndex + 1)}`;
|
|
4362
|
+
return newLineIndex <= 0 ? code : `${code.substring(0, newLineIndex)}\n// @ts-nocheck\n${code.substring(newLineIndex + 1)}`;
|
|
4342
4363
|
}
|
|
4343
4364
|
function afterLeadingComment(code) {
|
|
4344
4365
|
const leadingComment = leadingCommentOf(code);
|
|
4345
|
-
|
|
4346
|
-
return `${leadingComment.concat("\n")}// @ts-nocheck\n${code.substring(leadingComment.length)}`;
|
|
4366
|
+
return leadingComment === void 0 ? `// @ts-nocheck\n${code}` : `${leadingComment.concat("\n")}// @ts-nocheck\n${code.substring(leadingComment.length)}`;
|
|
4347
4367
|
}
|
|
4348
4368
|
function leadingCommentOf(code) {
|
|
4349
4369
|
return STARTING_COMMENT.exec(code)?.[0];
|
|
@@ -4365,31 +4385,26 @@ function removeRanges(text, ranges) {
|
|
|
4365
4385
|
return remaining.pruned + text.substring(remaining.cursor);
|
|
4366
4386
|
}
|
|
4367
4387
|
function tryParseTSDirective(comment) {
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4388
|
+
return Option.match(Option.fromNullishOr(commentDirectiveRegEx.exec(comment.value)), {
|
|
4389
|
+
onNone: () => void 0,
|
|
4390
|
+
onSome: (match) => directiveRangeAt(comment.start, lengthOf(match[1]), lengthOf(match[2]))
|
|
4391
|
+
});
|
|
4392
|
+
}
|
|
4393
|
+
const lengthOf = (text) => Option.getOrElse(Option.map(Option.fromNullishOr(text), (value) => value.length), () => 0);
|
|
4394
|
+
const directiveRangeAt = (commentStart, prefixLength, nameLength) => {
|
|
4395
|
+
const startPos = commentStart + prefixLength + 2;
|
|
4373
4396
|
return {
|
|
4374
4397
|
startPos,
|
|
4375
|
-
endPos: startPos +
|
|
4398
|
+
endPos: startPos + nameLength + 1
|
|
4376
4399
|
};
|
|
4377
|
-
}
|
|
4378
|
-
function requirePart(part, message) {
|
|
4379
|
-
if (part === void 0) throw new Error(message);
|
|
4380
|
-
return part;
|
|
4381
|
-
}
|
|
4400
|
+
};
|
|
4382
4401
|
//#endregion
|
|
4383
4402
|
//#region src/Format.ts
|
|
4384
|
-
const
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
};
|
|
4388
|
-
const
|
|
4389
|
-
const requireTsFamilyAst = (ast) => Match.value(ast).pipe(Match.when({ format: "ts" }, (ts) => ts), Match.when({ format: "tsx" }, (tsx) => tsx), Match.orElse((other) => rejectAst(other, "a ts script")));
|
|
4390
|
-
function rejectAst(ast, expected) {
|
|
4391
|
-
throw new Error(`Expected ${expected} AST, received the "${ast.format}" format`);
|
|
4392
|
-
}
|
|
4403
|
+
const scriptAstError = (ast) => InstrumentError.make({
|
|
4404
|
+
message: `Expected a script AST, received the "${ast.format}" format`,
|
|
4405
|
+
cause: void 0
|
|
4406
|
+
});
|
|
4407
|
+
const scriptAstOf = (ast) => Match.value(ast).pipe(Match.when({ format: "embedded" }, () => Option.none()), Match.orElse((script) => Option.some(script)));
|
|
4393
4408
|
const extensionOf = (fileName) => {
|
|
4394
4409
|
const dot = fileName.lastIndexOf(".");
|
|
4395
4410
|
return dot > Math.max(fileName.lastIndexOf("/"), fileName.lastIndexOf("\\")) ? fileName.slice(dot).toLowerCase() : "";
|
|
@@ -4421,12 +4436,19 @@ const resolutionCommandOfDataFirst = (registry, fileName, formatIdOverride) => F
|
|
|
4421
4436
|
const resolutionCommandOf = dual((args) => typeof args[0] !== "string", resolutionCommandOfDataFirst);
|
|
4422
4437
|
const scriptHooks = (scriptFormat) => ({
|
|
4423
4438
|
parse: (text, fileName, _context) => parseScriptFormat(scriptFormat, text, fileName),
|
|
4424
|
-
transform: (ast, mutantCollector, context) =>
|
|
4439
|
+
transform: (ast, mutantCollector, context) => Option.match(scriptAstOf(ast), {
|
|
4440
|
+
onNone: () => Effect.fail(scriptAstError(ast)),
|
|
4441
|
+
onSome: (script) => transformScript(script, mutantCollector, context)
|
|
4442
|
+
}),
|
|
4425
4443
|
print: (ast, context) => printScriptAst(ast, context),
|
|
4426
|
-
disableTypeChecks: (ast) =>
|
|
4444
|
+
disableTypeChecks: (ast) => Option.match(scriptAstOf(ast), {
|
|
4445
|
+
onNone: () => Effect.fail(scriptAstError(ast)),
|
|
4446
|
+
onSome: (script) => Effect.succeed(disableTypeCheckingInScript(script))
|
|
4447
|
+
})
|
|
4427
4448
|
});
|
|
4428
4449
|
const parseScriptFormat = (scriptFormat, text, fileName) => Match.value(scriptFormat).pipe(Match.when("js", () => parseJS(text, fileName)), Match.when("ts", () => parseTS(text, fileName)), Match.when("tsx", () => parseTsx(text, fileName)), Match.exhaustive);
|
|
4429
|
-
const printScriptAst = (ast, context) => ast.format
|
|
4450
|
+
const printScriptAst = (ast, context) => Match.value(ast).pipe(Match.when({ format: "js" }, (js) => jsPrint(js, context)), Match.when({ format: "ts" }, (ts) => tsPrint(ts, context)), Match.when({ format: "tsx" }, (tsx) => tsPrint(tsx, context)), Match.orElse(printNothingForEmbeddedAst));
|
|
4451
|
+
const printNothingForEmbeddedAst = () => "";
|
|
4430
4452
|
const coreFormatRegistry = formatRegistry([
|
|
4431
4453
|
{
|
|
4432
4454
|
claim: {
|
|
@@ -4545,20 +4567,9 @@ const spliceForAssigned = (request) => Option.match(request.registry.entryForFor
|
|
|
4545
4567
|
onSome: (entry) => spliceFileFor(request.file, entry)
|
|
4546
4568
|
});
|
|
4547
4569
|
//#endregion
|
|
4548
|
-
//#region src/
|
|
4549
|
-
const toOneBasedOrigin = (origin) => ({
|
|
4550
|
-
line: origin.line + 1,
|
|
4551
|
-
column: origin.column
|
|
4552
|
-
});
|
|
4570
|
+
//#region src/drivers/oxc-program.ts
|
|
4553
4571
|
const EMBEDDED_SCRIPT_FILE = "embedded-script.js";
|
|
4554
|
-
const
|
|
4555
|
-
const oxc = yield* loadOxc;
|
|
4556
|
-
return {
|
|
4557
|
-
parseScript: (source, scriptFormat) => parseScriptProgram(oxc, source, scriptFormat),
|
|
4558
|
-
header: yield* instrumentationHeader
|
|
4559
|
-
};
|
|
4560
|
-
});
|
|
4561
|
-
const parseScriptProgram = (oxc, source, scriptFormat) => {
|
|
4572
|
+
const makeScriptParser = (oxc) => (source, scriptFormat) => {
|
|
4562
4573
|
const result = oxc.parseSync(EMBEDDED_SCRIPT_FILE, source, {
|
|
4563
4574
|
lang: scriptFormat,
|
|
4564
4575
|
range: true
|
|
@@ -4568,19 +4579,29 @@ const parseScriptProgram = (oxc, source, scriptFormat) => {
|
|
|
4568
4579
|
fileName: EMBEDDED_SCRIPT_FILE,
|
|
4569
4580
|
message: first.message,
|
|
4570
4581
|
location: {
|
|
4571
|
-
line:
|
|
4572
|
-
column:
|
|
4582
|
+
line: 1,
|
|
4583
|
+
column: 1
|
|
4573
4584
|
},
|
|
4574
4585
|
cause: first
|
|
4575
4586
|
});
|
|
4576
4587
|
return result.program;
|
|
4577
4588
|
};
|
|
4589
|
+
//#endregion
|
|
4590
|
+
//#region src/FrameworkEntry.ts
|
|
4591
|
+
const loadFrameworkToolkit = Effect.gen(function* () {
|
|
4592
|
+
const oxc = yield* loadOxc;
|
|
4593
|
+
const header = yield* instrumentationHeader;
|
|
4594
|
+
return {
|
|
4595
|
+
parseScript: makeScriptParser(oxc),
|
|
4596
|
+
header
|
|
4597
|
+
};
|
|
4598
|
+
});
|
|
4578
4599
|
const frameworkContextOf = (toolkit) => ({
|
|
4579
4600
|
parseScript: toolkit.parseScript,
|
|
4580
4601
|
printScript: (script) => printProgram(script, { hashbang: null }),
|
|
4581
4602
|
instrumentationHeader: () => toolkit.header
|
|
4582
4603
|
});
|
|
4583
|
-
const embeddedScriptsOf = (document, rawContent, originFileName,
|
|
4604
|
+
const embeddedScriptsOf = (document, rawContent, originFileName, lineStarts) => document.regions.flatMap((region, index) => {
|
|
4584
4605
|
return [{
|
|
4585
4606
|
region: index,
|
|
4586
4607
|
ast: {
|
|
@@ -4589,14 +4610,16 @@ const embeddedScriptsOf = (document, rawContent, originFileName, lineTable) => d
|
|
|
4589
4610
|
comments: [],
|
|
4590
4611
|
rawContent: rawContent.slice(region.start, region.end),
|
|
4591
4612
|
originFileName,
|
|
4592
|
-
offset:
|
|
4613
|
+
offset: originAt(lineStarts, region.start)
|
|
4593
4614
|
}
|
|
4594
4615
|
}];
|
|
4595
4616
|
});
|
|
4596
|
-
const
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
};
|
|
4617
|
+
const embeddedAstError = (ast) => InstrumentError.make({
|
|
4618
|
+
message: `Expected an embedded document AST, received the "${ast.format}" format`,
|
|
4619
|
+
cause: void 0
|
|
4620
|
+
});
|
|
4621
|
+
const embeddedOf = (ast) => Match.value(ast).pipe(Match.when({ format: "embedded" }, (embedded) => Option.some(embedded)), Match.orElse(() => Option.none()));
|
|
4622
|
+
const printNothingForNonEmbeddedAst = () => "";
|
|
4600
4623
|
const frameworkFailure = (moduleName, fileName, detail) => InstrumentError.make({
|
|
4601
4624
|
message: `The framework "${moduleName}" could not parse ${fileName}: ${detail}`,
|
|
4602
4625
|
cause: new Error(detail)
|
|
@@ -4605,7 +4628,7 @@ const hookFailure = (moduleName, hook, fileName, cause) => InstrumentError.make(
|
|
|
4605
4628
|
message: `The framework "${moduleName}" threw while running ${hook} for ${fileName}: ${errorTextOf(cause)}`,
|
|
4606
4629
|
cause
|
|
4607
4630
|
});
|
|
4608
|
-
const errorTextOf = (cause) => Option.getOrElse(Option.map(
|
|
4631
|
+
const errorTextOf = (cause) => Option.getOrElse(Option.map(errorTextOf$2(cause), (rendered) => rendered.text), () => "");
|
|
4609
4632
|
const runHook = (moduleName, hook, fileName, run) => Effect.try({
|
|
4610
4633
|
try: run,
|
|
4611
4634
|
catch: (cause) => hookFailure(moduleName, hook, fileName, cause)
|
|
@@ -4620,9 +4643,9 @@ const frameworkEntryOfDataFirst = (moduleName, framework) => ({
|
|
|
4620
4643
|
},
|
|
4621
4644
|
owner: moduleName,
|
|
4622
4645
|
ownerVersion: framework.claim.ownerVersion,
|
|
4623
|
-
parse:
|
|
4646
|
+
parse: Effect.fn("stryker.instrument.framework_entry.parse")(function* (text, fileName) {
|
|
4624
4647
|
const context = frameworkContextOf(yield* loadFrameworkToolkit);
|
|
4625
|
-
const
|
|
4648
|
+
const lineStarts = lineStartsOf(text);
|
|
4626
4649
|
const result = yield* runHook(moduleName, "parse", fileName, () => framework.parse(text, context));
|
|
4627
4650
|
const document = yield* settled(moduleName, fileName, result);
|
|
4628
4651
|
return {
|
|
@@ -4632,23 +4655,25 @@ const frameworkEntryOfDataFirst = (moduleName, framework) => ({
|
|
|
4632
4655
|
rawContent: text,
|
|
4633
4656
|
document,
|
|
4634
4657
|
context,
|
|
4635
|
-
scripts: embeddedScriptsOf(document, text, fileName,
|
|
4658
|
+
scripts: embeddedScriptsOf(document, text, fileName, lineStarts)
|
|
4636
4659
|
};
|
|
4637
4660
|
}),
|
|
4638
|
-
transform: (ast, mutantCollector, context) =>
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4661
|
+
transform: (ast, mutantCollector, context) => Option.match(embeddedOf(ast), {
|
|
4662
|
+
onNone: () => Effect.fail(embeddedAstError(ast)),
|
|
4663
|
+
onSome: Effect.fn("stryker.instrument.framework_entry.transform")(function* (embedded) {
|
|
4664
|
+
const warnings = yield* Effect.forEach(embedded.scripts, (script) => context.transform(script.ast, mutantCollector, context));
|
|
4665
|
+
embedded.document = yield* runHook(moduleName, "transform", embedded.originFileName, () => framework.transform(embedded.document, embedded.context));
|
|
4666
|
+
return warnings.flat();
|
|
4667
|
+
})
|
|
4643
4668
|
}),
|
|
4644
|
-
print: (ast) => {
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
},
|
|
4648
|
-
disableTypeChecks: (ast) => {
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
}
|
|
4669
|
+
print: (ast) => Option.match(embeddedOf(ast), {
|
|
4670
|
+
onNone: printNothingForNonEmbeddedAst,
|
|
4671
|
+
onSome: (embedded) => framework.print(embedded.document, embedded.context)
|
|
4672
|
+
}),
|
|
4673
|
+
disableTypeChecks: (ast) => Option.match(embeddedOf(ast), {
|
|
4674
|
+
onNone: () => Effect.fail(embeddedAstError(ast)),
|
|
4675
|
+
onSome: (embedded) => runHook(moduleName, "disableTypeChecks", embedded.originFileName, () => framework.disableTypeChecks(embedded.rawContent)).pipe(Effect.flatMap((result) => settled(moduleName, embedded.originFileName, result)))
|
|
4676
|
+
})
|
|
4652
4677
|
});
|
|
4653
4678
|
const frameworkEntryOf = dual((args) => args.length >= 2, frameworkEntryOfDataFirst);
|
|
4654
4679
|
//#endregion
|
|
@@ -4711,22 +4736,26 @@ const admitInstrumentFiles = Workflow.make({
|
|
|
4711
4736
|
});
|
|
4712
4737
|
//#endregion
|
|
4713
4738
|
//#region src/instrument-files.cell.ts
|
|
4714
|
-
const printedFile = (raw, { file, ast }) => ({
|
|
4739
|
+
const printedFile = (raw, { file, ast }) => Result.map(print(ast, raw.registry.entryForFormat), (content) => ({
|
|
4715
4740
|
name: file.name,
|
|
4716
4741
|
mutate: file.mutate,
|
|
4717
|
-
content
|
|
4718
|
-
});
|
|
4719
|
-
const
|
|
4720
|
-
|
|
4742
|
+
content
|
|
4743
|
+
}));
|
|
4744
|
+
const printedFiles = (raw) => Effect.forEach(raw.parsed, (parsed) => Effect.fromResult(printedFile(raw, parsed)), { concurrency: 1 }).pipe(Effect.mapError((failure) => InstrumentError.make({
|
|
4745
|
+
message: failure.message,
|
|
4746
|
+
cause: failure
|
|
4747
|
+
})));
|
|
4748
|
+
const instrumentedResult = (raw) => Effect.map(printedFiles(raw), (files) => InstrumentResult.make({
|
|
4749
|
+
files: [...files],
|
|
4721
4750
|
mutants: [...raw.mutants],
|
|
4722
4751
|
skipped: [...raw.skipped]
|
|
4723
|
-
});
|
|
4752
|
+
}));
|
|
4724
4753
|
const skippedOnlyResult = (raw) => InstrumentResult.make({
|
|
4725
4754
|
files: [],
|
|
4726
4755
|
mutants: [],
|
|
4727
4756
|
skipped: [...raw.skipped]
|
|
4728
4757
|
});
|
|
4729
|
-
const readInstrumentFiles =
|
|
4758
|
+
const readInstrumentFiles = Effect.fn("stryker.instrument.read_files")(function* (input) {
|
|
4730
4759
|
const outcomes = yield* Effect.forEach(input.files, (file) => fileOutcome({
|
|
4731
4760
|
registry: input.registry,
|
|
4732
4761
|
file
|
|
@@ -4755,7 +4784,7 @@ const readInstrumentFiles = (input) => Effect.gen(function* () {
|
|
|
4755
4784
|
};
|
|
4756
4785
|
});
|
|
4757
4786
|
const instrumentFilesCell = Sandwich.named("stryker.instrument.files")((input) => readInstrumentFiles(input)).decide(admitInstrumentFiles).write({
|
|
4758
|
-
InstrumentFilesAdmitted: (_admitted, raw) =>
|
|
4787
|
+
InstrumentFilesAdmitted: (_admitted, raw) => instrumentedResult(raw),
|
|
4759
4788
|
InstrumentFilesSkippedOnly: (_skipped, raw) => Effect.succeed(skippedOnlyResult(raw)),
|
|
4760
4789
|
CommandRejected: ({ issue }) => Effect.fail(InstrumentError.make({
|
|
4761
4790
|
message: issue,
|
|
@@ -4765,7 +4794,7 @@ const instrumentFilesCell = Sandwich.named("stryker.instrument.files")((input) =
|
|
|
4765
4794
|
//#endregion
|
|
4766
4795
|
//#region src/Instrument.service.ts
|
|
4767
4796
|
const toSchemaFile = (file) => ({
|
|
4768
|
-
name: file.name,
|
|
4797
|
+
name: CanonicalFileName.make(file.name),
|
|
4769
4798
|
content: file.content,
|
|
4770
4799
|
mutate: file.mutate
|
|
4771
4800
|
});
|
|
@@ -4810,9 +4839,14 @@ var Instrument_exports = /* @__PURE__ */ __exportAll({
|
|
|
4810
4839
|
//#endregion
|
|
4811
4840
|
//#region src/Mutant/mod.ts
|
|
4812
4841
|
var Mutant_exports = /* @__PURE__ */ __exportAll({
|
|
4842
|
+
ActionableStatusSchema: () => ActionableStatusSchema,
|
|
4813
4843
|
CanonicalFileName: () => CanonicalFileName,
|
|
4844
|
+
Column: () => Column,
|
|
4845
|
+
EphemeralStatusSchema: () => EphemeralStatusSchema,
|
|
4846
|
+
HitCount: () => HitCount,
|
|
4814
4847
|
InstrumenterContext: () => InstrumenterContext,
|
|
4815
|
-
|
|
4848
|
+
Line: () => Line,
|
|
4849
|
+
Location: () => Location,
|
|
4816
4850
|
Mutant: () => Mutant,
|
|
4817
4851
|
MutantActivationSchema: () => MutantActivationSchema,
|
|
4818
4852
|
MutantCoverageSchema: () => MutantCoverageSchema,
|
|
@@ -4821,10 +4855,14 @@ var Mutant_exports = /* @__PURE__ */ __exportAll({
|
|
|
4821
4855
|
MutantRunOptionsSchema: () => MutantRunOptionsSchema,
|
|
4822
4856
|
MutantStatusSchema: () => MutantStatusSchema,
|
|
4823
4857
|
MutatorName: () => MutatorName,
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4858
|
+
Offset: () => Offset,
|
|
4859
|
+
OpenEndLocation: () => OpenEndLocation,
|
|
4860
|
+
Position: () => Position,
|
|
4861
|
+
RememberedStatusSchema: () => RememberedStatusSchema,
|
|
4862
|
+
RunOptionsFields: () => RunOptionsFields,
|
|
4863
|
+
ScriptOrigin: () => ScriptOrigin,
|
|
4864
|
+
Span: () => Span,
|
|
4865
|
+
SurvivorStatusSchema: () => SurvivorStatusSchema
|
|
4828
4866
|
});
|
|
4829
4867
|
//#endregion
|
|
4830
|
-
export { ErrorText_exports as ErrorText, Format_exports as Format, Instrument_exports as Instrument, Mutant_exports as Mutant };
|
|
4868
|
+
export { ErrorText_exports as ErrorText, Format_exports as Format, Instrument_exports as Instrument, Mutant_exports as Mutant, lineStartsOf, locationOf, offsetAt, originAt, positionAt };
|