Skip to content

Commit 1c9e073

Browse files
committed
Add support for parsing .npmrc files #4494
1 parent ea42c1d commit 1c9e073

7 files changed

Lines changed: 109 additions & 6 deletions

File tree

.VERSION

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
refs=$Format:%D$
2-
commit=$Format:%H$
3-
abbrev_commit=$Format:%h$
4-
committer_date=$Format:%cs$
5-
git_describe=$Format:%(describe)$
1+
refs=HEAD -> develop
2+
commit=ea42c1d09d38444c6206a245d67ca3d99930c9b0
3+
abbrev_commit=ea42c1d09d3
4+
committer_date=2026-05-26
5+
git_describe=v33.0.0rc1-4-gea42c1d09d3

src/packagedcode/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
npm.PnpmShrinkwrapYamlHandler,
153153
npm.PnpmLockYamlHandler,
154154
npm.PnpmWorkspaceYamlHandler,
155+
npm.NpmrcHandler,
155156

156157
nuget.NugetNupkgHandler,
157158
nuget.NugetNuspecHandler,

src/packagedcode/npm.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2023,3 +2023,38 @@ def keywords_mapper(keywords, package):
20232023

20242024
package.keywords = keywords
20252025
return package
2026+
2027+
2028+
class NpmrcHandler(models.NonAssemblableDatafileHandler):
2029+
datasource_id = 'npmrc'
2030+
path_patterns = (
2031+
'*/.npmrc',
2032+
)
2033+
default_package_type = 'npm'
2034+
default_primary_language = 'JavaScript'
2035+
description = 'npmrc configuration file'
2036+
documentation_url = 'https://docs.npmjs.com/cli/v9/configuring-npm/npmrc'
2037+
2038+
@classmethod
2039+
def parse(cls, location, package_only=False):
2040+
"""
2041+
Parses an .npmrc file and yields package data.
2042+
"""
2043+
extra_data = {}
2044+
with io.open(location, encoding='utf-8') as f:
2045+
for line in f:
2046+
stripped = line.strip()
2047+
if not stripped or stripped.startswith('#') or stripped.startswith(';'):
2048+
continue
2049+
if '=' in stripped:
2050+
key, _, value = stripped.partition('=')
2051+
extra_data[key.strip()] = value.strip()
2052+
2053+
package_data = dict(
2054+
datasource_id=cls.datasource_id,
2055+
type=cls.default_package_type,
2056+
primary_language=cls.default_primary_language,
2057+
extra_data=extra_data,
2058+
)
2059+
yield models.PackageData.from_data(package_data, package_only)
2060+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# this is a comment
2+
; another comment
3+
4+
registry=https://registry.npmjs.org/
5+
@my-scope:registry=https://npm.pkg.github.com
6+
always-auth=true
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[
2+
{
3+
"type": "npm",
4+
"namespace": null,
5+
"name": null,
6+
"version": null,
7+
"qualifiers": {},
8+
"subpath": null,
9+
"primary_language": "JavaScript",
10+
"description": null,
11+
"release_date": null,
12+
"parties": [],
13+
"keywords": [],
14+
"homepage_url": null,
15+
"download_url": null,
16+
"size": null,
17+
"sha1": null,
18+
"md5": null,
19+
"sha256": null,
20+
"sha512": null,
21+
"bug_tracking_url": null,
22+
"code_view_url": null,
23+
"vcs_url": null,
24+
"copyright": null,
25+
"holder": null,
26+
"declared_license_expression": null,
27+
"declared_license_expression_spdx": null,
28+
"license_detections": [],
29+
"other_license_expression": null,
30+
"other_license_expression_spdx": null,
31+
"other_license_detections": [],
32+
"extracted_license_statement": null,
33+
"notice_text": null,
34+
"source_packages": [],
35+
"file_references": [],
36+
"is_private": false,
37+
"is_virtual": false,
38+
"extra_data": {
39+
"registry": "https://registry.npmjs.org/",
40+
"@my-scope:registry": "https://npm.pkg.github.com",
41+
"always-auth": "true"
42+
},
43+
"dependencies": [],
44+
"repository_homepage_url": null,
45+
"repository_download_url": null,
46+
"api_data_url": null,
47+
"datasource_id": "npmrc",
48+
"purl": null
49+
}
50+
]

tests/packagedcode/data/pypi/unpacked_sdist/metadata-2.1/commoncode-21.5.12/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
55
[tool.setuptools_scm]
66
# this is used populated when creating a git archive
77
# and when there is .git dir and/or there is no git installed
8-
fallback_version = "v9999.$Format:%h-%cs$"
8+
fallback_version = "v9999.ea42c1d09d3-2026-05-26"
99

1010
[tool.pytest.ini_options]
1111
norecursedirs = [

tests/packagedcode/test_npm.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,17 @@ def test_npm_scan_with_private_package_json_and_yarn_lock(self):
550550
expected_file, result_file, remove_uuid=True, regen=REGEN_TEST_FIXTURES
551551
)
552552

553+
def test_is_datafile_npmrc(self):
554+
test_file = self.get_test_loc('npm/npmrc/.npmrc')
555+
assert npm.NpmrcHandler.is_datafile(test_file)
556+
557+
def test_parse_npmrc(self):
558+
test_file = self.get_test_loc('npm/npmrc/.npmrc')
559+
expected_loc = self.get_test_loc('npm/npmrc/.npmrc-expected.json')
560+
packages = npm.NpmrcHandler.parse(test_file)
561+
self.check_packages_data(packages, expected_loc, regen=REGEN_TEST_FIXTURES)
562+
563+
553564

554565
test_data = [
555566
(['MIT'], 'mit'),

0 commit comments

Comments
 (0)