doc-freshness-checker 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +305 -0
- package/dist/cache/cacheManager.d.ts +42 -0
- package/dist/cache/cacheManager.js +138 -0
- package/dist/cache/cacheManager.js.map +1 -0
- package/dist/cache/cacheManager.test.d.ts +1 -0
- package/dist/cache/cacheManager.test.js +142 -0
- package/dist/cache/cacheManager.test.js.map +1 -0
- package/dist/cli.d.ts +32 -0
- package/dist/cli.js +137 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +184 -0
- package/dist/cli.test.js.map +1 -0
- package/dist/config/defaults.d.ts +5 -0
- package/dist/config/defaults.js +135 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/defineConfig.d.ts +28 -0
- package/dist/config/defineConfig.js +30 -0
- package/dist/config/defineConfig.js.map +1 -0
- package/dist/config/defineConfig.test.d.ts +1 -0
- package/dist/config/defineConfig.test.js +10 -0
- package/dist/config/defineConfig.test.js.map +1 -0
- package/dist/config/loader.d.ts +7 -0
- package/dist/config/loader.js +250 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/loader.test.d.ts +1 -0
- package/dist/config/loader.test.js +276 -0
- package/dist/config/loader.test.js.map +1 -0
- package/dist/git/changeTracker.d.ts +44 -0
- package/dist/git/changeTracker.js +149 -0
- package/dist/git/changeTracker.js.map +1 -0
- package/dist/git/changeTracker.test.d.ts +1 -0
- package/dist/git/changeTracker.test.js +184 -0
- package/dist/git/changeTracker.test.js.map +1 -0
- package/dist/graph/codeDocGraph.d.ts +43 -0
- package/dist/graph/codeDocGraph.js +103 -0
- package/dist/graph/codeDocGraph.js.map +1 -0
- package/dist/graph/codeDocGraph.test.d.ts +1 -0
- package/dist/graph/codeDocGraph.test.js +78 -0
- package/dist/graph/codeDocGraph.test.js.map +1 -0
- package/dist/graph/graphBuilder.d.ts +17 -0
- package/dist/graph/graphBuilder.js +76 -0
- package/dist/graph/graphBuilder.js.map +1 -0
- package/dist/graph/graphBuilder.test.d.ts +1 -0
- package/dist/graph/graphBuilder.test.js +87 -0
- package/dist/graph/graphBuilder.test.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/documentParser.d.ts +22 -0
- package/dist/parsers/documentParser.js +76 -0
- package/dist/parsers/documentParser.js.map +1 -0
- package/dist/parsers/documentParser.test.d.ts +1 -0
- package/dist/parsers/documentParser.test.js +116 -0
- package/dist/parsers/documentParser.test.js.map +1 -0
- package/dist/parsers/extractors/baseExtractor.d.ts +19 -0
- package/dist/parsers/extractors/baseExtractor.js +33 -0
- package/dist/parsers/extractors/baseExtractor.js.map +1 -0
- package/dist/parsers/extractors/baseExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/baseExtractor.test.js +43 -0
- package/dist/parsers/extractors/baseExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/codePatternExtractor.d.ts +13 -0
- package/dist/parsers/extractors/codePatternExtractor.js +108 -0
- package/dist/parsers/extractors/codePatternExtractor.js.map +1 -0
- package/dist/parsers/extractors/codePatternExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/codePatternExtractor.test.js +49 -0
- package/dist/parsers/extractors/codePatternExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/dependencyExtractor.d.ts +12 -0
- package/dist/parsers/extractors/dependencyExtractor.js +92 -0
- package/dist/parsers/extractors/dependencyExtractor.js.map +1 -0
- package/dist/parsers/extractors/dependencyExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/dependencyExtractor.test.js +48 -0
- package/dist/parsers/extractors/dependencyExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/directoryStructureExtractor.d.ts +34 -0
- package/dist/parsers/extractors/directoryStructureExtractor.js +168 -0
- package/dist/parsers/extractors/directoryStructureExtractor.js.map +1 -0
- package/dist/parsers/extractors/directoryStructureExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/directoryStructureExtractor.test.js +121 -0
- package/dist/parsers/extractors/directoryStructureExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/externalUrlExtractor.d.ts +14 -0
- package/dist/parsers/extractors/externalUrlExtractor.js +53 -0
- package/dist/parsers/extractors/externalUrlExtractor.js.map +1 -0
- package/dist/parsers/extractors/externalUrlExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/externalUrlExtractor.test.js +85 -0
- package/dist/parsers/extractors/externalUrlExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/filePathExtractor.d.ts +18 -0
- package/dist/parsers/extractors/filePathExtractor.js +72 -0
- package/dist/parsers/extractors/filePathExtractor.js.map +1 -0
- package/dist/parsers/extractors/filePathExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/filePathExtractor.test.js +73 -0
- package/dist/parsers/extractors/filePathExtractor.test.js.map +1 -0
- package/dist/parsers/extractors/versionExtractor.d.ts +11 -0
- package/dist/parsers/extractors/versionExtractor.js +74 -0
- package/dist/parsers/extractors/versionExtractor.js.map +1 -0
- package/dist/parsers/extractors/versionExtractor.test.d.ts +1 -0
- package/dist/parsers/extractors/versionExtractor.test.js +55 -0
- package/dist/parsers/extractors/versionExtractor.test.js.map +1 -0
- package/dist/plugins/plugin.d.ts +32 -0
- package/dist/plugins/plugin.js +40 -0
- package/dist/plugins/plugin.js.map +1 -0
- package/dist/plugins/plugin.test.d.ts +1 -0
- package/dist/plugins/plugin.test.js +23 -0
- package/dist/plugins/plugin.test.js.map +1 -0
- package/dist/reporters/consoleReporter.d.ts +15 -0
- package/dist/reporters/consoleReporter.js +73 -0
- package/dist/reporters/consoleReporter.js.map +1 -0
- package/dist/reporters/consoleReporter.test.d.ts +1 -0
- package/dist/reporters/consoleReporter.test.js +155 -0
- package/dist/reporters/consoleReporter.test.js.map +1 -0
- package/dist/reporters/enhancedReporter.d.ts +12 -0
- package/dist/reporters/enhancedReporter.js +81 -0
- package/dist/reporters/enhancedReporter.js.map +1 -0
- package/dist/reporters/enhancedReporter.test.d.ts +1 -0
- package/dist/reporters/enhancedReporter.test.js +152 -0
- package/dist/reporters/enhancedReporter.test.js.map +1 -0
- package/dist/reporters/jsonReporter.d.ts +11 -0
- package/dist/reporters/jsonReporter.js +20 -0
- package/dist/reporters/jsonReporter.js.map +1 -0
- package/dist/reporters/jsonReporter.test.d.ts +1 -0
- package/dist/reporters/jsonReporter.test.js +31 -0
- package/dist/reporters/jsonReporter.test.js.map +1 -0
- package/dist/reporters/markdownReporter.d.ts +11 -0
- package/dist/reporters/markdownReporter.js +55 -0
- package/dist/reporters/markdownReporter.js.map +1 -0
- package/dist/reporters/markdownReporter.test.d.ts +1 -0
- package/dist/reporters/markdownReporter.test.js +136 -0
- package/dist/reporters/markdownReporter.test.js.map +1 -0
- package/dist/runner.d.ts +9 -0
- package/dist/runner.js +265 -0
- package/dist/runner.js.map +1 -0
- package/dist/runner.test.d.ts +1 -0
- package/dist/runner.test.js +353 -0
- package/dist/runner.test.js.map +1 -0
- package/dist/scoring/freshnessScorer.d.ts +40 -0
- package/dist/scoring/freshnessScorer.js +170 -0
- package/dist/scoring/freshnessScorer.js.map +1 -0
- package/dist/scoring/freshnessScorer.test.d.ts +1 -0
- package/dist/scoring/freshnessScorer.test.js +397 -0
- package/dist/scoring/freshnessScorer.test.js.map +1 -0
- package/dist/semantic/vectorSearch.d.ts +84 -0
- package/dist/semantic/vectorSearch.js +484 -0
- package/dist/semantic/vectorSearch.js.map +1 -0
- package/dist/semantic/vectorSearch.test.d.ts +1 -0
- package/dist/semantic/vectorSearch.test.js +660 -0
- package/dist/semantic/vectorSearch.test.js.map +1 -0
- package/dist/setupTests.d.ts +4 -0
- package/dist/setupTests.js +11 -0
- package/dist/setupTests.js.map +1 -0
- package/dist/test-utils/console.d.ts +2 -0
- package/dist/test-utils/console.js +3 -0
- package/dist/test-utils/console.js.map +1 -0
- package/dist/test-utils/factories.d.ts +3 -0
- package/dist/test-utils/factories.js +25 -0
- package/dist/test-utils/factories.js.map +1 -0
- package/dist/test-utils/tempFiles.d.ts +1 -0
- package/dist/test-utils/tempFiles.js +12 -0
- package/dist/test-utils/tempFiles.js.map +1 -0
- package/dist/types.d.ts +304 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/boundedMap.d.ts +8 -0
- package/dist/utils/boundedMap.js +22 -0
- package/dist/utils/boundedMap.js.map +1 -0
- package/dist/utils/boundedMap.test.d.ts +1 -0
- package/dist/utils/boundedMap.test.js +57 -0
- package/dist/utils/boundedMap.test.js.map +1 -0
- package/dist/utils/illustrativePatterns.d.ts +28 -0
- package/dist/utils/illustrativePatterns.js +80 -0
- package/dist/utils/illustrativePatterns.js.map +1 -0
- package/dist/utils/illustrativePatterns.test.d.ts +1 -0
- package/dist/utils/illustrativePatterns.test.js +48 -0
- package/dist/utils/illustrativePatterns.test.js.map +1 -0
- package/dist/utils/incremental.d.ts +36 -0
- package/dist/utils/incremental.js +87 -0
- package/dist/utils/incremental.js.map +1 -0
- package/dist/utils/incremental.test.d.ts +1 -0
- package/dist/utils/incremental.test.js +84 -0
- package/dist/utils/incremental.test.js.map +1 -0
- package/dist/utils/parallel.d.ts +14 -0
- package/dist/utils/parallel.js +43 -0
- package/dist/utils/parallel.js.map +1 -0
- package/dist/utils/parallel.test.d.ts +1 -0
- package/dist/utils/parallel.test.js +48 -0
- package/dist/utils/parallel.test.js.map +1 -0
- package/dist/utils/pathSecurity.d.ts +12 -0
- package/dist/utils/pathSecurity.js +22 -0
- package/dist/utils/pathSecurity.js.map +1 -0
- package/dist/utils/pathSecurity.test.d.ts +1 -0
- package/dist/utils/pathSecurity.test.js +34 -0
- package/dist/utils/pathSecurity.test.js.map +1 -0
- package/dist/utils/similarity.d.ts +12 -0
- package/dist/utils/similarity.js +64 -0
- package/dist/utils/similarity.js.map +1 -0
- package/dist/utils/similarity.test.d.ts +1 -0
- package/dist/utils/similarity.test.js +49 -0
- package/dist/utils/similarity.test.js.map +1 -0
- package/dist/utils/validation.d.ts +13 -0
- package/dist/utils/validation.js +24 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils/validation.test.d.ts +1 -0
- package/dist/utils/validation.test.js +28 -0
- package/dist/utils/validation.test.js.map +1 -0
- package/dist/validators/codePatternValidator.d.ts +28 -0
- package/dist/validators/codePatternValidator.js +200 -0
- package/dist/validators/codePatternValidator.js.map +1 -0
- package/dist/validators/codePatternValidator.test.d.ts +1 -0
- package/dist/validators/codePatternValidator.test.js +86 -0
- package/dist/validators/codePatternValidator.test.js.map +1 -0
- package/dist/validators/dependencyValidator.d.ts +12 -0
- package/dist/validators/dependencyValidator.js +102 -0
- package/dist/validators/dependencyValidator.js.map +1 -0
- package/dist/validators/dependencyValidator.test.d.ts +1 -0
- package/dist/validators/dependencyValidator.test.js +179 -0
- package/dist/validators/dependencyValidator.test.js.map +1 -0
- package/dist/validators/directoryValidator.d.ts +30 -0
- package/dist/validators/directoryValidator.js +192 -0
- package/dist/validators/directoryValidator.js.map +1 -0
- package/dist/validators/directoryValidator.test.d.ts +1 -0
- package/dist/validators/directoryValidator.test.js +193 -0
- package/dist/validators/directoryValidator.test.js.map +1 -0
- package/dist/validators/fileValidator.d.ts +16 -0
- package/dist/validators/fileValidator.js +114 -0
- package/dist/validators/fileValidator.js.map +1 -0
- package/dist/validators/fileValidator.test.d.ts +1 -0
- package/dist/validators/fileValidator.test.js +108 -0
- package/dist/validators/fileValidator.test.js.map +1 -0
- package/dist/validators/urlValidator.d.ts +25 -0
- package/dist/validators/urlValidator.js +320 -0
- package/dist/validators/urlValidator.js.map +1 -0
- package/dist/validators/urlValidator.test.d.ts +1 -0
- package/dist/validators/urlValidator.test.js +252 -0
- package/dist/validators/urlValidator.test.js.map +1 -0
- package/dist/validators/validationEngine.d.ts +23 -0
- package/dist/validators/validationEngine.js +117 -0
- package/dist/validators/validationEngine.js.map +1 -0
- package/dist/validators/validationEngine.test.d.ts +1 -0
- package/dist/validators/validationEngine.test.js +82 -0
- package/dist/validators/validationEngine.test.js.map +1 -0
- package/dist/validators/versionValidator.d.ts +18 -0
- package/dist/validators/versionValidator.js +211 -0
- package/dist/validators/versionValidator.js.map +1 -0
- package/dist/validators/versionValidator.test.d.ts +1 -0
- package/dist/validators/versionValidator.test.js +308 -0
- package/dist/validators/versionValidator.test.js.map +1 -0
- package/package.json +98 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Validates that mentioned dependencies exist in manifest files
|
|
5
|
+
*/
|
|
6
|
+
export class DependencyValidator {
|
|
7
|
+
dependencies;
|
|
8
|
+
loadedFromKey;
|
|
9
|
+
constructor() {
|
|
10
|
+
this.dependencies = null;
|
|
11
|
+
this.loadedFromKey = null;
|
|
12
|
+
}
|
|
13
|
+
async loadDependencies(config) {
|
|
14
|
+
const rootDir = config.rootDir || process.cwd();
|
|
15
|
+
const manifestFiles = config.manifestFiles || ['package.json'];
|
|
16
|
+
const configKey = `${rootDir}::${manifestFiles.join('|')}`;
|
|
17
|
+
if (this.dependencies && this.loadedFromKey === configKey)
|
|
18
|
+
return;
|
|
19
|
+
this.dependencies = new Set();
|
|
20
|
+
this.loadedFromKey = configKey;
|
|
21
|
+
for (const manifestPath of manifestFiles) {
|
|
22
|
+
const fullPath = path.join(rootDir, manifestPath);
|
|
23
|
+
const fileName = path.basename(manifestPath);
|
|
24
|
+
try {
|
|
25
|
+
const content = await fs.promises.readFile(fullPath, 'utf-8');
|
|
26
|
+
const deps = await this.parseManifest(fileName, content);
|
|
27
|
+
for (const dep of deps) {
|
|
28
|
+
this.dependencies.add(dep.toLowerCase());
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Manifest not found
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async parseManifest(fileName, content) {
|
|
37
|
+
const parser = manifestDependencyParsers[fileName];
|
|
38
|
+
return parser ? parser(content) : [];
|
|
39
|
+
}
|
|
40
|
+
async validateBatch(references, _document, config) {
|
|
41
|
+
await this.loadDependencies(config);
|
|
42
|
+
const results = [];
|
|
43
|
+
for (const ref of references) {
|
|
44
|
+
const pkg = ref.value.toLowerCase();
|
|
45
|
+
// Check if package exists in dependencies
|
|
46
|
+
const found = this.dependencies.has(pkg);
|
|
47
|
+
if (found) {
|
|
48
|
+
results.push({
|
|
49
|
+
reference: ref,
|
|
50
|
+
valid: true,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
results.push({
|
|
55
|
+
reference: ref,
|
|
56
|
+
valid: false,
|
|
57
|
+
severity: config.rules?.dependency?.severity || 'info',
|
|
58
|
+
message: `Package not found in dependencies: ${ref.value}`,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return results;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const manifestDependencyParsers = {
|
|
66
|
+
'package.json': (content) => {
|
|
67
|
+
const json = JSON.parse(content);
|
|
68
|
+
return ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'].flatMap((key) => Object.keys(json[key] || {}));
|
|
69
|
+
},
|
|
70
|
+
'requirements.txt': (content) => content
|
|
71
|
+
.split('\n')
|
|
72
|
+
.map((line) => line.trim())
|
|
73
|
+
.filter((line) => line && !line.startsWith('#'))
|
|
74
|
+
.map((line) => line.match(/^([a-zA-Z0-9\-_]+)/)?.[1])
|
|
75
|
+
.filter((dep) => Boolean(dep)),
|
|
76
|
+
'pyproject.toml': (content) => {
|
|
77
|
+
const depsMatch = content.match(/\[project\.dependencies\]([\s\S]*?)(?:\[|$)/);
|
|
78
|
+
if (!depsMatch)
|
|
79
|
+
return [];
|
|
80
|
+
return Array.from(depsMatch[1].matchAll(/"([^"<>=!]+)/g), (match) => match[1].split(/[<>=!]/)[0]);
|
|
81
|
+
},
|
|
82
|
+
'go.mod': (content) => {
|
|
83
|
+
const requireMatch = content.match(/require\s+\(([\s\S]*?)\)/);
|
|
84
|
+
if (!requireMatch)
|
|
85
|
+
return [];
|
|
86
|
+
return requireMatch[1]
|
|
87
|
+
.split('\n')
|
|
88
|
+
.map((line) => line.trim().match(/^([^\s]+)/)?.[1])
|
|
89
|
+
.filter((dep) => Boolean(dep));
|
|
90
|
+
},
|
|
91
|
+
'Cargo.toml': (content) => {
|
|
92
|
+
const depsMatch = content.match(/\[dependencies\]([\s\S]*?)(?:\[|$)/);
|
|
93
|
+
if (!depsMatch)
|
|
94
|
+
return [];
|
|
95
|
+
return depsMatch[1]
|
|
96
|
+
.split('\n')
|
|
97
|
+
.map((line) => line.match(/^([a-zA-Z0-9\-_]+)\s*=/)?.[1])
|
|
98
|
+
.filter((dep) => Boolean(dep));
|
|
99
|
+
},
|
|
100
|
+
'pom.xml': (content) => Array.from(content.matchAll(/<artifactId>([^<]+)<\/artifactId>/g), (match) => match[1]),
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=dependencyValidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencyValidator.js","sourceRoot":"","sources":["../../src/validators/dependencyValidator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { DependencyValidator } from './dependencyValidator.js';
|
|
4
|
+
import { makeDoc, makeRef as makeBaseRef } from '../test-utils/factories.js';
|
|
5
|
+
function makeRef(value) {
|
|
6
|
+
return makeBaseRef('dependency', value, { ecosystem: 'npm' });
|
|
7
|
+
}
|
|
8
|
+
const doc = makeDoc();
|
|
9
|
+
const tmpBase = path.join(process.cwd(), '.doc-freshness-cache', 'dep-test');
|
|
10
|
+
async function writeManifestAndValidate(dirName, fileName, content, pkgNames) {
|
|
11
|
+
const dir = path.join(tmpBase, dirName);
|
|
12
|
+
const filePath = path.join(dir, fileName);
|
|
13
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
14
|
+
await fs.promises.writeFile(filePath, content);
|
|
15
|
+
const validator = new DependencyValidator();
|
|
16
|
+
const config = {
|
|
17
|
+
rootDir: process.cwd(),
|
|
18
|
+
manifestFiles: [path.relative(process.cwd(), filePath)],
|
|
19
|
+
};
|
|
20
|
+
const results = await validator.validateBatch(pkgNames.map(makeRef), doc, config);
|
|
21
|
+
return results.map((r) => r.valid);
|
|
22
|
+
}
|
|
23
|
+
describe('DependencyValidator', () => {
|
|
24
|
+
afterAll(async () => {
|
|
25
|
+
await fs.promises.rm(tmpBase, { recursive: true, force: true }).catch(() => { });
|
|
26
|
+
});
|
|
27
|
+
it('validates dependencies found in package.json', async () => {
|
|
28
|
+
const validator = new DependencyValidator();
|
|
29
|
+
const config = { rootDir: process.cwd(), manifestFiles: ['package.json'] };
|
|
30
|
+
const results = await validator.validateBatch([makeRef('vitest')], doc, config);
|
|
31
|
+
expect(results[0].valid).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it('marks unknown dependencies as invalid with default severity', async () => {
|
|
34
|
+
const validator = new DependencyValidator();
|
|
35
|
+
const config = { rootDir: process.cwd(), manifestFiles: ['package.json'] };
|
|
36
|
+
const results = await validator.validateBatch([makeRef('nonexistent-pkg-xyz')], doc, config);
|
|
37
|
+
expect(results[0].valid).toBe(false);
|
|
38
|
+
expect(results[0].severity).toBe('info');
|
|
39
|
+
expect(results[0].message).toContain('nonexistent-pkg-xyz');
|
|
40
|
+
});
|
|
41
|
+
it('respects custom severity from config', async () => {
|
|
42
|
+
const validator = new DependencyValidator();
|
|
43
|
+
const config = {
|
|
44
|
+
rootDir: process.cwd(),
|
|
45
|
+
manifestFiles: ['package.json'],
|
|
46
|
+
rules: { dependency: { severity: 'error' } },
|
|
47
|
+
};
|
|
48
|
+
const results = await validator.validateBatch([makeRef('nonexistent-pkg')], doc, config);
|
|
49
|
+
expect(results[0].severity).toBe('error');
|
|
50
|
+
});
|
|
51
|
+
it('handles missing manifest files gracefully', async () => {
|
|
52
|
+
const validator = new DependencyValidator();
|
|
53
|
+
const config = { rootDir: process.cwd(), manifestFiles: ['nonexistent.json'] };
|
|
54
|
+
const results = await validator.validateBatch([makeRef('anything')], doc, config);
|
|
55
|
+
expect(results[0].valid).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
it('uses default manifestFiles when not specified', async () => {
|
|
58
|
+
const validator = new DependencyValidator();
|
|
59
|
+
const config = { rootDir: process.cwd() };
|
|
60
|
+
const results = await validator.validateBatch([makeRef('vitest')], doc, config);
|
|
61
|
+
expect(results[0].valid).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
describe('manifest format parsing', () => {
|
|
64
|
+
async function writeAndValidate(fileName, content, pkgNames) {
|
|
65
|
+
const dirName = fileName.replace(/\./g, '-');
|
|
66
|
+
return writeManifestAndValidate(dirName, fileName, content, pkgNames);
|
|
67
|
+
}
|
|
68
|
+
it('parses requirements.txt', async () => {
|
|
69
|
+
const results = await writeAndValidate('requirements.txt', 'flask>=2.0\nrequests\n# comment\ndjango==4.0', [
|
|
70
|
+
'flask',
|
|
71
|
+
'requests',
|
|
72
|
+
'django',
|
|
73
|
+
]);
|
|
74
|
+
expect(results).toEqual([true, true, true]);
|
|
75
|
+
});
|
|
76
|
+
it('parses pyproject.toml', async () => {
|
|
77
|
+
const results = await writeAndValidate('pyproject.toml', '[project.dependencies]\n"fastapi>=0.100"\n"uvicorn"', [
|
|
78
|
+
'fastapi',
|
|
79
|
+
'uvicorn',
|
|
80
|
+
]);
|
|
81
|
+
expect(results).toEqual([true, true]);
|
|
82
|
+
});
|
|
83
|
+
it('parses go.mod', async () => {
|
|
84
|
+
const results = await writeAndValidate('go.mod', 'module example.com/app\n\ngo 1.21\n\nrequire (\n\tgithub.com/gin-gonic/gin v1.9.1\n\tgolang.org/x/text v0.14.0\n)', ['github.com/gin-gonic/gin', 'golang.org/x/text']);
|
|
85
|
+
expect(results).toEqual([true, true]);
|
|
86
|
+
});
|
|
87
|
+
it('parses Cargo.toml', async () => {
|
|
88
|
+
const results = await writeAndValidate('Cargo.toml', '[dependencies]\nserde = "1.0"\ntokio = "1.28"', ['serde', 'tokio']);
|
|
89
|
+
expect(results).toEqual([true, true]);
|
|
90
|
+
});
|
|
91
|
+
it('parses pom.xml', async () => {
|
|
92
|
+
const results = await writeAndValidate('pom.xml', '<project><dependencies><dependency><artifactId>spring-boot</artifactId></dependency></dependencies></project>', ['spring-boot']);
|
|
93
|
+
expect(results).toEqual([true]);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
it('is case-insensitive for dependency names', async () => {
|
|
97
|
+
const validator = new DependencyValidator();
|
|
98
|
+
const config = { rootDir: process.cwd(), manifestFiles: ['package.json'] };
|
|
99
|
+
const results = await validator.validateBatch([makeRef('Vitest'), makeRef('VITEST')], doc, config);
|
|
100
|
+
expect(results.every((r) => r.valid)).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
describe('manifest edge cases', () => {
|
|
103
|
+
async function writeAndValidate(fileName, content, pkgNames) {
|
|
104
|
+
const dirName = `edge-${fileName.replace(/\./g, '-')}`;
|
|
105
|
+
return writeManifestAndValidate(dirName, fileName, content, pkgNames);
|
|
106
|
+
}
|
|
107
|
+
it('handles go.mod without require block', async () => {
|
|
108
|
+
const results = await writeAndValidate('go.mod', 'module example.com/app\n\ngo 1.22\n', ['example.com/app']);
|
|
109
|
+
expect(results).toEqual([false]);
|
|
110
|
+
});
|
|
111
|
+
it('handles pyproject.toml without project.dependencies section', async () => {
|
|
112
|
+
const results = await writeAndValidate('pyproject.toml', '[project]\nname = "myapp"\nversion = "1.0"\n', ['myapp']);
|
|
113
|
+
expect(results).toEqual([false]);
|
|
114
|
+
});
|
|
115
|
+
it('handles Cargo.toml without dependencies section', async () => {
|
|
116
|
+
const results = await writeAndValidate('Cargo.toml', '[package]\nname = "myapp"\nversion = "0.1.0"\n', ['myapp']);
|
|
117
|
+
expect(results).toEqual([false]);
|
|
118
|
+
});
|
|
119
|
+
it('handles unknown manifest format gracefully', async () => {
|
|
120
|
+
const results = await writeAndValidate('build.gradle', 'implementation "org.something:artifact:1.0"\n', ['org.something']);
|
|
121
|
+
expect(results).toEqual([false]);
|
|
122
|
+
});
|
|
123
|
+
it('handles requirements.txt with blank lines and comments only', async () => {
|
|
124
|
+
const results = await writeAndValidate('requirements.txt', '# this is a comment\n\n# another comment\n', ['flask']);
|
|
125
|
+
expect(results).toEqual([false]);
|
|
126
|
+
});
|
|
127
|
+
it('handles pom.xml with no artifactId', async () => {
|
|
128
|
+
const results = await writeAndValidate('pom.xml', '<project><groupId>com.example</groupId></project>', ['anything']);
|
|
129
|
+
expect(results).toEqual([false]);
|
|
130
|
+
});
|
|
131
|
+
it('parses package.json with peerDependencies and optionalDependencies', async () => {
|
|
132
|
+
const results = await writeAndValidate('package.json', JSON.stringify({
|
|
133
|
+
dependencies: { 'dep-a': '1.0' },
|
|
134
|
+
devDependencies: { 'dev-b': '2.0' },
|
|
135
|
+
peerDependencies: { 'peer-c': '>=3.0' },
|
|
136
|
+
optionalDependencies: { 'opt-d': '4.0' },
|
|
137
|
+
}), ['dep-a', 'dev-b', 'peer-c', 'opt-d']);
|
|
138
|
+
expect(results).toEqual([true, true, true, true]);
|
|
139
|
+
});
|
|
140
|
+
it('parses package.json with only dependencies (no devDependencies)', async () => {
|
|
141
|
+
const results = await writeAndValidate('package.json', JSON.stringify({ dependencies: { 'only-dep': '1.0' } }), ['only-dep']);
|
|
142
|
+
expect(results).toEqual([true]);
|
|
143
|
+
});
|
|
144
|
+
it('parses requirements.txt with pip editable installs (non-matching lines)', async () => {
|
|
145
|
+
const results = await writeAndValidate('requirements.txt', '-e git+https://github.com/org/repo.git#egg=mypackage\nflask>=2.0\n-r other-requirements.txt', ['flask']);
|
|
146
|
+
expect(results).toEqual([true]);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
it('uses process.cwd when rootDir is not specified', async () => {
|
|
150
|
+
const validator = new DependencyValidator();
|
|
151
|
+
const config = { manifestFiles: ['package.json'] };
|
|
152
|
+
const results = await validator.validateBatch([makeRef('vitest')], doc, config);
|
|
153
|
+
expect(results[0].valid).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
it('reloads dependencies when manifest config changes on the same instance', async () => {
|
|
156
|
+
const validator = new DependencyValidator();
|
|
157
|
+
const dirA = path.join(tmpBase, 'reload-a');
|
|
158
|
+
const dirB = path.join(tmpBase, 'reload-b');
|
|
159
|
+
await fs.promises.mkdir(dirA, { recursive: true });
|
|
160
|
+
await fs.promises.mkdir(dirB, { recursive: true });
|
|
161
|
+
const manifestA = path.join(dirA, 'package.json');
|
|
162
|
+
const manifestB = path.join(dirB, 'package.json');
|
|
163
|
+
await fs.promises.writeFile(manifestA, JSON.stringify({ dependencies: { 'pkg-a': '1.0.0' } }));
|
|
164
|
+
await fs.promises.writeFile(manifestB, JSON.stringify({ dependencies: { 'pkg-b': '1.0.0' } }));
|
|
165
|
+
const configA = {
|
|
166
|
+
rootDir: process.cwd(),
|
|
167
|
+
manifestFiles: [path.relative(process.cwd(), manifestA)],
|
|
168
|
+
};
|
|
169
|
+
const configB = {
|
|
170
|
+
rootDir: process.cwd(),
|
|
171
|
+
manifestFiles: [path.relative(process.cwd(), manifestB)],
|
|
172
|
+
};
|
|
173
|
+
const first = await validator.validateBatch([makeRef('pkg-a')], doc, configA);
|
|
174
|
+
expect(first[0].valid).toBe(true);
|
|
175
|
+
const second = await validator.validateBatch([makeRef('pkg-b')], doc, configB);
|
|
176
|
+
expect(second[0].valid).toBe(true);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
//# sourceMappingURL=dependencyValidator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencyValidator.test.js","sourceRoot":"","sources":["../../src/validators/dependencyValidator.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DocFreshnessConfig, Document, Reference, ValidationResult } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates that directory structure references exist
|
|
4
|
+
*
|
|
5
|
+
* Simple approach: The extractor builds full paths from the tree structure,
|
|
6
|
+
* so we just check if each path exists from the project root.
|
|
7
|
+
*/
|
|
8
|
+
export declare class DirectoryValidator {
|
|
9
|
+
private pathCache;
|
|
10
|
+
private customPatterns;
|
|
11
|
+
constructor();
|
|
12
|
+
/**
|
|
13
|
+
* Initialize custom illustrative patterns from config
|
|
14
|
+
*/
|
|
15
|
+
private initCustomPatterns;
|
|
16
|
+
validateBatch(references: Reference[], document: Document, config: DocFreshnessConfig): Promise<ValidationResult[]>;
|
|
17
|
+
private validateReference;
|
|
18
|
+
private pathExists;
|
|
19
|
+
/**
|
|
20
|
+
* Try to find a similar path for suggestions
|
|
21
|
+
* Handles common issues like singular/plural mismatches
|
|
22
|
+
*/
|
|
23
|
+
private findSimilarPath;
|
|
24
|
+
/**
|
|
25
|
+
* Calculate similarity between two strings (0-1)
|
|
26
|
+
* Uses a combination of techniques for better matching
|
|
27
|
+
*/
|
|
28
|
+
private calculateSimilarity;
|
|
29
|
+
private toSingular;
|
|
30
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { isIllustrativePath, compilePatterns } from '../utils/illustrativePatterns.js';
|
|
4
|
+
import { similarityRatio } from '../utils/similarity.js';
|
|
5
|
+
import { isWithinRoot, resolveDocumentDir, resolveProjectRoot } from '../utils/pathSecurity.js';
|
|
6
|
+
import { createIllustrativeSkippedResult, getRuleSeverity, severityForIllustrative } from '../utils/validation.js';
|
|
7
|
+
/**
|
|
8
|
+
* Validates that directory structure references exist
|
|
9
|
+
*
|
|
10
|
+
* Simple approach: The extractor builds full paths from the tree structure,
|
|
11
|
+
* so we just check if each path exists from the project root.
|
|
12
|
+
*/
|
|
13
|
+
export class DirectoryValidator {
|
|
14
|
+
pathCache;
|
|
15
|
+
customPatterns;
|
|
16
|
+
constructor() {
|
|
17
|
+
this.pathCache = new Map();
|
|
18
|
+
this.customPatterns = [];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Initialize custom illustrative patterns from config
|
|
22
|
+
*/
|
|
23
|
+
initCustomPatterns(config) {
|
|
24
|
+
const configPatterns = config.rules?.['directory-structure']?.illustrativePatterns;
|
|
25
|
+
this.customPatterns = configPatterns && configPatterns.length > 0 ? compilePatterns(configPatterns) : [];
|
|
26
|
+
}
|
|
27
|
+
async validateBatch(references, document, config) {
|
|
28
|
+
this.initCustomPatterns(config);
|
|
29
|
+
const results = [];
|
|
30
|
+
const skipIllustrative = config.rules?.['directory-structure']?.skipIllustrative !== false;
|
|
31
|
+
for (const ref of references) {
|
|
32
|
+
// Check if this is an illustrative path (marked by extractor or detected here)
|
|
33
|
+
const illustrative = ref.isIllustrative || isIllustrativePath(ref.value, this.customPatterns);
|
|
34
|
+
if (illustrative && skipIllustrative) {
|
|
35
|
+
results.push(createIllustrativeSkippedResult(ref, 'Skipped: illustrative/example path'));
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const result = await this.validateReference(ref, document, config, illustrative);
|
|
39
|
+
results.push(result);
|
|
40
|
+
}
|
|
41
|
+
return results;
|
|
42
|
+
}
|
|
43
|
+
async validateReference(ref, document, config, isIllustrative = false) {
|
|
44
|
+
const itemPath = ref.value;
|
|
45
|
+
const rootDir = resolveProjectRoot(config.rootDir);
|
|
46
|
+
const baseSeverity = getRuleSeverity(config, 'directory-structure', 'warning');
|
|
47
|
+
const cacheKey = `${rootDir}::${document.path}::${itemPath}`;
|
|
48
|
+
// Check cache first
|
|
49
|
+
if (this.pathCache.has(cacheKey)) {
|
|
50
|
+
const cached = this.pathCache.get(cacheKey);
|
|
51
|
+
return {
|
|
52
|
+
reference: ref,
|
|
53
|
+
valid: cached.found,
|
|
54
|
+
foundAt: cached.foundAt,
|
|
55
|
+
severity: cached.found ? undefined : baseSeverity,
|
|
56
|
+
message: cached.found ? undefined : `Directory/file not found: ${itemPath}`,
|
|
57
|
+
suggestion: cached.suggestion,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Strategy 1: Check if the path exists from project root
|
|
61
|
+
// This handles full paths like "frontend/src/apps/domains"
|
|
62
|
+
const fullPath = path.resolve(rootDir, itemPath);
|
|
63
|
+
if (isWithinRoot(fullPath, rootDir) && (await this.pathExists(fullPath))) {
|
|
64
|
+
this.pathCache.set(cacheKey, { found: true, foundAt: itemPath });
|
|
65
|
+
return {
|
|
66
|
+
reference: ref,
|
|
67
|
+
valid: true,
|
|
68
|
+
foundAt: itemPath,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Strategy 2: The path might be relative to the document's location
|
|
72
|
+
// e.g., a doc in "docs/" might reference "../src/..."
|
|
73
|
+
const docDir = resolveDocumentDir(rootDir, document.path);
|
|
74
|
+
const relativeToDoc = path.resolve(docDir, itemPath);
|
|
75
|
+
if (isWithinRoot(relativeToDoc, rootDir) && (await this.pathExists(relativeToDoc))) {
|
|
76
|
+
const foundAt = path.relative(rootDir, relativeToDoc);
|
|
77
|
+
this.pathCache.set(cacheKey, { found: true, foundAt });
|
|
78
|
+
return {
|
|
79
|
+
reference: ref,
|
|
80
|
+
valid: true,
|
|
81
|
+
foundAt,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// If both candidate paths resolve outside root, fail early with explicit message.
|
|
85
|
+
if (!isWithinRoot(fullPath, rootDir) && !isWithinRoot(relativeToDoc, rootDir)) {
|
|
86
|
+
return {
|
|
87
|
+
reference: ref,
|
|
88
|
+
valid: false,
|
|
89
|
+
severity: severityForIllustrative(isIllustrative, baseSeverity),
|
|
90
|
+
message: isIllustrative ? `Path escapes project root (illustrative): ${itemPath}` : `Path escapes project root: ${itemPath}`,
|
|
91
|
+
suggestion: null,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// Strategy 3: Try to find a similar path (for suggestions)
|
|
95
|
+
const suggestion = await this.findSimilarPath(itemPath, config);
|
|
96
|
+
// Not found - reduce severity for illustrative paths that weren't skipped
|
|
97
|
+
this.pathCache.set(cacheKey, { found: false, suggestion });
|
|
98
|
+
return {
|
|
99
|
+
reference: ref,
|
|
100
|
+
valid: false,
|
|
101
|
+
severity: severityForIllustrative(isIllustrative, baseSeverity),
|
|
102
|
+
message: isIllustrative ? `Directory/file not found (illustrative): ${itemPath}` : `Directory/file not found: ${itemPath}`,
|
|
103
|
+
suggestion,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
async pathExists(fullPath) {
|
|
107
|
+
try {
|
|
108
|
+
await fs.promises.access(fullPath);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Try to find a similar path for suggestions
|
|
117
|
+
* Handles common issues like singular/plural mismatches
|
|
118
|
+
*/
|
|
119
|
+
async findSimilarPath(itemPath, config) {
|
|
120
|
+
const rootDir = resolveProjectRoot(config.rootDir);
|
|
121
|
+
const segments = itemPath.split('/');
|
|
122
|
+
const lastSegment = segments[segments.length - 1];
|
|
123
|
+
// Get the parent directory path
|
|
124
|
+
const parentPath = segments.slice(0, -1).join('/');
|
|
125
|
+
const parentFullPath = path.resolve(rootDir, parentPath);
|
|
126
|
+
if (!isWithinRoot(parentFullPath, rootDir)) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
// Check if parent exists
|
|
130
|
+
if (!(await this.pathExists(parentFullPath))) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
// List files in parent directory
|
|
134
|
+
try {
|
|
135
|
+
const entries = await fs.promises.readdir(parentFullPath);
|
|
136
|
+
const lastLower = lastSegment.toLowerCase();
|
|
137
|
+
let bestMatch = null;
|
|
138
|
+
let bestScore = 0;
|
|
139
|
+
for (const entry of entries) {
|
|
140
|
+
const entryLower = entry.toLowerCase();
|
|
141
|
+
// Exact case-insensitive match
|
|
142
|
+
if (entryLower === lastLower && entry !== lastSegment) {
|
|
143
|
+
return `${parentPath}/${entry}`;
|
|
144
|
+
}
|
|
145
|
+
// Calculate similarity score
|
|
146
|
+
const score = this.calculateSimilarity(lastLower, entryLower);
|
|
147
|
+
if (score > bestScore && score >= 0.7) {
|
|
148
|
+
bestScore = score;
|
|
149
|
+
bestMatch = entry;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (bestMatch) {
|
|
153
|
+
return `${parentPath}/${bestMatch}`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Parent directory read failed
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Calculate similarity between two strings (0-1)
|
|
163
|
+
* Uses a combination of techniques for better matching
|
|
164
|
+
*/
|
|
165
|
+
calculateSimilarity(a, b) {
|
|
166
|
+
const aBase = a.replace(/\.[^.]+$/, '');
|
|
167
|
+
const bBase = b.replace(/\.[^.]+$/, '');
|
|
168
|
+
if (aBase === bBase)
|
|
169
|
+
return 1;
|
|
170
|
+
// Check for singular/plural by removing common plural suffixes
|
|
171
|
+
const aSingular = this.toSingular(aBase);
|
|
172
|
+
const bSingular = this.toSingular(bBase);
|
|
173
|
+
if (aSingular === bSingular)
|
|
174
|
+
return 0.95;
|
|
175
|
+
if (aSingular === bBase || aBase === bSingular)
|
|
176
|
+
return 0.95;
|
|
177
|
+
return similarityRatio(aBase, bBase);
|
|
178
|
+
}
|
|
179
|
+
toSingular(word) {
|
|
180
|
+
if (word.endsWith('ies')) {
|
|
181
|
+
return word.slice(0, -3) + 'y';
|
|
182
|
+
}
|
|
183
|
+
if (word.endsWith('es') && !word.endsWith('ses') && !word.endsWith('xes')) {
|
|
184
|
+
return word.slice(0, -2);
|
|
185
|
+
}
|
|
186
|
+
if (word.endsWith('s') && !word.endsWith('ss')) {
|
|
187
|
+
return word.slice(0, -1);
|
|
188
|
+
}
|
|
189
|
+
return word;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=directoryValidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directoryValidator.js","sourceRoot":"","sources":["../../src/validators/directoryValidator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|