@systemfsoftware/stryker-js 5.0.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/README.md +144 -39
- package/dist/Plugins-DF95m9KO.d.mts +1400 -0
- package/dist/{builtin-reporters-wAuKFSsd.mjs → builtin-reporters-BqePgAN0.mjs} +3 -4
- package/dist/builtin-reporters.d.mts +299 -1
- package/dist/builtin-reporters.mjs +3 -2
- package/dist/compiler-C8aLIb5U.mjs +48406 -0
- package/dist/config.d.mts +2 -1
- package/dist/dist-CWbipfGB.mjs +711 -0
- package/dist/index-BInJWa0x.d.mts +1148 -0
- package/dist/index.d.mts +2538 -312
- package/dist/index.mjs +4 -3
- package/dist/main.mjs +499 -1893
- package/dist/{mutation-test.cell-RmF-sBUm.d.mts → mutation-test.cell-Cw1ZzDnb.d.mts} +70 -18
- package/dist/plugin-loader.d.mts +1577 -10
- package/dist/plugin-loader.mjs +3 -2
- package/dist/{plugin-worker-entry-DNu0SHO8.mjs → plugin-worker-entry-D00fedxV.mjs} +356 -184
- package/dist/promises.d.mts +2 -2
- package/dist/promises.mjs +1 -1
- package/dist/rolldown-runtime-XITHOX9H.mjs +15 -0
- package/dist/src-BoVgxGWD.mjs +12466 -0
- package/dist/src-js-DRK8boNV.mjs +1009 -0
- package/dist/{stryker-config-BlfHxS7n.d.mts → stryker-config-DIGmQrm1.d.mts} +2 -2
- package/package.json +35 -13
- package/dist/Plugins-sA_9VKus.d.mts +0 -96
- package/dist/src-Bzh7ykDp.mjs +0 -4724
|
@@ -1,37 +1,42 @@
|
|
|
1
|
+
import { A as formatTraceparent, C as TestRunnerFailed, D as WorkerEntryUrl, E as TraceContextReference, M as parseTraceparent, N as toMutantRunResult, O as WorkerPluginKind, P as INSTRUMENTER_CONSTANTS, T as TraceContextMiddleware, b as TRACEPARENT_HEADER, f as PropagatedTrace, j as isCustomTestRunner, w as TestRunnerRpcs, x as TRACESTATE_HEADER, y as StrykerOptionsSchema } from "./dist-CWbipfGB.mjs";
|
|
1
2
|
import { t as mergeConfig } from "./merge-config-BrrNQWje.mjs";
|
|
2
|
-
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Effect$1 from "effect/Effect";
|
|
3
4
|
import * as FileSystem from "effect/FileSystem";
|
|
4
5
|
import * as Path from "effect/Path";
|
|
5
|
-
import
|
|
6
|
+
import * as Predicate from "effect/Predicate";
|
|
7
|
+
import * as S from "effect/Schema";
|
|
8
|
+
import * as Match from "effect/Match";
|
|
9
|
+
import * as Option from "effect/Option";
|
|
10
|
+
import * as Context from "effect/Context";
|
|
11
|
+
import * as RpcMiddleware from "effect/unstable/rpc/RpcMiddleware";
|
|
12
|
+
import { Schema } from "effect";
|
|
6
13
|
import * as Clock from "effect/Clock";
|
|
7
14
|
import * as Config from "effect/Config";
|
|
8
|
-
import * as Context from "effect/Context";
|
|
9
15
|
import * as Layer from "effect/Layer";
|
|
10
|
-
import * as Match from "effect/Match";
|
|
11
|
-
import * as Option from "effect/Option";
|
|
12
16
|
import * as Result from "effect/Result";
|
|
13
|
-
import * as S from "effect/Schema";
|
|
14
17
|
import * as Stream from "effect/Stream";
|
|
15
|
-
import { INSTRUMENTER_CONSTANTS } from "@systemfsoftware/stryker-js-instrumenter/mutants";
|
|
16
18
|
import * as Array$1 from "effect/Array";
|
|
17
19
|
import * as Cause from "effect/Cause";
|
|
18
20
|
import "effect/Exit";
|
|
19
|
-
import * as Predicate from "effect/Predicate";
|
|
20
21
|
import * as Duration from "effect/Duration";
|
|
21
|
-
import { Schema } from "effect";
|
|
22
22
|
import * as HashMap from "effect/HashMap";
|
|
23
23
|
import * as HashSet from "effect/HashSet";
|
|
24
|
-
import
|
|
25
|
-
import
|
|
24
|
+
import * as api from "@opentelemetry/api";
|
|
25
|
+
import * as Tracer from "effect/Tracer";
|
|
26
|
+
import * as Headers from "effect/unstable/http/Headers";
|
|
27
|
+
import { NodeFileSystem, NodePath, NodeSocket, NodeStdio } from "@effect/platform-node";
|
|
28
|
+
import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
|
|
29
|
+
import "effect/unstable/rpc/RpcServer";
|
|
30
|
+
import * as NodeSdk from "@effect/opentelemetry/NodeSdk";
|
|
31
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
32
|
+
import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
26
33
|
import * as Ref from "effect/Ref";
|
|
27
34
|
import * as ChildProcess from "effect/unstable/process/ChildProcess";
|
|
28
35
|
import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";
|
|
29
|
-
import { NodeFileSystem, NodePath, NodeSocket, NodeStdio } from "@effect/platform-node";
|
|
30
36
|
import * as NodeChildProcessSpawner from "@effect/platform-node-shared/NodeChildProcessSpawner";
|
|
31
37
|
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto";
|
|
32
38
|
import * as Crypto from "effect/Crypto";
|
|
33
39
|
import * as RpcClient from "effect/unstable/rpc/RpcClient";
|
|
34
|
-
import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
|
|
35
40
|
import * as Schedule from "effect/Schedule";
|
|
36
41
|
//#region src/Run.schema.ts
|
|
37
42
|
const TypeId = "~stryker/mutation-run/StageError";
|
|
@@ -83,6 +88,42 @@ var PrepareError = class extends S.TaggedError()("PrepareError", {
|
|
|
83
88
|
reason: S.String
|
|
84
89
|
}) {};
|
|
85
90
|
//#endregion
|
|
91
|
+
//#region src/glob-match.ts
|
|
92
|
+
const GLOB_META = /[*?[{]/;
|
|
93
|
+
const isGlob = (value) => GLOB_META.test(value);
|
|
94
|
+
const escapeRegex = (value) => value.replace(/[\\^$.|()+]/g, "\\$&");
|
|
95
|
+
const globSegmentToRegex = (segment) => Match.value(segment.length === 0).pipe(Match.when(true, () => ""), Match.orElse(() => {
|
|
96
|
+
const char = segment[0] ?? "";
|
|
97
|
+
const rest = segment.slice(1);
|
|
98
|
+
return Match.value(char).pipe(Match.when("*", () => Match.value(rest.startsWith("*")).pipe(Match.when(true, () => {
|
|
99
|
+
const afterStars = rest.slice(1);
|
|
100
|
+
return Match.value(afterStars.startsWith("/")).pipe(Match.when(true, () => `(?:(?:[^/]+/)*)?${globSegmentToRegex(afterStars.slice(1))}`), Match.orElse(() => `.*${globSegmentToRegex(afterStars)}`));
|
|
101
|
+
}), Match.orElse(() => `[^/]*${globSegmentToRegex(rest)}`))), Match.when("?", () => `[^/]${globSegmentToRegex(rest)}`), Match.when("{", () => {
|
|
102
|
+
const close = segment.indexOf("}");
|
|
103
|
+
return Match.value(close < 0).pipe(Match.when(true, () => `${escapeRegex(char)}${globSegmentToRegex(rest)}`), Match.orElse(() => {
|
|
104
|
+
const inner = segment.slice(1, close);
|
|
105
|
+
const after = segment.slice(close + 1);
|
|
106
|
+
return `(?:${inner.split(",").map(globSegmentToRegex).join("|")})${globSegmentToRegex(after)}`;
|
|
107
|
+
}));
|
|
108
|
+
}), Match.when("[", () => {
|
|
109
|
+
const close = segment.indexOf("]", 1);
|
|
110
|
+
return Match.value(close < 0).pipe(Match.when(true, () => `${escapeRegex(char)}${globSegmentToRegex(rest)}`), Match.orElse(() => `${segment.slice(0, close + 1)}${globSegmentToRegex(segment.slice(close + 1))}`));
|
|
111
|
+
}), Match.orElse(() => `${escapeRegex(char)}${globSegmentToRegex(rest)}`));
|
|
112
|
+
}));
|
|
113
|
+
const globToRegExp = (pattern, caseInsensitive) => new RegExp(`^${globSegmentToRegex(pattern)}$`, Match.value(caseInsensitive).pipe(Match.when(true, () => "i"), Match.orElse(() => "")));
|
|
114
|
+
const matchesGlob = (path, pattern, caseInsensitive = false) => globToRegExp(pattern, caseInsensitive).test(path);
|
|
115
|
+
const compileIgnoreRule = (pattern) => {
|
|
116
|
+
const negate = pattern.startsWith("!");
|
|
117
|
+
const glob = Match.value(negate).pipe(Match.when(true, () => pattern.slice(1)), Match.orElse(() => pattern));
|
|
118
|
+
const re = globToRegExp(glob, true);
|
|
119
|
+
const prefix = new RegExp(re.source.replace(/\$$/, ""), re.flags);
|
|
120
|
+
return {
|
|
121
|
+
negate,
|
|
122
|
+
matches: (candidate) => re.test(candidate),
|
|
123
|
+
matchesPrefix: (candidate) => prefix.test(candidate)
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
//#endregion
|
|
86
127
|
//#region src/Config.schema.ts
|
|
87
128
|
const ConfigDocumentSchema = S.Record(S.String, S.Unknown);
|
|
88
129
|
const ImportedModuleSchema = S.Struct({ default: S.optional(S.Unknown) });
|
|
@@ -122,6 +163,27 @@ var MergeCommand = class extends S.TaggedClass()("MergeCommand", {
|
|
|
122
163
|
overrides: S.Record(S.String, S.Unknown)
|
|
123
164
|
}) {};
|
|
124
165
|
var MergeResult = class extends S.TaggedClass()("MergeResult", { merged: S.Record(S.String, S.Unknown) }) {};
|
|
166
|
+
const ExtendsStepDocumentSchema = S.Struct({
|
|
167
|
+
path: S.String,
|
|
168
|
+
options: ConfigDocumentSchema
|
|
169
|
+
});
|
|
170
|
+
const ExtendsStepStateSchema = S.Struct({
|
|
171
|
+
visited: S.Array(S.String),
|
|
172
|
+
documents: S.Array(ExtendsStepDocumentSchema)
|
|
173
|
+
});
|
|
174
|
+
var ExtendsStepDone = class extends S.TaggedClass()("done", { options: ConfigDocumentSchema }) {};
|
|
175
|
+
var ExtendsStepRead = class extends S.TaggedClass()("read", {
|
|
176
|
+
path: S.String,
|
|
177
|
+
state: ExtendsStepStateSchema
|
|
178
|
+
}) {};
|
|
179
|
+
var ExtendsStepResolve = class extends S.TaggedClass()("resolve", {
|
|
180
|
+
specifier: S.String,
|
|
181
|
+
state: ExtendsStepStateSchema
|
|
182
|
+
}) {};
|
|
183
|
+
var ExtendsStepRefused = class extends S.TaggedClass()("refused", {
|
|
184
|
+
reason: S.Literals(["cycle", "non-string-extends"]),
|
|
185
|
+
file: S.String
|
|
186
|
+
}) {};
|
|
125
187
|
const survivorsPriorReport = S.optionalKey(S.String.pipe(S.annotate({ description: "The path of the prior mutation report a --survivors run admits against and re-tests the survivors of. Defaults to \"reports/mutation-report.json\" when unset. Deliberately has no default: a default would be injected into every resolved options object and written into every report, poisoning the KTD7 marker that identifies a report produced by a survivors run." })));
|
|
126
188
|
const extendsPropertySchema = S.optionalKey(S.String.pipe(S.annotate({ description: "Path to another stryker config file whose options merge underneath this one. Resolved relative to this file. A child scalar or array replaces the inherited value; a child object merges one level deep; a child key set to null deletes the inherited key. Inheritance chains are not rewritten, so an inherited relative path value still resolves against the working directory of the run that reads it." })));
|
|
127
189
|
const forkOptionsSchema = S.StructWithRest(S.Struct({
|
|
@@ -148,11 +210,108 @@ var StrykerError = class extends Schema.TaggedError()("StrykerError", {
|
|
|
148
210
|
cause: Schema.optional(Schema.Unknown)
|
|
149
211
|
}) {};
|
|
150
212
|
//#endregion
|
|
213
|
+
//#region ../stryker-js-plugin-runtime/dist/index.mjs
|
|
214
|
+
const CURRENT_VERSION = "00";
|
|
215
|
+
const serializedTraceState = (traceState) => Option.filter(Option.map(Option.fromUndefinedOr(traceState), (state) => state.serialize()), (serialized) => serialized.length > 0);
|
|
216
|
+
const traceStateField = (traceState) => Option.match(serializedTraceState(traceState), {
|
|
217
|
+
onNone: () => ({}),
|
|
218
|
+
onSome: (serialized) => ({ traceState: serialized })
|
|
219
|
+
});
|
|
220
|
+
const tracePartsOf = (context) => Option.map(Option.liftPredicate(context, (candidate) => api.trace.isSpanContextValid(candidate)), (valid) => ({
|
|
221
|
+
version: CURRENT_VERSION,
|
|
222
|
+
traceId: valid.traceId,
|
|
223
|
+
spanId: valid.spanId,
|
|
224
|
+
traceFlags: valid.traceFlags,
|
|
225
|
+
...traceStateField(valid.traceState)
|
|
226
|
+
}));
|
|
227
|
+
const partsOfSpan = (span) => Option.flatMap(Option.map(Option.fromUndefinedOr(span), (present) => present.spanContext()), tracePartsOf);
|
|
228
|
+
const remotePartsFromHeaders = (headers) => Option.map(Option.flatMap(Headers.get(headers, TRACEPARENT_HEADER), parseTraceparent), (parts) => ({
|
|
229
|
+
...parts,
|
|
230
|
+
traceState: Option.getOrUndefined(Headers.get(headers, TRACESTATE_HEADER))
|
|
231
|
+
}));
|
|
232
|
+
const traceHeaders = (parts) => {
|
|
233
|
+
const traceparent = Headers.set(Headers.empty, TRACEPARENT_HEADER, formatTraceparent(parts));
|
|
234
|
+
if (parts.traceState === void 0) return traceparent;
|
|
235
|
+
return Headers.set(traceparent, TRACESTATE_HEADER, parts.traceState);
|
|
236
|
+
};
|
|
237
|
+
const inject = (request, parts) => ({
|
|
238
|
+
...request,
|
|
239
|
+
headers: Headers.merge(request.headers, traceHeaders(parts))
|
|
240
|
+
});
|
|
241
|
+
const hostParts = Effect$1.map(Effect$1.context(), (context) => Option.orElse(Context.get(context, TraceContextReference), () => partsOfSpan(api.trace.getSpan(api.context.active()))));
|
|
242
|
+
const clientMiddleware = (options) => hostParts.pipe(Effect$1.flatMap((parts) => Option.match(parts, {
|
|
243
|
+
onNone: () => options.next(options.request),
|
|
244
|
+
onSome: (present) => options.next(inject(options.request, present))
|
|
245
|
+
})));
|
|
246
|
+
const layerTraceContextClient = RpcMiddleware.layerClient(TraceContextMiddleware, clientMiddleware);
|
|
247
|
+
const SAMPLED_FLAG = 1;
|
|
248
|
+
const traceStateOption = (traceState) => Option.match(Option.fromUndefinedOr(traceState), {
|
|
249
|
+
onNone: () => ({}),
|
|
250
|
+
onSome: (state) => ({ traceState: state })
|
|
251
|
+
});
|
|
252
|
+
const sampledFlagOf = (sampled) => {
|
|
253
|
+
if (sampled) return SAMPLED_FLAG;
|
|
254
|
+
return 0;
|
|
255
|
+
};
|
|
256
|
+
const externalSpanOf = (parts) => Tracer.externalSpan({
|
|
257
|
+
traceId: parts.traceId,
|
|
258
|
+
spanId: parts.spanId,
|
|
259
|
+
sampled: (parts.traceFlags & SAMPLED_FLAG) === SAMPLED_FLAG,
|
|
260
|
+
...traceStateOption(parts.traceState)
|
|
261
|
+
});
|
|
262
|
+
const partsOfEffectSpan = (span) => ({
|
|
263
|
+
version: CURRENT_VERSION,
|
|
264
|
+
traceId: span.traceId,
|
|
265
|
+
spanId: span.spanId,
|
|
266
|
+
traceFlags: sampledFlagOf(span.sampled)
|
|
267
|
+
});
|
|
268
|
+
const serverMiddleware = (effect, options) => {
|
|
269
|
+
const remote = remotePartsFromHeaders(options.headers);
|
|
270
|
+
const attributes = { "rpc.method": options.rpc._tag };
|
|
271
|
+
return Option.match(Option.map(remote, externalSpanOf), {
|
|
272
|
+
onNone: () => Effect$1.useSpan(`rpc.${options.rpc._tag}`, { attributes }, () => effect),
|
|
273
|
+
onSome: (parent) => Effect$1.useSpan(`rpc.${options.rpc._tag}`, {
|
|
274
|
+
attributes,
|
|
275
|
+
parent
|
|
276
|
+
}, () => effect)
|
|
277
|
+
}).pipe(Effect$1.provideService(PropagatedTrace, remote));
|
|
278
|
+
};
|
|
279
|
+
Layer.succeed(TraceContextMiddleware, serverMiddleware);
|
|
280
|
+
let wire;
|
|
281
|
+
const workerOptionsWire = () => wire ??= S.fromJsonString(S.toCodecJson(StrykerOptionsSchema));
|
|
282
|
+
const encodeWorkerOptions = (options) => S.encodeEffect(workerOptionsWire())(options).pipe(Effect$1.orDie);
|
|
283
|
+
const decodeWorkerOptions = (raw) => S.decodeUnknownEffect(workerOptionsWire())(raw);
|
|
284
|
+
Effect$1.gen(function* () {
|
|
285
|
+
const workerDir = yield* Config.string("STRYKER_WORKER_DIR");
|
|
286
|
+
const fs = yield* FileSystem.FileSystem;
|
|
287
|
+
const path = yield* Path.Path;
|
|
288
|
+
const raw = yield* fs.readFileString(path.join(workerDir, "options.json"));
|
|
289
|
+
return yield* decodeWorkerOptions(raw);
|
|
290
|
+
});
|
|
291
|
+
const WORKER_SERVICE_NAME = "stryker-js-plugin-worker";
|
|
292
|
+
const DEFAULT_ENDPOINT = "http://127.0.0.1:4318";
|
|
293
|
+
const TRACES_SUFFIX = "/v1/traces";
|
|
294
|
+
const tracesUrl = (endpoint) => {
|
|
295
|
+
const trimmed = endpoint.replace(/\/+$/u, "");
|
|
296
|
+
if (trimmed.endsWith(TRACES_SUFFIX)) return trimmed;
|
|
297
|
+
return `${trimmed}${TRACES_SUFFIX}`;
|
|
298
|
+
};
|
|
299
|
+
const otlpLayer = (serviceName, endpoint) => NodeSdk.layer(() => ({
|
|
300
|
+
resource: { serviceName },
|
|
301
|
+
spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter({ url: tracesUrl(endpoint) }))
|
|
302
|
+
}));
|
|
303
|
+
Layer.unwrap(Effect$1.gen(function* () {
|
|
304
|
+
if (!(yield* Config.boolean("OTEL_ENABLED").pipe(Config.withDefault(false)))) return Layer.empty;
|
|
305
|
+
const serviceName = yield* Config.string("OTEL_SERVICE_NAME").pipe(Config.withDefault(WORKER_SERVICE_NAME));
|
|
306
|
+
const endpoint = yield* Config.string("OTEL_EXPORTER_OTLP_ENDPOINT").pipe(Config.withDefault(DEFAULT_ENDPOINT));
|
|
307
|
+
return otlpLayer(serviceName, endpoint);
|
|
308
|
+
}));
|
|
309
|
+
//#endregion
|
|
151
310
|
//#region src/VmRunner.ts
|
|
152
311
|
var VmRunner = class extends Context.Service()("@systemfsoftware/stryker-js/VmRunner") {};
|
|
153
312
|
const vmRunnerName = "vm";
|
|
154
313
|
const ALL_TESTS_NAME = "All tests";
|
|
155
|
-
const isVmRunner = (name) => name.toLowerCase() === "vm";
|
|
314
|
+
const isVmRunner = (name) => typeof name === "string" && name.toLowerCase() === "vm";
|
|
156
315
|
const vmRunnerCapabilities = { reloadEnvironment: true };
|
|
157
316
|
const FALLBACK_FILE_NAME = "stryker-vm-tests.js";
|
|
158
317
|
const errorText = (error) => Match.value(error).pipe(Match.when(Match.instanceOf(Error), (failure) => failure.message), Match.orElse((value) => String(value)));
|
|
@@ -161,15 +320,15 @@ const compileFailure = (file, cause) => TestRunnerFailed.make({
|
|
|
161
320
|
phase: "init",
|
|
162
321
|
cause: `Could not compile "${file}" for the in-memory runner: ${errorText(cause)}`
|
|
163
322
|
});
|
|
164
|
-
const compileTests = (platform, fs, testFiles) => Effect.gen(function* () {
|
|
165
|
-
const sources = yield* Effect.forEach(testFiles, (file) => fs.readFileString(file).pipe(Effect.mapError((cause) => compileFailure(file, cause)), Effect.flatMap((source) => Effect.try({
|
|
323
|
+
const compileTests = (platform, fs, testFiles) => Effect$1.gen(function* () {
|
|
324
|
+
const sources = yield* Effect$1.forEach(testFiles, (file) => fs.readFileString(file).pipe(Effect$1.mapError((cause) => compileFailure(file, cause)), Effect$1.flatMap((source) => Effect$1.try({
|
|
166
325
|
try: () => platform.module.stripTypeScriptTypes(source, { mode: "transform" }),
|
|
167
326
|
catch: (cause) => compileFailure(file, cause)
|
|
168
327
|
}))));
|
|
169
328
|
const fileName = Option.getOrElse(Option.fromUndefinedOr(testFiles.at(0)), () => FALLBACK_FILE_NAME);
|
|
170
329
|
return {
|
|
171
330
|
fileName,
|
|
172
|
-
script: yield* Effect.try({
|
|
331
|
+
script: yield* Effect$1.try({
|
|
173
332
|
try: () => new platform.vm.Script(sources.join("\n;\n"), { filename: fileName }),
|
|
174
333
|
catch: (cause) => compileFailure(fileName, cause)
|
|
175
334
|
})
|
|
@@ -209,7 +368,7 @@ const resultFromRun = (failureMessage, timeSpentMs) => Match.value(failureMessag
|
|
|
209
368
|
timeSpentMs
|
|
210
369
|
}]
|
|
211
370
|
})));
|
|
212
|
-
const runInFreshContext = (platform, compiled, activeMutantId) => Effect.sync(() => {
|
|
371
|
+
const runInFreshContext = (platform, compiled, activeMutantId) => Effect$1.sync(() => {
|
|
213
372
|
const context = platform.vm.createContext(sandboxFor(platform, compiled.fileName, activeMutantId));
|
|
214
373
|
try {
|
|
215
374
|
compiled.script.runInContext(context);
|
|
@@ -218,30 +377,30 @@ const runInFreshContext = (platform, compiled, activeMutantId) => Effect.sync(()
|
|
|
218
377
|
return errorText(cause);
|
|
219
378
|
}
|
|
220
379
|
});
|
|
221
|
-
const runOnce = (platform, compiled, activeMutantId) => Effect.gen(function* () {
|
|
380
|
+
const runOnce = (platform, compiled, activeMutantId) => Effect$1.gen(function* () {
|
|
222
381
|
const startedAt = yield* Clock.currentTimeMillis;
|
|
223
382
|
const failure = yield* runInFreshContext(platform, compiled, activeMutantId);
|
|
224
383
|
const elapsedMs = (yield* Clock.currentTimeMillis) - startedAt;
|
|
225
384
|
return resultFromRun(failure, elapsedMs);
|
|
226
385
|
});
|
|
227
|
-
const vmTestRunner = (config) => Effect.gen(function* () {
|
|
386
|
+
const vmTestRunner = (config) => Effect$1.gen(function* () {
|
|
228
387
|
const platform = yield* VmRunner;
|
|
229
388
|
const fs = yield* FileSystem.FileSystem;
|
|
230
389
|
const compiled = yield* Ref.make(Option.none());
|
|
231
|
-
const compiledTests = (testFiles) => Effect.gen(function* () {
|
|
390
|
+
const compiledTests = (testFiles) => Effect$1.gen(function* () {
|
|
232
391
|
const cached = yield* Ref.get(compiled);
|
|
233
392
|
if (Option.isSome(cached)) return cached.value;
|
|
234
393
|
const tests = yield* compileTests(platform, fs, testFiles);
|
|
235
394
|
yield* Ref.set(compiled, Option.some(tests));
|
|
236
395
|
return tests;
|
|
237
396
|
});
|
|
238
|
-
const run = (testFiles, activeMutantId) => compiledTests(testFiles).pipe(Effect.flatMap((tests) => runOnce(platform, tests, activeMutantId)));
|
|
397
|
+
const run = (testFiles, activeMutantId) => compiledTests(testFiles).pipe(Effect$1.flatMap((tests) => runOnce(platform, tests, activeMutantId)));
|
|
239
398
|
const testFilesOf = (override) => Match.value(config.testFiles).pipe(Match.when((files) => files.length > 0, (files) => files), Match.orElse(() => override ?? []));
|
|
240
399
|
return {
|
|
241
|
-
capabilities: Effect.succeed(vmRunnerCapabilities),
|
|
242
|
-
init: Effect.void,
|
|
400
|
+
capabilities: Effect$1.succeed(vmRunnerCapabilities),
|
|
401
|
+
init: Effect$1.void,
|
|
243
402
|
dryRun: (options) => run(testFilesOf(options.testFiles), void 0),
|
|
244
|
-
mutantRun: (options) => run(config.testFiles, options.activeMutant.id).pipe(Effect.map((result) => toMutantRunResult(result, true)), Effect.catchTag("TestRunnerFailed", (failure) => Effect.succeed(toMutantRunResult({
|
|
403
|
+
mutantRun: (options) => run(config.testFiles, options.activeMutant.id).pipe(Effect$1.map((result) => toMutantRunResult(result, true)), Effect$1.catchTag("TestRunnerFailed", (failure) => Effect$1.succeed(toMutantRunResult({
|
|
245
404
|
status: "error",
|
|
246
405
|
errorMessage: failure.cause
|
|
247
406
|
}, true))))
|
|
@@ -289,7 +448,7 @@ var WorkerBootTimeoutError = class extends Schema.TaggedError()("WorkerBootTimeo
|
|
|
289
448
|
//#endregion
|
|
290
449
|
//#region src/Worker.ts
|
|
291
450
|
var IdGenerator = class extends Context.Service()("@systemfsoftware/stryker-js/Worker/IdGenerator") {};
|
|
292
|
-
const makeIdGenerator = Effect.gen(function* () {
|
|
451
|
+
const makeIdGenerator = Effect$1.gen(function* () {
|
|
293
452
|
const ref = yield* Ref.make(0);
|
|
294
453
|
return { next: Ref.getAndUpdate(ref, (n) => n + 1) };
|
|
295
454
|
});
|
|
@@ -329,10 +488,10 @@ const splitConcurrency = (total, checkerCount) => {
|
|
|
329
488
|
checkers: 0
|
|
330
489
|
};
|
|
331
490
|
};
|
|
332
|
-
const announcePercentage = (resolved) => Match.value(resolved.isPercentage).pipe(Match.when(true, () => Effect.logDebug(`Computed concurrency ${resolved.total} from "${resolved.option}" based on ${resolved.availableParallelism} available parallelism.`)), Match.orElse(() => Effect.void));
|
|
333
|
-
const announceProcesses = (split) => Match.value(split.checkers > 0).pipe(Match.when(true, () => Effect.logInfo(`Creating ${split.checkers} checker process(es) and ${split.testRunners} test runner process(es).`)), Match.orElse(() => Effect.logInfo(`Creating ${split.testRunners} test runner process(es).`)));
|
|
334
|
-
const makeConcurrency = (options) => Effect.gen(function* () {
|
|
335
|
-
const availableParallelism = yield* Effect.sync(getAvailableParallelism);
|
|
491
|
+
const announcePercentage = (resolved) => Match.value(resolved.isPercentage).pipe(Match.when(true, () => Effect$1.logDebug(`Computed concurrency ${resolved.total} from "${resolved.option}" based on ${resolved.availableParallelism} available parallelism.`)), Match.orElse(() => Effect$1.void));
|
|
492
|
+
const announceProcesses = (split) => Match.value(split.checkers > 0).pipe(Match.when(true, () => Effect$1.logInfo(`Creating ${split.checkers} checker process(es) and ${split.testRunners} test runner process(es).`)), Match.orElse(() => Effect$1.logInfo(`Creating ${split.testRunners} test runner process(es).`)));
|
|
493
|
+
const makeConcurrency = (options) => Effect$1.gen(function* () {
|
|
494
|
+
const availableParallelism = yield* Effect$1.sync(getAvailableParallelism);
|
|
336
495
|
const { total, isPercentage } = computeTotalConcurrencyDetails(options.concurrency, availableParallelism);
|
|
337
496
|
yield* announcePercentage({
|
|
338
497
|
option: options.concurrency,
|
|
@@ -361,7 +520,7 @@ const classifyWorkerExit = (pid, exitCode) => Match.value(exitCode).pipe(Match.w
|
|
|
361
520
|
//#region src/WorkerLauncher.ts
|
|
362
521
|
const connectRetry = Schedule.max([Schedule.spaced(50), Schedule.recurs(100)]);
|
|
363
522
|
var WorkerLauncher = class extends Context.Service()("@systemfsoftware/stryker-js/WorkerLauncher") {};
|
|
364
|
-
const makeWorkerClient = (params) => Effect.gen(function* () {
|
|
523
|
+
const makeWorkerClient = (params) => Effect$1.gen(function* () {
|
|
365
524
|
const worker = yield* (yield* WorkerLauncher).spawn({
|
|
366
525
|
entrypoint: params.entrypoint,
|
|
367
526
|
workingDirectory: params.workingDirectory,
|
|
@@ -370,25 +529,24 @@ const makeWorkerClient = (params) => Effect.gen(function* () {
|
|
|
370
529
|
tempDirPrefix: params.tempDirPrefix,
|
|
371
530
|
env: params.env
|
|
372
531
|
});
|
|
373
|
-
const protocol = yield* Layer.build(worker.clientLayer).pipe(Effect.retry(connectRetry), Effect.raceFirst(worker.exited), Effect.catchTag("SocketError", () => Effect.fail(WorkerBootTimeoutError.make({ pid: worker.pid }))));
|
|
374
|
-
return yield* RpcClient.make(params.rpcs).pipe(Effect.provideContext(protocol), Effect.provide(layerTraceContextClient));
|
|
532
|
+
const protocol = yield* Layer.build(worker.clientLayer).pipe(Effect$1.retry(connectRetry), Effect$1.raceFirst(worker.exited), Effect$1.catchTag("SocketError", () => Effect$1.fail(WorkerBootTimeoutError.make({ pid: worker.pid }))));
|
|
533
|
+
return yield* RpcClient.make(params.rpcs).pipe(Effect$1.provideContext(protocol), Effect$1.provide(layerTraceContextClient));
|
|
375
534
|
});
|
|
376
535
|
//#endregion
|
|
377
536
|
//#region src/platform/node.ts
|
|
378
|
-
const restrictToOwnerOrWarn = (fs, file) => fs.chmod(file, 384).pipe(Effect.catchTag("PlatformError", (cause) => Effect.logWarning(`Could not restrict "${file}" to its owner; the worker directory's own mode still protects it.`).pipe(Effect.annotateLogs("cause", cause))));
|
|
379
|
-
const restrictSocketToOwnerOrWarn = (fs, socketPath) => Match.value(process.platform).pipe(Match.when("win32", () => Effect.void), Match.orElse(() => restrictToOwnerOrWarn(fs, socketPath)));
|
|
537
|
+
const restrictToOwnerOrWarn = (fs, file) => fs.chmod(file, 384).pipe(Effect$1.catchTag("PlatformError", (cause) => Effect$1.logWarning(`Could not restrict "${file}" to its owner; the worker directory's own mode still protects it.`).pipe(Effect$1.annotateLogs("cause", cause))));
|
|
380
538
|
/**
|
|
381
539
|
* The Node worker launcher: spawn a worker child with this runtime's
|
|
382
540
|
* executable, host the RPC server's address as a `net` `path` endpoint (a
|
|
383
541
|
* socket file in the worker directory on POSIX, a same-user named pipe on
|
|
384
542
|
* Windows), and connect the NDJSON protocol client over `NodeSocket`.
|
|
385
543
|
*/
|
|
386
|
-
const nodeWorkerLauncherLayer = Layer.effect(WorkerLauncher, Effect.gen(function* () {
|
|
544
|
+
const nodeWorkerLauncherLayer = Layer.effect(WorkerLauncher, Effect$1.gen(function* () {
|
|
387
545
|
const crypto = yield* Crypto.Crypto;
|
|
388
546
|
const fs = yield* FileSystem.FileSystem;
|
|
389
547
|
const path = yield* Path.Path;
|
|
390
548
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
391
|
-
return { spawn: (params) => Effect.gen(function* () {
|
|
549
|
+
return { spawn: (params) => Effect$1.gen(function* () {
|
|
392
550
|
const workerDir = yield* fs.makeTempDirectoryScoped({ prefix: params.tempDirPrefix });
|
|
393
551
|
const workerId = yield* crypto.randomUUIDv4;
|
|
394
552
|
const socketPath = Match.value(process.platform).pipe(Match.when("win32", () => `\\\\.\\pipe\\stryker-worker-${workerId}`), Match.orElse(() => path.join(workerDir, "worker.sock")));
|
|
@@ -405,15 +563,15 @@ const nodeWorkerLauncherLayer = Layer.effect(WorkerLauncher, Effect.gen(function
|
|
|
405
563
|
...params.env
|
|
406
564
|
},
|
|
407
565
|
stderr: "inherit"
|
|
408
|
-
}).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner));
|
|
409
|
-
const clientLayer = RpcClient.layerProtocolSocket({ retryTransientErrors: true }).pipe(Layer.provide(NodeSocket.layerNet({ path: socketPath })), Layer.provide(RpcSerialization.layerNdjson)
|
|
410
|
-
const exited = handle.exitCode.pipe(Effect.orDie, Effect.flatMap((exitCode) => Effect.fail(classifyWorkerExit(Number(handle.pid), exitCode))));
|
|
566
|
+
}).pipe(Effect$1.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner));
|
|
567
|
+
const clientLayer = RpcClient.layerProtocolSocket({ retryTransientErrors: true }).pipe(Layer.provide(NodeSocket.layerNet({ path: socketPath })), Layer.provide(RpcSerialization.layerNdjson));
|
|
568
|
+
const exited = handle.exitCode.pipe(Effect$1.orDie, Effect$1.flatMap((exitCode) => Effect$1.fail(classifyWorkerExit(Number(handle.pid), exitCode))));
|
|
411
569
|
return {
|
|
412
570
|
pid: Number(handle.pid),
|
|
413
571
|
clientLayer,
|
|
414
572
|
exited
|
|
415
573
|
};
|
|
416
|
-
}).pipe(Effect.catchIf(S.is(ChildProcessCrashedError), (error) => Effect.fail(error), () => Effect.fail(ChildProcessCrashedError.make({
|
|
574
|
+
}).pipe(Effect$1.catchIf(S.is(ChildProcessCrashedError), (error) => Effect$1.fail(error), () => Effect$1.fail(ChildProcessCrashedError.make({
|
|
417
575
|
pid: 0,
|
|
418
576
|
exit: {
|
|
419
577
|
_tag: "Code",
|
|
@@ -435,7 +593,7 @@ const nodePlatformLayer = Layer.mergeAll(nodeWorkerLauncherLayer.pipe(Layer.prov
|
|
|
435
593
|
* `Effect.promise` so a platform without `node:vm` fails as a defect — the
|
|
436
594
|
* runner has no fallback to fall back to.
|
|
437
595
|
*/
|
|
438
|
-
const nodeVmPlatformLayer = Layer.effect(VmRunner, Effect.sync(() => ({
|
|
596
|
+
const nodeVmPlatformLayer = Layer.effect(VmRunner, Effect$1.sync(() => ({
|
|
439
597
|
module: process.getBuiltinModule("node:module"),
|
|
440
598
|
vm: process.getBuiltinModule("node:vm")
|
|
441
599
|
})));
|
|
@@ -476,14 +634,15 @@ const toRunnerFailure = (runnerName, phase) => (error) => Match.value(error).pip
|
|
|
476
634
|
* `Pool.invalidate` and the crash-retry combinator can only act on a failure they
|
|
477
635
|
* can see.
|
|
478
636
|
*/
|
|
479
|
-
const makeChildProcessTestRunner = (params) => Effect.gen(function* () {
|
|
480
|
-
const runnerName = params.options.testRunner;
|
|
637
|
+
const makeChildProcessTestRunner = (params) => Effect$1.gen(function* () {
|
|
638
|
+
const runnerName = Match.value(params.options.testRunner).pipe(Match.when(isCustomTestRunner, (runner) => runner.plugin), Match.orElse((name) => name));
|
|
639
|
+
const execArgv = Match.value(params.options.testRunner).pipe(Match.when(isCustomTestRunner, (runner) => Match.value(runner.nodeArgs).pipe(Match.when(Match.undefined, () => params.options.testRunnerNodeArgs), Match.orElse((args) => args))), Match.orElse(() => params.options.testRunnerNodeArgs));
|
|
481
640
|
const optionsJson = yield* encodeWorkerOptions(params.options);
|
|
482
641
|
const client = yield* makeWorkerClient({
|
|
483
642
|
rpcs: TestRunnerRpcs,
|
|
484
643
|
entrypoint: params.workerEntrypoint,
|
|
485
644
|
workingDirectory: params.sandboxWorkingDirectory,
|
|
486
|
-
execArgv: [...
|
|
645
|
+
execArgv: [...execArgv],
|
|
487
646
|
optionsJson,
|
|
488
647
|
tempDirPrefix: "stryker-test-runner-",
|
|
489
648
|
env: {
|
|
@@ -491,12 +650,12 @@ const makeChildProcessTestRunner = (params) => Effect.gen(function* () {
|
|
|
491
650
|
VITEST: "1",
|
|
492
651
|
STRYKER_SANDBOX_DIR: params.sandboxWorkingDirectory
|
|
493
652
|
}
|
|
494
|
-
}).pipe(Effect.mapError(toRunnerBootFailure(runnerName)));
|
|
653
|
+
}).pipe(Effect$1.mapError(toRunnerBootFailure(runnerName)));
|
|
495
654
|
return {
|
|
496
|
-
capabilities: client.capabilities().pipe(Effect.mapError(toRunnerFailure(runnerName, "capabilities"))),
|
|
497
|
-
init: Effect.void,
|
|
498
|
-
dryRun: (options) => client.dryRun({ options }).pipe(Effect.mapError(toRunnerFailure(runnerName, "dryRun"))),
|
|
499
|
-
mutantRun: (options) => client.mutantRun({ options }).pipe(Effect.mapError(toRunnerFailure(runnerName, "mutantRun")))
|
|
655
|
+
capabilities: client.capabilities().pipe(Effect$1.mapError(toRunnerFailure(runnerName, "capabilities"))),
|
|
656
|
+
init: Effect$1.void,
|
|
657
|
+
dryRun: (options) => client.dryRun({ options }).pipe(Effect$1.mapError(toRunnerFailure(runnerName, "dryRun"))),
|
|
658
|
+
mutantRun: (options) => client.mutantRun({ options }).pipe(Effect$1.mapError(toRunnerFailure(runnerName, "mutantRun")))
|
|
500
659
|
};
|
|
501
660
|
});
|
|
502
661
|
/**
|
|
@@ -509,16 +668,16 @@ const makeChildProcessTestRunner = (params) => Effect.gen(function* () {
|
|
|
509
668
|
const withTimeout = (inner) => ({
|
|
510
669
|
...inner,
|
|
511
670
|
dryRun: (options) => {
|
|
512
|
-
const policy = Effect.timeoutOrElse({
|
|
671
|
+
const policy = Effect$1.timeoutOrElse({
|
|
513
672
|
duration: Duration.millis(options.timeout),
|
|
514
|
-
orElse: () => Effect.succeed({ status: "timeout" })
|
|
673
|
+
orElse: () => Effect$1.succeed({ status: "timeout" })
|
|
515
674
|
});
|
|
516
675
|
return inner.dryRun(options).pipe(policy);
|
|
517
676
|
},
|
|
518
677
|
mutantRun: (options) => {
|
|
519
|
-
const policy = Effect.timeoutOrElse({
|
|
678
|
+
const policy = Effect$1.timeoutOrElse({
|
|
520
679
|
duration: Duration.millis(options.timeout),
|
|
521
|
-
orElse: () => Effect.succeed({ status: "timeout" })
|
|
680
|
+
orElse: () => Effect$1.succeed({ status: "timeout" })
|
|
522
681
|
});
|
|
523
682
|
return inner.mutantRun(options).pipe(policy);
|
|
524
683
|
}
|
|
@@ -534,7 +693,7 @@ const withTimeout = (inner) => ({
|
|
|
534
693
|
* keeps the chain that led there.
|
|
535
694
|
*/
|
|
536
695
|
const withRetry = (inner) => {
|
|
537
|
-
const attempt = (run, onExhausted) => run.pipe(Effect.tapError((error) => Match.value(error).pipe(Match.when(S.is(OutOfMemoryError), (outOfMemory) => Effect.logInfo(`Test runner process [${outOfMemory.pid}] ran out of memory. That usually means the tests leak memory. Stryker restarts the process and carries on, but the run is slower for it.`)), Match.orElse(() => Effect.void))), Effect.retry({ times: 2 }), Effect.catchCause((cause) => Effect.succeed(onExhausted(`Test runner crashed. Tried 2 times to restart it without any luck. ${Cause.pretty(cause)}`))));
|
|
696
|
+
const attempt = (run, onExhausted) => run.pipe(Effect$1.tapError((error) => Match.value(error).pipe(Match.when(S.is(OutOfMemoryError), (outOfMemory) => Effect$1.logInfo(`Test runner process [${outOfMemory.pid}] ran out of memory. That usually means the tests leak memory. Stryker restarts the process and carries on, but the run is slower for it.`)), Match.orElse(() => Effect$1.void))), Effect$1.retry({ times: 2 }), Effect$1.catchCause((cause) => Effect$1.succeed(onExhausted(`Test runner crashed. Tried 2 times to restart it without any luck. ${Cause.pretty(cause)}`))));
|
|
538
697
|
return {
|
|
539
698
|
...inner,
|
|
540
699
|
dryRun: (options) => attempt(inner.dryRun(options), (errorMessage) => ({
|
|
@@ -556,14 +715,14 @@ const withRetry = (inner) => {
|
|
|
556
715
|
* `Pool.invalidate`, and a runner that restarts itself behind the pool's back
|
|
557
716
|
* would create two owners of one process.
|
|
558
717
|
*/
|
|
559
|
-
const withMaxReuse = (options, retire) => (inner) => Effect.gen(function* () {
|
|
718
|
+
const withMaxReuse = (options, retire) => (inner) => Effect$1.gen(function* () {
|
|
560
719
|
const restartAfter = options.maxTestRunnerReuse;
|
|
561
720
|
if (restartAfter <= 0) return inner;
|
|
562
721
|
const runs = yield* Ref.make(0);
|
|
563
722
|
return {
|
|
564
723
|
...inner,
|
|
565
724
|
mutantRun: (runOptions) => {
|
|
566
|
-
const policy = (self) => Effect.gen(function* () {
|
|
725
|
+
const policy = (self) => Effect$1.gen(function* () {
|
|
567
726
|
if ((yield* Ref.updateAndGet(runs, (n) => n + 1)) > restartAfter) {
|
|
568
727
|
yield* retire;
|
|
569
728
|
yield* Ref.set(runs, 1);
|
|
@@ -590,15 +749,15 @@ const environmentPlan = (request) => ({
|
|
|
590
749
|
* mutated, so a caller that reuses its own options does not read a value it
|
|
591
750
|
* never wrote.
|
|
592
751
|
*/
|
|
593
|
-
const withEnvironmentReload = (retire) => (inner) => Effect.gen(function* () {
|
|
752
|
+
const withEnvironmentReload = (retire) => (inner) => Effect$1.gen(function* () {
|
|
594
753
|
const state = yield* Ref.make("pristine");
|
|
595
754
|
return {
|
|
596
755
|
...inner,
|
|
597
756
|
dryRun: (options) => {
|
|
598
|
-
const policy = (self) => Ref.set(state, "loaded").pipe(Effect.andThen(self));
|
|
757
|
+
const policy = (self) => Ref.set(state, "loaded").pipe(Effect$1.andThen(self));
|
|
599
758
|
return policy(inner.dryRun(options));
|
|
600
759
|
},
|
|
601
|
-
mutantRun: (options) => Effect.gen(function* () {
|
|
760
|
+
mutantRun: (options) => Effect$1.gen(function* () {
|
|
602
761
|
const current = yield* Ref.get(state);
|
|
603
762
|
const canReload = (yield* inner.capabilities).reloadEnvironment;
|
|
604
763
|
const plan = environmentPlan({
|
|
@@ -607,7 +766,7 @@ const withEnvironmentReload = (retire) => (inner) => Effect.gen(function* () {
|
|
|
607
766
|
canReload
|
|
608
767
|
});
|
|
609
768
|
if (plan.retire) yield* retire;
|
|
610
|
-
const policy = (self) => Effect.gen(function* () {
|
|
769
|
+
const policy = (self) => Effect$1.gen(function* () {
|
|
611
770
|
const result = yield* self;
|
|
612
771
|
yield* Ref.set(state, plan.nextState);
|
|
613
772
|
return result;
|
|
@@ -619,10 +778,8 @@ const withEnvironmentReload = (retire) => (inner) => Effect.gen(function* () {
|
|
|
619
778
|
})
|
|
620
779
|
};
|
|
621
780
|
});
|
|
622
|
-
/** "command" — the name this runner answers to in the options. */
|
|
623
|
-
const commandRunnerName = "command";
|
|
624
781
|
/** Whether a configured runner name selects this runner. */
|
|
625
|
-
const isCommandRunner = (name) => name.toLowerCase() ===
|
|
782
|
+
const isCommandRunner = (name) => typeof name === "string" && name.toLowerCase() === "command";
|
|
626
783
|
/** Each call is a fresh process, so the environment is always reloadable. */
|
|
627
784
|
const commandRunnerCapabilities = { reloadEnvironment: true };
|
|
628
785
|
/** One test result standing for the whole command, decided by its exit code. */
|
|
@@ -661,7 +818,7 @@ const spawnResult = (outcome, elapsed) => Match.value(outcome).pipe(Match.tag("F
|
|
|
661
818
|
* `activeMutantId` is threaded through the environment rather than the command
|
|
662
819
|
* line, which is how the instrumented code finds the mutant to activate.
|
|
663
820
|
*/
|
|
664
|
-
const runCommand = (config, activeMutantId) => Effect.gen(function* () {
|
|
821
|
+
const runCommand = (config, activeMutantId) => Effect$1.gen(function* () {
|
|
665
822
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
666
823
|
const startedAt = yield* Clock.currentTimeMillis;
|
|
667
824
|
const command = ChildProcess.make(config.options.commandRunner.command, {
|
|
@@ -669,20 +826,20 @@ const runCommand = (config, activeMutantId) => Effect.gen(function* () {
|
|
|
669
826
|
cwd: config.workingDir,
|
|
670
827
|
...mutantActivation(activeMutantId)
|
|
671
828
|
});
|
|
672
|
-
const outcome = yield* Effect.scoped(Effect.gen(function* () {
|
|
829
|
+
const outcome = yield* Effect$1.scoped(Effect$1.gen(function* () {
|
|
673
830
|
const handle = yield* spawner.spawn(command);
|
|
674
831
|
return {
|
|
675
832
|
output: yield* Stream.mkString(Stream.decodeText(handle.all)),
|
|
676
833
|
exitCode: yield* handle.exitCode
|
|
677
834
|
};
|
|
678
|
-
})).pipe(Effect.exit);
|
|
835
|
+
})).pipe(Effect$1.exit);
|
|
679
836
|
const elapsed = (yield* Clock.currentTimeMillis) - startedAt;
|
|
680
837
|
return spawnResult(outcome, elapsed);
|
|
681
838
|
});
|
|
682
839
|
/** Run the tests with no mutant active, to establish the baseline. */
|
|
683
840
|
const commandRunnerDryRun = (config) => runCommand(config, void 0);
|
|
684
841
|
/** Run the tests with one mutant active. */
|
|
685
|
-
const commandRunnerMutantRun = (config, { activeMutant }) => runCommand(config, activeMutant.id).pipe(Effect.map((result) => toMutantRunResult(result, true)));
|
|
842
|
+
const commandRunnerMutantRun = (config, { activeMutant }) => runCommand(config, activeMutant.id).pipe(Effect$1.map((result) => toMutantRunResult(result, true)));
|
|
686
843
|
/**
|
|
687
844
|
* The command runner, wrapped in the two adjustments that apply to it.
|
|
688
845
|
*
|
|
@@ -700,17 +857,17 @@ const commandRunner = (context, spawner) => {
|
|
|
700
857
|
workingDir: context.sandboxWorkingDirectory,
|
|
701
858
|
options: context.options
|
|
702
859
|
};
|
|
703
|
-
const provided = Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner);
|
|
860
|
+
const provided = Effect$1.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner);
|
|
704
861
|
const base = {
|
|
705
|
-
capabilities: Effect.succeed(commandRunnerCapabilities),
|
|
706
|
-
init: Effect.void,
|
|
862
|
+
capabilities: Effect$1.succeed(commandRunnerCapabilities),
|
|
863
|
+
init: Effect$1.void,
|
|
707
864
|
dryRun: () => commandRunnerDryRun(config).pipe(provided),
|
|
708
865
|
mutantRun: (options) => commandRunnerMutantRun(config, options).pipe(provided)
|
|
709
866
|
};
|
|
710
867
|
return withRetry(withTimeout(base));
|
|
711
868
|
};
|
|
712
|
-
const inProcessRunner = (context) => Match.value(context.options.testRunner).pipe(Match.when(isCommandRunner, () => Option.some(ChildProcessSpawner.ChildProcessSpawner.pipe(Effect.map((spawner) => commandRunner(context, spawner))))), Match.when(isVmRunner, () => Option.some(vmTestRunner({ testFiles: context.testFiles }).pipe(Effect.provide(nodeVmPlatformLayer)))), Match.orElse(() => Option.none()));
|
|
713
|
-
const buildTestRunner = (context, childProcessRunner) => Effect.gen(function* () {
|
|
869
|
+
const inProcessRunner = (context) => Match.value(context.options.testRunner).pipe(Match.when(isCommandRunner, () => Option.some(ChildProcessSpawner.ChildProcessSpawner.pipe(Effect$1.map((spawner) => commandRunner(context, spawner))))), Match.when(isVmRunner, () => Option.some(vmTestRunner({ testFiles: context.testFiles }).pipe(Effect$1.provide(nodeVmPlatformLayer)))), Match.orElse(() => Option.none()));
|
|
870
|
+
const buildTestRunner = (context, childProcessRunner) => Effect$1.gen(function* () {
|
|
714
871
|
const inProcess = inProcessRunner(context);
|
|
715
872
|
if (Option.isSome(inProcess)) return yield* inProcess.value;
|
|
716
873
|
const base = yield* childProcessRunner;
|
|
@@ -830,7 +987,7 @@ function describeErrors(error) {
|
|
|
830
987
|
return errorMessageOrFallback(completedErrorMessages(state), error.message);
|
|
831
988
|
}
|
|
832
989
|
function importModule(moduleName) {
|
|
833
|
-
return Effect.tryPromise({
|
|
990
|
+
return Effect$1.tryPromise({
|
|
834
991
|
try: () => import(moduleName),
|
|
835
992
|
catch: (cause) => StrykerError.make({
|
|
836
993
|
message: `Failed to import module "${moduleName}"`,
|
|
@@ -842,10 +999,6 @@ const initialExtendsStepState = {
|
|
|
842
999
|
visited: [],
|
|
843
1000
|
documents: []
|
|
844
1001
|
};
|
|
845
|
-
const DoneTag = { _tag: "done" };
|
|
846
|
-
const ReadTag = { _tag: "read" };
|
|
847
|
-
const ResolveTag = { _tag: "resolve" };
|
|
848
|
-
const RefusedTag = { _tag: "refused" };
|
|
849
1002
|
const asUnknownArray = (value) => {
|
|
850
1003
|
if (Array.isArray(value)) return value;
|
|
851
1004
|
return [];
|
|
@@ -888,11 +1041,10 @@ const stripExtends = (document) => {
|
|
|
888
1041
|
};
|
|
889
1042
|
const mergeChainDocuments = (documents) => documents.reduceRight((merged, entry) => mergeConfigs(merged, stripExtends(entry.options)), {});
|
|
890
1043
|
const decideExtendsStep = (state, document, file, pathService) => {
|
|
891
|
-
if (state.visited.includes(file)) return {
|
|
892
|
-
...RefusedTag,
|
|
1044
|
+
if (state.visited.includes(file)) return ExtendsStepRefused.make({
|
|
893
1045
|
reason: "cycle",
|
|
894
1046
|
file
|
|
895
|
-
};
|
|
1047
|
+
});
|
|
896
1048
|
const nextState = {
|
|
897
1049
|
visited: [...state.visited, file],
|
|
898
1050
|
documents: [...state.documents, {
|
|
@@ -900,34 +1052,25 @@ const decideExtendsStep = (state, document, file, pathService) => {
|
|
|
900
1052
|
options: document
|
|
901
1053
|
}]
|
|
902
1054
|
};
|
|
903
|
-
return Match.value(document["extends"]).pipe(Match.when(void 0, () => ({
|
|
904
|
-
...DoneTag,
|
|
905
|
-
options: mergeChainDocuments(nextState.documents)
|
|
906
|
-
})), Match.when(null, () => ({
|
|
907
|
-
...DoneTag,
|
|
908
|
-
options: mergeChainDocuments(nextState.documents)
|
|
909
|
-
})), Match.when(Match.string, (extendValue) => Match.value(isModuleSpecifier(extendValue)).pipe(Match.when(true, () => ({
|
|
910
|
-
...ResolveTag,
|
|
1055
|
+
return Match.value(document["extends"]).pipe(Match.when(void 0, () => ExtendsStepDone.make({ options: mergeChainDocuments(nextState.documents) })), Match.when(null, () => ExtendsStepDone.make({ options: mergeChainDocuments(nextState.documents) })), Match.when(Match.string, (extendValue) => Match.value(isModuleSpecifier(extendValue)).pipe(Match.when(true, () => ExtendsStepResolve.make({
|
|
911
1056
|
specifier: extendValue,
|
|
912
1057
|
state: nextState
|
|
913
|
-
})), Match.when(false, () => ({
|
|
914
|
-
...ReadTag,
|
|
1058
|
+
})), Match.when(false, () => ExtendsStepRead.make({
|
|
915
1059
|
path: pathService.resolve(pathService.dirname(file), extendValue),
|
|
916
1060
|
state: nextState
|
|
917
|
-
})), Match.exhaustive)), Match.orElse(() => ({
|
|
918
|
-
...RefusedTag,
|
|
1061
|
+
})), Match.exhaustive)), Match.orElse(() => ExtendsStepRefused.make({
|
|
919
1062
|
reason: "non-string-extends",
|
|
920
1063
|
file
|
|
921
1064
|
})));
|
|
922
1065
|
};
|
|
923
|
-
const decodeConfigDocument = (configFile, document) => S.decodeUnknownEffect(ConfigDocumentSchema)(document).pipe(Effect.mapError((cause) => ConfigFileInvalidError.make({
|
|
1066
|
+
const decodeConfigDocument = (configFile, document) => S.decodeUnknownEffect(ConfigDocumentSchema)(document).pipe(Effect$1.mapError((cause) => ConfigFileInvalidError.make({
|
|
924
1067
|
file: configFile,
|
|
925
1068
|
cause
|
|
926
1069
|
})));
|
|
927
|
-
const requireDefaultExport = (configFile, defaultExport) => Match.value(defaultExport).pipe(Match.when(void 0, () => Effect.fail(ConfigFileInvalidError.make({
|
|
1070
|
+
const requireDefaultExport = (configFile, defaultExport) => Match.value(defaultExport).pipe(Match.when(void 0, () => Effect$1.fail(ConfigFileInvalidError.make({
|
|
928
1071
|
file: configFile,
|
|
929
1072
|
cause: "Config file must have a default export!"
|
|
930
|
-
}))), Match.when(isNonNullObject, (value) => Effect.succeed(value)), Match.orElse(() => Effect.fail(ConfigFileInvalidError.make({
|
|
1073
|
+
}))), Match.when(isNonNullObject, (value) => Effect$1.succeed(value)), Match.orElse(() => Effect$1.fail(ConfigFileInvalidError.make({
|
|
931
1074
|
file: configFile,
|
|
932
1075
|
cause: "Default export of config file must be an object!"
|
|
933
1076
|
}))));
|
|
@@ -942,13 +1085,13 @@ const factoryFailureCause = (cause) => Match.value(cause).pipe(Match.when(Match.
|
|
|
942
1085
|
* config is being read for; the promise is awaited here because a promise is a
|
|
943
1086
|
* non-null object and would otherwise reach the document decoder as one.
|
|
944
1087
|
*/
|
|
945
|
-
const settleConfigDefault = (configFile, defaultExport, configEnv) => Effect.tryPromise({
|
|
1088
|
+
const settleConfigDefault = (configFile, defaultExport, configEnv) => Effect$1.tryPromise({
|
|
946
1089
|
try: () => Promise.resolve(defaultExport).then((exported) => Match.value(exported).pipe(Match.when(isConfigFactory, (factory) => factory(configEnv)), Match.orElse((value) => value))),
|
|
947
1090
|
catch: (cause) => ConfigFileInvalidError.make({
|
|
948
1091
|
file: configFile,
|
|
949
1092
|
cause: factoryFailureCause(cause)
|
|
950
1093
|
})
|
|
951
|
-
}).pipe(Effect.flatMap((settled) => requireDefaultExport(configFile, settled)));
|
|
1094
|
+
}).pipe(Effect$1.flatMap((settled) => requireDefaultExport(configFile, settled)));
|
|
952
1095
|
const ERASABLE_SYNTAX_HELP = "Config modules may use only erasable TypeScript syntax: no enums, no namespaces with runtime code, no parameter properties, and no decorators.";
|
|
953
1096
|
const carriesCode = (cause) => cause instanceof Error && "code" in cause;
|
|
954
1097
|
const errorCodeOf = (cause) => {
|
|
@@ -961,50 +1104,50 @@ const errorMessageOf = (cause) => {
|
|
|
961
1104
|
};
|
|
962
1105
|
const importFailureDetail = (failure) => errorMessageOf(failure.cause) ?? failure.message;
|
|
963
1106
|
const configImportCause = (configFile, failure) => Match.value(errorCodeOf(failure.cause)).pipe(Match.when("ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX", () => new Error(`The config file "${configFile}" uses TypeScript syntax Node cannot execute. ${ERASABLE_SYNTAX_HELP}`, { cause: failure.cause })), Match.when("ERR_UNKNOWN_FILE_EXTENSION", () => new Error(`The config file "${configFile}" has an extension Node cannot load. Stryker reads config modules with ${SUPPORTED_CONFIG_FILE_EXTENSION_GUIDE}.`, { cause: failure.cause })), Match.when("ERR_MODULE_NOT_FOUND", () => new Error(`The config file "${configFile}" imports a module Node cannot find. The specifier is most likely not installed — install it as a dependency of the project, or remove the import. Node reported: ${importFailureDetail(failure)}`, { cause: failure.cause })), Match.when("ERR_PACKAGE_PATH_NOT_EXPORTED", () => new Error(`The config file "${configFile}" imports a path a package does not export. The package is installed but its "exports" map leaves this specifier out — import a path the package publishes. Node reported: ${importFailureDetail(failure)}`, { cause: failure.cause })), Match.orElse(() => failure));
|
|
964
|
-
const configModuleUrl = (configFile, pathService) => Match.value(configFile.startsWith("file:")).pipe(Match.when(true, () => Effect.try({
|
|
1107
|
+
const configModuleUrl = (configFile, pathService) => Match.value(configFile.startsWith("file:")).pipe(Match.when(true, () => Effect$1.try({
|
|
965
1108
|
try: () => new URL(configFile),
|
|
966
1109
|
catch: (cause) => ConfigFileUnreadableError.make({
|
|
967
1110
|
file: configFile,
|
|
968
1111
|
cause
|
|
969
1112
|
})
|
|
970
|
-
})), Match.orElse(() => pathService.toFileUrl(pathService.resolve(configFile)).pipe(Effect.mapError((cause) => ConfigFileUnreadableError.make({
|
|
1113
|
+
})), Match.orElse(() => pathService.toFileUrl(pathService.resolve(configFile)).pipe(Effect$1.mapError((cause) => ConfigFileUnreadableError.make({
|
|
971
1114
|
file: configFile,
|
|
972
1115
|
cause
|
|
973
1116
|
})))));
|
|
974
|
-
const readConfigModule = (configFile, configEnv) => Effect.gen(function* () {
|
|
1117
|
+
const readConfigModule = (configFile, configEnv) => Effect$1.gen(function* () {
|
|
975
1118
|
const pathService = yield* Path.Path;
|
|
976
|
-
const importedModule = yield* importModule((yield* configModuleUrl(configFile, pathService)).href).pipe(Effect.mapError((failure) => ConfigFileUnreadableError.make({
|
|
1119
|
+
const importedModule = yield* importModule((yield* configModuleUrl(configFile, pathService)).href).pipe(Effect$1.mapError((failure) => ConfigFileUnreadableError.make({
|
|
977
1120
|
file: configFile,
|
|
978
1121
|
cause: configImportCause(configFile, failure)
|
|
979
1122
|
})));
|
|
980
|
-
const exported = yield* S.decodeUnknownEffect(ImportedModuleSchema)(importedModule).pipe(Effect.mapError((cause) => ConfigFileInvalidError.make({
|
|
1123
|
+
const exported = yield* S.decodeUnknownEffect(ImportedModuleSchema)(importedModule).pipe(Effect$1.mapError((cause) => ConfigFileInvalidError.make({
|
|
981
1124
|
file: configFile,
|
|
982
1125
|
cause
|
|
983
|
-
})), Effect.map((decoded) => decoded.default));
|
|
1126
|
+
})), Effect$1.map((decoded) => decoded.default));
|
|
984
1127
|
const document = yield* settleConfigDefault(configFile, exported, configEnv);
|
|
985
1128
|
return yield* decodeConfigDocument(configFile, document);
|
|
986
1129
|
});
|
|
987
1130
|
const configFileExtension = (configFile, pathService) => pathService.extname(configFile).toLowerCase().slice(1);
|
|
988
1131
|
const isLegacyConfigFile = (configFile, pathService) => LEGACY_CONFIG_FILE_EXTENSIONS.includes(configFileExtension(configFile, pathService));
|
|
989
|
-
const readExtendsChild = (configFile, configEnv) => Effect.gen(function* () {
|
|
1132
|
+
const readExtendsChild = (configFile, configEnv) => Effect$1.gen(function* () {
|
|
990
1133
|
const pathService = yield* Path.Path;
|
|
991
|
-
return yield* Match.value(isLegacyConfigFile(configFile, pathService)).pipe(Match.when(true, () => Effect.fail(ConfigFileUnsupportedError.make({
|
|
1134
|
+
return yield* Match.value(isLegacyConfigFile(configFile, pathService)).pipe(Match.when(true, () => Effect$1.fail(ConfigFileUnsupportedError.make({
|
|
992
1135
|
file: configFile,
|
|
993
1136
|
hint: extendsChildHint(configFile)
|
|
994
1137
|
}))), Match.orElse(() => readConfigModule(configFile, configEnv)));
|
|
995
1138
|
});
|
|
996
|
-
const canonicalConfigFile = (file, pathService) => Match.value(file.startsWith("file:")).pipe(Match.when(true, () => Effect.try({
|
|
1139
|
+
const canonicalConfigFile = (file, pathService) => Match.value(file.startsWith("file:")).pipe(Match.when(true, () => Effect$1.try({
|
|
997
1140
|
try: () => new URL(file),
|
|
998
1141
|
catch: (cause) => ConfigFileUnreadableError.make({
|
|
999
1142
|
file,
|
|
1000
1143
|
cause
|
|
1001
1144
|
})
|
|
1002
|
-
}).pipe(Effect.flatMap((url) => pathService.fromFileUrl(url).pipe(Effect.mapError((cause) => ConfigFileUnreadableError.make({
|
|
1145
|
+
}).pipe(Effect$1.flatMap((url) => pathService.fromFileUrl(url).pipe(Effect$1.mapError((cause) => ConfigFileUnreadableError.make({
|
|
1003
1146
|
file,
|
|
1004
1147
|
cause
|
|
1005
|
-
})))))), Match.orElse(() => Effect.succeed(pathService.resolve(file))));
|
|
1148
|
+
})))))), Match.orElse(() => Effect$1.succeed(pathService.resolve(file))));
|
|
1006
1149
|
function resolveExtendsSpecifier(specifier) {
|
|
1007
|
-
return Effect.try({
|
|
1150
|
+
return Effect$1.try({
|
|
1008
1151
|
try: () => import.meta.resolve(specifier),
|
|
1009
1152
|
catch: (cause) => ConfigFileUnreadableError.make({
|
|
1010
1153
|
file: specifier,
|
|
@@ -1013,14 +1156,14 @@ function resolveExtendsSpecifier(specifier) {
|
|
|
1013
1156
|
});
|
|
1014
1157
|
}
|
|
1015
1158
|
function resolveExtends(configFile, document, configEnv) {
|
|
1016
|
-
return Effect.gen(function* () {
|
|
1159
|
+
return Effect$1.gen(function* () {
|
|
1017
1160
|
const pathService = yield* Path.Path;
|
|
1018
|
-
const loop = (state, file, currentDocument) => Effect.gen(function* () {
|
|
1161
|
+
const loop = (state, file, currentDocument) => Effect$1.gen(function* () {
|
|
1019
1162
|
const canonicalFile = yield* canonicalConfigFile(file, pathService);
|
|
1020
|
-
return yield* Match.value(decideExtendsStep(state, currentDocument, canonicalFile, pathService)).pipe(Match.tag("done", (d) => Effect.succeed(d.options)), Match.tag("read", (d) => readExtendsChild(d.path, configEnv).pipe(Effect.flatMap((nextDocument) => loop(d.state, d.path, nextDocument)))), Match.tag("resolve", (d) => resolveExtendsSpecifier(d.specifier).pipe(Effect.flatMap((resolvedUrl) => readExtendsChild(resolvedUrl, configEnv).pipe(Effect.flatMap((nextDocument) => loop(d.state, resolvedUrl, nextDocument)))))), Match.tag("refused", (d) => {
|
|
1163
|
+
return yield* Match.value(decideExtendsStep(state, currentDocument, canonicalFile, pathService)).pipe(Match.tag("done", (d) => Effect$1.succeed(d.options)), Match.tag("read", (d) => readExtendsChild(d.path, configEnv).pipe(Effect$1.flatMap((nextDocument) => loop(d.state, d.path, nextDocument)))), Match.tag("resolve", (d) => resolveExtendsSpecifier(d.specifier).pipe(Effect$1.flatMap((resolvedUrl) => readExtendsChild(resolvedUrl, configEnv).pipe(Effect$1.flatMap((nextDocument) => loop(d.state, resolvedUrl, nextDocument)))))), Match.tag("refused", (d) => {
|
|
1021
1164
|
let message = `Invalid config file "${d.file}". "extends" must be a string`;
|
|
1022
1165
|
if (d.reason === "cycle") message = `Config inheritance cycle detected at "${d.file}"`;
|
|
1023
|
-
return Effect.fail(ConfigFileInvalidError.make({
|
|
1166
|
+
return Effect$1.fail(ConfigFileInvalidError.make({
|
|
1024
1167
|
file: d.file,
|
|
1025
1168
|
cause: message
|
|
1026
1169
|
}));
|
|
@@ -1045,10 +1188,10 @@ const mutationRangeBoundErrors = (index, match) => {
|
|
|
1045
1188
|
const end = matchLineNumber(match, 5);
|
|
1046
1189
|
return [...startLineErrors(index, mutationRange, start), ...lineOrderErrors(index, mutationRange, start, end)];
|
|
1047
1190
|
};
|
|
1048
|
-
const requireUnmagicalMutationRange = (mutateString, index, match) => Match.value(
|
|
1191
|
+
const requireUnmagicalMutationRange = (mutateString, index, match) => Match.value(isGlob(mutateString)).pipe(Match.when(true, () => [`Config option "mutate[${index}]" is invalid. Cannot combine a glob expression with a mutation range in "${mutateString}".`]), Match.orElse(() => mutationRangeBoundErrors(index, match)));
|
|
1049
1192
|
const mutationRangeErrors = (mutateString, index) => Match.value(MUTATION_RANGE_REGEX.exec(mutateString)).pipe(Match.when((match) => match !== null, (match) => requireUnmagicalMutationRange(mutateString, index, match)), Match.orElse(() => []));
|
|
1050
|
-
const warnOnIgnoredNodeArgs = (nodeArgs) => Match.value(nodeArgs.length > 0).pipe(Match.when(true, () => Effect.logWarning("Using \"testRunnerNodeArgs\" together with the \"command\" test runner is not supported, these arguments will be ignored. You can add your custom arguments by setting the \"commandRunner.command\" option.")), Match.orElse(() => Effect.void));
|
|
1051
|
-
const warnOnCommandRunnerNodeArgs = (options) => Match.value(isCommandRunner(options.testRunner)).pipe(Match.when(true, () => warnOnIgnoredNodeArgs(options.testRunnerNodeArgs)), Match.orElse(() => Effect.void));
|
|
1193
|
+
const warnOnIgnoredNodeArgs = (nodeArgs) => Match.value(nodeArgs.length > 0).pipe(Match.when(true, () => Effect$1.logWarning("Using \"testRunnerNodeArgs\" together with the \"command\" test runner is not supported, these arguments will be ignored. You can add your custom arguments by setting the \"commandRunner.command\" option.")), Match.orElse(() => Effect$1.void));
|
|
1194
|
+
const warnOnCommandRunnerNodeArgs = (options) => Match.value(isCommandRunner(options.testRunner)).pipe(Match.when(true, () => warnOnIgnoredNodeArgs(options.testRunnerNodeArgs)), Match.orElse(() => Effect$1.void));
|
|
1052
1195
|
const customValidationErrors = (options) => [
|
|
1053
1196
|
...thresholdErrors(options),
|
|
1054
1197
|
...ignoreStaticErrors(options),
|
|
@@ -1056,23 +1199,23 @@ const customValidationErrors = (options) => [
|
|
|
1056
1199
|
];
|
|
1057
1200
|
function customValidation(options) {
|
|
1058
1201
|
const additionalErrors = customValidationErrors(options);
|
|
1059
|
-
return warnOnCommandRunnerNodeArgs(options).pipe(Effect.flatMap(() => logConfigErrors(additionalErrors)), Effect.flatMap(() => throwErrorIfNeeded(additionalErrors)));
|
|
1202
|
+
return warnOnCommandRunnerNodeArgs(options).pipe(Effect$1.flatMap(() => logConfigErrors(additionalErrors)), Effect$1.flatMap(() => throwErrorIfNeeded(additionalErrors)));
|
|
1060
1203
|
}
|
|
1061
1204
|
function schemaValidate(options) {
|
|
1062
1205
|
const decoded = decodeOptions(options);
|
|
1063
1206
|
if (Result.isFailure(decoded)) return failWithConfigErrors(describeErrors(decoded.failure));
|
|
1064
1207
|
Object.assign(options, decoded.success);
|
|
1065
|
-
return Effect.succeed(decoded.success);
|
|
1208
|
+
return Effect$1.succeed(decoded.success);
|
|
1066
1209
|
}
|
|
1067
1210
|
const configErrorHeadline = (errors) => Match.value(errors.length === 1).pipe(Match.when(true, () => "Please correct this configuration error and try again."), Match.orElse(() => "Please correct these configuration errors and try again."));
|
|
1068
1211
|
const configErrorMessage = (errors) => `${configErrorHeadline(errors)} ${errors.join(" ")}`;
|
|
1069
|
-
const logConfigErrors = (errors) => Effect.gen(function* () {
|
|
1070
|
-
for (const error of errors) yield* Effect.logError(error);
|
|
1212
|
+
const logConfigErrors = (errors) => Effect$1.gen(function* () {
|
|
1213
|
+
for (const error of errors) yield* Effect$1.logError(error);
|
|
1071
1214
|
});
|
|
1072
|
-
const failWithConfigErrors = (errors) => logConfigErrors(errors).pipe(Effect.flatMap(() => Effect.fail(ConfigError.make({ message: configErrorMessage(errors) }))));
|
|
1215
|
+
const failWithConfigErrors = (errors) => logConfigErrors(errors).pipe(Effect$1.flatMap(() => Effect$1.fail(ConfigError.make({ message: configErrorMessage(errors) }))));
|
|
1073
1216
|
function throwErrorIfNeeded(errors) {
|
|
1074
|
-
if (errors.length === 0) return Effect.void;
|
|
1075
|
-
return Effect.fail(ConfigError.make({ message: configErrorMessage(errors) }));
|
|
1217
|
+
if (errors.length === 0) return Effect$1.void;
|
|
1218
|
+
return Effect$1.fail(ConfigError.make({ message: configErrorMessage(errors) }));
|
|
1076
1219
|
}
|
|
1077
1220
|
const OPTIONS_ADDED_BY_STRYKER = [
|
|
1078
1221
|
"set",
|
|
@@ -1084,10 +1227,10 @@ const excessOptionNames = (options, schema) => {
|
|
|
1084
1227
|
const schemaKeys = schemaPropertyNames(schema);
|
|
1085
1228
|
return Object.keys(options).filter((key) => key.endsWith("_comment") === false).filter((key) => OPTIONS_ADDED_BY_STRYKER.includes(key) === false).filter((key) => schemaKeys.includes(key) === false);
|
|
1086
1229
|
};
|
|
1087
|
-
const warnAboutUnknownOptions = (options, excessNames) => Effect.gen(function* () {
|
|
1088
|
-
for (const excessPropertyName of excessNames) yield* Effect.logWarning(`Unknown stryker config option "${excessPropertyName}".`);
|
|
1089
|
-
const pluginsJson = yield* S.encodeEffect(S.fromJsonString(S.Array(S.String)))([...options.plugins]).pipe(Effect.orDie);
|
|
1090
|
-
yield* Effect.logWarning(`Possible causes:
|
|
1230
|
+
const warnAboutUnknownOptions = (options, excessNames) => Effect$1.gen(function* () {
|
|
1231
|
+
for (const excessPropertyName of excessNames) yield* Effect$1.logWarning(`Unknown stryker config option "${excessPropertyName}".`);
|
|
1232
|
+
const pluginsJson = yield* S.encodeEffect(S.fromJsonString(S.Array(S.String)))([...options.plugins]).pipe(Effect$1.orDie);
|
|
1233
|
+
yield* Effect$1.logWarning(`Possible causes:
|
|
1091
1234
|
* Is it a typo on your end?
|
|
1092
1235
|
* Did you only write this property as a comment? If so, please postfix it with "_comment".
|
|
1093
1236
|
* You might be missing a plugin that is supposed to use it. Stryker loaded plugins from: ${pluginsJson}
|
|
@@ -1096,39 +1239,39 @@ const warnAboutUnknownOptions = (options, excessNames) => Effect.gen(function* (
|
|
|
1096
1239
|
});
|
|
1097
1240
|
const unknownOptionWarning = (options, schema) => {
|
|
1098
1241
|
const excessNames = excessOptionNames(options, schema);
|
|
1099
|
-
return Match.value(excessNames.length > 0).pipe(Match.when(true, () => warnAboutUnknownOptions(options, excessNames)), Match.orElse(() => Effect.void));
|
|
1242
|
+
return Match.value(excessNames.length > 0).pipe(Match.when(true, () => warnAboutUnknownOptions(options, excessNames)), Match.orElse(() => Effect$1.void));
|
|
1100
1243
|
};
|
|
1101
1244
|
function markExcessOptions(options, schema) {
|
|
1102
|
-
return Effect.gen(function* () {
|
|
1245
|
+
return Effect$1.gen(function* () {
|
|
1103
1246
|
if (isWarningEnabled("unknownOptions", options.warnings)) yield* unknownOptionWarning(options, schema);
|
|
1104
1247
|
});
|
|
1105
1248
|
}
|
|
1106
|
-
const logUnserializableWarnings = (unserializables) => Effect.gen(function* () {
|
|
1107
|
-
for (const unserializable of unserializables) yield* Effect.logWarning(`Config option "${unserializable.path.join(".")}" is not (fully) serializable. ${unserializable.reason}. Any test runner or checker worker processes might not receive this value as intended.`);
|
|
1108
|
-
yield* Effect.logWarning(`(disable ${optionsPath("warnings", "unserializableOptions")} to ignore this warning)`);
|
|
1249
|
+
const logUnserializableWarnings = (unserializables) => Effect$1.gen(function* () {
|
|
1250
|
+
for (const unserializable of unserializables) yield* Effect$1.logWarning(`Config option "${unserializable.path.join(".")}" is not (fully) serializable. ${unserializable.reason}. Any test runner or checker worker processes might not receive this value as intended.`);
|
|
1251
|
+
yield* Effect$1.logWarning(`(disable ${optionsPath("warnings", "unserializableOptions")} to ignore this warning)`);
|
|
1109
1252
|
});
|
|
1110
|
-
const warnAboutUnserializableOptions = (options) => Match.value(findUnserializables(options)).pipe(Match.when(void 0, () => Effect.void), Match.orElse((unserializables) => logUnserializableWarnings(unserializables)));
|
|
1253
|
+
const warnAboutUnserializableOptions = (options) => Match.value(findUnserializables(options)).pipe(Match.when(void 0, () => Effect$1.void), Match.orElse((unserializables) => logUnserializableWarnings(unserializables)));
|
|
1111
1254
|
function markUnserializableOptions(options) {
|
|
1112
|
-
return Effect.gen(function* () {
|
|
1255
|
+
return Effect$1.gen(function* () {
|
|
1113
1256
|
if (isWarningEnabled("unserializableOptions", options.warnings)) yield* warnAboutUnserializableOptions(options);
|
|
1114
1257
|
});
|
|
1115
1258
|
}
|
|
1116
1259
|
function markOptions(options, schema) {
|
|
1117
|
-
return Effect.gen(function* () {
|
|
1260
|
+
return Effect$1.gen(function* () {
|
|
1118
1261
|
yield* markExcessOptions(options, schema);
|
|
1119
1262
|
yield* markUnserializableOptions(options);
|
|
1120
1263
|
});
|
|
1121
1264
|
}
|
|
1122
1265
|
function validateOptions(options, schema) {
|
|
1123
|
-
return Effect.gen(function* () {
|
|
1266
|
+
return Effect$1.gen(function* () {
|
|
1124
1267
|
const typed = yield* schemaValidate(options);
|
|
1125
1268
|
yield* customValidation(typed);
|
|
1126
1269
|
yield* markOptions(typed, schema);
|
|
1127
1270
|
return typed;
|
|
1128
1271
|
});
|
|
1129
1272
|
}
|
|
1130
|
-
const createDefaultOptions = S.decodeEffect(StrykerOptionsSchema)({}).pipe(Effect.orDie);
|
|
1131
|
-
Effect.map(createDefaultOptions, (opts) => deepFreeze(opts));
|
|
1273
|
+
const createDefaultOptions = S.decodeEffect(StrykerOptionsSchema)({}).pipe(Effect$1.orDie);
|
|
1274
|
+
Effect$1.map(createDefaultOptions, (opts) => deepFreeze(opts));
|
|
1132
1275
|
`
|
|
1133
1276
|
Example of how a config file should look:
|
|
1134
1277
|
/**
|
|
@@ -1142,29 +1285,29 @@ A config file is a TypeScript or ESM JavaScript module: stryker.conf.ts, stryker
|
|
|
1142
1285
|
|
|
1143
1286
|
See https://stryker-mutator.io/docs/stryker-js/config-file for more information.`.trim();
|
|
1144
1287
|
function exists(fileName) {
|
|
1145
|
-
return Effect.gen(function* () {
|
|
1146
|
-
return yield* (yield* FileSystem.FileSystem).access(fileName).pipe(Effect.as(true), Effect.catchTag("PlatformError", (error) => Match.value(error.reason).pipe(Match.tag("NotFound", () => Effect.succeed(false)), Match.orElse(() => Effect.fail(ConfigFileUnreadableError.make({
|
|
1288
|
+
return Effect$1.gen(function* () {
|
|
1289
|
+
return yield* (yield* FileSystem.FileSystem).access(fileName).pipe(Effect$1.as(true), Effect$1.catchTag("PlatformError", (error) => Match.value(error.reason).pipe(Match.tag("NotFound", () => Effect$1.succeed(false)), Match.orElse(() => Effect$1.fail(ConfigFileUnreadableError.make({
|
|
1147
1290
|
file: fileName,
|
|
1148
1291
|
cause: error
|
|
1149
|
-
}))))), Effect.mapError((error) => ConfigFileUnreadableError.make({
|
|
1292
|
+
}))))), Effect$1.mapError((error) => ConfigFileUnreadableError.make({
|
|
1150
1293
|
file: fileName,
|
|
1151
1294
|
cause: error
|
|
1152
1295
|
})));
|
|
1153
1296
|
});
|
|
1154
1297
|
}
|
|
1155
|
-
const requireExistingConfigFile = (configFileName) => exists(configFileName).pipe(Effect.flatMap((doesExist) => Match.value(doesExist).pipe(Match.when(true, () => Effect.succeed(configFileName)), Match.orElse(() => Effect.fail(ConfigFileNotFoundError.make({ file: configFileName }))))));
|
|
1298
|
+
const requireExistingConfigFile = (configFileName) => exists(configFileName).pipe(Effect$1.flatMap((doesExist) => Match.value(doesExist).pipe(Match.when(true, () => Effect$1.succeed(configFileName)), Match.orElse(() => Effect$1.fail(ConfigFileNotFoundError.make({ file: configFileName }))))));
|
|
1156
1299
|
const firstExistingConfigFile = (fileNames) => Option.match(Option.fromUndefinedOr(fileNames[0]), {
|
|
1157
|
-
onNone: () => Effect.succeedNone,
|
|
1158
|
-
onSome: (head) => exists(head).pipe(Effect.flatMap((doesExist) => {
|
|
1159
|
-
if (doesExist) return Effect.succeedSome(head);
|
|
1300
|
+
onNone: () => Effect$1.succeedNone,
|
|
1301
|
+
onSome: (head) => exists(head).pipe(Effect$1.flatMap((doesExist) => {
|
|
1302
|
+
if (doesExist) return Effect$1.succeedSome(head);
|
|
1160
1303
|
return firstExistingConfigFile(fileNames.slice(1));
|
|
1161
1304
|
}))
|
|
1162
1305
|
});
|
|
1163
|
-
const configFileFor = (configFileName) => Effect.gen(function* () {
|
|
1306
|
+
const configFileFor = (configFileName) => Effect$1.gen(function* () {
|
|
1164
1307
|
const pathService = yield* Path.Path;
|
|
1165
1308
|
const supported = SUPPORTED_CONFIG_FILE_EXTENSIONS.includes(configFileExtension(configFileName, pathService));
|
|
1166
1309
|
const legacy = isLegacyConfigFile(configFileName, pathService);
|
|
1167
|
-
return yield* Match.value(supported).pipe(Match.when(true, () => requireExistingConfigFile(configFileName)), Match.orElse(() => Effect.fail(ConfigFileUnsupportedError.make({
|
|
1310
|
+
return yield* Match.value(supported).pipe(Match.when(true, () => requireExistingConfigFile(configFileName)), Match.orElse(() => Effect$1.fail(ConfigFileUnsupportedError.make({
|
|
1168
1311
|
file: configFileName,
|
|
1169
1312
|
hint: Match.value(legacy).pipe(Match.when(true, () => legacyConfigHint(configFileName)), Match.orElse(() => unsupportedConfigHint(configFileName)))
|
|
1170
1313
|
}))));
|
|
@@ -1174,43 +1317,43 @@ const legacyConfigError = (file) => ConfigFileUnsupportedError.make({
|
|
|
1174
1317
|
file,
|
|
1175
1318
|
hint: legacyConfigHint(file)
|
|
1176
1319
|
});
|
|
1177
|
-
const legacyConfigWarning = (file, supportedFile) => Effect.logWarning(shadowedLegacyWarning(file, supportedFile));
|
|
1178
|
-
const refuseLegacyOnlyProject = () => firstLegacyConfigFile().pipe(Effect.flatMap((legacyFile) => Option.match(legacyFile, {
|
|
1179
|
-
onNone: () => Effect.succeedNone,
|
|
1180
|
-
onSome: (file) => Effect.fail(legacyConfigError(file))
|
|
1320
|
+
const legacyConfigWarning = (file, supportedFile) => Effect$1.logWarning(shadowedLegacyWarning(file, supportedFile));
|
|
1321
|
+
const refuseLegacyOnlyProject = () => firstLegacyConfigFile().pipe(Effect$1.flatMap((legacyFile) => Option.match(legacyFile, {
|
|
1322
|
+
onNone: () => Effect$1.succeedNone,
|
|
1323
|
+
onSome: (file) => Effect$1.fail(legacyConfigError(file))
|
|
1181
1324
|
})));
|
|
1182
|
-
const warnShadowedLegacyConfig = (supportedFile) => firstLegacyConfigFile().pipe(Effect.flatMap((legacyFile) => Option.match(legacyFile, {
|
|
1183
|
-
onNone: () => Effect.succeed(supportedFile),
|
|
1184
|
-
onSome: (file) => Effect.as(legacyConfigWarning(file, supportedFile), supportedFile)
|
|
1325
|
+
const warnShadowedLegacyConfig = (supportedFile) => firstLegacyConfigFile().pipe(Effect$1.flatMap((legacyFile) => Option.match(legacyFile, {
|
|
1326
|
+
onNone: () => Effect$1.succeed(supportedFile),
|
|
1327
|
+
onSome: (file) => Effect$1.as(legacyConfigWarning(file, supportedFile), supportedFile)
|
|
1185
1328
|
})));
|
|
1186
|
-
const discoverConfigFile = () => firstExistingConfigFile([...SUPPORTED_CONFIG_FILE_NAMES]).pipe(Effect.flatMap((found) => Option.match(found, {
|
|
1187
|
-
onNone: () => refuseLegacyOnlyProject().pipe(Effect.map((legacy) => Option.getOrUndefined(legacy))),
|
|
1329
|
+
const discoverConfigFile = () => firstExistingConfigFile([...SUPPORTED_CONFIG_FILE_NAMES]).pipe(Effect$1.flatMap((found) => Option.match(found, {
|
|
1330
|
+
onNone: () => refuseLegacyOnlyProject().pipe(Effect$1.map((legacy) => Option.getOrUndefined(legacy))),
|
|
1188
1331
|
onSome: (supportedFile) => warnShadowedLegacyConfig(supportedFile)
|
|
1189
1332
|
})));
|
|
1190
1333
|
function findConfigFile(configFileName) {
|
|
1191
1334
|
return Match.value(configFileName).pipe(Match.when(Match.string, (fileName) => configFileFor(fileName)), Match.orElse(() => discoverConfigFile()));
|
|
1192
1335
|
}
|
|
1193
|
-
const resolveChildExtends = (configFile, child, configEnv) => Match.value("extends" in child).pipe(Match.when(true, () => resolveExtends(configFile, child, configEnv)), Match.orElse(() => Effect.succeed(child)));
|
|
1194
|
-
const isCiEnvironment = Config.string("CI").pipe(Effect.map((value) => Match.value(value.trim().toLowerCase()).pipe(Match.when("", () => false), Match.when("0", () => false), Match.when("false", () => false), Match.orElse(() => true))), Effect.orElseSucceed(() => false));
|
|
1336
|
+
const resolveChildExtends = (configFile, child, configEnv) => Match.value("extends" in child).pipe(Match.when(true, () => resolveExtends(configFile, child, configEnv)), Match.orElse(() => Effect$1.succeed(child)));
|
|
1337
|
+
const isCiEnvironment = Config.string("CI").pipe(Effect$1.map((value) => Match.value(value.trim().toLowerCase()).pipe(Match.when("", () => false), Match.when("0", () => false), Match.when("false", () => false), Match.orElse(() => true))), Effect$1.orElseSucceed(() => false));
|
|
1195
1338
|
function loadOptionsFromConfigFile(cliOptions, configEnv) {
|
|
1196
|
-
return findConfigFile(cliOptions["configFile"]).pipe(Effect.flatMap((configFile) => Match.value(configFile).pipe(Match.when(void 0, () => Effect.succeed({})), Match.orElse((found) => readConfigModule(found, configEnv).pipe(Effect.flatMap((child) => resolveChildExtends(found, child, configEnv)))))));
|
|
1339
|
+
return findConfigFile(cliOptions["configFile"]).pipe(Effect$1.flatMap((configFile) => Match.value(configFile).pipe(Match.when(void 0, () => Effect$1.succeed({})), Match.orElse((found) => readConfigModule(found, configEnv).pipe(Effect$1.flatMap((child) => resolveChildExtends(found, child, configEnv)))))));
|
|
1197
1340
|
}
|
|
1198
1341
|
function readConfig(cliOptions, invocation) {
|
|
1199
|
-
return Effect.gen(function* () {
|
|
1342
|
+
return Effect$1.gen(function* () {
|
|
1200
1343
|
const configEnv = {
|
|
1201
1344
|
command: invocation.command,
|
|
1202
1345
|
mode: invocation.mode,
|
|
1203
1346
|
isDryRun: cliOptions["dryRunOnly"] === true,
|
|
1204
1347
|
isCi: yield* isCiEnvironment
|
|
1205
1348
|
};
|
|
1206
|
-
const cliRecord = yield* S.decodeUnknownEffect(ConfigDocumentSchema)(cliOptions).pipe(Effect.orDie);
|
|
1349
|
+
const cliRecord = yield* S.decodeUnknownEffect(ConfigDocumentSchema)(cliOptions).pipe(Effect$1.orDie);
|
|
1207
1350
|
const fileRecord = yield* loadOptionsFromConfigFile(cliRecord, configEnv);
|
|
1208
1351
|
const fileOptions = yield* Result.match(S.decodeUnknownResult(ConfigDocumentSchema)(fileRecord), {
|
|
1209
|
-
onFailure: (cause) => Effect.fail(ConfigFileInvalidError.make({
|
|
1352
|
+
onFailure: (cause) => Effect$1.fail(ConfigFileInvalidError.make({
|
|
1210
1353
|
file: "config",
|
|
1211
1354
|
cause
|
|
1212
1355
|
})),
|
|
1213
|
-
onSuccess: (options) => Effect.succeed(options)
|
|
1356
|
+
onSuccess: (options) => Effect$1.succeed(options)
|
|
1214
1357
|
});
|
|
1215
1358
|
const merged = mergeConfig(fileOptions, cliRecord);
|
|
1216
1359
|
const decoded = S.decodeUnknownResult(StrykerOptionsSchema)(merged);
|
|
@@ -1313,7 +1456,7 @@ const buildPluginLoadPlan = (entries) => {
|
|
|
1313
1456
|
shadowings: shadowingState.shadowings
|
|
1314
1457
|
};
|
|
1315
1458
|
};
|
|
1316
|
-
const failPluginLoad = (descriptor, error) => Effect.logWarning(`Error during loading "${descriptor}" plugin`).pipe(Effect.andThen(() => Effect.fail(PluginLoadFailedError.make({
|
|
1459
|
+
const failPluginLoad = (descriptor, error) => Effect$1.logWarning(`Error during loading "${descriptor}" plugin`).pipe(Effect$1.andThen(() => Effect$1.fail(PluginLoadFailedError.make({
|
|
1317
1460
|
descriptor,
|
|
1318
1461
|
cause: error
|
|
1319
1462
|
}))));
|
|
@@ -1330,37 +1473,37 @@ const hasContribution = (contributions) => [
|
|
|
1330
1473
|
contributions.ignorers,
|
|
1331
1474
|
contributions.schemaContribution
|
|
1332
1475
|
].some((contribution) => contribution !== void 0);
|
|
1333
|
-
const warnUndescribedPluginModule = (descriptor) => Effect.logWarning(`Module "${descriptor}" did not contribute a StrykerJS plugin. It didn't export a "strykerPlugins", "strykerIgnorers", or "strykerValidationSchema".`).pipe(Effect.as(void 0));
|
|
1476
|
+
const warnUndescribedPluginModule = (descriptor) => Effect$1.logWarning(`Module "${descriptor}" did not contribute a StrykerJS plugin. It didn't export a "strykerPlugins", "strykerIgnorers", or "strykerValidationSchema".`).pipe(Effect$1.as(void 0));
|
|
1334
1477
|
const describeLoadedPlugin = (descriptor, module) => Result.match(pluginContributionsOf(module), {
|
|
1335
1478
|
onFailure: (cause) => failPluginLoad(descriptor, cause),
|
|
1336
|
-
onSuccess: (contributions) => Match.value(hasContribution(contributions)).pipe(Match.when(true, () => Effect.succeed(contributions)), Match.orElse(() => warnUndescribedPluginModule(descriptor)))
|
|
1479
|
+
onSuccess: (contributions) => Match.value(hasContribution(contributions)).pipe(Match.when(true, () => Effect$1.succeed(contributions)), Match.orElse(() => warnUndescribedPluginModule(descriptor)))
|
|
1337
1480
|
});
|
|
1338
1481
|
function loadPlugin(descriptor, entrypoint) {
|
|
1339
|
-
return Effect.gen(function* () {
|
|
1340
|
-
yield* Effect.logDebug(`Loading plugin ${descriptor}`);
|
|
1341
|
-
const maybeModule = yield* importModule(entrypoint).pipe(Effect.catch((error) => failPluginLoad(descriptor, error)));
|
|
1482
|
+
return Effect$1.gen(function* () {
|
|
1483
|
+
yield* Effect$1.logDebug(`Loading plugin ${descriptor}`);
|
|
1484
|
+
const maybeModule = yield* importModule(entrypoint).pipe(Effect$1.catch((error) => failPluginLoad(descriptor, error)));
|
|
1342
1485
|
const module = Option.getOrUndefined(Option.fromUndefinedOr(maybeModule));
|
|
1343
1486
|
if (module === void 0) return;
|
|
1344
1487
|
return yield* describeLoadedPlugin(descriptor, module);
|
|
1345
1488
|
});
|
|
1346
1489
|
}
|
|
1347
1490
|
function loadPlugins(pluginDescriptors) {
|
|
1348
|
-
return Effect.gen(function* () {
|
|
1491
|
+
return Effect$1.gen(function* () {
|
|
1349
1492
|
const path = yield* Path.Path;
|
|
1350
|
-
const entrypoints = yield* Effect.forEach(Array$1.fromIterable(HashSet.fromIterable(pluginDescriptors)), (specifier) => path.fromFileUrl(new URL(specifier)).pipe(Effect.map((entrypoint) => ({
|
|
1493
|
+
const entrypoints = yield* Effect$1.forEach(Array$1.fromIterable(HashSet.fromIterable(pluginDescriptors)), (specifier) => path.fromFileUrl(new URL(specifier)).pipe(Effect$1.map((entrypoint) => ({
|
|
1351
1494
|
specifier,
|
|
1352
1495
|
entrypoint
|
|
1353
|
-
})), Effect.mapError((cause) => PluginLoadFailedError.make({
|
|
1496
|
+
})), Effect$1.mapError((cause) => PluginLoadFailedError.make({
|
|
1354
1497
|
descriptor: specifier,
|
|
1355
1498
|
cause
|
|
1356
1499
|
}))), { concurrency: "unbounded" });
|
|
1357
|
-
const loaded = yield* Effect.forEach(entrypoints, (resolved) => loadPlugin(resolved.specifier, resolved.entrypoint).pipe(Effect.map((plugin) => {
|
|
1500
|
+
const loaded = yield* Effect$1.forEach(entrypoints, (resolved) => loadPlugin(resolved.specifier, resolved.entrypoint).pipe(Effect$1.map((plugin) => {
|
|
1358
1501
|
if (plugin === void 0) return;
|
|
1359
1502
|
return {
|
|
1360
1503
|
...plugin,
|
|
1361
|
-
moduleName: resolved.
|
|
1504
|
+
moduleName: resolved.specifier
|
|
1362
1505
|
};
|
|
1363
|
-
})), { concurrency: "unbounded" }).pipe(Effect.map((arr) => arr.filter(Predicate.isNotNullish)));
|
|
1506
|
+
})), { concurrency: "unbounded" }).pipe(Effect$1.map((arr) => arr.filter(Predicate.isNotNullish)));
|
|
1364
1507
|
const ignorers = loaded.flatMap((entry) => entry.ignorers ?? NO_IGNORERS);
|
|
1365
1508
|
const entries = loaded.map((entry) => ({
|
|
1366
1509
|
moduleName: entry.moduleName,
|
|
@@ -1368,7 +1511,7 @@ function loadPlugins(pluginDescriptors) {
|
|
|
1368
1511
|
schemaContribution: entry.schemaContribution
|
|
1369
1512
|
}));
|
|
1370
1513
|
const plan_ = buildPluginLoadPlan(entries);
|
|
1371
|
-
for (const shadowing of plan_.shadowings) yield* Effect.logWarning(`Plugin "${shadowing.name}" of kind "${shadowing.kind}" at index ${shadowing.winnerIndex} shadows plugin at index ${shadowing.shadowedIndex}.`);
|
|
1514
|
+
for (const shadowing of plan_.shadowings) yield* Effect$1.logWarning(`Plugin "${shadowing.name}" of kind "${shadowing.kind}" at index ${shadowing.winnerIndex} shadows plugin at index ${shadowing.shadowedIndex}.`);
|
|
1372
1515
|
return {
|
|
1373
1516
|
schemaContributions: plan_.schemaContributions,
|
|
1374
1517
|
pluginsByKind: plan_.pluginsByKind,
|
|
@@ -1378,14 +1521,21 @@ function loadPlugins(pluginDescriptors) {
|
|
|
1378
1521
|
};
|
|
1379
1522
|
});
|
|
1380
1523
|
}
|
|
1524
|
+
const pluginUrlsFromOptions = (options) => [
|
|
1525
|
+
...options.plugins,
|
|
1526
|
+
...options.appendPlugins,
|
|
1527
|
+
...options.ignorers,
|
|
1528
|
+
...Match.value(options.testRunner).pipe(Match.when(isCustomTestRunner, (runner) => [runner.plugin]), Match.orElse(() => [])),
|
|
1529
|
+
...options.checkers.map((checker) => checker.plugin)
|
|
1530
|
+
];
|
|
1381
1531
|
function hasValidationSchemaContribution(module) {
|
|
1382
1532
|
return Schema.is(SchemaValidationContributionSchema)(module);
|
|
1383
1533
|
}
|
|
1384
1534
|
const findByKindAndName = (items, kind, name) => Option.fromUndefinedOr(items.find((item) => item.kind === kind && item.name.toLowerCase() === name.toLowerCase()));
|
|
1385
|
-
const findContribution = (descriptors, kind, name) => Effect.fromOption(Option.filter(findByKindAndName(descriptors, kind, name), (descriptor) => descriptor.kind === kind), () => PluginNotFoundError.make({ descriptor: `${kind}:${name} (available: ${descriptors.map((d) => d.name).join(", ")})` }));
|
|
1535
|
+
const findContribution = (descriptors, kind, name) => Effect$1.fromOption(Option.filter(findByKindAndName(descriptors, kind, name), (descriptor) => descriptor.kind === kind), () => PluginNotFoundError.make({ descriptor: `${kind}:${name} (available: ${descriptors.map((d) => d.name).join(", ")})` }));
|
|
1386
1536
|
function findPlugin(pluginsByKind, kind, name) {
|
|
1387
1537
|
return Option.match(HashMap.get(pluginsByKind, kind), {
|
|
1388
|
-
onNone: () => Effect.fail(PluginNotFoundError.make({ descriptor: `${kind}:${name} (no ${kind} plugins were loaded)` })),
|
|
1538
|
+
onNone: () => Effect$1.fail(PluginNotFoundError.make({ descriptor: `${kind}:${name} (no ${kind} plugins were loaded)` })),
|
|
1389
1539
|
onSome: (descriptors) => findContribution(descriptors, kind, name)
|
|
1390
1540
|
});
|
|
1391
1541
|
}
|
|
@@ -1394,8 +1544,8 @@ function create(pluginsByKind, kind, name) {
|
|
|
1394
1544
|
}
|
|
1395
1545
|
function createAll(pluginsByKind, kind) {
|
|
1396
1546
|
const descriptors = HashMap.get(pluginsByKind, kind);
|
|
1397
|
-
if (Option.isNone(descriptors)) return Effect.succeed([]);
|
|
1398
|
-
return Effect.succeed(descriptors.value.filter((d) => d.kind === kind));
|
|
1547
|
+
if (Option.isNone(descriptors)) return Effect$1.succeed([]);
|
|
1548
|
+
return Effect$1.succeed(descriptors.value.filter((d) => d.kind === kind));
|
|
1399
1549
|
}
|
|
1400
1550
|
//#endregion
|
|
1401
1551
|
//#region src/plugin-worker-entry.ts
|
|
@@ -1404,10 +1554,32 @@ const missingWorkerEntry = (stage, kind, name) => (failure) => StageError.make({
|
|
|
1404
1554
|
reason: `the ${kind} plugin "${name}" is not among the loaded plugins`,
|
|
1405
1555
|
cause: failure
|
|
1406
1556
|
});
|
|
1407
|
-
const requiredSource = (params) => Effect.fromOption(findByKindAndName(params.loaded.pluginSources, params.kind, params.name), () => PluginNotFoundError.make({ descriptor: `${params.kind}:${params.name}` }));
|
|
1408
|
-
const resolvePluginWorkerEntry = (params) => requiredSource(params).pipe(Effect.map((source) => ({
|
|
1557
|
+
const requiredSource = (params) => Effect$1.fromOption(findByKindAndName(params.loaded.pluginSources, params.kind, params.name), () => PluginNotFoundError.make({ descriptor: `${params.kind}:${params.name}` }));
|
|
1558
|
+
const resolvePluginWorkerEntry = (params) => requiredSource(params).pipe(Effect$1.map((source) => ({
|
|
1409
1559
|
kind: params.kind,
|
|
1410
1560
|
entrypoint: source.workerEntry
|
|
1411
1561
|
})));
|
|
1562
|
+
const resolveConfiguredWorkerSpawn = (params) => {
|
|
1563
|
+
const byName = (name) => requiredSource({
|
|
1564
|
+
loaded: params.loaded,
|
|
1565
|
+
kind: params.kind,
|
|
1566
|
+
name
|
|
1567
|
+
}).pipe(Effect$1.map((source) => ({
|
|
1568
|
+
name,
|
|
1569
|
+
spawn: {
|
|
1570
|
+
kind: params.kind,
|
|
1571
|
+
entrypoint: source.workerEntry
|
|
1572
|
+
}
|
|
1573
|
+
})));
|
|
1574
|
+
if (typeof params.configured === "string") return byName(params.configured);
|
|
1575
|
+
const pluginUrl = params.configured.plugin;
|
|
1576
|
+
return Effect$1.fromOption(Option.map(Option.fromUndefinedOr(params.loaded.pluginSources.find((source) => source.kind === params.kind && source.modulePath === pluginUrl)), (source) => ({
|
|
1577
|
+
name: source.name,
|
|
1578
|
+
spawn: {
|
|
1579
|
+
kind: params.kind,
|
|
1580
|
+
entrypoint: source.workerEntry
|
|
1581
|
+
}
|
|
1582
|
+
})), () => PluginNotFoundError.make({ descriptor: `${params.kind}:${pluginUrl}` }));
|
|
1583
|
+
};
|
|
1412
1584
|
//#endregion
|
|
1413
|
-
export {
|
|
1585
|
+
export { ExtendsStepRefused as $, makeConcurrency as A, partsOfEffectSpan as B, nodePlatformLayer as C, IdGenerator as D, makeWorkerClient as E, isVmRunner as F, ConfigError as G, ALWAYS_IGNORE as H, vmRunnerCapabilities as I, ConfigFileUnreadableError as J, ConfigFileInvalidError as K, vmRunnerName as L, OutOfMemoryError as M, WorkerBootTimeoutError as N, classifyWorkerExit as O, VmRunner as P, ExtendsStepRead as Q, vmTestRunner as R, makeChildProcessTestRunner as S, connectRetry as T, MUTATION_RANGE_REGEX as U, StrykerError as V, ConfigDocumentSchema as W, ExtendsStepDocumentSchema as X, ConfigFileUnsupportedError as Y, ExtendsStepDone as Z, readConfig as _, createAll as a, ReadConfigCommand as at, buildTestRunner as b, PluginLoadFailedError as c, survivorsPriorReport as ct, describeErrors as d, PrepareError as dt, ExtendsStepResolve as et, forkCoreSchema as f, StageError as ft, mergeConfigs as g, loadConfigCell as h, create as i, MergeResult as it, ChildProcessCrashedError as j, layer as k, PluginNotFoundError as l, compileIgnoreRule as lt, initialExtendsStepState as m, resolveConfiguredWorkerSpawn as n, ImportedModuleSchema as nt, loadPlugins as o, extendsPropertySchema as ot, importModule as p, ConfigFileNotFoundError as q, resolvePluginWorkerEntry as r, MergeCommand as rt, pluginUrlsFromOptions as s, forkOptionsSchema as st, missingWorkerEntry as t, ExtendsStepStateSchema as tt, decideExtendsStep as u, matchesGlob as ut, resolveExtends as v, WorkerLauncher as w, isCommandRunner as x, validateOptions as y, encodeWorkerOptions as z };
|