@poleski/quality-tools 0.1.3 → 0.1.4
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 +6 -0
- package/dist/cli/main.js +112 -2
- package/package.json +2 -2
- package/stryker/quality-tools-vitest-runner.mjs +4 -0
- package/stryker.config.cjs +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @poleski/quality-tools
|
|
2
2
|
|
|
3
|
+
## 0.1.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 289c605: Stop host projects from seeing Stryker warnings about the upstream Vitest runner plugin when using the bundled mutation config, and throttle Stryker mutation progress into the existing one-minute heartbeat.
|
|
8
|
+
|
|
3
9
|
## 0.1.3
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/dist/cli/main.js
CHANGED
|
@@ -1472,6 +1472,99 @@ function buildMutationEnv(options = {}) {
|
|
|
1472
1472
|
};
|
|
1473
1473
|
}
|
|
1474
1474
|
|
|
1475
|
+
// src/mutation/runner/progress.ts
|
|
1476
|
+
var ANSI_PATTERN = new RegExp(
|
|
1477
|
+
`${String.fromCharCode(27)}(?:[@-Z\\\\-_]|\\[[0-?]*[ -/]*[@-~])`,
|
|
1478
|
+
"g"
|
|
1479
|
+
);
|
|
1480
|
+
var PROGRESS_PATTERN = /Mutation testing\s+(?:\[(?<bracketStatus>[^\]]*)\]\s*)?(?<percent>\d+%)\s+\((?<timing>elapsed:[^)]+)\)\s+(?<count>\d+\/\d+)\s+(?:Mutants?|tested)(?:\s+\((?<tailStatus>\d+\s+survived,\s*\d+\s+timed out)\))?/i;
|
|
1481
|
+
var STATUS_TAIL_PATTERN = /(?:^|\s)tested\s+\((?<status>\d+\s+survived,\s*\d+\s+timed out)\)\s*$/i;
|
|
1482
|
+
function cleanProgressText(text) {
|
|
1483
|
+
return text.replace(ANSI_PATTERN, "").trim();
|
|
1484
|
+
}
|
|
1485
|
+
function normalizeStatus(status) {
|
|
1486
|
+
const trimmed = status?.trim();
|
|
1487
|
+
return trimmed && trimmed.length > 0 ? trimmed : void 0;
|
|
1488
|
+
}
|
|
1489
|
+
var MutationProgressTracker = class {
|
|
1490
|
+
latest;
|
|
1491
|
+
formatLatest() {
|
|
1492
|
+
if (!this.latest) {
|
|
1493
|
+
return void 0;
|
|
1494
|
+
}
|
|
1495
|
+
return [
|
|
1496
|
+
"Mutation testing",
|
|
1497
|
+
`[${this.latest.status ?? ""}]`,
|
|
1498
|
+
this.latest.percent,
|
|
1499
|
+
`(${this.latest.timing})`,
|
|
1500
|
+
this.latest.count,
|
|
1501
|
+
"Mutants"
|
|
1502
|
+
].join(" ");
|
|
1503
|
+
}
|
|
1504
|
+
observe(text) {
|
|
1505
|
+
const cleanText = cleanProgressText(text);
|
|
1506
|
+
if (cleanText.length === 0) {
|
|
1507
|
+
return false;
|
|
1508
|
+
}
|
|
1509
|
+
const progressMatch = PROGRESS_PATTERN.exec(cleanText);
|
|
1510
|
+
if (progressMatch?.groups) {
|
|
1511
|
+
this.latest = {
|
|
1512
|
+
count: progressMatch.groups.count,
|
|
1513
|
+
percent: progressMatch.groups.percent,
|
|
1514
|
+
timing: progressMatch.groups.timing,
|
|
1515
|
+
status: normalizeStatus(progressMatch.groups.tailStatus) ?? normalizeStatus(progressMatch.groups.bracketStatus) ?? this.latest?.status
|
|
1516
|
+
};
|
|
1517
|
+
return true;
|
|
1518
|
+
}
|
|
1519
|
+
const statusMatch = STATUS_TAIL_PATTERN.exec(cleanText);
|
|
1520
|
+
if (statusMatch?.groups && this.latest) {
|
|
1521
|
+
this.latest = {
|
|
1522
|
+
...this.latest,
|
|
1523
|
+
status: normalizeStatus(statusMatch.groups.status) ?? this.latest.status
|
|
1524
|
+
};
|
|
1525
|
+
return true;
|
|
1526
|
+
}
|
|
1527
|
+
return false;
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
function createMutationProgressOutputForwarder(tracker, writeOutput) {
|
|
1531
|
+
let pending = "";
|
|
1532
|
+
const handleSegment = (segment, delimiter) => {
|
|
1533
|
+
if (tracker.observe(segment)) {
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
writeOutput(`${segment}${delimiter}`);
|
|
1537
|
+
};
|
|
1538
|
+
const flushPendingProgress = () => {
|
|
1539
|
+
if (tracker.observe(pending)) {
|
|
1540
|
+
pending = "";
|
|
1541
|
+
return true;
|
|
1542
|
+
}
|
|
1543
|
+
return false;
|
|
1544
|
+
};
|
|
1545
|
+
return {
|
|
1546
|
+
flush() {
|
|
1547
|
+
if (pending.length === 0 || flushPendingProgress()) {
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
writeOutput(pending);
|
|
1551
|
+
pending = "";
|
|
1552
|
+
},
|
|
1553
|
+
write(text) {
|
|
1554
|
+
pending += text;
|
|
1555
|
+
let delimiterIndex = pending.search(/[\r\n]/);
|
|
1556
|
+
while (delimiterIndex >= 0) {
|
|
1557
|
+
const segment = pending.slice(0, delimiterIndex);
|
|
1558
|
+
const delimiter = pending[delimiterIndex] === "\n" ? "\n" : "";
|
|
1559
|
+
pending = pending.slice(delimiterIndex + 1);
|
|
1560
|
+
handleSegment(segment, delimiter);
|
|
1561
|
+
delimiterIndex = pending.search(/[\r\n]/);
|
|
1562
|
+
}
|
|
1563
|
+
flushPendingProgress();
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1475
1568
|
// src/mutation/runner/run.ts
|
|
1476
1569
|
var MUTATION_PROGRESS_INTERVAL_MS = 6e4;
|
|
1477
1570
|
function formatElapsedDuration(durationMs) {
|
|
@@ -1483,18 +1576,35 @@ function formatElapsedDuration(durationMs) {
|
|
|
1483
1576
|
function runStryker(args2, env, target) {
|
|
1484
1577
|
return new Promise((resolve8, reject) => {
|
|
1485
1578
|
const startedAt = Date.now();
|
|
1579
|
+
const progressTracker = new MutationProgressTracker();
|
|
1580
|
+
const stdoutForwarder = createMutationProgressOutputForwarder(
|
|
1581
|
+
progressTracker,
|
|
1582
|
+
(text) => process.stdout.write(text)
|
|
1583
|
+
);
|
|
1584
|
+
const stderrForwarder = createMutationProgressOutputForwarder(
|
|
1585
|
+
progressTracker,
|
|
1586
|
+
(text) => process.stderr.write(text)
|
|
1587
|
+
);
|
|
1486
1588
|
const child = spawn(process.execPath, [strykerBinPath(), ...args2], {
|
|
1487
1589
|
cwd: REPO_ROOT,
|
|
1488
1590
|
env,
|
|
1489
|
-
stdio: "inherit"
|
|
1591
|
+
stdio: ["inherit", "pipe", "pipe"]
|
|
1592
|
+
});
|
|
1593
|
+
child.stdout?.on("data", (chunk) => {
|
|
1594
|
+
stdoutForwarder.write(String(chunk));
|
|
1595
|
+
});
|
|
1596
|
+
child.stderr?.on("data", (chunk) => {
|
|
1597
|
+
stderrForwarder.write(String(chunk));
|
|
1490
1598
|
});
|
|
1491
1599
|
const progressTimer = setInterval(() => {
|
|
1492
1600
|
console.error(
|
|
1493
|
-
`[mutation] Still running ${target.relativePath} after ${formatElapsedDuration(Date.now() - startedAt)}...`
|
|
1601
|
+
progressTracker.formatLatest() ?? `[mutation] Still running ${target.relativePath} after ${formatElapsedDuration(Date.now() - startedAt)}...`
|
|
1494
1602
|
);
|
|
1495
1603
|
}, MUTATION_PROGRESS_INTERVAL_MS);
|
|
1496
1604
|
const clearProgressTimer = () => {
|
|
1497
1605
|
clearInterval(progressTimer);
|
|
1606
|
+
stdoutForwarder.flush();
|
|
1607
|
+
stderrForwarder.flush();
|
|
1498
1608
|
};
|
|
1499
1609
|
child.once("error", (error) => {
|
|
1500
1610
|
clearProgressTimer();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poleski/quality-tools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Portable TypeScript quality checks for project structure, complexity, mutation, and test health",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "esbuild src/cli/main.ts --bundle --platform=node --format=esm --packages=external --outfile=dist/cli/main.js",
|
|
45
45
|
"ci": "pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run build",
|
|
46
|
-
"release": "pnpm run ci && pnpm publish --
|
|
46
|
+
"release": "pnpm run ci && pnpm publish --no-git-checks",
|
|
47
47
|
"changeset": "changeset",
|
|
48
48
|
"cli": "tsx src/cli/main.ts",
|
|
49
49
|
"quality-tools": "pnpm run cli",
|
|
@@ -13,6 +13,10 @@ const { vitestWrapper } = await import(path.join(vitestRunnerRoot, 'dist/src/vit
|
|
|
13
13
|
const STRYKER_SETUP = path.join(vitestRunnerRoot, 'dist/src/stryker-setup.js');
|
|
14
14
|
const STRYKER_SETUP_SOURCE_MAP = 'stryker-setup.js.map';
|
|
15
15
|
|
|
16
|
+
export const strykerValidationSchema = JSON.parse(
|
|
17
|
+
fs.readFileSync(path.join(vitestRunnerRoot, 'dist/schema/vitest-runner-options.json'), 'utf-8'),
|
|
18
|
+
);
|
|
19
|
+
|
|
16
20
|
function createStrykerSetupSourceMap(setupFilePath) {
|
|
17
21
|
return JSON.stringify({
|
|
18
22
|
version: 3,
|
package/stryker.config.cjs
CHANGED
|
@@ -21,7 +21,6 @@ module.exports = {
|
|
|
21
21
|
testRunner: 'quality-tools-vitest',
|
|
22
22
|
plugins: [
|
|
23
23
|
path.join(packageRoot, 'stryker/quality-tools-vitest-runner.mjs'),
|
|
24
|
-
'@stryker-mutator/vitest-runner',
|
|
25
24
|
],
|
|
26
25
|
vitest: {
|
|
27
26
|
configFile: path.isAbsolute(vitestConfig) ? vitestConfig : path.join(hostRoot, vitestConfig),
|