doc-detective-common 4.0.0 → 4.0.1-dev.2
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/package.json
CHANGED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Coverage Ratchet Script
|
|
5
|
+
*
|
|
6
|
+
* Compares current coverage against baseline thresholds.
|
|
7
|
+
* Fails if any metric has decreased.
|
|
8
|
+
*
|
|
9
|
+
* Usage: node scripts/check-coverage-ratchet.cjs
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
const THRESHOLDS_FILE = path.join(__dirname, '..', 'coverage-thresholds.json');
|
|
16
|
+
const COVERAGE_SUMMARY_FILE = path.join(__dirname, '..', 'coverage', 'coverage-summary.json');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Load and parse JSON from the given path, exiting the process with code 1 if the file is missing or cannot be parsed.
|
|
20
|
+
* @param {string} filePath - Filesystem path to the JSON file.
|
|
21
|
+
* @param {string} description - Human-readable name for the file used in error messages.
|
|
22
|
+
* @returns {Object} The parsed JSON object.
|
|
23
|
+
*/
|
|
24
|
+
function loadJSON(filePath, description) {
|
|
25
|
+
if (!fs.existsSync(filePath)) {
|
|
26
|
+
console.error(`Error: ${description} not found at ${filePath}`);
|
|
27
|
+
console.error(`Run 'npm run test:coverage' first to generate coverage data.`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(`Error parsing ${description}: ${error.message}`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Compares current test coverage against the stored baseline thresholds and enforces the coverage ratchet.
|
|
41
|
+
*
|
|
42
|
+
* Loads baseline thresholds and the current coverage summary, prints a per-metric table of baseline vs current values and statuses, and enforces policy:
|
|
43
|
+
* - Exits with code 1 if any metric has decreased relative to the baseline.
|
|
44
|
+
* - If one or more metrics have improved, prints suggested threshold updates.
|
|
45
|
+
* - Exits with code 0 when all metrics meet or exceed their baselines.
|
|
46
|
+
*/
|
|
47
|
+
function main() {
|
|
48
|
+
// Load baseline thresholds
|
|
49
|
+
const thresholds = loadJSON(THRESHOLDS_FILE, 'Coverage thresholds file');
|
|
50
|
+
|
|
51
|
+
// Load current coverage
|
|
52
|
+
const coverageSummary = loadJSON(COVERAGE_SUMMARY_FILE, 'Coverage summary');
|
|
53
|
+
|
|
54
|
+
const current = coverageSummary.total;
|
|
55
|
+
const metrics = ['lines', 'statements', 'functions', 'branches'];
|
|
56
|
+
|
|
57
|
+
let failed = false;
|
|
58
|
+
const results = [];
|
|
59
|
+
|
|
60
|
+
console.log('\n=== Coverage Ratchet Check ===\n');
|
|
61
|
+
console.log('Metric | Baseline | Current | Status');
|
|
62
|
+
console.log('------------|----------|----------|--------');
|
|
63
|
+
|
|
64
|
+
for (const metric of metrics) {
|
|
65
|
+
const baseline = thresholds[metric];
|
|
66
|
+
const currentMetric = current?.[metric];
|
|
67
|
+
if (typeof baseline !== 'number' || typeof currentMetric?.pct !== 'number') {
|
|
68
|
+
console.error(`Invalid or missing coverage metric: "${metric}"`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
const currentValue = currentMetric.pct;
|
|
72
|
+
const diff = (currentValue - baseline).toFixed(2);
|
|
73
|
+
|
|
74
|
+
let status;
|
|
75
|
+
if (currentValue < baseline) {
|
|
76
|
+
status = `FAIL (${diff}%)`;
|
|
77
|
+
failed = true;
|
|
78
|
+
} else if (currentValue > baseline) {
|
|
79
|
+
status = `PASS (+${diff}%)`;
|
|
80
|
+
} else {
|
|
81
|
+
status = 'PASS';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const baselineStr = `${baseline.toFixed(2)}%`.padEnd(8);
|
|
85
|
+
const currentStr = `${currentValue.toFixed(2)}%`.padEnd(8);
|
|
86
|
+
const metricStr = metric.padEnd(11);
|
|
87
|
+
|
|
88
|
+
console.log(`${metricStr} | ${baselineStr} | ${currentStr} | ${status}`);
|
|
89
|
+
|
|
90
|
+
results.push({
|
|
91
|
+
metric,
|
|
92
|
+
baseline,
|
|
93
|
+
current: currentValue,
|
|
94
|
+
passed: currentValue >= baseline
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log('');
|
|
99
|
+
|
|
100
|
+
if (failed) {
|
|
101
|
+
console.error('Coverage ratchet check FAILED!');
|
|
102
|
+
console.error('Coverage has decreased from the baseline.');
|
|
103
|
+
console.error('Please add tests to restore coverage before committing.');
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check if we can bump thresholds
|
|
108
|
+
const canBump = results.filter(r => r.current > r.baseline);
|
|
109
|
+
if (canBump.length > 0) {
|
|
110
|
+
console.log('Coverage has improved! Consider updating thresholds:');
|
|
111
|
+
console.log('');
|
|
112
|
+
for (const r of canBump) {
|
|
113
|
+
console.log(` "${r.metric}": ${r.current.toFixed(2)}`);
|
|
114
|
+
}
|
|
115
|
+
console.log('');
|
|
116
|
+
console.log(`Update ${THRESHOLDS_FILE} to lock in the new baseline.`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log('Coverage ratchet check PASSED!');
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
main();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { build } from "esbuild";
|
|
2
|
+
import { copyFile } from "fs/promises";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
const distDir = path.join(__dirname, "..", "dist");
|
|
9
|
+
|
|
10
|
+
await build({
|
|
11
|
+
entryPoints: [path.join(distDir, "index.js")],
|
|
12
|
+
outfile: path.join(distDir, "index.cjs"),
|
|
13
|
+
bundle: true,
|
|
14
|
+
format: "cjs",
|
|
15
|
+
platform: "node",
|
|
16
|
+
packages: "external",
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
await copyFile(
|
|
20
|
+
path.join(distDir, "index.d.ts"),
|
|
21
|
+
path.join(distDir, "index.d.cts")
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
console.log("Created CJS bundle at dist/index.cjs");
|
|
25
|
+
console.log("Copied type definitions to dist/index.d.cts");
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const { compile } = require("json-schema-to-typescript");
|
|
2
|
+
const fs = require("fs").promises;
|
|
3
|
+
const path = require("path");
|
|
4
|
+
|
|
5
|
+
async function generateTypes() {
|
|
6
|
+
const schemasDir = path.join(__dirname, "..", "src", "schemas", "output_schemas");
|
|
7
|
+
const outputDir = path.join(__dirname, "..", "src", "types", "generated");
|
|
8
|
+
|
|
9
|
+
// Create output directory
|
|
10
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
11
|
+
|
|
12
|
+
// Get all v3 schema files (current version)
|
|
13
|
+
const files = await fs.readdir(schemasDir);
|
|
14
|
+
const v3Schemas = files.filter((f) => f.endsWith("_v3.schema.json"));
|
|
15
|
+
|
|
16
|
+
console.log(`Generating TypeScript types for ${v3Schemas.length} schemas...`);
|
|
17
|
+
|
|
18
|
+
let hadErrors = false;
|
|
19
|
+
const failedFiles = [];
|
|
20
|
+
|
|
21
|
+
for (const file of v3Schemas) {
|
|
22
|
+
const schemaPath = path.join(schemasDir, file);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const schema = JSON.parse(await fs.readFile(schemaPath, "utf-8"));
|
|
26
|
+
const ts = await compile(schema, schema.title || file.replace(".schema.json", ""), {
|
|
27
|
+
bannerComment: `/* eslint-disable */\n/**\n * Auto-generated from ${file}\n * Do not edit manually\n */`,
|
|
28
|
+
style: {
|
|
29
|
+
semi: true,
|
|
30
|
+
trailingComma: "es5",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const outputPath = path.join(outputDir, file.replace(".schema.json", ".ts"));
|
|
35
|
+
await fs.writeFile(outputPath, ts);
|
|
36
|
+
console.log(` ✓ ${file} → ${path.basename(outputPath)}`);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
hadErrors = true;
|
|
39
|
+
failedFiles.push(file);
|
|
40
|
+
console.error(` ✗ Failed to generate ${file}:`, error.message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (hadErrors) {
|
|
45
|
+
throw new Error(`One or more schemas failed to generate TypeScript types: ${failedFiles.join(", ")}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log("Type generation complete!");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
generateTypes().catch((error) => {
|
|
52
|
+
console.error("Type generation failed:", error);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
});
|