@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/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 Arr from "effect/Array";
12
- import * as SGetter from "effect/SchemaGetter";
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 ErrorText extends S.Class("ErrorText")({ text: S.NonEmptyString }) {
16
- static fromCause = (cause) => Option.flatMap(nonEmptyText(errorTextOf$2(cause)), (text) => ErrorText.makeOption({ text }));
17
- };
18
- var CauseText = class CauseText extends S.Class("CauseText")({ text: S.NonEmptyString }) {
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
- function errorTextOf$2(error) {
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: () => causeTextOfValue(cause, depth)
91
+ onFalse: () => causeNodeTextOf(cause, depth)
91
92
  });
92
93
  }
93
- function causeTextOfValue(cause, depth) {
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
- * File coordinates in the mutation-testing-report-schema contract: both line
109
- * and column are 1-based. The first line of a file is line 1, and the first
110
- * character of a line is column 1. Slicing a source line by one of these
111
- * positions uses `line - 1` for the line index and `column - 1` for the
112
- * character offset.
113
- */
114
- const PositionSchema = S.Struct({
115
- line: S.Int.pipe(S.check(S.isGreaterThanOrEqualTo(0))),
116
- column: S.Int.pipe(S.check(S.isGreaterThanOrEqualTo(0)))
117
- });
118
- const LocationSchema$1 = S.Struct({
119
- start: PositionSchema,
120
- end: PositionSchema
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 OpenEndLocationSchema = S.Struct({
123
- start: PositionSchema,
124
- end: S.optional(PositionSchema)
136
+ const OpenEnds = S.Struct({
137
+ start: Position,
138
+ end: S.optional(Position)
125
139
  });
126
- /**
127
- * A mutant's `location` already speaks the report contract (1-based line and
128
- * column), so the report's location is that location unchanged.
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 middleIndex = (low, high) => low + (high - low >> 1);
158
- const zeroBasedPositionOf = (lineStarts, offset) => {
159
- const search = (low, high, start) => Boolean$1.match(low > high, {
160
- onTrue: () => ({
161
- line: low - 1,
162
- column: offset - start
163
- }),
164
- onFalse: () => Option.match(Arr.get(lineStarts, middleIndex(low, high)), {
165
- onNone: () => ({
166
- line: low - 1,
167
- column: offset - start
168
- }),
169
- onSome: (found) => Boolean$1.match(found === offset, {
170
- onTrue: () => ({
171
- line: middleIndex(low, high),
172
- column: offset - found
173
- }),
174
- onFalse: () => Boolean$1.match(found < offset, {
175
- onTrue: () => search(middleIndex(low, high) + 1, high, found),
176
- onFalse: () => search(low, middleIndex(low, high) - 1, start)
177
- })
178
- })
179
- })
180
- });
181
- return search(0, lineStarts.length - 1, 0);
182
- };
183
- const positionOf = (lineStarts, offset) => {
184
- const zeroBased = zeroBasedPositionOf(lineStarts, offset);
185
- return {
186
- line: zeroBased.line + 1,
187
- column: zeroBased.column + 1
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 MutantId = S.NonEmptyString.pipe(S.brand("MutantId"));
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: SGetter.transform((fileName) => fileName.replace(/\\/g, "/")),
206
- encode: SGetter.transform((canonical) => canonical)
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
- var Mutant = class extends S.TaggedClass()("Mutant", {
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: LocationSchema$1,
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(S.String, HitCount)),
245
- static: S.Record(S.String, HitCount)
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(S.Finite),
252
- hitLimit: S.optional(S.Finite)
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(LocationSchema$1)]);
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: S.String,
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(S.String),
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(S.String),
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: S.String
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, lineTable) => Boolean$1.match(comments.length === 0, {
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, lineTable, "leadingComments");
570
- assignComments(groups.trailing, lineTable, "trailingComments");
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, lineTable, field) => map.forEach((list, node) => Object.assign(node, { [field]: list.map((comment) => ({
661
+ const assignComments = (map, lineStarts, field) => map.forEach((list, node) => Object.assign(node, { [field]: list.map((comment) => ({
597
662
  ...comment,
598
- loc: {
599
- start: lineTable.positionAt(comment.start),
600
- end: lineTable.positionAt(comment.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 visitSlot = (value, key, visitors) => Option.match(Option.filter(Option.some(value), isChildList), {
639
- onSome: (items) => items.forEach((item, index) => visitIfNode(item, {
640
- key,
641
- index
642
- }, visitors)),
643
- onNone: () => visitIfNode(value, {
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
- }, visitors)
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 visitIfNode = (value, context, visitors) => Option.match(Option.filter(Option.some(value), isAstNode), {
649
- onNone: () => void 0,
650
- onSome: (child) => visitNode(child, context, visitors)
651
- });
652
- const walker = (root, visitors) => {
653
- const ancestors = [];
654
- walkNodes(root, {
655
- enter(node) {
656
- visitors.enter?.(node, [...ancestors]);
657
- ancestors.push(node);
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, stack, visitors) => {
693
- walkNodes(root, {
694
- enter(node, context, controls) {
695
- readPath(stack, node, controls, context, visitors);
696
- },
697
- leave(_node, context) {
698
- closePath(stack, context, visitors);
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 traverse = dual(2, (self, visitors) => walkTraverse(self.root, [], visitors));
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 current = createPath(node, parentOf(stack), controls, context);
708
- stack.push(current);
709
- notify(visitors.enter, current);
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 closePath = (stack, context, visitors) => Boolean$1.match(isCommentKey(context.key), {
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, importTableFor(program)));
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
- const span = spanOf(node);
1345
- if (span === void 0) return;
1346
- return `${node.type}:${span.start}:${span.end}`;
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
- if (!applied) applied = replaceFirstMatch(path, original, replacement);
1431
+ applied = applied || replaceFirstMatch(path, original, replacement);
1395
1432
  } });
1396
1433
  return applied;
1397
1434
  }
1398
1435
  function replaceFirstMatch(path, original, replacement) {
1399
- if (eqNode(path.node, original) === false) return false;
1400
- path.replaceWith(replacement);
1401
- return true;
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
- if (pattern.length === 0) return [];
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.sort((a, b) => a.start - b.start || a.priority - b.priority);
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
- ...groups.rest
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
- if (characterClass.negate) groups.rest.push({
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
- if (propertyMarker !== void 0) return propertyMarker;
1545
- return invertLetterCase(marker);
1580
+ return propertyMarker === void 0 ? invertLetterCase(marker) : propertyMarker;
1546
1581
  }
1547
1582
  function invertLetterCase(letter) {
1548
- if (letter === letter.toUpperCase()) return letter.toLowerCase();
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
- if (spliceText(pattern, splice).length === 0) return;
1566
- return splice;
1599
+ return spliceText(pattern, splice).length === 0 ? void 0 : splice;
1567
1600
  }
1568
1601
  function pushWhen(list, splice) {
1569
- if (splice !== void 0) list.push(splice);
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
- if (node.left.type === "BinaryExpression") return node.left.right;
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
- if (array.elements.length > 0) return arrayExpression([]);
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
- if (construct.type === "NewExpression") return newExpression(cloneNode(construct.callee), mutatedCallArgs);
1646
- return callExpression(cloneNode(construct.callee), mutatedCallArgs);
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
- if (args.length > 0) return [];
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
- if (!Predicate.hasProperty(value, "value")) return false;
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
- if (Array.isArray(value)) return value.some(containsSuperCall);
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 replacements = new Map(Object.entries(baseReplacements));
1923
- for (const [key, value] of Object.entries(baseReplacements)) if (value !== null && !noReverseReplacements.includes(key)) replacements.set(value, key);
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
- return Match.value(replacements.get(callee.property.name)).pipe(Match.when(void 0, () => void 0), Match.orElse((newName) => ({
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
- if (first === void 0) return NO_MUTANTS;
2021
- return [emptyOrPlaceholderTemplate(template, first)];
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
- if (isEmpty) return PLACEHOLDER;
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: S.Struct({
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) => Effect.gen(function* () {
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 lineTable = yield* Effect.orDie(S.decodeEffect(LineTableFromText)(text));
2204
- const failure = oxcParseFailure(result.errors, fileName, lineTable);
2205
- if (failure !== void 0) return yield* failure;
2206
- return {
2207
- root: result.program,
2208
- comments: result.comments
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, lineTable) => {
2213
- const first = errors.at(0);
2214
- if (first === void 0) return;
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: lineTable.positionAt(oxcErrorLabelStart(first)),
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 = (text, fileName) => Effect.map(parseWithOxc(text, fileName, "js"), ({ root, comments }) => ({
2229
- originFileName: fileName,
2230
- rawContent: text,
2231
- format: "js",
2232
- root,
2233
- comments
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 = (text, fileName) => Effect.map(parseWithOxc(text, fileName, "ts"), ({ root, comments }) => ({
2237
- originFileName: fileName,
2238
- rawContent: text,
2239
- format: "ts",
2240
- root,
2241
- comments
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 = (text, fileName) => Effect.map(parseWithOxc(text, fileName, "tsx"), ({ root, comments }) => ({
2245
- root,
2246
- comments,
2247
- format: "tsx",
2248
- originFileName: fileName,
2249
- rawContent: text
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: () => missingFormatPrint(file),
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
- function missingFormatPrint(file) {
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
- function hashbangValue(field) {
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 = instrumentationHeaderValue !== void 0 ? Effect.succeed(instrumentationHeaderValue) : loadHeader;
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) => [commentedHeader(leadingComments, header), ...header.slice(1)]
3504
+ onSome: ([firstHeader, leadingComments]) => commentedHeader(firstHeader, leadingComments, header.slice(1))
3506
3505
  }));
3507
- const placeHeaderIfNeededDataFirst = (placed, options, root) => placed ? placeHeaderWhenWanted(options, root) : Effect.void;
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 ? Effect.void : placeHeader(root);
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
- if (!hasLeadingComments$1(first)) return Option.none();
3513
- return Option.some(first.leadingComments);
3517
+ return Option.map(Option.liftPredicate(first, hasLeadingComments$1), (value) => value.leadingComments);
3514
3518
  }
3515
- function commentedHeader(leadingComments, header) {
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: S.String,
3536
- mutatorName: S.String,
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(S.String)
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(S.String)
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(S.String)
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: S.String,
3574
- mutatorName: S.String,
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: S.String,
3633
+ mutatorName: MutatorNameSchema,
3635
3634
  replacementCode: S.String,
3636
- location: S.optional(LocationSchema),
3635
+ location: S.optional(Location),
3637
3636
  ignorerReason: S.optional(S.String)
3638
3637
  });
3639
3638
  const PlannedMutantSchema = S.Struct({
3640
- id: S.String,
3641
- mutatorName: S.String,
3639
+ id: MutantId,
3640
+ mutatorName: MutatorNameSchema,
3642
3641
  replacementCode: S.String,
3643
- location: LocationSchema,
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: NodePositionSchema,
3655
- line: SourceLineSchema,
3656
- mutatorNames: S.Array(S.String),
3657
- excludedMutations: S.Array(S.String),
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: S.String
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
- * Both the node span (`source`) and the region origin (`offset`) are 1-based
3701
- * file coordinates: the region's first line carries its column origin, later
3702
- * lines start at column 1, so the shift adds `offset.line - 1` lines and —
3703
- * only when the node sits on the region's first line — `offset.column`.
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.column), Match.when(false, () => 0), Match.exhaustive);
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(ErrorText.fromCause(cause), (rendered) => rendered.text), () => "");
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, governedLineOf) => attachedComments(node).flatMap((comment) => Option.toArray(locatedDirective(comment, governedLineOf(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
- function attachedComments(node) {
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 ancestorsOf(path) {
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(path.node.type),
3798
- flowTypeAnnotationNodeTypes.includes(path.node.type),
3799
- isDeclareVariableStatement(path.node),
3800
- isDeclareModule(path.node)
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(path) {
3837
- return nodeType(path.node) === "TSImportEqualsDeclaration" || path.node.type === "ImportDeclaration";
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
- if (targetExpression !== void 0) sequence.push(targetExpression);
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 placementFailure = (refusal, nodePath, mutants, fileName, lineTable, basePath) => {
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 /* @__PURE__ */ new Error(`${placementLocation(nodePath.node, fileName, lineTable, 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)}`);
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 narrowNode(node, isKind, `Cannot place mutant ${mutant.id}: expected ${kind}, got ${node.type}`);
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 narrowNode(node, isExpressionKind, `Expected an expression, got ${node.type}`);
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 narrowNode(node, isStatementKind, `Expected a statement, got ${node.type}`);
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 narrowNode(node, isSwitchCase, `Expected a switch case, got ${node.type}`);
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
- if (basePath === void 0) return fileName;
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) => lineTable.positionAt(span.start));
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
- if (normalized.endsWith("/")) return normalized;
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
- if (!normalizedFile.startsWith(prefix)) return fileName;
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 Match.value(path.node).pipe(Match.when(isAnonymousFunctionOrClass, (node) => nameFromParent(path, node)), Match.orElse(() => void 0));
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(() => void 0));
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(() => void 0));
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(() => void 0));
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.match(declaratorIdentifier(parentPath), {
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.match(declaredName(declarator), {
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) ?? arrowNameOrNode(path);
3955
- }
3956
- function arrowNameOrNode(path) {
3957
- return arrowFunctionExpressionNamedIfNeeded(path) ?? expressionOf(path.node);
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(path) {
3976
- const parent = path.parentPath;
3977
- return parent === null || !isUnmutatableContext(path, parent);
3955
+ function isValidExpression(node, parent) {
3956
+ return parent === null || !isUnmutatableContext(node, parent);
3978
3957
  }
3979
- function isUnmutatableContext(path, parent) {
3958
+ function isUnmutatableContext(node, parent) {
3980
3959
  return [
3981
- isObjectPropertyKey(path, parent),
3982
- isPartOfChain(path, parent),
3960
+ isObjectPropertyKey(node, parent),
3961
+ isPartOfChain(node, parent),
3983
3962
  isTaggedTemplateTag(parent),
3984
- isDeletedOperand(path, parent),
3985
- isAssignedTarget(path, parent)
3963
+ isDeletedOperand(node, parent),
3964
+ isAssignedTarget(node, parent)
3986
3965
  ].some((invalid) => invalid);
3987
3966
  }
3988
- function isObjectPropertyKey(path, parent) {
3989
- const parentNode = parent.node;
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.node.type === "TaggedTemplateExpression";
3971
+ return parent.type === "TaggedTemplateExpression";
3994
3972
  }
3995
- function isDeletedOperand(path, parent) {
3996
- const parentNode = parent.node;
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(path, parent) {
4000
- const parentNode = parent.node;
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(path, parent) {
4004
- return isChainLink(path.node) && chainContinuesIn(path, parent);
3979
+ function isPartOfChain(node, parent) {
3980
+ return isChainLink(node) && chainContinuesIn(node, parent);
4005
3981
  }
4006
- function chainContinuesIn(path, parent) {
3982
+ function chainContinuesIn(node, parent) {
4007
3983
  return [
4008
- isMemberAccessParent(path, parent),
4009
- isNonNullExpression(parent.node),
4010
- isCalleeParent(path, parent)
3984
+ isMemberAccessParent(node, parent),
3985
+ isNonNullExpression(parent),
3986
+ isCalleeParent(node, parent)
4011
3987
  ].some((continues) => continues);
4012
3988
  }
4013
- function isMemberAccessParent(path, parent) {
4014
- const parentNode = parent.node;
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(path, parent) {
4018
- const parentNode = parent.node;
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
- let expression = nameIfAnonymous(path);
4037
- expression = mutationCoverageSequenceExpression(appliedMutants.keys(), expression);
4038
- for (const [mutant, appliedMutant] of appliedMutants) expression = conditionalExpression(mutantTestExpression(mutant.id), nodeOfKind(mutant, unwrapParenthesizedExpression(appliedMutant), isExpressionKind, "an expression"), expression);
4039
- path.replaceWith(expression);
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
- const body = [expressionStatement(mutationCoverageSequenceExpression(appliedMutants.keys())), ...statementsOf(path)];
4046
- const statement = [...appliedMutants].reduce(guardedStatement, blockStatement(body));
4047
- path.replaceWith(wrappedStatement(path, statement));
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
- function statementsOf(path) {
4029
+ const statementsOf = (path) => {
4051
4030
  const node = path.node;
4052
- if (node.type === "BlockStatement") return node.body;
4053
- return [statementOf(node)];
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 ifStatement(mutantTestExpression(entry[0].id), blockStatement([nodeOfKind(entry[0], entry[1], isStatementKind, "a statement")]), statement);
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
- const currentCase = switchCaseOf(path.node);
4068
- let consequence = blockStatement([expressionStatement(mutationCoverageSequenceExpression(appliedMutants.keys())), ...currentCase.consequent]);
4069
- for (const [mutant, appliedMutant] of appliedMutants) {
4070
- const appliedCase = nodeOfKind(mutant, appliedMutant, isSwitchCase, "a switch case");
4071
- consequence = ifStatement(mutantTestExpression(mutant.id), blockStatement(appliedCase.consequent), consequence);
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 transformScriptDataFirst = ({ root, originFileName, rawContent, offset, comments }, mutantCollector, { options, mutateDescription, basePath }) => {
4098
- const placementMap = /* @__PURE__ */ new Map();
4099
- return Effect.gen(function* () {
4100
- const lineTable = yield* Effect.orDie(S.decodeEffect(LineTableFromText)(rawContent));
4101
- attachComments(make(root), comments, lineTable);
4102
- let directiveRule = [];
4103
- let hasLiveMutants = false;
4104
- const selection = selectMutators(DEFAULT_MUTATOR_REGISTRY, options.optInMutations);
4105
- const mutatorEntries = selection.active;
4106
- const allMutatorNames = selection.known.map((name) => name.toLowerCase());
4107
- const warnings = [];
4108
- yield* Effect.try({
4109
- try: () => traverse(make(root), {
4110
- enter(path) {
4111
- const directives = directivesOf(path.node, (node) => getNodeLocation(node).start.line);
4112
- directiveRule = directives.reduce(foldInto, directiveRule);
4113
- visitNode(path, directives);
4114
- },
4115
- exit(path) {
4116
- const placement = placementMap.get(path.node);
4117
- if (hasAppliedMutants(placement)) applyPlacement(path, placement);
4118
- }
4119
- }),
4120
- catch: traversalFailure
4121
- });
4122
- yield* placeHeaderIfNeeded(hasLiveMutants, options, root);
4123
- return warnings;
4124
- function visitNode(path, directives) {
4125
- if (shouldSkip(path)) {
4126
- path.skip();
4127
- return;
4128
- }
4129
- addToPlacementMapIfPossible(path);
4130
- placeCollectedMutants(path, directives);
4131
- }
4132
- function placementFacts(path) {
4133
- return {
4134
- isExpression: path.isExpression(),
4135
- isStatement: path.isStatement(),
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
- function raisePlacementRefusal(refusal, placement, path) {
4171
- throw placementFailure(refusal, path, [...placement.appliedMutants.keys()], originFileName, lineTable, basePath);
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
- function placeSite(site, placement, path) {
4174
- try {
4175
- placerBuilders[placerNameOf(site)].place(path, placement.appliedMutants);
4176
- path.skip();
4177
- } catch (error) {
4178
- raisePlacementRefusal(MutantsUnapplied.make({
4179
- fileName: originFileName,
4180
- placer: placerNameOf(site),
4181
- mutatorNames: [...placement.appliedMutants.keys()].map((mutant) => mutant.mutatorName),
4182
- cause: toError(error)
4183
- }), placement, path);
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
- function applyPlacement(path, placement) {
4187
- const decision = placeMutants(PlaceMutantsCommand.make({
4188
- fileName: originFileName,
4189
- facts: placement.facts,
4190
- mutants: [...placement.appliedMutants].map(([mutant, applied]) => replacementRecord(mutant, applied))
4191
- }));
4192
- Match.value(decision).pipe(Match.when(Result.isFailure, (refused) => raisePlacementRefusal(refused.failure, placement, path)), Match.orElse((decided) => placeSite(decided.success, placement, path)));
4193
- }
4194
- function placeCollectedMutants(path, directives) {
4195
- const candidates = candidateSteps(path);
4196
- Match.value(needsPlan(candidates, directives)).pipe(Match.when(true, () => planAndPlace(path, candidates, directives)), Match.when(false, () => void 0), Match.exhaustive);
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
- function toError(value) {
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
- if (code.startsWith("#")) return afterHashbang(code);
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
- if (newLineIndex <= 0) return code;
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
- if (leadingComment === void 0) return `// @ts-nocheck\n${code}`;
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
- const match = commentDirectiveRegEx.exec(comment.value);
4369
- if (match === null) return void 0;
4370
- const directivePrefix = requirePart(match[1], "TS directive match without prefix");
4371
- const directiveName = requirePart(match[2], "TS directive match without directive name");
4372
- const startPos = comment.start + directivePrefix.length + 2;
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 + directiveName.length + 1
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 requireScriptAst = (ast) => {
4385
- if (ast.format === "embedded") return rejectAst(ast, "a script");
4386
- return ast;
4387
- };
4388
- const requireJsAst = (ast) => ast.format === "js" ? ast : rejectAst(ast, "a js script");
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) => transformScript(requireScriptAst(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) => Effect.succeed(disableTypeCheckingInScript(requireScriptAst(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 === "js" ? jsPrint(requireJsAst(ast), context) : tsPrint(requireTsFamilyAst(ast), context);
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/FrameworkEntry.ts
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 loadFrameworkToolkit = Effect.gen(function* () {
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: 0,
4572
- column: 0
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, lineTable) => document.regions.flatMap((region, index) => {
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: toOneBasedOrigin(lineTable.zeroBasedPositionAt(region.start))
4613
+ offset: originAt(lineStarts, region.start)
4593
4614
  }
4594
4615
  }];
4595
4616
  });
4596
- const embeddedOf = (ast) => {
4597
- if (ast.format !== "embedded") throw new Error(`Expected an embedded document AST, received the "${ast.format}" format`);
4598
- return ast;
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(ErrorText.fromCause(cause), (text) => text.text), () => "");
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: (text, fileName) => Effect.gen(function* () {
4646
+ parse: Effect.fn("stryker.instrument.framework_entry.parse")(function* (text, fileName) {
4624
4647
  const context = frameworkContextOf(yield* loadFrameworkToolkit);
4625
- const lineTable = yield* Effect.orDie(S.decodeEffect(LineTableFromText)(text));
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, lineTable)
4658
+ scripts: embeddedScriptsOf(document, text, fileName, lineStarts)
4636
4659
  };
4637
4660
  }),
4638
- transform: (ast, mutantCollector, context) => Effect.gen(function* () {
4639
- const embedded = embeddedOf(ast);
4640
- const warnings = yield* Effect.forEach(embedded.scripts, (script) => context.transform(script.ast, mutantCollector, context));
4641
- embedded.document = yield* runHook(moduleName, "transform", embedded.originFileName, () => framework.transform(embedded.document, embedded.context));
4642
- return warnings.flat();
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
- const embedded = embeddedOf(ast);
4646
- return framework.print(embedded.document, embedded.context);
4647
- },
4648
- disableTypeChecks: (ast) => {
4649
- const embedded = embeddedOf(ast);
4650
- return runHook(moduleName, "disableTypeChecks", embedded.originFileName, () => framework.disableTypeChecks(embedded.rawContent)).pipe(Effect.flatMap((result) => settled(moduleName, embedded.originFileName, result)));
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: print(ast, raw.registry.entryForFormat)
4718
- });
4719
- const instrumentedResult = (raw) => InstrumentResult.make({
4720
- files: raw.parsed.map((parsed) => printedFile(raw, parsed)),
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 = (input) => Effect.gen(function* () {
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) => Effect.succeed(instrumentedResult(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
- LocationSchema: () => LocationSchema$1,
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
- OpenEndLocationSchema: () => OpenEndLocationSchema,
4825
- PositionSchema: () => PositionSchema,
4826
- ReportLocationFromMutant: () => ReportLocationFromMutant,
4827
- RunOptionsFields: () => RunOptionsFields
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 };