doc-freshness-checker 2.0.17 → 2.0.19
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/parsers/extractors/dependencyExtractor.js +14 -7
- package/dist/parsers/extractors/dependencyExtractor.js.map +1 -1
- package/dist/parsers/extractors/dependencyExtractor.test.js +23 -1
- package/dist/parsers/extractors/dependencyExtractor.test.js.map +1 -1
- package/dist/test-utils/manifestFixtures.d.ts +43 -0
- package/dist/test-utils/manifestFixtures.js +85 -0
- package/dist/test-utils/manifestFixtures.js.map +1 -0
- package/dist/utils/manifestPaths.js +1 -1
- package/dist/utils/manifestPaths.js.map +1 -1
- package/dist/utils/pythonDependencies.d.ts +3 -0
- package/dist/utils/pythonDependencies.js +114 -0
- package/dist/utils/pythonDependencies.js.map +1 -0
- package/dist/validators/dependencyValidator.d.ts +1 -1
- package/dist/validators/dependencyValidator.js +24 -24
- package/dist/validators/dependencyValidator.js.map +1 -1
- package/dist/validators/dependencyValidator.test.js +43 -15
- package/dist/validators/dependencyValidator.test.js.map +1 -1
- package/dist/validators/manifestFailureContract.test.d.ts +1 -0
- package/dist/validators/manifestFailureContract.test.js +137 -0
- package/dist/validators/manifestFailureContract.test.js.map +1 -0
- package/dist/validators/versionValidator.d.ts +1 -0
- package/dist/validators/versionValidator.js +41 -35
- package/dist/validators/versionValidator.js.map +1 -1
- package/dist/validators/versionValidator.test.js +204 -19
- package/dist/validators/versionValidator.test.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -340,6 +340,8 @@ through 1. Omitted factors retain their defaults; weights are not normalized, an
|
|
|
340
340
|
documented `0..100` range. Grade thresholds must be finite values from 0
|
|
341
341
|
through 100 in strictly descending order: `gradeA > gradeB > gradeC > gradeD`.
|
|
342
342
|
|
|
343
|
+
Configured supported manifests (`package.json`, `requirements.txt`, `pyproject.toml`, `go.mod`, `Cargo.toml`, and `pom.xml`) must be readable; read failures abort validation. Parser errors also abort when a parser reports them. The regex-based parsers may treat malformed content as empty or partial and report missing dependencies instead. Unknown manifest basenames are ignored for compatibility. These parsers do not execute package managers.
|
|
344
|
+
|
|
343
345
|
For details on CLI-to-config mapping and precedence, see [CLI and Configuration Precedence](docs/cli-and-config-precedence.md). For the internal execution pipeline, see [Runtime Architecture](docs/runtime-architecture.md).
|
|
344
346
|
|
|
345
347
|
## CI Integration
|
|
@@ -11,15 +11,19 @@ export class DependencyExtractor extends BaseExtractor {
|
|
|
11
11
|
}
|
|
12
12
|
extract(document) {
|
|
13
13
|
const references = [];
|
|
14
|
+
const plainPackageEcosystem = this.ecosystems.includes('npm') ? 'npm' : 'pypi';
|
|
15
|
+
const pypiOnly = this.ecosystems.length === 1 && this.ecosystems[0] === 'pypi';
|
|
14
16
|
const patterns = [
|
|
15
17
|
// npm scoped packages: @scope/package-name
|
|
16
18
|
{ regex: /`(@[a-z0-9-]+\/[a-z0-9-]+)`/g, ecosystem: 'npm' },
|
|
17
|
-
//
|
|
18
|
-
{ regex: /`([a-z][a-z0-9-]{2,})`/g, ecosystem: '
|
|
19
|
+
// PyPI names distinguished by underscores or dots
|
|
20
|
+
{ regex: /`([a-z][a-z0-9._-]{2,})`/g, ecosystem: 'pypi', requiresPythonSeparator: true },
|
|
21
|
+
// Plain/hyphen names are npm by default and PyPI when npm is disabled
|
|
22
|
+
{ regex: /`([a-z][a-z0-9-]{2,})`/g, ecosystem: plainPackageEcosystem },
|
|
19
23
|
// Go packages
|
|
20
24
|
{ regex: /`(github\.com\/[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_]+)`/g, ecosystem: 'go' },
|
|
21
25
|
];
|
|
22
|
-
for (const { regex, ecosystem } of patterns) {
|
|
26
|
+
for (const { regex, ecosystem, requiresPythonSeparator } of patterns) {
|
|
23
27
|
if (!this.ecosystems.includes(ecosystem)) {
|
|
24
28
|
continue;
|
|
25
29
|
}
|
|
@@ -27,8 +31,11 @@ export class DependencyExtractor extends BaseExtractor {
|
|
|
27
31
|
const re = new RegExp(regex.source, regex.flags);
|
|
28
32
|
while ((match = re.exec(document.content)) !== null) {
|
|
29
33
|
const pkg = match[1];
|
|
34
|
+
if (requiresPythonSeparator && !/[._]/.test(pkg)) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
30
37
|
// Filter out common false positives
|
|
31
|
-
if (this.isFalsePositive(pkg)) {
|
|
38
|
+
if (this.isFalsePositive(pkg, ecosystem === 'pypi' && pypiOnly)) {
|
|
32
39
|
continue;
|
|
33
40
|
}
|
|
34
41
|
references.push({
|
|
@@ -43,7 +50,7 @@ export class DependencyExtractor extends BaseExtractor {
|
|
|
43
50
|
}
|
|
44
51
|
return references;
|
|
45
52
|
}
|
|
46
|
-
isFalsePositive(pkg) {
|
|
53
|
+
isFalsePositive(pkg, allowDottedPyPI) {
|
|
47
54
|
// Skip very short names
|
|
48
55
|
if (pkg.length < 3) {
|
|
49
56
|
return true;
|
|
@@ -87,8 +94,8 @@ export class DependencyExtractor extends BaseExtractor {
|
|
|
87
94
|
if (commonWords.includes(pkg.toLowerCase())) {
|
|
88
95
|
return true;
|
|
89
96
|
}
|
|
90
|
-
//
|
|
91
|
-
if (/\.[a-z]{2,4}
|
|
97
|
+
// Dotted names are ambiguous with file names unless PyPI is the only enabled ecosystem.
|
|
98
|
+
if (!allowDottedPyPI && /\.[a-z]{2,4}$/i.test(pkg)) {
|
|
92
99
|
return true;
|
|
93
100
|
}
|
|
94
101
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependencyExtractor.js","sourceRoot":"","sources":["../../../src/parsers/extractors/dependencyExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"dependencyExtractor.js","sourceRoot":"","sources":["../../../src/parsers/extractors/dependencyExtractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,28 @@ describe('DependencyExtractor', () => {
|
|
|
23
23
|
expect(values).toContain('express');
|
|
24
24
|
expect(values).toContain('commander');
|
|
25
25
|
});
|
|
26
|
+
it('extracts underscore PyPI names without treating dotted names as packages by default', () => {
|
|
27
|
+
const refs = extractor.extract(makeDoc('Use `foo_bar`, `foo.bar`, and `foo-bar`'));
|
|
28
|
+
expect(refs.map(({ value, ecosystem }) => ({ value, ecosystem }))).toEqual([
|
|
29
|
+
{ value: 'foo_bar', ecosystem: 'pypi' },
|
|
30
|
+
{ value: 'foo-bar', ecosystem: 'npm' },
|
|
31
|
+
]);
|
|
32
|
+
});
|
|
33
|
+
it('assigns ordinary packages to PyPI when npm is disabled', () => {
|
|
34
|
+
const pypiOnly = new DependencyExtractor({ ecosystems: ['pypi'] });
|
|
35
|
+
const refs = pypiOnly.extract(makeDoc('Use `requests`, `pytest-cov`, `foo_bar`, and `foo.bar`'));
|
|
36
|
+
expect(refs.map(({ value, ecosystem }) => ({ value, ecosystem }))).toEqual([
|
|
37
|
+
{ value: 'foo_bar', ecosystem: 'pypi' },
|
|
38
|
+
{ value: 'foo.bar', ecosystem: 'pypi' },
|
|
39
|
+
{ value: 'requests', ecosystem: 'pypi' },
|
|
40
|
+
{ value: 'pytest-cov', ecosystem: 'pypi' },
|
|
41
|
+
]);
|
|
42
|
+
});
|
|
43
|
+
it.each(['go', 'crates'])('filters dotted names when PyPI is combined with %s', (otherEcosystem) => {
|
|
44
|
+
const mixed = new DependencyExtractor({ ecosystems: ['pypi', otherEcosystem] });
|
|
45
|
+
const refs = mixed.extract(makeDoc('Use `foo.bar` and edit `config.mjs`'));
|
|
46
|
+
expect(refs).toHaveLength(0);
|
|
47
|
+
});
|
|
26
48
|
it('extracts Go packages', () => {
|
|
27
49
|
const doc = makeDoc('Import `github.com/gin-gonic/gin`');
|
|
28
50
|
const refs = extractor.extract(doc);
|
|
@@ -34,7 +56,7 @@ describe('DependencyExtractor', () => {
|
|
|
34
56
|
expect(refs).toHaveLength(0);
|
|
35
57
|
});
|
|
36
58
|
it('filters out file extensions', () => {
|
|
37
|
-
const doc = makeDoc('
|
|
59
|
+
const doc = makeDoc('Open `archive.zip`, `config.mjs`, `index.php`, `app.vue`, `schema.sql`, and `package.lock`');
|
|
38
60
|
const refs = extractor.extract(doc);
|
|
39
61
|
expect(refs).toHaveLength(0);
|
|
40
62
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependencyExtractor.test.js","sourceRoot":"","sources":["../../../src/parsers/extractors/dependencyExtractor.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAG/D,SAAS,OAAO,CAAC,OAAe;IAC9B,OAAO;QACL,IAAI,EAAE,cAAc;QACpB,YAAY,EAAE,uBAAuB;QACrC,OAAO;QACP,MAAM,EAAE,UAAU;QAClB,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;QAC1B,UAAU,EAAE,EAAE;KACf,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,MAAM,SAAS,GAAG,IAAI,mBAAmB,EAAE,CAAC;IAE5C,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,GAAG,GAAG,OAAO,CAAC,iCAAiC,CAAC,CAAC;QACvD,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,GAAG,GAAG,OAAO,CAAC,uCAAuC,CAAC,CAAC;QAC7D,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,mCAAmC,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,0BAA0B,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,GAAG,GAAG,OAAO,CAAC,6CAA6C,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,GAAG,GAAG,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"dependencyExtractor.test.js","sourceRoot":"","sources":["../../../src/parsers/extractors/dependencyExtractor.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export declare const PEP621_PYPROJECT_FIXTURES: readonly [{
|
|
2
|
+
readonly name: "core and optional dependency arrays";
|
|
3
|
+
readonly content: "[project]\nname = \"example\"\ndependencies = [\n \"fastapi>=0.100,<1\",\n \"uvicorn==0.30.1\", # server\n # \"ghost>=9\" ]\n \"typing_extensions[docs]===4.8; python_version < '3.13'\",\n \"direct_dep @ https://example.com/archive.whl#sha256=abc\",\n]\n\n[project.optional-dependencies]\ntest = [\"pytest==8.3.0\", \"httpx[http2]>=0.27\"]\ndocs = [\n # \"ghost-optional==9\" ]\n \"sphinx!=7\",\n \"mkdocs_material==9.5\",\n]\n";
|
|
4
|
+
readonly dependencies: {
|
|
5
|
+
readonly fastapi: "any";
|
|
6
|
+
readonly uvicorn: "0.30.1";
|
|
7
|
+
readonly typing_extensions: "4.8";
|
|
8
|
+
readonly direct_dep: "any";
|
|
9
|
+
readonly pytest: "8.3.0";
|
|
10
|
+
readonly httpx: "any";
|
|
11
|
+
readonly sphinx: "any";
|
|
12
|
+
readonly mkdocs_material: "9.5";
|
|
13
|
+
};
|
|
14
|
+
}, {
|
|
15
|
+
readonly name: "inline arrays with whitespace, pins, and canonical collisions";
|
|
16
|
+
readonly content: "[project]\ndependencies = [\" space_pkg [x] ==3.0 \", \"paren_pkg (==4.0)\", \"compatible_pkg (~= 2.0)\", \"foo_bar==1.0\", \"foo-bar==2.0\", \"reverse-name==2.0\", \"reverse_name==1.0\", \"same.name==5.0\", \"same_name==5.0\", \"epoch_pkg==2!4.2\", \"date_pkg==2024.01\"]\n";
|
|
17
|
+
readonly dependencies: {
|
|
18
|
+
readonly space_pkg: "3.0";
|
|
19
|
+
readonly paren_pkg: "4.0";
|
|
20
|
+
readonly compatible_pkg: "2.0";
|
|
21
|
+
readonly foo_bar: "any";
|
|
22
|
+
readonly 'foo-bar': "any";
|
|
23
|
+
readonly 'reverse-name': "any";
|
|
24
|
+
readonly reverse_name: "any";
|
|
25
|
+
readonly 'same.name': "5.0";
|
|
26
|
+
readonly same_name: "5.0";
|
|
27
|
+
readonly epoch_pkg: "2!4.2";
|
|
28
|
+
readonly date_pkg: "2024.01";
|
|
29
|
+
};
|
|
30
|
+
}, {
|
|
31
|
+
readonly name: "array-of-table boundary after optional dependencies";
|
|
32
|
+
readonly content: "[project]\ndependencies = [\"core_pkg==1.0\"]\n\n[project.optional-dependencies]\ntest = [\"optional_pkg==2.0\"]\n\n[[tool.demo]]\ntags = [\"ghost==9\"]\n";
|
|
33
|
+
readonly dependencies: {
|
|
34
|
+
readonly core_pkg: "1.0";
|
|
35
|
+
readonly optional_pkg: "2.0";
|
|
36
|
+
};
|
|
37
|
+
}, {
|
|
38
|
+
readonly name: "multiline project strings before dependencies";
|
|
39
|
+
readonly content: "[project]\ndescription = \"\"\"\n[not-a-real-section]\n\"ghost==9\"\n\"\"\"\nreadme = '''\ndependencies = [\"evil==9.9.9\"]\n'''\ndependencies = [\"requests==2.0\"]\n";
|
|
40
|
+
readonly dependencies: {
|
|
41
|
+
readonly requests: "2.0";
|
|
42
|
+
};
|
|
43
|
+
}];
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export const PEP621_PYPROJECT_FIXTURES = [
|
|
2
|
+
{
|
|
3
|
+
name: 'core and optional dependency arrays',
|
|
4
|
+
content: `[project]
|
|
5
|
+
name = "example"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"fastapi>=0.100,<1",
|
|
8
|
+
"uvicorn==0.30.1", # server
|
|
9
|
+
# "ghost>=9" ]
|
|
10
|
+
"typing_extensions[docs]===4.8; python_version < '3.13'",
|
|
11
|
+
"direct_dep @ https://example.com/archive.whl#sha256=abc",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.optional-dependencies]
|
|
15
|
+
test = ["pytest==8.3.0", "httpx[http2]>=0.27"]
|
|
16
|
+
docs = [
|
|
17
|
+
# "ghost-optional==9" ]
|
|
18
|
+
"sphinx!=7",
|
|
19
|
+
"mkdocs_material==9.5",
|
|
20
|
+
]
|
|
21
|
+
`,
|
|
22
|
+
dependencies: {
|
|
23
|
+
fastapi: 'any',
|
|
24
|
+
uvicorn: '0.30.1',
|
|
25
|
+
typing_extensions: '4.8',
|
|
26
|
+
direct_dep: 'any',
|
|
27
|
+
pytest: '8.3.0',
|
|
28
|
+
httpx: 'any',
|
|
29
|
+
sphinx: 'any',
|
|
30
|
+
mkdocs_material: '9.5',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'inline arrays with whitespace, pins, and canonical collisions',
|
|
35
|
+
content: `[project]
|
|
36
|
+
dependencies = [" space_pkg [x] ==3.0 ", "paren_pkg (==4.0)", "compatible_pkg (~= 2.0)", "foo_bar==1.0", "foo-bar==2.0", "reverse-name==2.0", "reverse_name==1.0", "same.name==5.0", "same_name==5.0", "epoch_pkg==2!4.2", "date_pkg==2024.01"]
|
|
37
|
+
`,
|
|
38
|
+
dependencies: {
|
|
39
|
+
space_pkg: '3.0',
|
|
40
|
+
paren_pkg: '4.0',
|
|
41
|
+
compatible_pkg: '2.0',
|
|
42
|
+
foo_bar: 'any',
|
|
43
|
+
'foo-bar': 'any',
|
|
44
|
+
'reverse-name': 'any',
|
|
45
|
+
reverse_name: 'any',
|
|
46
|
+
'same.name': '5.0',
|
|
47
|
+
same_name: '5.0',
|
|
48
|
+
epoch_pkg: '2!4.2',
|
|
49
|
+
date_pkg: '2024.01',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'array-of-table boundary after optional dependencies',
|
|
54
|
+
content: `[project]
|
|
55
|
+
dependencies = ["core_pkg==1.0"]
|
|
56
|
+
|
|
57
|
+
[project.optional-dependencies]
|
|
58
|
+
test = ["optional_pkg==2.0"]
|
|
59
|
+
|
|
60
|
+
[[tool.demo]]
|
|
61
|
+
tags = ["ghost==9"]
|
|
62
|
+
`,
|
|
63
|
+
dependencies: {
|
|
64
|
+
core_pkg: '1.0',
|
|
65
|
+
optional_pkg: '2.0',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'multiline project strings before dependencies',
|
|
70
|
+
content: `[project]
|
|
71
|
+
description = """
|
|
72
|
+
[not-a-real-section]
|
|
73
|
+
"ghost==9"
|
|
74
|
+
"""
|
|
75
|
+
readme = '''
|
|
76
|
+
dependencies = ["evil==9.9.9"]
|
|
77
|
+
'''
|
|
78
|
+
dependencies = ["requests==2.0"]
|
|
79
|
+
`,
|
|
80
|
+
dependencies: {
|
|
81
|
+
requests: '2.0',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
];
|
|
85
|
+
//# sourceMappingURL=manifestFixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifestFixtures.js","sourceRoot":"","sources":["../../src/test-utils/manifestFixtures.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC;QACE,IAAI,EAAE,qCAAqC;QAC3C,OAAO,EAAE;;;;;;;;;;;;;;;;;CAiBZ;QACG,YAAY,EAAE;YACZ,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,QAAQ;YACjB,iBAAiB,EAAE,KAAK;YACxB,UAAU,EAAE,KAAK;YACjB,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,KAAK;YACb,eAAe,EAAE,KAAK;SACvB;KACF;IACD;QACE,IAAI,EAAE,+DAA+D;QACrE,OAAO,EAAE;;CAEZ;QACG,YAAY,EAAE;YACZ,SAAS,EAAE,KAAK;YAChB,SAAS,EAAE,KAAK;YAChB,cAAc,EAAE,KAAK;YACrB,OAAO,EAAE,KAAK;YACd,SAAS,EAAE,KAAK;YAChB,cAAc,EAAE,KAAK;YACrB,YAAY,EAAE,KAAK;YACnB,WAAW,EAAE,KAAK;YAClB,SAAS,EAAE,KAAK;YAChB,SAAS,EAAE,OAAO;YAClB,QAAQ,EAAE,SAAS;SACpB;KACF;IACD;QACE,IAAI,EAAE,qDAAqD;QAC3D,OAAO,EAAE;;;;;;;;CAQZ;QACG,YAAY,EAAE;YACZ,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,KAAK;SACpB;KACF;IACD;QACE,IAAI,EAAE,+CAA+C;QACrD,OAAO,EAAE;;;;;;;;;CASZ;QACG,YAAY,EAAE;YACZ,QAAQ,EAAE,KAAK;SAChB;KACF;CACO,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
export function resolveManifestPaths(config) {
|
|
3
3
|
const rootDir = config.rootDir || process.cwd();
|
|
4
|
-
return (config.manifestFiles || ['package.json']).map((manifest) => path.
|
|
4
|
+
return (config.manifestFiles || ['package.json']).map((manifest) => path.resolve(rootDir, manifest));
|
|
5
5
|
}
|
|
6
6
|
//# sourceMappingURL=manifestPaths.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifestPaths.js","sourceRoot":"","sources":["../../src/utils/manifestPaths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,UAAU,oBAAoB,CAAC,MAA0B;IAC7D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAChD,OAAO,CAAC,MAAM,CAAC,aAAa,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"manifestPaths.js","sourceRoot":"","sources":["../../src/utils/manifestPaths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,UAAU,oBAAoB,CAAC,MAA0B;IAC7D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAChD,OAAO,CAAC,MAAM,CAAC,aAAa,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AACvG,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const QUOTED_STRING = /"((?:\\.|[^"\\])*)"|'([^']*)'/g;
|
|
2
|
+
const TOML_COMMENT = /("(?:\\.|[^"\\])*"|'[^']*')|#[^\r\n]*/g;
|
|
3
|
+
const TOML_ARRAY = /^\[((?:[^"'\]]|"(?:\\.|[^"\\])*"|'[^']*')*)\]/;
|
|
4
|
+
const TOML_MULTILINE_STRING = /"""(?:\\[\s\S]|"{1,2}(?!")|[^"\\])*"""|'''(?:'{1,2}(?!')|[^'])*'''/g;
|
|
5
|
+
export function parsePyprojectDependencies(content) {
|
|
6
|
+
const dependencyGroups = new Map();
|
|
7
|
+
const uncommented = stripComments(content.replace(TOML_MULTILINE_STRING, ''));
|
|
8
|
+
const coreSection = getSection(uncommented, 'project');
|
|
9
|
+
const optionalSection = getSection(uncommented, 'project.optional-dependencies');
|
|
10
|
+
const arrays = [
|
|
11
|
+
...findArrays(coreSection, /^[ \t]*dependencies[ \t]*=[ \t]*\[/gm),
|
|
12
|
+
...findArrays(optionalSection, /^[ \t]*[a-zA-Z0-9._-]+[ \t]*=[ \t]*\[/gm),
|
|
13
|
+
];
|
|
14
|
+
for (const source of arrays) {
|
|
15
|
+
for (const match of source.matchAll(QUOTED_STRING)) {
|
|
16
|
+
const parsed = parsePythonRequirement(match[1] ?? match[2]);
|
|
17
|
+
if (!parsed) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
addDependency(dependencyGroups, parsed.name, parsed.version);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return flattenDependencyGroups(dependencyGroups);
|
|
24
|
+
}
|
|
25
|
+
export function parseRequirementsDependencies(content) {
|
|
26
|
+
const dependencyGroups = new Map();
|
|
27
|
+
for (const line of content.split('\n')) {
|
|
28
|
+
const parsed = parsePythonRequirement(stripRequirementInlineComment(line));
|
|
29
|
+
if (!parsed) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
addDependency(dependencyGroups, parsed.name, parsed.version);
|
|
33
|
+
}
|
|
34
|
+
return flattenDependencyGroups(dependencyGroups);
|
|
35
|
+
}
|
|
36
|
+
function flattenDependencyGroups(dependencyGroups) {
|
|
37
|
+
const dependencies = new Map();
|
|
38
|
+
for (const group of dependencyGroups.values()) {
|
|
39
|
+
for (const name of group.names) {
|
|
40
|
+
dependencies.set(name, group.version);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return dependencies;
|
|
44
|
+
}
|
|
45
|
+
export function canonicalizePythonPackageName(name) {
|
|
46
|
+
return name.toLowerCase().replace(/[-_.]+/g, '-');
|
|
47
|
+
}
|
|
48
|
+
function stripComments(content) {
|
|
49
|
+
return content.replace(TOML_COMMENT, (_match, quoted) => quoted ?? '');
|
|
50
|
+
}
|
|
51
|
+
function getSection(content, name) {
|
|
52
|
+
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
53
|
+
const header = new RegExp(`^[ \\t]*\\[${escapedName}\\][ \\t]*\\r?$`, 'm').exec(content);
|
|
54
|
+
if (!header) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const remainder = content.slice(header.index + header[0].length);
|
|
58
|
+
const nextSection = remainder.search(/^[ \t]*(?:\[[^\]\r\n]+\]|\[\[[^\]\r\n]+\]\])[ \t]*\r?$/m);
|
|
59
|
+
return nextSection === -1 ? remainder : remainder.slice(0, nextSection);
|
|
60
|
+
}
|
|
61
|
+
function findArrays(section, assignmentPattern) {
|
|
62
|
+
if (!section) {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const arrays = [];
|
|
66
|
+
for (const match of section.matchAll(assignmentPattern)) {
|
|
67
|
+
const openingBracket = match.index + match[0].lastIndexOf('[');
|
|
68
|
+
const array = section.slice(openingBracket).match(TOML_ARRAY)?.[1];
|
|
69
|
+
if (array !== undefined) {
|
|
70
|
+
arrays.push(array);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return arrays;
|
|
74
|
+
}
|
|
75
|
+
function getComparableVersion(constraint) {
|
|
76
|
+
const version = constraint.match(/^(?:={2,3}|~=)\s*([^,\s*]+)\s*$/) ?? constraint.match(/^\(\s*(?:={2,3}|~=)\s*([^,\s*()]+)\s*\)$/);
|
|
77
|
+
return version?.[1];
|
|
78
|
+
}
|
|
79
|
+
function parsePythonRequirement(requirement) {
|
|
80
|
+
const parsed = requirement.trim().match(/^([a-zA-Z0-9][a-zA-Z0-9._-]*)(?:\s*\[[^\]]+\])?\s*(.*)$/);
|
|
81
|
+
if (!parsed) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
const constraint = parsed[2].split(';', 1)[0].trim();
|
|
85
|
+
return { name: parsed[1], version: getComparableVersion(constraint) ?? 'any' };
|
|
86
|
+
}
|
|
87
|
+
function stripRequirementInlineComment(requirement) {
|
|
88
|
+
let quote;
|
|
89
|
+
for (let index = 0; index < requirement.length; index += 1) {
|
|
90
|
+
const character = requirement[index];
|
|
91
|
+
if ((character === '"' || character === "'") && requirement[index - 1] !== '\\') {
|
|
92
|
+
quote = quote === character ? undefined : quote || character;
|
|
93
|
+
}
|
|
94
|
+
else if (character === '#' && !quote && index > 0 && /\s/.test(requirement[index - 1])) {
|
|
95
|
+
return requirement.slice(0, index).trimEnd();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return requirement;
|
|
99
|
+
}
|
|
100
|
+
function addDependency(dependencies, name, version) {
|
|
101
|
+
const normalizedName = name.toLowerCase();
|
|
102
|
+
const canonicalName = canonicalizePythonPackageName(normalizedName);
|
|
103
|
+
const existing = dependencies.get(canonicalName);
|
|
104
|
+
if (existing) {
|
|
105
|
+
existing.names.add(normalizedName);
|
|
106
|
+
if (existing.version !== version) {
|
|
107
|
+
existing.version = 'any';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
dependencies.set(canonicalName, { names: new Set([normalizedName]), version });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=pythonDependencies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pythonDependencies.js","sourceRoot":"","sources":["../../src/utils/pythonDependencies.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,9 +4,9 @@ import type { DocFreshnessConfig, Document, Reference, ValidationResult } from '
|
|
|
4
4
|
*/
|
|
5
5
|
export declare class DependencyValidator {
|
|
6
6
|
private dependencies;
|
|
7
|
+
private pythonDependencies;
|
|
7
8
|
private loadedFromKey;
|
|
8
9
|
constructor();
|
|
9
10
|
private loadDependencies;
|
|
10
|
-
private parseManifest;
|
|
11
11
|
validateBatch(references: Reference[], _document: Document, config: DocFreshnessConfig): Promise<ValidationResult[]>;
|
|
12
12
|
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { resolveManifestPaths } from '../utils/manifestPaths.js';
|
|
4
|
+
import { canonicalizePythonPackageName, parsePyprojectDependencies, parseRequirementsDependencies } from '../utils/pythonDependencies.js';
|
|
4
5
|
/**
|
|
5
6
|
* Validates that mentioned dependencies exist in manifest files
|
|
6
7
|
*/
|
|
7
8
|
export class DependencyValidator {
|
|
8
9
|
dependencies;
|
|
10
|
+
pythonDependencies;
|
|
9
11
|
loadedFromKey;
|
|
10
12
|
constructor() {
|
|
11
13
|
this.dependencies = null;
|
|
14
|
+
this.pythonDependencies = new Set();
|
|
12
15
|
this.loadedFromKey = null;
|
|
13
16
|
}
|
|
14
17
|
/** @internal */
|
|
@@ -21,25 +24,31 @@ export class DependencyValidator {
|
|
|
21
24
|
if (this.dependencies && this.loadedFromKey === configKey) {
|
|
22
25
|
return;
|
|
23
26
|
}
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
const dependencies = new Set();
|
|
28
|
+
const pythonDependencies = new Set();
|
|
26
29
|
for (const manifestPath of manifestPaths) {
|
|
27
30
|
const fileName = path.basename(manifestPath);
|
|
31
|
+
const parser = manifestDependencyParsers[fileName];
|
|
32
|
+
if (!parser) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
28
35
|
try {
|
|
29
36
|
const content = await fs.promises.readFile(manifestPath, 'utf-8');
|
|
30
|
-
const deps =
|
|
37
|
+
const deps = parser(content);
|
|
31
38
|
for (const dep of deps) {
|
|
32
|
-
|
|
39
|
+
dependencies.add(dep.toLowerCase());
|
|
40
|
+
if (fileName === 'pyproject.toml' || fileName === 'requirements.txt') {
|
|
41
|
+
pythonDependencies.add(canonicalizePythonPackageName(dep));
|
|
42
|
+
}
|
|
33
43
|
}
|
|
34
44
|
}
|
|
35
|
-
catch {
|
|
36
|
-
|
|
45
|
+
catch (cause) {
|
|
46
|
+
throw new Error(`Failed to load manifest: ${manifestPath}: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
37
47
|
}
|
|
38
48
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return parser ? parser(content) : [];
|
|
49
|
+
this.dependencies = dependencies;
|
|
50
|
+
this.pythonDependencies = pythonDependencies;
|
|
51
|
+
this.loadedFromKey = configKey;
|
|
43
52
|
}
|
|
44
53
|
async validateBatch(references, _document, config) {
|
|
45
54
|
await this.loadDependencies(config);
|
|
@@ -47,7 +56,9 @@ export class DependencyValidator {
|
|
|
47
56
|
for (const ref of references) {
|
|
48
57
|
const pkg = ref.value.toLowerCase();
|
|
49
58
|
// Check if package exists in dependencies
|
|
50
|
-
const found =
|
|
59
|
+
const found = ref.ecosystem === 'pypi'
|
|
60
|
+
? this.pythonDependencies.has(canonicalizePythonPackageName(pkg))
|
|
61
|
+
: this.dependencies.has(pkg) || (!ref.ecosystem && this.pythonDependencies.has(canonicalizePythonPackageName(pkg)));
|
|
51
62
|
if (found) {
|
|
52
63
|
results.push({
|
|
53
64
|
reference: ref,
|
|
@@ -71,19 +82,8 @@ const manifestDependencyParsers = {
|
|
|
71
82
|
const json = JSON.parse(content);
|
|
72
83
|
return ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'].flatMap((key) => Object.keys(json[key] || {}));
|
|
73
84
|
},
|
|
74
|
-
'requirements.txt': (content) => content
|
|
75
|
-
|
|
76
|
-
.map((line) => line.trim())
|
|
77
|
-
.filter((line) => line && !line.startsWith('#'))
|
|
78
|
-
.map((line) => line.match(/^([a-zA-Z0-9\-_]+)/)?.[1])
|
|
79
|
-
.filter((dep) => Boolean(dep)),
|
|
80
|
-
'pyproject.toml': (content) => {
|
|
81
|
-
const depsMatch = content.match(/\[project\.dependencies\]([\s\S]*?)(?:\[|$)/);
|
|
82
|
-
if (!depsMatch) {
|
|
83
|
-
return [];
|
|
84
|
-
}
|
|
85
|
-
return Array.from(depsMatch[1].matchAll(/"([^"<>=!]+)/g), (match) => match[1].split(/[<>=!]/)[0]);
|
|
86
|
-
},
|
|
85
|
+
'requirements.txt': (content) => Array.from(parseRequirementsDependencies(content).keys()),
|
|
86
|
+
'pyproject.toml': (content) => Array.from(parsePyprojectDependencies(content).keys()),
|
|
87
87
|
'go.mod': (content) => {
|
|
88
88
|
const requireMatch = content.match(/require\s+\(([\s\S]*?)\)/);
|
|
89
89
|
if (!requireMatch) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependencyValidator.js","sourceRoot":"","sources":["../../src/validators/dependencyValidator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"dependencyValidator.js","sourceRoot":"","sources":["../../src/validators/dependencyValidator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { DependencyValidator } from './dependencyValidator.js';
|
|
4
|
+
import { DependencyExtractor } from '../parsers/extractors/dependencyExtractor.js';
|
|
4
5
|
import { makeDoc, makeRef as makeBaseRef } from '../test-utils/factories.js';
|
|
6
|
+
import { PEP621_PYPROJECT_FIXTURES } from '../test-utils/manifestFixtures.js';
|
|
5
7
|
function makeRef(value) {
|
|
6
8
|
return makeBaseRef('dependency', value, { ecosystem: 'npm' });
|
|
7
9
|
}
|
|
@@ -54,12 +56,6 @@ describe('DependencyValidator', () => {
|
|
|
54
56
|
const results = await validator.validateBatch([makeRef('nonexistent-pkg')], doc, config);
|
|
55
57
|
expect(results[0].severity).toBe('error');
|
|
56
58
|
});
|
|
57
|
-
it('handles missing manifest files gracefully', async () => {
|
|
58
|
-
const validator = new DependencyValidator();
|
|
59
|
-
const config = { rootDir: process.cwd(), manifestFiles: ['nonexistent.json'] };
|
|
60
|
-
const results = await validator.validateBatch([makeRef('anything')], doc, config);
|
|
61
|
-
expect(results[0].valid).toBe(false);
|
|
62
|
-
});
|
|
63
59
|
it('uses default manifestFiles when not specified', async () => {
|
|
64
60
|
const validator = new DependencyValidator();
|
|
65
61
|
const config = { rootDir: process.cwd() };
|
|
@@ -79,11 +75,22 @@ describe('DependencyValidator', () => {
|
|
|
79
75
|
]);
|
|
80
76
|
expect(results).toEqual([true, true, true]);
|
|
81
77
|
});
|
|
82
|
-
it('parses
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
it('parses dotted requirements names and validates their Python aliases', async () => {
|
|
79
|
+
const dir = path.join(tmpBase, 'requirements-dotted-name');
|
|
80
|
+
const filePath = path.join(dir, 'requirements.txt');
|
|
81
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
82
|
+
await fs.promises.writeFile(filePath, 'foo.bar==7\n');
|
|
83
|
+
const validator = new DependencyValidator();
|
|
84
|
+
const config = {
|
|
85
|
+
rootDir: process.cwd(),
|
|
86
|
+
manifestFiles: [path.relative(process.cwd(), filePath)],
|
|
87
|
+
};
|
|
88
|
+
const references = ['foo.bar', 'foo_bar', 'foo-bar'].map((value) => makeBaseRef('dependency', value, { ecosystem: 'pypi' }));
|
|
89
|
+
const results = await validator.validateBatch(references, doc, config);
|
|
90
|
+
expect(results.map((result) => result.valid)).toEqual([true, true, true]);
|
|
91
|
+
});
|
|
92
|
+
it('parses PEP 621 core and optional dependencies', async () => {
|
|
93
|
+
const results = await writeAndValidate('pyproject.toml', PEP621_PYPROJECT_FIXTURES[0].content, ['fastapi', 'pytest']);
|
|
87
94
|
expect(results).toEqual([true, true]);
|
|
88
95
|
});
|
|
89
96
|
it('parses go.mod', async () => {
|
|
@@ -105,6 +112,31 @@ describe('DependencyValidator', () => {
|
|
|
105
112
|
const results = await validator.validateBatch([makeRef('Vitest'), makeRef('VITEST')], doc, config);
|
|
106
113
|
expect(results.every((r) => r.valid)).toBe(true);
|
|
107
114
|
});
|
|
115
|
+
it('validates extracted PyPI aliases without treating npm names as equivalent', async () => {
|
|
116
|
+
const dir = path.join(tmpBase, 'mixed-ecosystems');
|
|
117
|
+
await fs.promises.mkdir(dir, { recursive: true });
|
|
118
|
+
const packageJson = path.join(dir, 'package.json');
|
|
119
|
+
const pyproject = path.join(dir, 'pyproject.toml');
|
|
120
|
+
await fs.promises.writeFile(packageJson, JSON.stringify({ dependencies: { foo_bar: '1.0.0', 'foo.bar': '1.0.0' } }));
|
|
121
|
+
await fs.promises.writeFile(pyproject, '[project]\ndependencies = ["python-name==1.0"]\n');
|
|
122
|
+
const validator = new DependencyValidator();
|
|
123
|
+
const config = {
|
|
124
|
+
rootDir: process.cwd(),
|
|
125
|
+
manifestFiles: [path.relative(process.cwd(), packageJson), path.relative(process.cwd(), pyproject)],
|
|
126
|
+
};
|
|
127
|
+
const content = 'Use `python_name`, `python.name`, `foo_bar`, and `foo.bar`';
|
|
128
|
+
const integrationDoc = makeDoc({ content, lines: [content] });
|
|
129
|
+
const references = new DependencyExtractor({ ecosystems: ['pypi'] }).extract(integrationDoc);
|
|
130
|
+
integrationDoc.references = references;
|
|
131
|
+
const results = await validator.validateBatch(references, integrationDoc, config);
|
|
132
|
+
expect(references.map(({ value, ecosystem }) => ({ value, ecosystem }))).toEqual([
|
|
133
|
+
{ value: 'python_name', ecosystem: 'pypi' },
|
|
134
|
+
{ value: 'python.name', ecosystem: 'pypi' },
|
|
135
|
+
{ value: 'foo_bar', ecosystem: 'pypi' },
|
|
136
|
+
{ value: 'foo.bar', ecosystem: 'pypi' },
|
|
137
|
+
]);
|
|
138
|
+
expect(results.map((result) => result.valid)).toEqual([true, true, false, false]);
|
|
139
|
+
});
|
|
108
140
|
describe('manifest edge cases', () => {
|
|
109
141
|
async function writeAndValidate(fileName, content, pkgNames) {
|
|
110
142
|
const dirName = `edge-${fileName.replace(/\./g, '-')}`;
|
|
@@ -122,10 +154,6 @@ describe('DependencyValidator', () => {
|
|
|
122
154
|
const results = await writeAndValidate('Cargo.toml', '[package]\nname = "myapp"\nversion = "0.1.0"\n', ['myapp']);
|
|
123
155
|
expect(results).toEqual([false]);
|
|
124
156
|
});
|
|
125
|
-
it('handles unknown manifest format gracefully', async () => {
|
|
126
|
-
const results = await writeAndValidate('build.gradle', 'implementation "org.something:artifact:1.0"\n', ['org.something']);
|
|
127
|
-
expect(results).toEqual([false]);
|
|
128
|
-
});
|
|
129
157
|
it('handles requirements.txt with blank lines and comments only', async () => {
|
|
130
158
|
const results = await writeAndValidate('requirements.txt', '# this is a comment\n\n# another comment\n', ['flask']);
|
|
131
159
|
expect(results).toEqual([false]);
|