doc-freshness-checker 2.0.6 → 2.0.7
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/README.md +2 -0
- package/dist/runner.js +35 -16
- package/dist/runner.js.map +1 -1
- package/dist/runner.test.js +433 -1
- package/dist/runner.test.js.map +1 -1
- package/dist/utils/incremental.d.ts +14 -18
- package/dist/utils/incremental.js +281 -39
- package/dist/utils/incremental.js.map +1 -1
- package/dist/utils/incremental.test.js +385 -74
- package/dist/utils/incremental.test.js.map +1 -1
- package/dist/utils/manifestPaths.d.ts +2 -0
- package/dist/utils/manifestPaths.js +6 -0
- package/dist/utils/manifestPaths.js.map +1 -0
- package/dist/validators/codePatternValidator.d.ts +3 -0
- package/dist/validators/codePatternValidator.js +31 -3
- package/dist/validators/codePatternValidator.js.map +1 -1
- package/dist/validators/codePatternValidator.test.js +64 -0
- package/dist/validators/codePatternValidator.test.js.map +1 -1
- package/dist/validators/dependencyValidator.js +9 -6
- package/dist/validators/dependencyValidator.js.map +1 -1
- package/dist/validators/dependencyValidator.test.js +6 -0
- package/dist/validators/dependencyValidator.test.js.map +1 -1
- package/dist/validators/directoryValidator.d.ts +1 -0
- package/dist/validators/directoryValidator.js +17 -4
- package/dist/validators/directoryValidator.js.map +1 -1
- package/dist/validators/directoryValidator.test.js +7 -0
- package/dist/validators/directoryValidator.test.js.map +1 -1
- package/dist/validators/fileValidator.d.ts +1 -0
- package/dist/validators/fileValidator.js +16 -14
- package/dist/validators/fileValidator.js.map +1 -1
- package/dist/validators/fileValidator.test.js +5 -0
- package/dist/validators/fileValidator.test.js.map +1 -1
- package/dist/validators/incrementalInputs.d.ts +1 -0
- package/dist/validators/incrementalInputs.js +6 -0
- package/dist/validators/incrementalInputs.js.map +1 -0
- package/dist/validators/validationEngine.d.ts +2 -0
- package/dist/validators/validationEngine.js +89 -0
- package/dist/validators/validationEngine.js.map +1 -1
- package/dist/validators/validationEngine.test.js +93 -1
- package/dist/validators/validationEngine.test.js.map +1 -1
- package/dist/validators/versionValidator.js +9 -6
- package/dist/validators/versionValidator.js.map +1 -1
- package/dist/validators/versionValidator.test.js +6 -0
- package/dist/validators/versionValidator.test.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -134,6 +134,8 @@ Options:
|
|
|
134
134
|
--vector-search Enable semantic vector search for doc-code mismatches
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
+
Incremental mode skips unchanged documents only after a clean run when the effective configuration and project inventory are unchanged. It conservatively revalidates all documents when filesystem or external inputs cannot be fingerprinted safely.
|
|
138
|
+
|
|
137
139
|
### Common Examples
|
|
138
140
|
|
|
139
141
|
```bash
|
package/dist/runner.js
CHANGED
|
@@ -27,6 +27,12 @@ import { FreshnessScorer } from './scoring/freshnessScorer.js';
|
|
|
27
27
|
import { IncrementalChecker } from './utils/incremental.js';
|
|
28
28
|
import { VectorSearch } from './semantic/vectorSearch.js';
|
|
29
29
|
import { loadConfig } from './config/loader.js';
|
|
30
|
+
const REPORTER_OUTPUT = {
|
|
31
|
+
console: 'console',
|
|
32
|
+
json: 'stable',
|
|
33
|
+
markdown: 'timestamped',
|
|
34
|
+
enhanced: 'timestamped',
|
|
35
|
+
};
|
|
30
36
|
/**
|
|
31
37
|
* Main entry point - run with config object
|
|
32
38
|
*/
|
|
@@ -94,34 +100,39 @@ export async function run(config) {
|
|
|
94
100
|
if (config.verbose) {
|
|
95
101
|
console.log('Scanning documentation files...');
|
|
96
102
|
}
|
|
97
|
-
|
|
103
|
+
const allDocuments = await parser.scanDocuments();
|
|
104
|
+
let documentsToValidate = allDocuments;
|
|
98
105
|
if (config.verbose) {
|
|
99
|
-
console.log(`Found ${
|
|
106
|
+
console.log(`Found ${allDocuments.length} documentation files.`);
|
|
100
107
|
}
|
|
101
108
|
// Apply incremental checking if enabled
|
|
102
109
|
let incrementalChecker = null;
|
|
103
|
-
if (config.incremental?.enabled) {
|
|
104
|
-
|
|
105
|
-
const
|
|
106
|
-
|
|
110
|
+
if (config.incremental?.enabled && allDocuments.length > 0) {
|
|
111
|
+
const stateDir = path.resolve(config.rootDir || process.cwd(), config.cache?.dir || config.graph?.cacheDir || '.doc-freshness-cache');
|
|
112
|
+
const incrementalInputs = await validationEngine.captureIncrementalInputs(allDocuments);
|
|
113
|
+
const finalFileReporter = (config.reporters || ['console']).findLast((reporter) => REPORTER_OUTPUT[reporter] === 'stable' || REPORTER_OUTPUT[reporter] === 'timestamped');
|
|
114
|
+
const inventoryExclusions = config.outputPath && finalFileReporter && REPORTER_OUTPUT[finalFileReporter] === 'timestamped'
|
|
115
|
+
? [path.resolve(config.outputPath)]
|
|
116
|
+
: [];
|
|
117
|
+
incrementalChecker = new IncrementalChecker(stateDir);
|
|
118
|
+
documentsToValidate = await incrementalChecker.filterChanged(allDocuments, config, incrementalInputs, inventoryExclusions);
|
|
107
119
|
if (config.verbose) {
|
|
108
|
-
const stats = incrementalChecker.getStats(
|
|
120
|
+
const stats = incrementalChecker.getStats(allDocuments.length, documentsToValidate.length);
|
|
109
121
|
console.log(`Incremental mode: checking ${stats.changed} changed files, skipping ${stats.skipped} unchanged.`);
|
|
110
122
|
}
|
|
111
123
|
}
|
|
124
|
+
else if (config.incremental?.enabled && config.verbose) {
|
|
125
|
+
console.log('Incremental mode: checking 0 changed files, skipping 0 unchanged.');
|
|
126
|
+
}
|
|
112
127
|
if (config.verbose) {
|
|
113
|
-
const totalRefs =
|
|
128
|
+
const totalRefs = allDocuments.reduce((sum, doc) => sum + doc.references.length, 0);
|
|
114
129
|
console.log(`Extracted ${totalRefs} references.`);
|
|
115
130
|
}
|
|
116
131
|
// Validate references
|
|
117
132
|
if (config.verbose) {
|
|
118
133
|
console.log('Validating references...');
|
|
119
134
|
}
|
|
120
|
-
const results = await validationEngine.validate(
|
|
121
|
-
// Save incremental state
|
|
122
|
-
if (incrementalChecker) {
|
|
123
|
-
await incrementalChecker.saveState();
|
|
124
|
-
}
|
|
135
|
+
const results = await validationEngine.validate(documentsToValidate);
|
|
125
136
|
// Build graph if enabled
|
|
126
137
|
let graph = null;
|
|
127
138
|
let gitTracker = null;
|
|
@@ -133,7 +144,7 @@ export async function run(config) {
|
|
|
133
144
|
const codeIndex = codePatternValidator.getSourceIndex();
|
|
134
145
|
// Build graph
|
|
135
146
|
const graphBuilder = new GraphBuilder(config);
|
|
136
|
-
graph = await graphBuilder.buildGraph(
|
|
147
|
+
graph = await graphBuilder.buildGraph(allDocuments, codeIndex);
|
|
137
148
|
if (gitTracker.isGitRepo()) {
|
|
138
149
|
graph.gitCommit = gitTracker.getCurrentCommit();
|
|
139
150
|
}
|
|
@@ -146,7 +157,7 @@ export async function run(config) {
|
|
|
146
157
|
// Calculate freshness scores if enabled
|
|
147
158
|
if (config.freshnessScoring?.enabled) {
|
|
148
159
|
const scorer = new FreshnessScorer(config);
|
|
149
|
-
freshnessScores = scorer.calculateProjectScores(
|
|
160
|
+
freshnessScores = scorer.calculateProjectScores(allDocuments, results, gitTracker, graph);
|
|
150
161
|
}
|
|
151
162
|
}
|
|
152
163
|
// Run vector search if enabled
|
|
@@ -159,7 +170,7 @@ export async function run(config) {
|
|
|
159
170
|
if (config.verbose) {
|
|
160
171
|
console.log(' Indexing documentation sections...');
|
|
161
172
|
}
|
|
162
|
-
await vectorSearch.indexDocumentation(
|
|
173
|
+
await vectorSearch.indexDocumentation(allDocuments);
|
|
163
174
|
// Ensure source index is built (may already be built if graph is enabled)
|
|
164
175
|
if (!codePatternValidator.getSourceIndex()) {
|
|
165
176
|
if (config.verbose) {
|
|
@@ -197,6 +208,14 @@ export async function run(config) {
|
|
|
197
208
|
}
|
|
198
209
|
// Generate reports
|
|
199
210
|
await generateReports(results, config, graph, gitTracker, freshnessScores, vectorMismatches);
|
|
211
|
+
// Persist only after validation and reporting complete; findings make the next run revalidate everything.
|
|
212
|
+
if (incrementalChecker) {
|
|
213
|
+
const clean = results.documents.length === 0 &&
|
|
214
|
+
!results.vectorMismatches?.length &&
|
|
215
|
+
!validationEngine.hadInvalidResults() &&
|
|
216
|
+
!validationEngine.hadIncompleteValidation();
|
|
217
|
+
await incrementalChecker.saveState(clean);
|
|
218
|
+
}
|
|
200
219
|
return results;
|
|
201
220
|
}
|
|
202
221
|
/**
|
package/dist/runner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.test.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
2
3
|
import path from 'path';
|
|
4
|
+
import { glob } from 'glob';
|
|
3
5
|
import { run, runWithConfig } from './runner.js';
|
|
6
|
+
import { ValidationEngine } from './validators/validationEngine.js';
|
|
7
|
+
import { IncrementalChecker } from './utils/incremental.js';
|
|
4
8
|
import { withOutputFile } from './test-utils/tempFiles.js';
|
|
5
9
|
import { captureConsoleLog, captureConsoleWarn } from './test-utils/console.js';
|
|
6
10
|
vi.mock('fastembed', () => ({
|
|
@@ -33,6 +37,19 @@ describe('runner', () => {
|
|
|
33
37
|
];
|
|
34
38
|
const captureLog = captureConsoleLog;
|
|
35
39
|
const captureWarn = captureConsoleWarn;
|
|
40
|
+
const mockDocumentScan = (docPath) => {
|
|
41
|
+
vi.mocked(glob).mockResolvedValueOnce([docPath]);
|
|
42
|
+
};
|
|
43
|
+
const withIncrementalRoot = async (name, test) => {
|
|
44
|
+
const rootDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), `runner-inc-${name}-`));
|
|
45
|
+
try {
|
|
46
|
+
await test(rootDir);
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
vi.mocked(glob).mockResolvedValue([]);
|
|
50
|
+
await fs.promises.rm(rootDir, { recursive: true, force: true });
|
|
51
|
+
}
|
|
52
|
+
};
|
|
36
53
|
const baseConfig = {
|
|
37
54
|
rootDir: process.cwd(),
|
|
38
55
|
include: [],
|
|
@@ -55,6 +72,17 @@ describe('runner', () => {
|
|
|
55
72
|
reporters: [],
|
|
56
73
|
verbose: false,
|
|
57
74
|
};
|
|
75
|
+
const incrementalConfig = (rootDir, overrides = {}) => ({
|
|
76
|
+
...baseConfig,
|
|
77
|
+
rootDir,
|
|
78
|
+
include: ['*.md'],
|
|
79
|
+
sourcePatterns: [],
|
|
80
|
+
manifestFiles: [],
|
|
81
|
+
incremental: { enabled: true },
|
|
82
|
+
cache: { enabled: false, dir: '.cache' },
|
|
83
|
+
...overrides,
|
|
84
|
+
rules: { ...baseConfig.rules, ...overrides.rules },
|
|
85
|
+
});
|
|
58
86
|
afterAll(async () => {
|
|
59
87
|
await Promise.all(transientCacheDirs.map((dir) => fs.promises.rm(path.join(process.cwd(), dir), { recursive: true, force: true }).catch(() => { })));
|
|
60
88
|
});
|
|
@@ -245,7 +273,7 @@ describe('runner', () => {
|
|
|
245
273
|
});
|
|
246
274
|
});
|
|
247
275
|
describe('incremental mode', () => {
|
|
248
|
-
it('
|
|
276
|
+
it('reports changed files in verbose mode', async () => {
|
|
249
277
|
const spy = captureLog();
|
|
250
278
|
await run({
|
|
251
279
|
...baseConfig,
|
|
@@ -255,6 +283,410 @@ describe('runner', () => {
|
|
|
255
283
|
});
|
|
256
284
|
expect(spy.mock.calls.flat().join('\n')).toContain('Incremental');
|
|
257
285
|
});
|
|
286
|
+
it('skips a second clean run with default validator configuration', async () => {
|
|
287
|
+
await withIncrementalRoot('defaults', async (rootDir) => {
|
|
288
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
289
|
+
const targetPath = path.join(rootDir, 'target.ts');
|
|
290
|
+
await fs.promises.writeFile(docPath, '[target](target.ts)');
|
|
291
|
+
await fs.promises.writeFile(targetPath, 'export const target = true;');
|
|
292
|
+
const config = {
|
|
293
|
+
rootDir,
|
|
294
|
+
include: ['*.md'],
|
|
295
|
+
incremental: { enabled: true },
|
|
296
|
+
graph: { enabled: false },
|
|
297
|
+
cache: { enabled: false, dir: '.cache' },
|
|
298
|
+
reporters: [],
|
|
299
|
+
};
|
|
300
|
+
mockDocumentScan(docPath);
|
|
301
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
302
|
+
mockDocumentScan(docPath);
|
|
303
|
+
expect((await run(config)).summary.total).toBe(0);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
it('allows reuse when git tracking is enabled without freshness scoring', async () => {
|
|
307
|
+
await withIncrementalRoot('git-only', async (rootDir) => {
|
|
308
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
309
|
+
const targetPath = path.join(rootDir, 'target.ts');
|
|
310
|
+
await fs.promises.writeFile(docPath, '[target](target.ts)');
|
|
311
|
+
await fs.promises.writeFile(targetPath, 'export const target = true;');
|
|
312
|
+
const config = incrementalConfig(rootDir, {
|
|
313
|
+
git: { enabled: true },
|
|
314
|
+
rules: { 'file-path': { enabled: true } },
|
|
315
|
+
});
|
|
316
|
+
mockDocumentScan(docPath);
|
|
317
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
318
|
+
mockDocumentScan(docPath);
|
|
319
|
+
expect((await run(config)).summary.total).toBe(0);
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
it('reuses expected skips but revalidates when a disabled unsafe rule is enabled', async () => {
|
|
323
|
+
await withIncrementalRoot('disabled-rule', async (rootDir) => {
|
|
324
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
325
|
+
await fs.promises.writeFile(docPath, '[site](https://example.com)');
|
|
326
|
+
const config = incrementalConfig(rootDir, {
|
|
327
|
+
rules: { 'external-url': { enabled: false } },
|
|
328
|
+
});
|
|
329
|
+
mockDocumentScan(docPath);
|
|
330
|
+
expect((await run(config)).summary.skipped).toBe(1);
|
|
331
|
+
mockDocumentScan(docPath);
|
|
332
|
+
expect((await run(config)).summary.total).toBe(0);
|
|
333
|
+
mockDocumentScan(docPath);
|
|
334
|
+
expect((await run({ ...config, rules: { ...config.rules, 'external-url': { enabled: true } } })).summary.total).toBe(1);
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
it('reuses a clean run containing an illustrative safe-rule skip', async () => {
|
|
338
|
+
await withIncrementalRoot('illustrative-skip', async (rootDir) => {
|
|
339
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
340
|
+
await fs.promises.writeFile(docPath, '[example](YourProject/file.ts)');
|
|
341
|
+
const config = incrementalConfig(rootDir, {
|
|
342
|
+
rules: { 'file-path': { enabled: true, skipIllustrative: true } },
|
|
343
|
+
});
|
|
344
|
+
mockDocumentScan(docPath);
|
|
345
|
+
expect((await run(config)).summary.skipped).toBe(1);
|
|
346
|
+
mockDocumentScan(docPath);
|
|
347
|
+
expect((await run(config)).summary.total).toBe(0);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
it('keeps state dirty when a validator is missing or throws', async () => {
|
|
351
|
+
await withIncrementalRoot('incomplete', async (rootDir) => {
|
|
352
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
353
|
+
const cacheDir = path.join(rootDir, '.cache');
|
|
354
|
+
await fs.promises.writeFile(docPath, 'unknown input');
|
|
355
|
+
const unknownExtractor = {
|
|
356
|
+
type: 'unknown-type',
|
|
357
|
+
supportedFormats: ['markdown'],
|
|
358
|
+
supportsFormat: () => true,
|
|
359
|
+
extract: (document) => [{ type: 'unknown-type', value: 'value', lineNumber: 1, raw: 'value', sourceFile: document.path }],
|
|
360
|
+
findLineNumber: () => 1,
|
|
361
|
+
getContext: () => '',
|
|
362
|
+
};
|
|
363
|
+
const missingConfig = incrementalConfig(rootDir, { customExtractors: [unknownExtractor] });
|
|
364
|
+
mockDocumentScan(docPath);
|
|
365
|
+
await run(missingConfig);
|
|
366
|
+
expect(JSON.parse(await fs.promises.readFile(path.join(cacheDir, 'file-hashes.json'), 'utf-8')).clean).toBe(false);
|
|
367
|
+
const throwingValidator = {
|
|
368
|
+
async validateBatch() {
|
|
369
|
+
throw new Error('validator failed');
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
const throwingConfig = incrementalConfig(rootDir, {
|
|
373
|
+
customValidators: { 'file-path': throwingValidator },
|
|
374
|
+
rules: { 'file-path': { enabled: true } },
|
|
375
|
+
});
|
|
376
|
+
await fs.promises.writeFile(docPath, '[target](target.ts)');
|
|
377
|
+
mockDocumentScan(docPath);
|
|
378
|
+
await run(throwingConfig);
|
|
379
|
+
expect(JSON.parse(await fs.promises.readFile(path.join(cacheDir, 'file-hashes.json'), 'utf-8')).clean).toBe(false);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
it('revalidates unchanged docs when a referenced source file is renamed', async () => {
|
|
383
|
+
await withIncrementalRoot('source', async (rootDir) => {
|
|
384
|
+
const docsDir = path.join(rootDir, 'docs');
|
|
385
|
+
const sourceDir = path.join(rootDir, 'src');
|
|
386
|
+
const docPath = path.join(docsDir, 'guide.md');
|
|
387
|
+
const sourcePath = path.join(sourceDir, 'server.ts');
|
|
388
|
+
const renamedSourcePath = path.join(sourceDir, 'renamed.ts');
|
|
389
|
+
const cacheDir = path.join(rootDir, '.cache');
|
|
390
|
+
await fs.promises.mkdir(docsDir, { recursive: true });
|
|
391
|
+
await fs.promises.mkdir(sourceDir, { recursive: true });
|
|
392
|
+
await fs.promises.writeFile(docPath, '[source](../src/server.ts)');
|
|
393
|
+
await fs.promises.writeFile(sourcePath, 'export const server = true;');
|
|
394
|
+
const config = incrementalConfig(rootDir, {
|
|
395
|
+
include: ['docs/**/*.md'],
|
|
396
|
+
sourcePatterns: ['src/**/*.ts'],
|
|
397
|
+
rules: { 'file-path': { enabled: true } },
|
|
398
|
+
});
|
|
399
|
+
mockDocumentScan(docPath);
|
|
400
|
+
expect((await run(config)).summary.errors).toBe(0);
|
|
401
|
+
await expect(fs.promises.access(path.join(cacheDir, 'file-hashes.json'))).resolves.toBeUndefined();
|
|
402
|
+
mockDocumentScan(docPath);
|
|
403
|
+
expect((await run(config)).summary.total).toBe(0);
|
|
404
|
+
await fs.promises.rename(sourcePath, renamedSourcePath);
|
|
405
|
+
mockDocumentScan(docPath);
|
|
406
|
+
expect((await run(config)).summary.errors).toBe(1);
|
|
407
|
+
const stateFile = path.join(cacheDir, 'file-hashes.json');
|
|
408
|
+
expect(JSON.parse(await fs.promises.readFile(stateFile, 'utf-8')).clean).toBe(false);
|
|
409
|
+
mockDocumentScan(docPath);
|
|
410
|
+
expect((await run(config)).summary.errors).toBe(1);
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
it('reuses the code-pattern source index and fingerprints excluded source matches', async () => {
|
|
414
|
+
await withIncrementalRoot('code-pattern-source', async (rootDir) => {
|
|
415
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
416
|
+
const sourceDir = path.join(rootDir, '.git');
|
|
417
|
+
const sourcePath = path.join(sourceDir, 'source.ts');
|
|
418
|
+
await fs.promises.mkdir(sourceDir);
|
|
419
|
+
await fs.promises.writeFile(docPath, ['```typescript', 'class StableService {}', '```'].join('\n'));
|
|
420
|
+
await fs.promises.writeFile(sourcePath, 'export class StableService {}');
|
|
421
|
+
const config = incrementalConfig(rootDir, {
|
|
422
|
+
sourcePatterns: ['.g{it,noop}/**/*.ts'],
|
|
423
|
+
rules: { 'code-pattern': { enabled: true, severity: 'warning' } },
|
|
424
|
+
});
|
|
425
|
+
const mockScans = () => {
|
|
426
|
+
mockDocumentScan(docPath);
|
|
427
|
+
vi.mocked(glob).mockResolvedValueOnce([sourcePath]);
|
|
428
|
+
};
|
|
429
|
+
mockScans();
|
|
430
|
+
expect((await run(config)).summary.warnings).toBe(0);
|
|
431
|
+
mockScans();
|
|
432
|
+
expect((await run(config)).summary.total).toBe(0);
|
|
433
|
+
await fs.promises.writeFile(sourcePath, 'export class ReplacementService {}');
|
|
434
|
+
mockScans();
|
|
435
|
+
expect((await run(config)).summary.warnings).toBe(1);
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
it('captures graph-only source inputs once per run and skips unchanged validation', async () => {
|
|
439
|
+
await withIncrementalRoot('graph-source', async (rootDir) => {
|
|
440
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
441
|
+
const sourceDir = path.join(rootDir, 'src');
|
|
442
|
+
const sourcePath = path.join(sourceDir, 'source.ts');
|
|
443
|
+
await fs.promises.mkdir(sourceDir);
|
|
444
|
+
await fs.promises.writeFile(docPath, 'No code-pattern references');
|
|
445
|
+
await fs.promises.writeFile(sourcePath, 'export class GraphSource {}');
|
|
446
|
+
const config = incrementalConfig(rootDir, {
|
|
447
|
+
graph: { enabled: true },
|
|
448
|
+
sourcePatterns: ['src/**/*.ts'],
|
|
449
|
+
});
|
|
450
|
+
const mockScans = () => {
|
|
451
|
+
mockDocumentScan(docPath);
|
|
452
|
+
vi.mocked(glob).mockResolvedValueOnce([sourcePath]);
|
|
453
|
+
};
|
|
454
|
+
const validateSpy = vi.spyOn(ValidationEngine.prototype, 'validate');
|
|
455
|
+
const readSpy = vi.spyOn(fs.promises, 'readFile');
|
|
456
|
+
vi.mocked(glob).mockClear();
|
|
457
|
+
try {
|
|
458
|
+
mockScans();
|
|
459
|
+
await run(config);
|
|
460
|
+
mockScans();
|
|
461
|
+
await run(config);
|
|
462
|
+
expect(validateSpy.mock.calls[0][0]).toHaveLength(1);
|
|
463
|
+
expect(validateSpy.mock.calls[1][0]).toEqual([]);
|
|
464
|
+
expect(vi.mocked(glob).mock.calls.filter(([pattern]) => pattern === 'src/**/*.ts')).toHaveLength(2);
|
|
465
|
+
expect(readSpy.mock.calls.filter(([file]) => file === sourcePath)).toHaveLength(2);
|
|
466
|
+
}
|
|
467
|
+
finally {
|
|
468
|
+
validateSpy.mockRestore();
|
|
469
|
+
readSpy.mockRestore();
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
it('bypasses incremental inventory for an empty graph-enabled scan', async () => {
|
|
474
|
+
await withIncrementalRoot('empty-graph', async (rootDir) => {
|
|
475
|
+
const sourceDir = path.join(rootDir, 'src');
|
|
476
|
+
const sourcePath = path.join(sourceDir, 'source.ts');
|
|
477
|
+
await fs.promises.mkdir(sourceDir);
|
|
478
|
+
await fs.promises.writeFile(sourcePath, 'export class GraphSource {}');
|
|
479
|
+
const config = incrementalConfig(rootDir, {
|
|
480
|
+
graph: { enabled: true },
|
|
481
|
+
sourcePatterns: ['src/**/*.ts'],
|
|
482
|
+
});
|
|
483
|
+
const filterSpy = vi.spyOn(IncrementalChecker.prototype, 'filterChanged');
|
|
484
|
+
const readSpy = vi.spyOn(fs.promises, 'readFile');
|
|
485
|
+
vi.mocked(glob).mockClear();
|
|
486
|
+
try {
|
|
487
|
+
vi.mocked(glob).mockResolvedValueOnce([]).mockResolvedValueOnce([sourcePath]);
|
|
488
|
+
await run(config);
|
|
489
|
+
expect(filterSpy).not.toHaveBeenCalled();
|
|
490
|
+
await expect(fs.promises.access(path.join(rootDir, '.cache', 'file-hashes.json'))).rejects.toThrow();
|
|
491
|
+
expect(vi.mocked(glob).mock.calls.filter(([pattern]) => pattern === 'src/**/*.ts')).toHaveLength(1);
|
|
492
|
+
expect(readSpy.mock.calls.filter(([file]) => file === sourcePath)).toHaveLength(1);
|
|
493
|
+
}
|
|
494
|
+
finally {
|
|
495
|
+
filterSpy.mockRestore();
|
|
496
|
+
readSpy.mockRestore();
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
it('revalidates code-snippet fallbacks under excluded directories', async () => {
|
|
501
|
+
await withIncrementalRoot('snippet', async (rootDir) => {
|
|
502
|
+
const docsDir = path.join(rootDir, 'docs');
|
|
503
|
+
const dependencyDir = path.join(rootDir, 'node_modules');
|
|
504
|
+
const docPath = path.join(docsDir, 'guide.md');
|
|
505
|
+
const sourcePath = path.join(dependencyDir, 'generated.js');
|
|
506
|
+
await fs.promises.mkdir(docsDir);
|
|
507
|
+
await fs.promises.mkdir(dependencyDir);
|
|
508
|
+
await fs.promises.writeFile(docPath, "```js\nimport * as generated from '../node_modules/generated';\n```");
|
|
509
|
+
await fs.promises.writeFile(sourcePath, 'export const generated = true;');
|
|
510
|
+
const config = incrementalConfig(rootDir, {
|
|
511
|
+
include: ['docs/**/*.md'],
|
|
512
|
+
rules: { 'code-snippet': { enabled: true } },
|
|
513
|
+
});
|
|
514
|
+
mockDocumentScan(docPath);
|
|
515
|
+
expect((await run(config)).summary.warnings).toBe(0);
|
|
516
|
+
mockDocumentScan(docPath);
|
|
517
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
518
|
+
await fs.promises.rm(sourcePath);
|
|
519
|
+
mockDocumentScan(docPath);
|
|
520
|
+
expect((await run(config)).summary.warnings).toBe(1);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
it('revalidates unchanged docs after an unreported info finding', async () => {
|
|
524
|
+
await withIncrementalRoot('info', async (rootDir) => {
|
|
525
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
526
|
+
const cacheDir = path.join(rootDir, '.cache');
|
|
527
|
+
await fs.promises.writeFile(docPath, 'Install `missingpkg`.');
|
|
528
|
+
await fs.promises.writeFile(path.join(rootDir, 'package.json'), '{}');
|
|
529
|
+
const config = incrementalConfig(rootDir, {
|
|
530
|
+
manifestFiles: ['package.json'],
|
|
531
|
+
rules: { dependency: { enabled: true, severity: 'info' } },
|
|
532
|
+
cache: { enabled: false, dir: cacheDir },
|
|
533
|
+
});
|
|
534
|
+
mockDocumentScan(docPath);
|
|
535
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
536
|
+
mockDocumentScan(docPath);
|
|
537
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
538
|
+
expect(JSON.parse(await fs.promises.readFile(path.join(cacheDir, 'file-hashes.json'), 'utf-8')).clean).toBe(false);
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
it('revalidates after generated output changes the project inventory', async () => {
|
|
542
|
+
await withIncrementalRoot('output', async (rootDir) => {
|
|
543
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
544
|
+
const targetPath = path.join(rootDir, 'target.ts');
|
|
545
|
+
const outputPath = path.join(rootDir, 'report.json');
|
|
546
|
+
await fs.promises.writeFile(docPath, '[target](target.ts)');
|
|
547
|
+
await fs.promises.writeFile(targetPath, 'export const target = true;');
|
|
548
|
+
const config = incrementalConfig(rootDir, {
|
|
549
|
+
rules: { 'file-path': { enabled: true } },
|
|
550
|
+
reporters: ['json'],
|
|
551
|
+
outputPath,
|
|
552
|
+
});
|
|
553
|
+
mockDocumentScan(docPath);
|
|
554
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
555
|
+
mockDocumentScan(docPath);
|
|
556
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
557
|
+
mockDocumentScan(docPath);
|
|
558
|
+
expect((await run(config)).summary.total).toBe(0);
|
|
559
|
+
mockDocumentScan(docPath);
|
|
560
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
it.each(['markdown', 'enhanced'])('skips unchanged docs when %s output is inside rootDir', async (reporter) => {
|
|
564
|
+
await withIncrementalRoot(`output-${reporter}`, async (rootDir) => {
|
|
565
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
566
|
+
const targetPath = path.join(rootDir, 'target.ts');
|
|
567
|
+
const outputPath = path.join(rootDir, `${reporter}.out`);
|
|
568
|
+
await fs.promises.writeFile(docPath, '[target](target.ts)');
|
|
569
|
+
await fs.promises.writeFile(targetPath, 'export const target = true;');
|
|
570
|
+
const config = incrementalConfig(rootDir, {
|
|
571
|
+
rules: { 'file-path': { enabled: true } },
|
|
572
|
+
reporters: [reporter],
|
|
573
|
+
outputPath,
|
|
574
|
+
});
|
|
575
|
+
mockDocumentScan(docPath);
|
|
576
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
577
|
+
await expect(fs.promises.access(outputPath)).resolves.toBeUndefined();
|
|
578
|
+
mockDocumentScan(docPath);
|
|
579
|
+
expect((await run(config)).summary.total).toBe(0);
|
|
580
|
+
mockDocumentScan(docPath);
|
|
581
|
+
expect((await run(config)).summary.total).toBe(0);
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
it.each([
|
|
585
|
+
{ reporters: ['markdown', 'json'], expectedTotals: [1, 1, 0, 1] },
|
|
586
|
+
{ reporters: ['json', 'markdown'], expectedTotals: [1, 0, 0, 0] },
|
|
587
|
+
])('keeps mixed reporter ordering stable for incremental output', async ({ reporters, expectedTotals }) => {
|
|
588
|
+
await withIncrementalRoot(`mixed-output-${reporters.join('-')}`, async (rootDir) => {
|
|
589
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
590
|
+
const targetPath = path.join(rootDir, 'target.ts');
|
|
591
|
+
const outputPath = path.join(rootDir, 'report.out');
|
|
592
|
+
await fs.promises.writeFile(docPath, '[target](target.ts)');
|
|
593
|
+
await fs.promises.writeFile(targetPath, 'export const target = true;');
|
|
594
|
+
const config = incrementalConfig(rootDir, {
|
|
595
|
+
rules: { 'file-path': { enabled: true } },
|
|
596
|
+
reporters,
|
|
597
|
+
outputPath,
|
|
598
|
+
});
|
|
599
|
+
const totals = [];
|
|
600
|
+
for (let runNumber = 0; runNumber < expectedTotals.length; runNumber++) {
|
|
601
|
+
mockDocumentScan(docPath);
|
|
602
|
+
totals.push((await run(config)).summary.total);
|
|
603
|
+
}
|
|
604
|
+
expect(totals).toEqual(expectedTotals);
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
it('does not throw for markdown reporting without outputPath', async () => {
|
|
608
|
+
await withIncrementalRoot('markdown-stdout', async (rootDir) => {
|
|
609
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
610
|
+
await fs.promises.writeFile(docPath, 'No references');
|
|
611
|
+
const config = incrementalConfig(rootDir, { reporters: ['markdown'] });
|
|
612
|
+
const log = captureLog();
|
|
613
|
+
try {
|
|
614
|
+
mockDocumentScan(docPath);
|
|
615
|
+
await expect(run(config)).resolves.toBeDefined();
|
|
616
|
+
}
|
|
617
|
+
finally {
|
|
618
|
+
log.mockRestore();
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
});
|
|
622
|
+
it('revalidates when reporting mutates a validation input', async () => {
|
|
623
|
+
await withIncrementalRoot('output-overlap', async (rootDir) => {
|
|
624
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
625
|
+
const targetPath = path.join(rootDir, 'target.ts');
|
|
626
|
+
await fs.promises.writeFile(docPath, '[target](target.ts)');
|
|
627
|
+
await fs.promises.writeFile(targetPath, 'export const target = true;');
|
|
628
|
+
const config = incrementalConfig(rootDir, {
|
|
629
|
+
rules: { 'file-path': { enabled: true } },
|
|
630
|
+
reporters: ['json'],
|
|
631
|
+
outputPath: targetPath,
|
|
632
|
+
});
|
|
633
|
+
mockDocumentScan(docPath);
|
|
634
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
635
|
+
mockDocumentScan(docPath);
|
|
636
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
it('excludes lexical and real symlinked cache directories', async () => {
|
|
640
|
+
await withIncrementalRoot('cache-link', async (rootDir) => {
|
|
641
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
642
|
+
const targetPath = path.join(rootDir, 'target.ts');
|
|
643
|
+
const realCacheDir = path.join(rootDir, '.real-cache');
|
|
644
|
+
await fs.promises.writeFile(docPath, '[target](target.ts)');
|
|
645
|
+
await fs.promises.writeFile(targetPath, 'export const target = true;');
|
|
646
|
+
await fs.promises.mkdir(realCacheDir);
|
|
647
|
+
await fs.promises.symlink('.real-cache', path.join(rootDir, '.cache'), 'dir');
|
|
648
|
+
const config = incrementalConfig(rootDir, { rules: { 'file-path': { enabled: true } } });
|
|
649
|
+
mockDocumentScan(docPath);
|
|
650
|
+
expect((await run(config)).summary.total).toBe(1);
|
|
651
|
+
await expect(fs.promises.access(path.join(realCacheDir, 'file-hashes.json'))).resolves.toBeUndefined();
|
|
652
|
+
mockDocumentScan(docPath);
|
|
653
|
+
expect((await run(config)).summary.total).toBe(0);
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
it('does not advance state when validation or reporting throws', async () => {
|
|
657
|
+
await withIncrementalRoot('report', async (rootDir) => {
|
|
658
|
+
const docPath = path.join(rootDir, 'guide.md');
|
|
659
|
+
const cacheDir = path.join(rootDir, '.cache');
|
|
660
|
+
await fs.promises.writeFile(docPath, 'No references');
|
|
661
|
+
const config = incrementalConfig(rootDir, {
|
|
662
|
+
rules: { 'file-path': { enabled: true } },
|
|
663
|
+
cache: { enabled: false, dir: cacheDir },
|
|
664
|
+
});
|
|
665
|
+
mockDocumentScan(docPath);
|
|
666
|
+
await run(config);
|
|
667
|
+
const stateFile = path.join(cacheDir, 'file-hashes.json');
|
|
668
|
+
const baselineState = await fs.promises.readFile(stateFile, 'utf-8');
|
|
669
|
+
await fs.promises.writeFile(docPath, '[missing](missing.ts)');
|
|
670
|
+
const validateSpy = vi.spyOn(ValidationEngine.prototype, 'validate').mockRejectedValueOnce(new Error('validation failed'));
|
|
671
|
+
try {
|
|
672
|
+
mockDocumentScan(docPath);
|
|
673
|
+
await expect(run(config)).rejects.toThrow('validation failed');
|
|
674
|
+
expect(await fs.promises.readFile(stateFile, 'utf-8')).toBe(baselineState);
|
|
675
|
+
}
|
|
676
|
+
finally {
|
|
677
|
+
validateSpy.mockRestore();
|
|
678
|
+
}
|
|
679
|
+
mockDocumentScan(docPath);
|
|
680
|
+
await expect(run({
|
|
681
|
+
...config,
|
|
682
|
+
reporters: ['json'],
|
|
683
|
+
outputPath: rootDir,
|
|
684
|
+
})).rejects.toThrow();
|
|
685
|
+
expect(await fs.promises.readFile(stateFile, 'utf-8')).toBe(baselineState);
|
|
686
|
+
mockDocumentScan(docPath);
|
|
687
|
+
expect((await run(config)).summary.errors).toBe(1);
|
|
688
|
+
});
|
|
689
|
+
});
|
|
258
690
|
});
|
|
259
691
|
it('loads URL cache when cache is enabled', async () => {
|
|
260
692
|
const cacheDir = '.doc-freshness-cache/runner-url';
|