@t09tanaka/test-gates 0.1.0 → 0.3.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 +40 -0
- package/README.md +194 -20
- package/dist/{chunk-LMD3ANJ2.js → chunk-F7WBEFIL.js} +53 -4
- package/dist/cli.js +368 -133
- package/dist/index.cjs +274 -81
- package/dist/index.d.cts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +222 -78
- package/dist/jest.cjs +53 -4
- package/dist/jest.js +1 -1
- package/dist/stryker.cjs +53 -4
- package/dist/stryker.js +1 -1
- package/dist/vitest.cjs +55 -5
- package/dist/vitest.d.cts +6 -0
- package/dist/vitest.d.ts +6 -0
- package/dist/vitest.js +3 -2
- package/package.json +9 -3
- package/schema/test-gates.schema.json +165 -28
package/dist/vitest.cjs
CHANGED
|
@@ -70,17 +70,17 @@ function allowanceProblems(entry) {
|
|
|
70
70
|
}
|
|
71
71
|
return problems;
|
|
72
72
|
}
|
|
73
|
-
function validateEquivalentMutants(gatePath, value) {
|
|
73
|
+
function validateEquivalentMutants(gatePath, value, field = "equivalentMutants") {
|
|
74
74
|
if (value === void 0) {
|
|
75
75
|
return [];
|
|
76
76
|
}
|
|
77
77
|
if (!Array.isArray(value)) {
|
|
78
|
-
return [{ file: MANIFEST_FILE, message: `${gatePath}:
|
|
78
|
+
return [{ file: MANIFEST_FILE, message: `${gatePath}: ${field} must be an array` }];
|
|
79
79
|
}
|
|
80
80
|
return value.flatMap(
|
|
81
81
|
(entry, index) => allowanceProblems(entry).map((problem) => ({
|
|
82
82
|
file: MANIFEST_FILE,
|
|
83
|
-
message: `${gatePath}:
|
|
83
|
+
message: `${gatePath}: ${field}[${index}]${problem}`
|
|
84
84
|
}))
|
|
85
85
|
);
|
|
86
86
|
}
|
|
@@ -160,6 +160,9 @@ function parseManifest(text) {
|
|
|
160
160
|
}
|
|
161
161
|
for (const gate of gates) {
|
|
162
162
|
violations.push(...validateEquivalentMutants(gate.path, gate.equivalentMutants));
|
|
163
|
+
violations.push(
|
|
164
|
+
...validateEquivalentMutants(gate.path, gate.expectedTimeouts, "expectedTimeouts")
|
|
165
|
+
);
|
|
163
166
|
}
|
|
164
167
|
return { gates, candidates, settings: raw.settings, violations };
|
|
165
168
|
}
|
|
@@ -208,8 +211,13 @@ var DEFAULT_FORBIDDEN_SOURCE = [
|
|
|
208
211
|
{
|
|
209
212
|
pattern: `^\\s*(['"])use (client|server)\\1`,
|
|
210
213
|
reason: "'use client' / 'use server' directive (not a pure module)"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
pattern: "\\bprocess\\??\\.env\\b",
|
|
217
|
+
reason: "reads process.env (not a pure module). Take the value as an argument"
|
|
211
218
|
}
|
|
212
219
|
];
|
|
220
|
+
var DEFAULT_MAX_TIMEOUTS = 0;
|
|
213
221
|
var SettingsError = class extends Error {
|
|
214
222
|
};
|
|
215
223
|
function isRecord2(value) {
|
|
@@ -309,7 +317,9 @@ function resolveSettings(raw) {
|
|
|
309
317
|
"gateCommand",
|
|
310
318
|
"selfcheck",
|
|
311
319
|
"stryker",
|
|
312
|
-
"lcov"
|
|
320
|
+
"lcov",
|
|
321
|
+
"imports",
|
|
322
|
+
"mutation"
|
|
313
323
|
],
|
|
314
324
|
"settings"
|
|
315
325
|
);
|
|
@@ -410,6 +420,42 @@ function resolveSettings(raw) {
|
|
|
410
420
|
"settings.selfcheck.failurePattern"
|
|
411
421
|
);
|
|
412
422
|
}
|
|
423
|
+
const importsSection = section(settings.imports, ["mode", "allow"], "settings.imports");
|
|
424
|
+
if (importsSection.mode !== void 0 && importsSection.mode !== "blocklist" && importsSection.mode !== "allowlist") {
|
|
425
|
+
throw new SettingsError('settings.imports.mode: must be "blocklist" or "allowlist"');
|
|
426
|
+
}
|
|
427
|
+
const importAllowRaw = importsSection.allow ?? [];
|
|
428
|
+
if (!Array.isArray(importAllowRaw)) {
|
|
429
|
+
throw new SettingsError("settings.imports.allow: must be an array");
|
|
430
|
+
}
|
|
431
|
+
const importAllow = importAllowRaw.map((item, index) => {
|
|
432
|
+
const at = `settings.imports.allow[${index}]`;
|
|
433
|
+
if (typeof item === "string" && item !== "") {
|
|
434
|
+
return { module: item };
|
|
435
|
+
}
|
|
436
|
+
if (isRecord2(item) && typeof item.pattern === "string" && item.pattern !== "") {
|
|
437
|
+
rejectUnknownKeys(item, ["pattern", "flags"], at);
|
|
438
|
+
if (item.flags !== void 0 && typeof item.flags !== "string") {
|
|
439
|
+
throw new SettingsError(`${at}.flags: must be a string`);
|
|
440
|
+
}
|
|
441
|
+
return { regex: compile(item.pattern, item.flags, at) };
|
|
442
|
+
}
|
|
443
|
+
if (isRecord2(item) && typeof item.module === "string" && item.module !== "") {
|
|
444
|
+
rejectUnknownKeys(item, ["module", "names"], at);
|
|
445
|
+
const names = stringArray(item.names, `${at}.names`);
|
|
446
|
+
if (names.length === 0) {
|
|
447
|
+
throw new SettingsError(`${at}.names: must list at least one name`);
|
|
448
|
+
}
|
|
449
|
+
return { module: item.module, names };
|
|
450
|
+
}
|
|
451
|
+
throw new SettingsError(
|
|
452
|
+
`${at}: must be a package name, { pattern, flags? } or { module, names }`
|
|
453
|
+
);
|
|
454
|
+
});
|
|
455
|
+
const mutation = section(settings.mutation, ["maxTimeouts"], "settings.mutation");
|
|
456
|
+
if (mutation.maxTimeouts !== void 0 && !(Number.isInteger(mutation.maxTimeouts) && mutation.maxTimeouts >= 0)) {
|
|
457
|
+
throw new SettingsError("settings.mutation.maxTimeouts: must be an integer >= 0");
|
|
458
|
+
}
|
|
413
459
|
const stryker = section(settings.stryker, ["configFile", "reportFile"], "settings.stryker");
|
|
414
460
|
const lcov = section(settings.lcov, ["file", "summaryExclude"], "settings.lcov");
|
|
415
461
|
return {
|
|
@@ -431,6 +477,9 @@ function resolveSettings(raw) {
|
|
|
431
477
|
selfcheckFailurePattern,
|
|
432
478
|
strykerConfigFile: optionalString(stryker.configFile, "settings.stryker.configFile"),
|
|
433
479
|
strykerReportFile: optionalString(stryker.reportFile, "settings.stryker.reportFile") ?? DEFAULT_REPORT_FILE,
|
|
480
|
+
importMode: importsSection.mode ?? "blocklist",
|
|
481
|
+
importAllow,
|
|
482
|
+
maxTimeouts: mutation.maxTimeouts ?? DEFAULT_MAX_TIMEOUTS,
|
|
434
483
|
lcovFile: optionalString(lcov.file, "settings.lcov.file"),
|
|
435
484
|
lcovSummaryExclude: lcov.summaryExclude === void 0 ? [] : stringArray(lcov.summaryExclude, "settings.lcov.summaryExclude").map(
|
|
436
485
|
(pattern, index) => compile(pattern, void 0, `settings.lcov.summaryExclude[${index}]`)
|
|
@@ -561,7 +610,7 @@ function resolveRootDir(rootDir) {
|
|
|
561
610
|
|
|
562
611
|
// src/vitest.ts
|
|
563
612
|
function createVitestGatesConfig(options = {}) {
|
|
564
|
-
const { rootDir: rootDirOption, test = {}, ...overrides } = options;
|
|
613
|
+
const { rootDir: rootDirOption, coverageProvider, test = {}, ...overrides } = options;
|
|
565
614
|
const rootDir = resolveRootDir(rootDirOption);
|
|
566
615
|
const manifest = loadGates(rootDir);
|
|
567
616
|
const { coverage = {}, ...testOverrides } = test;
|
|
@@ -581,6 +630,7 @@ function createVitestGatesConfig(options = {}) {
|
|
|
581
630
|
reporter: ["text"],
|
|
582
631
|
reportsDirectory: "coverage/gates",
|
|
583
632
|
...coverage,
|
|
633
|
+
...coverageProvider === void 0 ? {} : { provider: coverageProvider },
|
|
584
634
|
enabled: true,
|
|
585
635
|
include: manifest.gates.map((gate) => gate.path),
|
|
586
636
|
thresholds: { perFile: true, ...FULL_COVERAGE }
|
package/dist/vitest.d.cts
CHANGED
|
@@ -5,6 +5,12 @@ interface VitestGatesOptions {
|
|
|
5
5
|
test?: Record<string, unknown> & {
|
|
6
6
|
coverage?: Record<string, unknown>;
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* Coverage provider. Default `v8`. `istanbul` (needs `@vitest/coverage-istanbul`) also counts
|
|
10
|
+
* the skipped side of an `if` without `else` on Vitest 3, and an unused default parameter on
|
|
11
|
+
* every version; see the README for the measurements. Same as `test.coverage.provider`.
|
|
12
|
+
*/
|
|
13
|
+
coverageProvider?: 'v8' | 'istanbul';
|
|
8
14
|
/** Any other Vite option: resolve.alias, esbuild, plugins, … */
|
|
9
15
|
[option: string]: unknown;
|
|
10
16
|
}
|
package/dist/vitest.d.ts
CHANGED
|
@@ -5,6 +5,12 @@ interface VitestGatesOptions {
|
|
|
5
5
|
test?: Record<string, unknown> & {
|
|
6
6
|
coverage?: Record<string, unknown>;
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* Coverage provider. Default `v8`. `istanbul` (needs `@vitest/coverage-istanbul`) also counts
|
|
10
|
+
* the skipped side of an `if` without `else` on Vitest 3, and an unused default parameter on
|
|
11
|
+
* every version; see the README for the measurements. Same as `test.coverage.provider`.
|
|
12
|
+
*/
|
|
13
|
+
coverageProvider?: 'v8' | 'istanbul';
|
|
8
14
|
/** Any other Vite option: resolve.alias, esbuild, plugins, … */
|
|
9
15
|
[option: string]: unknown;
|
|
10
16
|
}
|
package/dist/vitest.js
CHANGED
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
NO_SPEC_PATTERN,
|
|
7
7
|
gateSpecs,
|
|
8
8
|
loadGates
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-F7WBEFIL.js";
|
|
10
10
|
|
|
11
11
|
// src/vitest.ts
|
|
12
12
|
function createVitestGatesConfig(options = {}) {
|
|
13
|
-
const { rootDir: rootDirOption, test = {}, ...overrides } = options;
|
|
13
|
+
const { rootDir: rootDirOption, coverageProvider, test = {}, ...overrides } = options;
|
|
14
14
|
const rootDir = resolveRootDir(rootDirOption);
|
|
15
15
|
const manifest = loadGates(rootDir);
|
|
16
16
|
const { coverage = {}, ...testOverrides } = test;
|
|
@@ -30,6 +30,7 @@ function createVitestGatesConfig(options = {}) {
|
|
|
30
30
|
reporter: ["text"],
|
|
31
31
|
reportsDirectory: "coverage/gates",
|
|
32
32
|
...coverage,
|
|
33
|
+
...coverageProvider === void 0 ? {} : { provider: coverageProvider },
|
|
33
34
|
enabled: true,
|
|
34
35
|
include: manifest.gates.map((gate) => gate.path),
|
|
35
36
|
thresholds: { perFile: true, ...FULL_COVERAGE }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t09tanaka/test-gates",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Test gates for the code that must not be wrong: per-file 100% coverage as the floor, mutation testing with a per-mutant allow list as the real check.",
|
|
5
5
|
"author": "t09tanaka",
|
|
6
6
|
"license": "MIT",
|
|
@@ -76,7 +76,8 @@
|
|
|
76
76
|
},
|
|
77
77
|
"files": [
|
|
78
78
|
"dist",
|
|
79
|
-
"schema"
|
|
79
|
+
"schema",
|
|
80
|
+
"CHANGELOG.md"
|
|
80
81
|
],
|
|
81
82
|
"engines": {
|
|
82
83
|
"node": ">=20"
|
|
@@ -103,7 +104,8 @@
|
|
|
103
104
|
"peerDependencies": {
|
|
104
105
|
"@stryker-mutator/core": ">=9",
|
|
105
106
|
"jest": ">=29",
|
|
106
|
-
"vitest": ">=3"
|
|
107
|
+
"vitest": ">=3",
|
|
108
|
+
"@vitest/coverage-istanbul": ">=3"
|
|
107
109
|
},
|
|
108
110
|
"peerDependenciesMeta": {
|
|
109
111
|
"@stryker-mutator/core": {
|
|
@@ -114,6 +116,9 @@
|
|
|
114
116
|
},
|
|
115
117
|
"vitest": {
|
|
116
118
|
"optional": true
|
|
119
|
+
},
|
|
120
|
+
"@vitest/coverage-istanbul": {
|
|
121
|
+
"optional": true
|
|
117
122
|
}
|
|
118
123
|
},
|
|
119
124
|
"devDependencies": {
|
|
@@ -121,6 +126,7 @@
|
|
|
121
126
|
"@stryker-mutator/core": "^10.0.0",
|
|
122
127
|
"@stryker-mutator/vitest-runner": "^10.0.0",
|
|
123
128
|
"@types/node": "^22.20.5",
|
|
129
|
+
"@vitest/coverage-istanbul": "^3.2.7",
|
|
124
130
|
"@vitest/coverage-v8": "^3.2.7",
|
|
125
131
|
"eslint": "^9.39.5",
|
|
126
132
|
"eslint-config-prettier": "^10.1.8",
|
|
@@ -7,22 +7,34 @@
|
|
|
7
7
|
"required": ["gates"],
|
|
8
8
|
"additionalProperties": false,
|
|
9
9
|
"properties": {
|
|
10
|
-
"$schema": {
|
|
10
|
+
"$schema": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
11
13
|
"gates": {
|
|
12
14
|
"description": "Pure modules that hold an important decision. Each must reach 100% on all four coverage metrics and leave no surviving mutant outside its allow list.",
|
|
13
15
|
"type": "array",
|
|
14
16
|
"minItems": 1,
|
|
15
|
-
"items": {
|
|
17
|
+
"items": {
|
|
18
|
+
"$ref": "#/definitions/gate"
|
|
19
|
+
}
|
|
16
20
|
},
|
|
17
21
|
"candidates": {
|
|
18
22
|
"description": "Important logic that cannot be a gate yet. Nothing is enforced for these beyond their existence.",
|
|
19
23
|
"type": "array",
|
|
20
|
-
"items": {
|
|
24
|
+
"items": {
|
|
25
|
+
"$ref": "#/definitions/candidate"
|
|
26
|
+
}
|
|
21
27
|
},
|
|
22
|
-
"settings": {
|
|
28
|
+
"settings": {
|
|
29
|
+
"$ref": "#/definitions/settings"
|
|
30
|
+
}
|
|
23
31
|
},
|
|
24
32
|
"definitions": {
|
|
25
|
-
"nonEmptyString": {
|
|
33
|
+
"nonEmptyString": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"minLength": 1,
|
|
36
|
+
"pattern": "\\S"
|
|
37
|
+
},
|
|
26
38
|
"gate": {
|
|
27
39
|
"type": "object",
|
|
28
40
|
"required": ["path", "decides", "impact"],
|
|
@@ -43,7 +55,16 @@
|
|
|
43
55
|
"equivalentMutants": {
|
|
44
56
|
"description": "Surviving mutants that are allowed because they cannot be observed from outside. One entry allows exactly one mutant.",
|
|
45
57
|
"type": "array",
|
|
46
|
-
"items": {
|
|
58
|
+
"items": {
|
|
59
|
+
"$ref": "#/definitions/equivalentMutant"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"expectedTimeouts": {
|
|
63
|
+
"description": "Since 0.2.0. Mutants that can never finish (a loop that no longer ends) and therefore always time out. Same key as equivalentMutants. They are not counted against settings.mutation.maxTimeouts. An entry whose mutant survives, or does not exist, fails the run; one that is killed is accepted.",
|
|
64
|
+
"type": "array",
|
|
65
|
+
"items": {
|
|
66
|
+
"$ref": "#/definitions/equivalentMutant"
|
|
67
|
+
}
|
|
47
68
|
}
|
|
48
69
|
}
|
|
49
70
|
},
|
|
@@ -84,7 +105,9 @@
|
|
|
84
105
|
"description": "File or directory, relative to the subproject.",
|
|
85
106
|
"$ref": "#/definitions/nonEmptyString"
|
|
86
107
|
},
|
|
87
|
-
"decides": {
|
|
108
|
+
"decides": {
|
|
109
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
110
|
+
},
|
|
88
111
|
"blocker": {
|
|
89
112
|
"description": "What has to happen before this can become a gate.",
|
|
90
113
|
"$ref": "#/definitions/nonEmptyString"
|
|
@@ -94,15 +117,23 @@
|
|
|
94
117
|
"patternRule": {
|
|
95
118
|
"description": "A regular expression, either as a string or with flags and a reason.",
|
|
96
119
|
"oneOf": [
|
|
97
|
-
{
|
|
120
|
+
{
|
|
121
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
122
|
+
},
|
|
98
123
|
{
|
|
99
124
|
"type": "object",
|
|
100
125
|
"required": ["pattern"],
|
|
101
126
|
"additionalProperties": false,
|
|
102
127
|
"properties": {
|
|
103
|
-
"pattern": {
|
|
104
|
-
|
|
105
|
-
|
|
128
|
+
"pattern": {
|
|
129
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
130
|
+
},
|
|
131
|
+
"flags": {
|
|
132
|
+
"type": "string"
|
|
133
|
+
},
|
|
134
|
+
"reason": {
|
|
135
|
+
"type": "string"
|
|
136
|
+
}
|
|
106
137
|
}
|
|
107
138
|
}
|
|
108
139
|
]
|
|
@@ -116,7 +147,12 @@
|
|
|
116
147
|
"type": "boolean",
|
|
117
148
|
"default": true
|
|
118
149
|
},
|
|
119
|
-
"add": {
|
|
150
|
+
"add": {
|
|
151
|
+
"type": "array",
|
|
152
|
+
"items": {
|
|
153
|
+
"$ref": "#/definitions/patternRule"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
120
156
|
}
|
|
121
157
|
},
|
|
122
158
|
"settings": {
|
|
@@ -128,7 +164,10 @@
|
|
|
128
164
|
"description": "File extensions a gate may have.",
|
|
129
165
|
"type": "array",
|
|
130
166
|
"minItems": 1,
|
|
131
|
-
"items": {
|
|
167
|
+
"items": {
|
|
168
|
+
"type": "string",
|
|
169
|
+
"pattern": "^\\."
|
|
170
|
+
},
|
|
132
171
|
"default": [".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"]
|
|
133
172
|
},
|
|
134
173
|
"spec": {
|
|
@@ -138,7 +177,9 @@
|
|
|
138
177
|
"suffixes": {
|
|
139
178
|
"description": "Replaces the gate's extension to form the spec path. Default: \".spec<ext>\" and \".test<ext>\". An empty array means the project has no spec convention.",
|
|
140
179
|
"type": "array",
|
|
141
|
-
"items": {
|
|
180
|
+
"items": {
|
|
181
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
182
|
+
}
|
|
142
183
|
},
|
|
143
184
|
"rewrite": {
|
|
144
185
|
"description": "Regular expression replacements applied to the gate path before the suffix, for specs that live in another directory.",
|
|
@@ -148,8 +189,12 @@
|
|
|
148
189
|
"required": ["from", "to"],
|
|
149
190
|
"additionalProperties": false,
|
|
150
191
|
"properties": {
|
|
151
|
-
"from": {
|
|
152
|
-
|
|
192
|
+
"from": {
|
|
193
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
194
|
+
},
|
|
195
|
+
"to": {
|
|
196
|
+
"type": "string"
|
|
197
|
+
}
|
|
153
198
|
}
|
|
154
199
|
}
|
|
155
200
|
}
|
|
@@ -160,12 +205,22 @@
|
|
|
160
205
|
"type": "object",
|
|
161
206
|
"additionalProperties": false,
|
|
162
207
|
"properties": {
|
|
163
|
-
"defaults": {
|
|
164
|
-
|
|
208
|
+
"defaults": {
|
|
209
|
+
"type": "boolean",
|
|
210
|
+
"default": true
|
|
211
|
+
},
|
|
212
|
+
"add": {
|
|
213
|
+
"type": "array",
|
|
214
|
+
"items": {
|
|
215
|
+
"$ref": "#/definitions/patternRule"
|
|
216
|
+
}
|
|
217
|
+
},
|
|
165
218
|
"allow": {
|
|
166
219
|
"description": "Module specifiers exempt from every rule (regular expressions).",
|
|
167
220
|
"type": "array",
|
|
168
|
-
"items": {
|
|
221
|
+
"items": {
|
|
222
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
223
|
+
}
|
|
169
224
|
}
|
|
170
225
|
}
|
|
171
226
|
},
|
|
@@ -174,7 +229,10 @@
|
|
|
174
229
|
"type": "object",
|
|
175
230
|
"additionalProperties": false,
|
|
176
231
|
"properties": {
|
|
177
|
-
"defaults": {
|
|
232
|
+
"defaults": {
|
|
233
|
+
"type": "boolean",
|
|
234
|
+
"default": true
|
|
235
|
+
},
|
|
178
236
|
"add": {
|
|
179
237
|
"type": "array",
|
|
180
238
|
"items": {
|
|
@@ -182,13 +240,19 @@
|
|
|
182
240
|
"required": ["module", "names"],
|
|
183
241
|
"additionalProperties": false,
|
|
184
242
|
"properties": {
|
|
185
|
-
"module": {
|
|
243
|
+
"module": {
|
|
244
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
245
|
+
},
|
|
186
246
|
"names": {
|
|
187
247
|
"type": "array",
|
|
188
248
|
"minItems": 1,
|
|
189
|
-
"items": {
|
|
249
|
+
"items": {
|
|
250
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
251
|
+
}
|
|
190
252
|
},
|
|
191
|
-
"reason": {
|
|
253
|
+
"reason": {
|
|
254
|
+
"type": "string"
|
|
255
|
+
}
|
|
192
256
|
}
|
|
193
257
|
}
|
|
194
258
|
}
|
|
@@ -199,7 +263,7 @@
|
|
|
199
263
|
"$ref": "#/definitions/ruleList"
|
|
200
264
|
},
|
|
201
265
|
"forbiddenSource": {
|
|
202
|
-
"description": "Lines that must not appear in a gate (regular expressions, tested one line at a time). Default: 'use client' / 'use server'.",
|
|
266
|
+
"description": "Lines that must not appear in a gate (regular expressions, tested one line at a time). Default: 'use client' / 'use server', and process.env (since 0.3.0).",
|
|
203
267
|
"$ref": "#/definitions/ruleList"
|
|
204
268
|
},
|
|
205
269
|
"importAliases": {
|
|
@@ -210,8 +274,12 @@
|
|
|
210
274
|
"required": ["prefix", "target"],
|
|
211
275
|
"additionalProperties": false,
|
|
212
276
|
"properties": {
|
|
213
|
-
"prefix": {
|
|
214
|
-
|
|
277
|
+
"prefix": {
|
|
278
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
279
|
+
},
|
|
280
|
+
"target": {
|
|
281
|
+
"type": "string"
|
|
282
|
+
}
|
|
215
283
|
}
|
|
216
284
|
}
|
|
217
285
|
},
|
|
@@ -219,7 +287,9 @@
|
|
|
219
287
|
"description": "The command `test-gates selfcheck` runs. Default: `jest --config jest.gates.config.*` or `vitest run --config vitest.gates.config.*`, whichever config exists.",
|
|
220
288
|
"type": "array",
|
|
221
289
|
"minItems": 1,
|
|
222
|
-
"items": {
|
|
290
|
+
"items": {
|
|
291
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
292
|
+
}
|
|
223
293
|
},
|
|
224
294
|
"selfcheck": {
|
|
225
295
|
"type": "object",
|
|
@@ -262,7 +332,74 @@
|
|
|
262
332
|
"summaryExclude": {
|
|
263
333
|
"description": "Paths left out of the reference total (regular expressions).",
|
|
264
334
|
"type": "array",
|
|
265
|
-
"items": {
|
|
335
|
+
"items": {
|
|
336
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
"imports": {
|
|
342
|
+
"description": "Since 0.2.0. How runtime imports of a gate are judged.",
|
|
343
|
+
"type": "object",
|
|
344
|
+
"additionalProperties": false,
|
|
345
|
+
"properties": {
|
|
346
|
+
"mode": {
|
|
347
|
+
"description": "blocklist (default): only the impureImports rules apply. allowlist: in addition, a runtime import must be relative, go through an import alias, or be listed in allow; a project import must not point at a file matched by impurePaths; a computed require() / import() is rejected.",
|
|
348
|
+
"enum": ["blocklist", "allowlist"],
|
|
349
|
+
"default": "blocklist"
|
|
350
|
+
},
|
|
351
|
+
"allow": {
|
|
352
|
+
"description": "Packages a gate may import in allowlist mode. A string is a package name (its subpaths included). { pattern } is a regular expression tested against the specifier. { module, names } allows only those named imports.",
|
|
353
|
+
"type": "array",
|
|
354
|
+
"items": {
|
|
355
|
+
"oneOf": [
|
|
356
|
+
{
|
|
357
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
"type": "object",
|
|
361
|
+
"required": ["pattern"],
|
|
362
|
+
"additionalProperties": false,
|
|
363
|
+
"properties": {
|
|
364
|
+
"pattern": {
|
|
365
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
366
|
+
},
|
|
367
|
+
"flags": {
|
|
368
|
+
"type": "string"
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
"type": "object",
|
|
374
|
+
"required": ["module", "names"],
|
|
375
|
+
"additionalProperties": false,
|
|
376
|
+
"properties": {
|
|
377
|
+
"module": {
|
|
378
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
379
|
+
},
|
|
380
|
+
"names": {
|
|
381
|
+
"type": "array",
|
|
382
|
+
"minItems": 1,
|
|
383
|
+
"items": {
|
|
384
|
+
"$ref": "#/definitions/nonEmptyString"
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
]
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
"mutation": {
|
|
395
|
+
"description": "Since 0.2.0.",
|
|
396
|
+
"type": "object",
|
|
397
|
+
"additionalProperties": false,
|
|
398
|
+
"properties": {
|
|
399
|
+
"maxTimeouts": {
|
|
400
|
+
"description": "How many timed-out mutants outside expectedTimeouts are tolerated. Above it the run fails. Default: 0 (since 0.3.0; before that, not set meant no limit).",
|
|
401
|
+
"type": "integer",
|
|
402
|
+
"minimum": 0
|
|
266
403
|
}
|
|
267
404
|
}
|
|
268
405
|
}
|