@doccov/sdk 0.30.7 → 0.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/index.d.ts +287 -15
- package/dist/analysis/index.js +14 -2
- package/dist/index.d.ts +625 -144
- package/dist/index.js +135 -13
- package/dist/shared/{chunk-ehne5cde.js → chunk-ee3ctqje.js} +481 -82
- package/dist/types/index.d.ts +45 -0
- package/package.json +3 -3
|
@@ -1452,6 +1452,8 @@ function detectAllExampleIssues(entry, registry) {
|
|
|
1452
1452
|
type: "example-syntax-error",
|
|
1453
1453
|
target: `example[${i}]`,
|
|
1454
1454
|
issue: `@example contains invalid syntax: ${message}`,
|
|
1455
|
+
expected: "valid syntax",
|
|
1456
|
+
actual: message,
|
|
1455
1457
|
suggestion: "Check for missing brackets, semicolons, or typos."
|
|
1456
1458
|
});
|
|
1457
1459
|
continue;
|
|
@@ -1482,6 +1484,8 @@ function detectAllExampleIssues(entry, registry) {
|
|
|
1482
1484
|
type: "example-drift",
|
|
1483
1485
|
target: identifier,
|
|
1484
1486
|
issue: `@example references "${identifier}" which does not exist in this package.`,
|
|
1487
|
+
expected: identifier,
|
|
1488
|
+
actual: hasCloseMatch ? suggestion.value : undefined,
|
|
1485
1489
|
suggestion: hasCloseMatch ? `Did you mean "${suggestion.value}"?` : undefined
|
|
1486
1490
|
});
|
|
1487
1491
|
}
|
|
@@ -1506,6 +1510,8 @@ function detectExampleRuntimeErrors(entry, runtimeResults) {
|
|
|
1506
1510
|
type: "example-runtime-error",
|
|
1507
1511
|
target: `example[${i}]`,
|
|
1508
1512
|
issue: isTimeout ? `@example timed out after ${result.duration}ms.` : `@example throws at runtime: ${errorMessage}`,
|
|
1513
|
+
expected: "successful execution",
|
|
1514
|
+
actual: errorMessage,
|
|
1509
1515
|
suggestion: isTimeout ? "Check for infinite loops or long-running operations." : "Fix the example code or update it to match the current API."
|
|
1510
1516
|
});
|
|
1511
1517
|
}
|
|
@@ -1571,6 +1577,8 @@ function detectExampleAssertionFailures(entry, runtimeResults) {
|
|
|
1571
1577
|
type: "example-assertion-failed",
|
|
1572
1578
|
target: `example[${i}]:line${assertion.lineNumber}`,
|
|
1573
1579
|
issue: `Assertion expected "${assertion.expected}" but no output was produced`,
|
|
1580
|
+
expected: assertion.expected,
|
|
1581
|
+
actual: "(no output)",
|
|
1574
1582
|
suggestion: "Ensure the example produces output for each assertion"
|
|
1575
1583
|
});
|
|
1576
1584
|
continue;
|
|
@@ -1580,6 +1588,8 @@ function detectExampleAssertionFailures(entry, runtimeResults) {
|
|
|
1580
1588
|
type: "example-assertion-failed",
|
|
1581
1589
|
target: `example[${i}]:line${assertion.lineNumber}`,
|
|
1582
1590
|
issue: `Assertion failed: expected "${assertion.expected}" but got "${actual}"`,
|
|
1591
|
+
expected: assertion.expected,
|
|
1592
|
+
actual,
|
|
1583
1593
|
suggestion: `Update assertion to: // => ${actual}`
|
|
1584
1594
|
});
|
|
1585
1595
|
}
|
|
@@ -1643,6 +1653,8 @@ function detectParamDrift(entry) {
|
|
|
1643
1653
|
type: "param-mismatch",
|
|
1644
1654
|
target: documentedName,
|
|
1645
1655
|
issue: `JSDoc documents property "${propertyPath}" on parameter "${prefix}" which does not exist.`,
|
|
1656
|
+
expected: documentedName,
|
|
1657
|
+
actual: propsArray.length > 0 ? propsArray.map((p) => `${prefix}.${p}`).join(", ") : undefined,
|
|
1646
1658
|
suggestion: suggestionText2
|
|
1647
1659
|
});
|
|
1648
1660
|
continue;
|
|
@@ -1662,6 +1674,8 @@ function detectParamDrift(entry) {
|
|
|
1662
1674
|
type: "param-mismatch",
|
|
1663
1675
|
target: documentedName,
|
|
1664
1676
|
issue: `JSDoc documents parameter "${documentedName}" which is not present in the signature.`,
|
|
1677
|
+
expected: documentedName,
|
|
1678
|
+
actual: paramsArray.join(", ") || undefined,
|
|
1665
1679
|
suggestion: suggestionText
|
|
1666
1680
|
});
|
|
1667
1681
|
}
|
|
@@ -1704,6 +1718,8 @@ function detectOptionalityDrift(entry) {
|
|
|
1704
1718
|
type: "optionality-mismatch",
|
|
1705
1719
|
target: docParam.name,
|
|
1706
1720
|
issue,
|
|
1721
|
+
expected: documentedOptional ? `[${docParam.name}]` : docParam.name,
|
|
1722
|
+
actual: actualOptional ? "optional" : "required",
|
|
1707
1723
|
suggestion
|
|
1708
1724
|
});
|
|
1709
1725
|
}
|
|
@@ -1751,6 +1767,8 @@ function detectParamTypeDrift(entry) {
|
|
|
1751
1767
|
type: "param-type-mismatch",
|
|
1752
1768
|
target: documentedParam.name,
|
|
1753
1769
|
issue: buildParamTypeMismatchIssue(documentedParam.name, documentedParam.type, declaredType),
|
|
1770
|
+
expected: documentedParam.type,
|
|
1771
|
+
actual: declaredType,
|
|
1754
1772
|
suggestion: `Update @param {${declaredType}} ${documentedParam.name} to match the signature.`
|
|
1755
1773
|
});
|
|
1756
1774
|
}
|
|
@@ -1771,6 +1789,8 @@ function detectDeprecatedDrift(entry) {
|
|
|
1771
1789
|
type: "deprecated-mismatch",
|
|
1772
1790
|
target,
|
|
1773
1791
|
issue: `Declaration for "${target}" is marked deprecated but @deprecated is missing from the docs.`,
|
|
1792
|
+
expected: "not deprecated",
|
|
1793
|
+
actual: "deprecated",
|
|
1774
1794
|
suggestion: "Add an @deprecated tag explaining the replacement or removal timeline."
|
|
1775
1795
|
}
|
|
1776
1796
|
];
|
|
@@ -1780,6 +1800,8 @@ function detectDeprecatedDrift(entry) {
|
|
|
1780
1800
|
type: "deprecated-mismatch",
|
|
1781
1801
|
target,
|
|
1782
1802
|
issue: `JSDoc marks "${target}" as deprecated but the TypeScript declaration is not.`,
|
|
1803
|
+
expected: "@deprecated",
|
|
1804
|
+
actual: "not deprecated",
|
|
1783
1805
|
suggestion: "Remove the @deprecated tag or deprecate the declaration."
|
|
1784
1806
|
}
|
|
1785
1807
|
];
|
|
@@ -1801,6 +1823,8 @@ function detectVisibilityDrift(entry) {
|
|
|
1801
1823
|
type: "visibility-mismatch",
|
|
1802
1824
|
target,
|
|
1803
1825
|
issue: buildVisibilityIssue(target, exportDocVisibility, exportActualVisibility),
|
|
1826
|
+
expected: formatDocVisibilityTag(exportDocVisibility.tagName),
|
|
1827
|
+
actual: exportActualVisibility,
|
|
1804
1828
|
suggestion: buildVisibilitySuggestion(exportDocVisibility, exportActualVisibility)
|
|
1805
1829
|
});
|
|
1806
1830
|
}
|
|
@@ -1821,6 +1845,8 @@ function detectVisibilityDrift(entry) {
|
|
|
1821
1845
|
type: "visibility-mismatch",
|
|
1822
1846
|
target: qualifiedTarget,
|
|
1823
1847
|
issue: buildVisibilityIssue(qualifiedTarget, memberDocVisibility, memberActualVisibility),
|
|
1848
|
+
expected: formatDocVisibilityTag(memberDocVisibility.tagName),
|
|
1849
|
+
actual: memberActualVisibility,
|
|
1824
1850
|
suggestion: buildVisibilitySuggestion(memberDocVisibility, memberActualVisibility)
|
|
1825
1851
|
});
|
|
1826
1852
|
}
|
|
@@ -1886,11 +1912,12 @@ function formatDocVisibilityTag(tagName) {
|
|
|
1886
1912
|
}
|
|
1887
1913
|
return trimmed.startsWith("@") ? trimmed : `@${trimmed}`;
|
|
1888
1914
|
}
|
|
1889
|
-
function detectBrokenLinks(entry, registry) {
|
|
1915
|
+
function detectBrokenLinks(entry, registry, options) {
|
|
1890
1916
|
if (!registry) {
|
|
1891
1917
|
return [];
|
|
1892
1918
|
}
|
|
1893
1919
|
const drifts = [];
|
|
1920
|
+
const { moduleGraph } = options ?? {};
|
|
1894
1921
|
const patterns = [
|
|
1895
1922
|
{ pattern: /\{@link\s+([^}\s|]+)(?:\s*\|[^}]*)?\}/g, type: "@link" },
|
|
1896
1923
|
{ pattern: /\{@see\s+([^}\s]+)\}/g, type: "@see" },
|
|
@@ -1914,15 +1941,21 @@ function detectBrokenLinks(entry, registry) {
|
|
|
1914
1941
|
if (target.includes("/") || target.includes("@")) {
|
|
1915
1942
|
continue;
|
|
1916
1943
|
}
|
|
1917
|
-
if (
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
issue: `{${type} ${target}} references a symbol that does not exist.`,
|
|
1923
|
-
suggestion: suggestion ? `Did you mean "${suggestion.value}"?` : undefined
|
|
1924
|
-
});
|
|
1944
|
+
if (registry.all.has(rootName) || registry.all.has(target)) {
|
|
1945
|
+
continue;
|
|
1946
|
+
}
|
|
1947
|
+
if (moduleGraph?.all.has(rootName) || moduleGraph?.all.has(target)) {
|
|
1948
|
+
continue;
|
|
1925
1949
|
}
|
|
1950
|
+
const suggestion = findClosestMatch(rootName, registry.allNames);
|
|
1951
|
+
drifts.push({
|
|
1952
|
+
type: "broken-link",
|
|
1953
|
+
target,
|
|
1954
|
+
issue: `{${type} ${target}} references a symbol that does not exist.`,
|
|
1955
|
+
expected: target,
|
|
1956
|
+
actual: suggestion?.value,
|
|
1957
|
+
suggestion: suggestion ? `Did you mean "${suggestion.value}"?` : undefined
|
|
1958
|
+
});
|
|
1926
1959
|
}
|
|
1927
1960
|
}
|
|
1928
1961
|
return drifts;
|
|
@@ -1946,6 +1979,8 @@ function detectAsyncMismatch(entry) {
|
|
|
1946
1979
|
type: "async-mismatch",
|
|
1947
1980
|
target: "returns",
|
|
1948
1981
|
issue: "Function returns Promise but documentation does not indicate async behavior.",
|
|
1982
|
+
expected: "sync",
|
|
1983
|
+
actual: "Promise",
|
|
1949
1984
|
suggestion: "Add @async tag or document @returns {Promise<...>}."
|
|
1950
1985
|
});
|
|
1951
1986
|
}
|
|
@@ -1954,6 +1989,8 @@ function detectAsyncMismatch(entry) {
|
|
|
1954
1989
|
type: "async-mismatch",
|
|
1955
1990
|
target: "returns",
|
|
1956
1991
|
issue: "Documentation indicates async but function does not return Promise.",
|
|
1992
|
+
expected: "@async / Promise",
|
|
1993
|
+
actual: "sync",
|
|
1957
1994
|
suggestion: "Remove @async tag or update @returns type."
|
|
1958
1995
|
});
|
|
1959
1996
|
}
|
|
@@ -1992,6 +2029,8 @@ function detectReturnTypeDrift(entry) {
|
|
|
1992
2029
|
type: "return-type-mismatch",
|
|
1993
2030
|
target: "returns",
|
|
1994
2031
|
issue: buildReturnTypeMismatchIssue(documentedType, documentedNormalized, declaredType),
|
|
2032
|
+
expected: documentedType,
|
|
2033
|
+
actual: declaredType,
|
|
1995
2034
|
suggestion: `Update @returns to ${declaredType}.`
|
|
1996
2035
|
}
|
|
1997
2036
|
];
|
|
@@ -2027,6 +2066,8 @@ function detectGenericConstraintDrift(entry) {
|
|
|
2027
2066
|
type: "generic-constraint-mismatch",
|
|
2028
2067
|
target: doc.name,
|
|
2029
2068
|
issue: buildGenericConstraintMismatchIssue(doc.name, doc.constraint, actualConstraint),
|
|
2069
|
+
expected: doc.constraint ?? "none",
|
|
2070
|
+
actual: actualConstraint ?? "none",
|
|
2030
2071
|
suggestion: buildGenericConstraintSuggestion(doc.name, actualConstraint)
|
|
2031
2072
|
});
|
|
2032
2073
|
}
|
|
@@ -2061,6 +2102,8 @@ function detectPropertyTypeDrift(entry) {
|
|
|
2061
2102
|
type: "property-type-drift",
|
|
2062
2103
|
target: memberName,
|
|
2063
2104
|
issue: `Property "${memberName}" documented as {${documentedType}} but actual type is ${actualType}.`,
|
|
2105
|
+
expected: documentedType,
|
|
2106
|
+
actual: actualType,
|
|
2064
2107
|
suggestion: `Update @type {${actualType}} to match the declaration.`
|
|
2065
2108
|
});
|
|
2066
2109
|
}
|
|
@@ -2117,16 +2160,16 @@ function buildExportRegistry(spec) {
|
|
|
2117
2160
|
const allNames = Array.from(all);
|
|
2118
2161
|
return { exports, types, all, callableNames, typeNames, allExportNames, allNames };
|
|
2119
2162
|
}
|
|
2120
|
-
function computeDrift(spec) {
|
|
2163
|
+
function computeDrift(spec, options) {
|
|
2121
2164
|
const registry = buildExportRegistry(spec);
|
|
2122
2165
|
const exports = new Map;
|
|
2123
2166
|
for (const entry of spec.exports ?? []) {
|
|
2124
|
-
const drift = computeExportDrift(entry, registry);
|
|
2167
|
+
const drift = computeExportDrift(entry, registry, options);
|
|
2125
2168
|
exports.set(entry.id ?? entry.name, drift);
|
|
2126
2169
|
}
|
|
2127
2170
|
return { exports };
|
|
2128
2171
|
}
|
|
2129
|
-
function computeExportDrift(entry, registry) {
|
|
2172
|
+
function computeExportDrift(entry, registry, options) {
|
|
2130
2173
|
const hasDescription = Boolean(entry.description);
|
|
2131
2174
|
const hasTags = (entry.tags?.length ?? 0) > 0;
|
|
2132
2175
|
const hasExamples = (entry.examples?.length ?? 0) > 0;
|
|
@@ -2141,12 +2184,20 @@ function computeExportDrift(entry, registry) {
|
|
|
2141
2184
|
drifts.push(...detectAllExampleIssues(entry, registry));
|
|
2142
2185
|
}
|
|
2143
2186
|
if (hasDescription || hasTags) {
|
|
2144
|
-
drifts.push(...detectBrokenLinks(entry, registry));
|
|
2187
|
+
drifts.push(...detectBrokenLinks(entry, registry, { moduleGraph: options?.moduleGraph }));
|
|
2145
2188
|
}
|
|
2146
2189
|
return drifts;
|
|
2147
2190
|
}
|
|
2148
2191
|
|
|
2149
2192
|
// src/analysis/health.ts
|
|
2193
|
+
function isExportDocumented(exp) {
|
|
2194
|
+
if (exp.description && exp.description.trim().length > 0)
|
|
2195
|
+
return true;
|
|
2196
|
+
const meaningfulTags = exp.tags?.filter((t) => t.name !== "internal") ?? [];
|
|
2197
|
+
if (meaningfulTags.length > 0)
|
|
2198
|
+
return true;
|
|
2199
|
+
return false;
|
|
2200
|
+
}
|
|
2150
2201
|
function computeHealth(input) {
|
|
2151
2202
|
const {
|
|
2152
2203
|
coverageScore,
|
|
@@ -2197,11 +2248,92 @@ function computeHealth(input) {
|
|
|
2197
2248
|
return result;
|
|
2198
2249
|
}
|
|
2199
2250
|
|
|
2251
|
+
// src/analysis/presets.ts
|
|
2252
|
+
var DEFAULT_REQUIREMENTS = {
|
|
2253
|
+
description: true,
|
|
2254
|
+
params: false,
|
|
2255
|
+
returns: false,
|
|
2256
|
+
examples: false,
|
|
2257
|
+
since: false
|
|
2258
|
+
};
|
|
2259
|
+
var PRESETS = {
|
|
2260
|
+
minimal: {
|
|
2261
|
+
description: true,
|
|
2262
|
+
params: false,
|
|
2263
|
+
returns: false,
|
|
2264
|
+
examples: false,
|
|
2265
|
+
since: false
|
|
2266
|
+
},
|
|
2267
|
+
verbose: {
|
|
2268
|
+
description: true,
|
|
2269
|
+
params: true,
|
|
2270
|
+
returns: true,
|
|
2271
|
+
examples: false,
|
|
2272
|
+
since: false
|
|
2273
|
+
},
|
|
2274
|
+
"types-only": {
|
|
2275
|
+
description: false,
|
|
2276
|
+
params: false,
|
|
2277
|
+
returns: false,
|
|
2278
|
+
examples: false,
|
|
2279
|
+
since: false
|
|
2280
|
+
}
|
|
2281
|
+
};
|
|
2282
|
+
function resolveRequirements(style, require2) {
|
|
2283
|
+
const base = style ? PRESETS[style] : DEFAULT_REQUIREMENTS;
|
|
2284
|
+
if (require2) {
|
|
2285
|
+
return {
|
|
2286
|
+
description: require2.description ?? base.description,
|
|
2287
|
+
params: require2.params ?? base.params,
|
|
2288
|
+
returns: require2.returns ?? base.returns,
|
|
2289
|
+
examples: require2.examples ?? base.examples,
|
|
2290
|
+
since: require2.since ?? base.since
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
return { ...base };
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2200
2296
|
// src/analysis/doccov-builder.ts
|
|
2201
2297
|
import { DRIFT_CATEGORIES } from "@doccov/spec";
|
|
2202
|
-
function
|
|
2203
|
-
|
|
2298
|
+
function hasInternalTag(exp) {
|
|
2299
|
+
return exp.tags?.some((t) => t.name === "internal") ?? false;
|
|
2300
|
+
}
|
|
2301
|
+
var YIELD_BATCH_SIZE = 5;
|
|
2302
|
+
async function buildDocCovSpec(options) {
|
|
2303
|
+
const {
|
|
2304
|
+
openpkg,
|
|
2305
|
+
openpkgPath,
|
|
2306
|
+
forgottenExports,
|
|
2307
|
+
apiSurfaceIgnore,
|
|
2308
|
+
entryExportNames,
|
|
2309
|
+
onProgress,
|
|
2310
|
+
onExportAnalyzed,
|
|
2311
|
+
style,
|
|
2312
|
+
require: require2
|
|
2313
|
+
} = options;
|
|
2204
2314
|
const registry = buildExportRegistry(openpkg);
|
|
2315
|
+
const requirements = resolveRequirements(style, require2);
|
|
2316
|
+
const allExports = (openpkg.exports ?? []).filter((exp) => !hasInternalTag(exp));
|
|
2317
|
+
const total = allExports.length;
|
|
2318
|
+
const analysisResults = [];
|
|
2319
|
+
for (let i = 0;i < total; i++) {
|
|
2320
|
+
const exp = allExports[i];
|
|
2321
|
+
onProgress?.(i + 1, total, exp.name);
|
|
2322
|
+
if (i % YIELD_BATCH_SIZE === 0 && i > 0) {
|
|
2323
|
+
await new Promise((r) => setImmediate(r));
|
|
2324
|
+
}
|
|
2325
|
+
const coverage = computeExportCoverage(exp, requirements);
|
|
2326
|
+
const rawDrifts = computeExportDrift(exp, registry);
|
|
2327
|
+
const categorizedDrifts = rawDrifts.map((d) => toCategorizedDrift(d));
|
|
2328
|
+
analysisResults.push({ coverage, drifts: categorizedDrifts, exp });
|
|
2329
|
+
}
|
|
2330
|
+
const byName = new Map;
|
|
2331
|
+
for (const result of analysisResults) {
|
|
2332
|
+
const name = result.exp.name;
|
|
2333
|
+
const existing = byName.get(name) ?? [];
|
|
2334
|
+
existing.push(result);
|
|
2335
|
+
byName.set(name, existing);
|
|
2336
|
+
}
|
|
2205
2337
|
const exports = {};
|
|
2206
2338
|
let totalScore = 0;
|
|
2207
2339
|
let documentedCount = 0;
|
|
@@ -2219,30 +2351,46 @@ function buildDocCovSpec(options) {
|
|
|
2219
2351
|
};
|
|
2220
2352
|
let totalDrift = 0;
|
|
2221
2353
|
let fixableDrift = 0;
|
|
2222
|
-
for (const
|
|
2223
|
-
const
|
|
2224
|
-
const
|
|
2225
|
-
const
|
|
2226
|
-
const
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2354
|
+
for (const [name, overloads] of byName) {
|
|
2355
|
+
const bestResult = overloads.reduce((best, curr) => curr.coverage.score > best.coverage.score ? curr : best);
|
|
2356
|
+
const exportId = bestResult.exp.id ?? name;
|
|
2357
|
+
const allMissing = new Set;
|
|
2358
|
+
for (const r of overloads) {
|
|
2359
|
+
for (const m of r.coverage.missing) {
|
|
2360
|
+
allMissing.add(m);
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
const allDrifts = [];
|
|
2364
|
+
for (const r of overloads) {
|
|
2365
|
+
allDrifts.push(...r.drifts);
|
|
2366
|
+
}
|
|
2367
|
+
const analysis = {
|
|
2368
|
+
coverageScore: bestResult.coverage.score,
|
|
2369
|
+
missing: allMissing.size > 0 ? Array.from(allMissing) : undefined,
|
|
2370
|
+
drift: allDrifts.length > 0 ? allDrifts : undefined
|
|
2231
2371
|
};
|
|
2232
|
-
|
|
2233
|
-
|
|
2372
|
+
if (overloads.length > 1) {
|
|
2373
|
+
analysis.overloadCount = overloads.length;
|
|
2374
|
+
}
|
|
2375
|
+
exports[exportId] = analysis;
|
|
2376
|
+
if (onExportAnalyzed) {
|
|
2377
|
+
const idx = Object.keys(exports).length;
|
|
2378
|
+
await onExportAnalyzed(exportId, name, analysis, idx, byName.size);
|
|
2379
|
+
}
|
|
2380
|
+
totalScore += bestResult.coverage.score;
|
|
2381
|
+
if (isExportDocumented(bestResult.exp))
|
|
2234
2382
|
documentedCount++;
|
|
2235
|
-
for (const rule of coverage.missing) {
|
|
2383
|
+
for (const rule of bestResult.coverage.missing) {
|
|
2236
2384
|
missingByRule[rule]++;
|
|
2237
2385
|
}
|
|
2238
|
-
for (const d of
|
|
2386
|
+
for (const d of allDrifts) {
|
|
2239
2387
|
driftByCategory[d.category]++;
|
|
2240
2388
|
totalDrift++;
|
|
2241
2389
|
if (d.fixable)
|
|
2242
2390
|
fixableDrift++;
|
|
2243
2391
|
}
|
|
2244
2392
|
}
|
|
2245
|
-
const exportCount =
|
|
2393
|
+
const exportCount = byName.size;
|
|
2246
2394
|
const coverageScore = exportCount > 0 ? Math.round(totalScore / exportCount) : 100;
|
|
2247
2395
|
const health = computeHealth({
|
|
2248
2396
|
coverageScore,
|
|
@@ -2265,7 +2413,7 @@ function buildDocCovSpec(options) {
|
|
|
2265
2413
|
},
|
|
2266
2414
|
health
|
|
2267
2415
|
};
|
|
2268
|
-
const apiSurface = computeApiSurface(forgottenExports, openpkg.types?.length ?? 0, apiSurfaceIgnore);
|
|
2416
|
+
const apiSurface = computeApiSurface(forgottenExports, openpkg.types?.length ?? 0, apiSurfaceIgnore, entryExportNames);
|
|
2269
2417
|
return {
|
|
2270
2418
|
doccov: "1.0.0",
|
|
2271
2419
|
source: {
|
|
@@ -2280,11 +2428,12 @@ function buildDocCovSpec(options) {
|
|
|
2280
2428
|
...apiSurface ? { apiSurface } : {}
|
|
2281
2429
|
};
|
|
2282
2430
|
}
|
|
2283
|
-
function computeApiSurface(forgottenExports, exportedTypesCount, ignoreList) {
|
|
2431
|
+
function computeApiSurface(forgottenExports, exportedTypesCount, ignoreList, entryExportNames) {
|
|
2284
2432
|
if (!forgottenExports)
|
|
2285
2433
|
return;
|
|
2286
2434
|
const ignoreSet = new Set(ignoreList ?? []);
|
|
2287
|
-
const
|
|
2435
|
+
const entryExportSet = new Set(entryExportNames ?? []);
|
|
2436
|
+
const filteredExports = forgottenExports.filter((f) => !ignoreSet.has(f.name) && !entryExportSet.has(f.name));
|
|
2288
2437
|
const forgotten = filteredExports.map((f) => ({
|
|
2289
2438
|
name: f.name,
|
|
2290
2439
|
definedIn: f.definedIn ? { file: f.definedIn } : undefined,
|
|
@@ -2305,36 +2454,52 @@ function computeApiSurface(forgottenExports, exportedTypesCount, ignoreList) {
|
|
|
2305
2454
|
completeness
|
|
2306
2455
|
};
|
|
2307
2456
|
}
|
|
2308
|
-
function computeExportCoverage(exp) {
|
|
2457
|
+
function computeExportCoverage(exp, requirements) {
|
|
2309
2458
|
const missing = [];
|
|
2310
2459
|
let points = 0;
|
|
2311
2460
|
let maxPoints = 0;
|
|
2312
|
-
|
|
2313
|
-
if (
|
|
2314
|
-
|
|
2461
|
+
const descRequired = requirements.description;
|
|
2462
|
+
if (descRequired) {
|
|
2463
|
+
maxPoints += 30;
|
|
2464
|
+
if (exp.description && exp.description.trim().length > 0) {
|
|
2465
|
+
points += 30;
|
|
2466
|
+
} else {
|
|
2467
|
+
missing.push("description");
|
|
2468
|
+
}
|
|
2315
2469
|
} else {
|
|
2316
|
-
|
|
2470
|
+
if (!exp.description || exp.description.trim().length === 0) {
|
|
2471
|
+
missing.push("description");
|
|
2472
|
+
}
|
|
2317
2473
|
}
|
|
2318
2474
|
const isCallable = exp.kind === "function" || exp.kind === "class";
|
|
2319
2475
|
if (isCallable && exp.signatures?.length) {
|
|
2320
2476
|
const sig = exp.signatures[0];
|
|
2321
2477
|
const params = sig.parameters ?? [];
|
|
2322
2478
|
if (params.length > 0) {
|
|
2323
|
-
|
|
2479
|
+
const paramsRequired = requirements.params;
|
|
2480
|
+
if (paramsRequired) {
|
|
2481
|
+
maxPoints += 25;
|
|
2482
|
+
}
|
|
2324
2483
|
const documentedParams = params.filter((p) => p.description && p.description.trim().length > 0);
|
|
2325
2484
|
if (documentedParams.length === params.length) {
|
|
2326
|
-
|
|
2485
|
+
if (paramsRequired)
|
|
2486
|
+
points += 25;
|
|
2327
2487
|
} else if (documentedParams.length > 0) {
|
|
2328
|
-
|
|
2488
|
+
if (paramsRequired)
|
|
2489
|
+
points += Math.round(documentedParams.length / params.length * 25);
|
|
2329
2490
|
missing.push("params");
|
|
2330
2491
|
} else {
|
|
2331
2492
|
missing.push("params");
|
|
2332
2493
|
}
|
|
2333
2494
|
}
|
|
2334
2495
|
if (exp.kind === "function" && sig.returns) {
|
|
2335
|
-
|
|
2496
|
+
const returnsRequired = requirements.returns;
|
|
2497
|
+
if (returnsRequired) {
|
|
2498
|
+
maxPoints += 20;
|
|
2499
|
+
}
|
|
2336
2500
|
if (sig.returns.description && sig.returns.description.trim().length > 0) {
|
|
2337
|
-
|
|
2501
|
+
if (returnsRequired)
|
|
2502
|
+
points += 20;
|
|
2338
2503
|
} else {
|
|
2339
2504
|
missing.push("returns");
|
|
2340
2505
|
}
|
|
@@ -2349,9 +2514,13 @@ function computeExportCoverage(exp) {
|
|
|
2349
2514
|
}
|
|
2350
2515
|
}
|
|
2351
2516
|
}
|
|
2352
|
-
|
|
2517
|
+
const examplesRequired = requirements.examples;
|
|
2518
|
+
if (examplesRequired) {
|
|
2519
|
+
maxPoints += 15;
|
|
2520
|
+
}
|
|
2353
2521
|
if (exp.examples && exp.examples.length > 0) {
|
|
2354
|
-
|
|
2522
|
+
if (examplesRequired)
|
|
2523
|
+
points += 15;
|
|
2355
2524
|
} else {
|
|
2356
2525
|
missing.push("examples");
|
|
2357
2526
|
}
|
|
@@ -2507,35 +2676,45 @@ function isExportFullyDocumented(exp, doccov) {
|
|
|
2507
2676
|
// src/analysis/report.ts
|
|
2508
2677
|
import * as fs3 from "node:fs";
|
|
2509
2678
|
import * as path3 from "node:path";
|
|
2510
|
-
function generateReport(spec, openpkgPath = "openpkg.json") {
|
|
2511
|
-
const doccov = buildDocCovSpec({ openpkg: spec, openpkgPath });
|
|
2679
|
+
async function generateReport(spec, openpkgPath = "openpkg.json") {
|
|
2680
|
+
const doccov = await buildDocCovSpec({ openpkg: spec, openpkgPath });
|
|
2512
2681
|
return generateReportFromDocCov(spec, doccov);
|
|
2513
2682
|
}
|
|
2514
2683
|
function generateReportFromDocCov(openpkg, doccov) {
|
|
2515
2684
|
const exportsData = {};
|
|
2516
2685
|
const missingByRule = {};
|
|
2686
|
+
const openpkgExportsById = new Map;
|
|
2687
|
+
for (const exp of openpkg.exports ?? []) {
|
|
2688
|
+
const id = exp.id ?? exp.name;
|
|
2689
|
+
if (!openpkgExportsById.has(id)) {
|
|
2690
|
+
openpkgExportsById.set(id, exp);
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2517
2693
|
let documentedExports = 0;
|
|
2518
2694
|
let totalDrift = 0;
|
|
2519
|
-
for (const
|
|
2520
|
-
const
|
|
2695
|
+
for (const [exportId, analysis] of Object.entries(doccov.exports)) {
|
|
2696
|
+
const openpkgExp = openpkgExportsById.get(exportId);
|
|
2521
2697
|
const data = {
|
|
2522
|
-
name:
|
|
2523
|
-
kind:
|
|
2524
|
-
coverageScore: analysis
|
|
2698
|
+
name: openpkgExp?.name ?? exportId,
|
|
2699
|
+
kind: openpkgExp?.kind ?? "unknown",
|
|
2700
|
+
coverageScore: analysis.coverageScore
|
|
2525
2701
|
};
|
|
2526
|
-
if (analysis
|
|
2702
|
+
if (analysis.missing && analysis.missing.length > 0) {
|
|
2527
2703
|
data.missing = analysis.missing;
|
|
2528
2704
|
for (const ruleId of analysis.missing) {
|
|
2529
2705
|
missingByRule[ruleId] = (missingByRule[ruleId] ?? 0) + 1;
|
|
2530
2706
|
}
|
|
2531
|
-
}
|
|
2707
|
+
}
|
|
2708
|
+
if (openpkgExp && isExportDocumented(openpkgExp)) {
|
|
2532
2709
|
documentedExports++;
|
|
2533
2710
|
}
|
|
2534
|
-
if (analysis
|
|
2711
|
+
if (analysis.drift && analysis.drift.length > 0) {
|
|
2535
2712
|
data.drift = analysis.drift;
|
|
2536
2713
|
totalDrift += analysis.drift.length;
|
|
2537
2714
|
}
|
|
2538
|
-
|
|
2715
|
+
if (analysis.overloadCount && analysis.overloadCount > 1) {
|
|
2716
|
+
data.overloadCount = analysis.overloadCount;
|
|
2717
|
+
}
|
|
2539
2718
|
exportsData[exportId] = data;
|
|
2540
2719
|
}
|
|
2541
2720
|
const coverage = {
|
|
@@ -2778,6 +2957,226 @@ function renderApiSurface(spec) {
|
|
|
2778
2957
|
`);
|
|
2779
2958
|
}
|
|
2780
2959
|
|
|
2960
|
+
// src/analysis/module-graph.ts
|
|
2961
|
+
function buildModuleGraph(specs) {
|
|
2962
|
+
const modules = new Map;
|
|
2963
|
+
const exports = new Map;
|
|
2964
|
+
const types = new Map;
|
|
2965
|
+
const all = new Set;
|
|
2966
|
+
for (const spec of specs) {
|
|
2967
|
+
const moduleName = spec.meta.name;
|
|
2968
|
+
const moduleExports = new Set;
|
|
2969
|
+
const moduleTypes = new Set;
|
|
2970
|
+
for (const exp of spec.exports ?? []) {
|
|
2971
|
+
moduleExports.add(exp.name);
|
|
2972
|
+
all.add(exp.name);
|
|
2973
|
+
if (exp.id) {
|
|
2974
|
+
moduleExports.add(exp.id);
|
|
2975
|
+
all.add(exp.id);
|
|
2976
|
+
}
|
|
2977
|
+
if (!exports.has(exp.name)) {
|
|
2978
|
+
exports.set(exp.name, moduleName);
|
|
2979
|
+
}
|
|
2980
|
+
if (exp.kind === "namespace" && exp.members) {
|
|
2981
|
+
for (const member of exp.members) {
|
|
2982
|
+
if (!member.name)
|
|
2983
|
+
continue;
|
|
2984
|
+
moduleExports.add(member.name);
|
|
2985
|
+
all.add(member.name);
|
|
2986
|
+
if (!exports.has(member.name)) {
|
|
2987
|
+
exports.set(member.name, moduleName);
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
for (const type of spec.types ?? []) {
|
|
2993
|
+
moduleTypes.add(type.name);
|
|
2994
|
+
all.add(type.name);
|
|
2995
|
+
if (type.id) {
|
|
2996
|
+
moduleTypes.add(type.id);
|
|
2997
|
+
all.add(type.id);
|
|
2998
|
+
}
|
|
2999
|
+
if (!types.has(type.name)) {
|
|
3000
|
+
types.set(type.name, moduleName);
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
modules.set(moduleName, {
|
|
3004
|
+
name: moduleName,
|
|
3005
|
+
exports: moduleExports,
|
|
3006
|
+
types: moduleTypes
|
|
3007
|
+
});
|
|
3008
|
+
}
|
|
3009
|
+
return { modules, exports, types, all };
|
|
3010
|
+
}
|
|
3011
|
+
function findSymbolModule(graph, symbol) {
|
|
3012
|
+
return graph.exports.get(symbol) ?? graph.types.get(symbol);
|
|
3013
|
+
}
|
|
3014
|
+
function symbolExistsInGraph(graph, symbol) {
|
|
3015
|
+
return graph.all.has(symbol);
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
// src/analysis/incremental.ts
|
|
3019
|
+
import * as fs4 from "node:fs";
|
|
3020
|
+
import * as os from "node:os";
|
|
3021
|
+
import * as path4 from "node:path";
|
|
3022
|
+
|
|
3023
|
+
class IncrementalAnalyzer {
|
|
3024
|
+
tempPath;
|
|
3025
|
+
totalExpected;
|
|
3026
|
+
resultCount = 0;
|
|
3027
|
+
fileHandle;
|
|
3028
|
+
constructor(options = {}) {
|
|
3029
|
+
const tempDir = options.tempDir ?? os.tmpdir();
|
|
3030
|
+
const prefix = options.prefix ?? "doccov";
|
|
3031
|
+
this.tempPath = path4.join(tempDir, `${prefix}-${Date.now()}-${process.pid}.ndjson`);
|
|
3032
|
+
}
|
|
3033
|
+
get path() {
|
|
3034
|
+
return this.tempPath;
|
|
3035
|
+
}
|
|
3036
|
+
get count() {
|
|
3037
|
+
return this.resultCount;
|
|
3038
|
+
}
|
|
3039
|
+
setTotal(total) {
|
|
3040
|
+
this.totalExpected = total;
|
|
3041
|
+
}
|
|
3042
|
+
async init() {
|
|
3043
|
+
if (!this.fileHandle) {
|
|
3044
|
+
this.fileHandle = await fs4.promises.open(this.tempPath, "a");
|
|
3045
|
+
const header = {
|
|
3046
|
+
type: "header",
|
|
3047
|
+
startedAt: new Date().toISOString(),
|
|
3048
|
+
totalExpected: this.totalExpected,
|
|
3049
|
+
pid: process.pid
|
|
3050
|
+
};
|
|
3051
|
+
await this.fileHandle.appendFile(JSON.stringify(header) + `
|
|
3052
|
+
`);
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
async writeResult(result) {
|
|
3056
|
+
await this.init();
|
|
3057
|
+
const line = JSON.stringify({ type: "result", ...result }) + `
|
|
3058
|
+
`;
|
|
3059
|
+
await this.fileHandle.appendFile(line);
|
|
3060
|
+
this.resultCount++;
|
|
3061
|
+
}
|
|
3062
|
+
async writeExportAnalysis(id, name, analysis) {
|
|
3063
|
+
const result = {
|
|
3064
|
+
id,
|
|
3065
|
+
name,
|
|
3066
|
+
coverageScore: analysis.coverageScore,
|
|
3067
|
+
missing: analysis.missing,
|
|
3068
|
+
drift: analysis.drift,
|
|
3069
|
+
overloadCount: analysis.overloadCount,
|
|
3070
|
+
timestamp: Date.now()
|
|
3071
|
+
};
|
|
3072
|
+
await this.writeResult(result);
|
|
3073
|
+
}
|
|
3074
|
+
async getPartialResults() {
|
|
3075
|
+
try {
|
|
3076
|
+
if (this.fileHandle) {
|
|
3077
|
+
await this.fileHandle.close();
|
|
3078
|
+
this.fileHandle = undefined;
|
|
3079
|
+
}
|
|
3080
|
+
const content = await fs4.promises.readFile(this.tempPath, "utf-8");
|
|
3081
|
+
const lines = content.split(`
|
|
3082
|
+
`).filter(Boolean);
|
|
3083
|
+
const results = [];
|
|
3084
|
+
let totalExpected;
|
|
3085
|
+
for (const line of lines) {
|
|
3086
|
+
try {
|
|
3087
|
+
const parsed = JSON.parse(line);
|
|
3088
|
+
if (parsed.type === "header") {
|
|
3089
|
+
totalExpected = parsed.totalExpected;
|
|
3090
|
+
} else if (parsed.type === "result") {
|
|
3091
|
+
const { type, ...result } = parsed;
|
|
3092
|
+
results.push(result);
|
|
3093
|
+
}
|
|
3094
|
+
} catch {}
|
|
3095
|
+
}
|
|
3096
|
+
return {
|
|
3097
|
+
results,
|
|
3098
|
+
totalExpected,
|
|
3099
|
+
interrupted: totalExpected !== undefined && results.length < totalExpected
|
|
3100
|
+
};
|
|
3101
|
+
} catch (err) {
|
|
3102
|
+
if (err.code === "ENOENT") {
|
|
3103
|
+
return { results: [], interrupted: false };
|
|
3104
|
+
}
|
|
3105
|
+
throw err;
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
async cleanup() {
|
|
3109
|
+
try {
|
|
3110
|
+
if (this.fileHandle) {
|
|
3111
|
+
await this.fileHandle.close();
|
|
3112
|
+
this.fileHandle = undefined;
|
|
3113
|
+
}
|
|
3114
|
+
await fs4.promises.unlink(this.tempPath);
|
|
3115
|
+
} catch {}
|
|
3116
|
+
}
|
|
3117
|
+
cleanupSync() {
|
|
3118
|
+
try {
|
|
3119
|
+
if (this.fileHandle) {
|
|
3120
|
+
this.fileHandle = undefined;
|
|
3121
|
+
}
|
|
3122
|
+
fs4.unlinkSync(this.tempPath);
|
|
3123
|
+
} catch {}
|
|
3124
|
+
}
|
|
3125
|
+
exists() {
|
|
3126
|
+
return fs4.existsSync(this.tempPath);
|
|
3127
|
+
}
|
|
3128
|
+
getPartialResultsSync() {
|
|
3129
|
+
try {
|
|
3130
|
+
const content = fs4.readFileSync(this.tempPath, "utf-8");
|
|
3131
|
+
const lines = content.split(`
|
|
3132
|
+
`).filter(Boolean);
|
|
3133
|
+
const results = [];
|
|
3134
|
+
let totalExpected;
|
|
3135
|
+
for (const line of lines) {
|
|
3136
|
+
try {
|
|
3137
|
+
const parsed = JSON.parse(line);
|
|
3138
|
+
if (parsed.type === "header") {
|
|
3139
|
+
totalExpected = parsed.totalExpected;
|
|
3140
|
+
} else if (parsed.type === "result") {
|
|
3141
|
+
const { type, ...result } = parsed;
|
|
3142
|
+
results.push(result);
|
|
3143
|
+
}
|
|
3144
|
+
} catch {}
|
|
3145
|
+
}
|
|
3146
|
+
return {
|
|
3147
|
+
results,
|
|
3148
|
+
totalExpected,
|
|
3149
|
+
interrupted: totalExpected !== undefined && results.length < totalExpected
|
|
3150
|
+
};
|
|
3151
|
+
} catch {
|
|
3152
|
+
return { results: [], interrupted: false };
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
function findOrphanedTempFiles(tempDir = os.tmpdir(), prefix = "doccov") {
|
|
3157
|
+
try {
|
|
3158
|
+
const files = fs4.readdirSync(tempDir);
|
|
3159
|
+
return files.filter((f) => f.startsWith(prefix) && f.endsWith(".ndjson")).map((f) => path4.join(tempDir, f));
|
|
3160
|
+
} catch {
|
|
3161
|
+
return [];
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
function cleanupOrphanedTempFiles(tempDir = os.tmpdir(), prefix = "doccov", maxAge = 60 * 60 * 1000) {
|
|
3165
|
+
const files = findOrphanedTempFiles(tempDir, prefix);
|
|
3166
|
+
const now = Date.now();
|
|
3167
|
+
let cleaned = 0;
|
|
3168
|
+
for (const file of files) {
|
|
3169
|
+
try {
|
|
3170
|
+
const stat = fs4.statSync(file);
|
|
3171
|
+
if (now - stat.mtimeMs > maxAge) {
|
|
3172
|
+
fs4.unlinkSync(file);
|
|
3173
|
+
cleaned++;
|
|
3174
|
+
}
|
|
3175
|
+
} catch {}
|
|
3176
|
+
}
|
|
3177
|
+
return cleaned;
|
|
3178
|
+
}
|
|
3179
|
+
|
|
2781
3180
|
// src/extract/schema/index.ts
|
|
2782
3181
|
import {
|
|
2783
3182
|
arktypeAdapter,
|
|
@@ -2847,8 +3246,8 @@ async function detectRuntimeSchemas(context) {
|
|
|
2847
3246
|
}
|
|
2848
3247
|
|
|
2849
3248
|
// src/utils/project-root.ts
|
|
2850
|
-
import * as
|
|
2851
|
-
import * as
|
|
3249
|
+
import * as fs5 from "node:fs";
|
|
3250
|
+
import * as path5 from "node:path";
|
|
2852
3251
|
var PROJECT_ROOT_MARKERS = [
|
|
2853
3252
|
".git",
|
|
2854
3253
|
"pnpm-workspace.yaml",
|
|
@@ -2858,39 +3257,39 @@ var PROJECT_ROOT_MARKERS = [
|
|
|
2858
3257
|
"rush.json"
|
|
2859
3258
|
];
|
|
2860
3259
|
function findProjectRoot(startDir) {
|
|
2861
|
-
let dir =
|
|
2862
|
-
const root =
|
|
3260
|
+
let dir = path5.resolve(startDir);
|
|
3261
|
+
const root = path5.parse(dir).root;
|
|
2863
3262
|
while (dir !== root) {
|
|
2864
3263
|
for (const marker of PROJECT_ROOT_MARKERS) {
|
|
2865
|
-
const markerPath =
|
|
2866
|
-
if (
|
|
3264
|
+
const markerPath = path5.join(dir, marker);
|
|
3265
|
+
if (fs5.existsSync(markerPath)) {
|
|
2867
3266
|
return dir;
|
|
2868
3267
|
}
|
|
2869
3268
|
}
|
|
2870
|
-
const pkgPath =
|
|
2871
|
-
if (
|
|
3269
|
+
const pkgPath = path5.join(dir, "package.json");
|
|
3270
|
+
if (fs5.existsSync(pkgPath)) {
|
|
2872
3271
|
try {
|
|
2873
|
-
const pkg = JSON.parse(
|
|
3272
|
+
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
2874
3273
|
if (pkg.workspaces) {
|
|
2875
3274
|
return dir;
|
|
2876
3275
|
}
|
|
2877
3276
|
} catch {}
|
|
2878
3277
|
}
|
|
2879
|
-
dir =
|
|
3278
|
+
dir = path5.dirname(dir);
|
|
2880
3279
|
}
|
|
2881
|
-
return
|
|
3280
|
+
return path5.resolve(startDir);
|
|
2882
3281
|
}
|
|
2883
3282
|
function getDoccovDir(cwd) {
|
|
2884
3283
|
const projectRoot = findProjectRoot(cwd);
|
|
2885
|
-
return
|
|
3284
|
+
return path5.join(projectRoot, ".doccov");
|
|
2886
3285
|
}
|
|
2887
3286
|
|
|
2888
3287
|
// src/analysis/history.ts
|
|
2889
|
-
import * as
|
|
2890
|
-
import * as
|
|
3288
|
+
import * as fs6 from "node:fs";
|
|
3289
|
+
import * as path6 from "node:path";
|
|
2891
3290
|
var HISTORY_DIR = ".doccov/history";
|
|
2892
3291
|
function getHistoryDir(cwd) {
|
|
2893
|
-
return
|
|
3292
|
+
return path6.join(getDoccovDir(cwd), "history");
|
|
2894
3293
|
}
|
|
2895
3294
|
var RETENTION_DAYS = 90;
|
|
2896
3295
|
function getSnapshotFilename(timestamp) {
|
|
@@ -2905,7 +3304,7 @@ function getSnapshotFilename(timestamp) {
|
|
|
2905
3304
|
}
|
|
2906
3305
|
function computeSnapshot(spec, options) {
|
|
2907
3306
|
const exports = spec.exports ?? [];
|
|
2908
|
-
const documented = exports.filter(
|
|
3307
|
+
const documented = exports.filter(isExportDocumented);
|
|
2909
3308
|
const driftCount = exports.reduce((sum, e) => {
|
|
2910
3309
|
const docs = e.docs;
|
|
2911
3310
|
return sum + (docs?.drift?.length ?? 0);
|
|
@@ -2925,23 +3324,23 @@ function computeSnapshot(spec, options) {
|
|
|
2925
3324
|
}
|
|
2926
3325
|
function saveSnapshot(snapshot, cwd) {
|
|
2927
3326
|
const historyDir = getHistoryDir(cwd);
|
|
2928
|
-
if (!
|
|
2929
|
-
|
|
3327
|
+
if (!fs6.existsSync(historyDir)) {
|
|
3328
|
+
fs6.mkdirSync(historyDir, { recursive: true });
|
|
2930
3329
|
}
|
|
2931
3330
|
const filename = getSnapshotFilename(new Date(snapshot.timestamp));
|
|
2932
|
-
const filepath =
|
|
2933
|
-
|
|
3331
|
+
const filepath = path6.join(historyDir, filename);
|
|
3332
|
+
fs6.writeFileSync(filepath, JSON.stringify(snapshot, null, 2));
|
|
2934
3333
|
}
|
|
2935
3334
|
function loadSnapshots(cwd) {
|
|
2936
3335
|
const historyDir = getHistoryDir(cwd);
|
|
2937
|
-
if (!
|
|
3336
|
+
if (!fs6.existsSync(historyDir)) {
|
|
2938
3337
|
return [];
|
|
2939
3338
|
}
|
|
2940
|
-
const files =
|
|
3339
|
+
const files = fs6.readdirSync(historyDir).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
2941
3340
|
const snapshots = [];
|
|
2942
3341
|
for (const file of files) {
|
|
2943
3342
|
try {
|
|
2944
|
-
const content =
|
|
3343
|
+
const content = fs6.readFileSync(path6.join(historyDir, file), "utf-8");
|
|
2945
3344
|
snapshots.push(JSON.parse(content));
|
|
2946
3345
|
} catch {}
|
|
2947
3346
|
}
|
|
@@ -2982,14 +3381,14 @@ function formatDelta(delta) {
|
|
|
2982
3381
|
}
|
|
2983
3382
|
function pruneHistory(cwd, keepCount = 100) {
|
|
2984
3383
|
const historyDir = getHistoryDir(cwd);
|
|
2985
|
-
if (!
|
|
3384
|
+
if (!fs6.existsSync(historyDir)) {
|
|
2986
3385
|
return 0;
|
|
2987
3386
|
}
|
|
2988
|
-
const files =
|
|
3387
|
+
const files = fs6.readdirSync(historyDir).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
2989
3388
|
const toDelete = files.slice(keepCount);
|
|
2990
3389
|
for (const file of toDelete) {
|
|
2991
3390
|
try {
|
|
2992
|
-
|
|
3391
|
+
fs6.unlinkSync(path6.join(historyDir, file));
|
|
2993
3392
|
} catch {}
|
|
2994
3393
|
}
|
|
2995
3394
|
return toDelete.length;
|
|
@@ -3085,4 +3484,4 @@ function getExtendedTrend(spec, cwd, options) {
|
|
|
3085
3484
|
};
|
|
3086
3485
|
}
|
|
3087
3486
|
|
|
3088
|
-
export { isFixableDrift, generateFix, generateFixesForExport, mergeFixes, categorizeDrifts, generateForgottenExportFixes, groupFixesByFile, applyForgottenExportFixes, previewForgottenExportFixes, ts, parseJSDocToPatch, applyPatchToJSDoc, serializeJSDoc, findJSDocLocation, applyEdits, createSourceFile, isBuiltInIdentifier, detectExampleRuntimeErrors, parseAssertions, hasNonAssertionComments, detectExampleAssertionFailures, buildExportRegistry, computeDrift, computeExportDrift, computeHealth, buildDocCovSpec, DRIFT_CATEGORIES2 as DRIFT_CATEGORIES, DRIFT_CATEGORY_LABELS, DRIFT_CATEGORY_DESCRIPTIONS, categorizeDrift, groupDriftsByCategory, getDriftSummary, formatDriftSummaryLine, calculateAggregateCoverage, ensureSpecCoverage, getExportAnalysis, getExportScore, getExportDrift, getExportMissing, isExportFullyDocumented, generateReport, generateReportFromDocCov, loadCachedReport, saveReport, isCachedReportValid, renderApiSurface, extractSchemaOutputType, getRegisteredAdapters, getSupportedLibraries, extractSchemaType, extractStandardSchemas, extractStandardSchemasFromProject, findAdapter, isSchemaType, isStandardJSONSchema, resolveCompiledPath, detectRuntimeSchemas, findProjectRoot, getDoccovDir, HISTORY_DIR, computeSnapshot, saveSnapshot, loadSnapshots, getTrend, renderSparkline, formatDelta, pruneHistory, loadSnapshotsForDays, generateWeeklySummaries, getExtendedTrend };
|
|
3487
|
+
export { isFixableDrift, generateFix, generateFixesForExport, mergeFixes, categorizeDrifts, generateForgottenExportFixes, groupFixesByFile, applyForgottenExportFixes, previewForgottenExportFixes, ts, parseJSDocToPatch, applyPatchToJSDoc, serializeJSDoc, findJSDocLocation, applyEdits, createSourceFile, isBuiltInIdentifier, detectExampleRuntimeErrors, parseAssertions, hasNonAssertionComments, detectExampleAssertionFailures, buildExportRegistry, computeDrift, computeExportDrift, isExportDocumented, computeHealth, DEFAULT_REQUIREMENTS, PRESETS, resolveRequirements, buildDocCovSpec, DRIFT_CATEGORIES2 as DRIFT_CATEGORIES, DRIFT_CATEGORY_LABELS, DRIFT_CATEGORY_DESCRIPTIONS, categorizeDrift, groupDriftsByCategory, getDriftSummary, formatDriftSummaryLine, calculateAggregateCoverage, ensureSpecCoverage, getExportAnalysis, getExportScore, getExportDrift, getExportMissing, isExportFullyDocumented, generateReport, generateReportFromDocCov, loadCachedReport, saveReport, isCachedReportValid, renderApiSurface, buildModuleGraph, findSymbolModule, symbolExistsInGraph, IncrementalAnalyzer, findOrphanedTempFiles, cleanupOrphanedTempFiles, extractSchemaOutputType, getRegisteredAdapters, getSupportedLibraries, extractSchemaType, extractStandardSchemas, extractStandardSchemasFromProject, findAdapter, isSchemaType, isStandardJSONSchema, resolveCompiledPath, detectRuntimeSchemas, findProjectRoot, getDoccovDir, HISTORY_DIR, computeSnapshot, saveSnapshot, loadSnapshots, getTrend, renderSparkline, formatDelta, pruneHistory, loadSnapshotsForDays, generateWeeklySummaries, getExtendedTrend };
|