Skip to content

Commit c553399

Browse files
committed
packagedcode: add NpmrcHandler to parse .npmrc files (#4494)
Add NpmrcHandler in src/packagedcode/npm.py to parse .npmrc configuration files and yield a PackageData with parsed key/value pairs in extra_data. Add unit test and fixtures under tests/packagedcode/data/npm/basic/. Signed-off-by: Shekhar <shekharsuman0397@gmail.com>
1 parent 930e30e commit c553399

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed

src/packagedcode/npm.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,48 @@ def update_workspace_members(cls, workspace_members, codebase):
500500
for member in workspace_members:
501501
member.save(codebase)
502502

503+
class NpmrcHandler(BaseNpmHandler):
504+
datasource_id = 'npmrc'
505+
path_patterns = ('*/.npmrc',)
506+
default_package_type = 'npm'
507+
default_primary_language = None
508+
description = 'npm .npmrc configuration file'
509+
documentation_url = 'https://docs.npmjs.com/cli/v11/configuring-npm/npmrc'
510+
511+
@classmethod
512+
def parse(cls, location, package_only=False):
513+
"""
514+
parse [.npmrc] file and store result in key : value pair.
515+
convert key : value pair to object and return it.
516+
"""
517+
extra_data = {}
518+
with io.open(location, encoding='utf-8') as lines:
519+
for line in lines:
520+
line = line.strip()
521+
if not line or line.startswith(';') or line.startswith('#'):
522+
continue
523+
if '=' not in line:
524+
continue
525+
key, value = line.split('=', 1)
526+
key = key.strip()
527+
value = value.strip()
528+
# ignore empty key but allow empty values
529+
if not key:
530+
continue
531+
# if value is in single quote or in double quote, strip them
532+
if (value.startswith('"') and value.endswith('"')) or (value.startswith("'") and value.endswith("'")):
533+
if len(value) >= 2:
534+
value = value[1:-1]
535+
extra_data[key] = value
536+
537+
package_data = dict(
538+
datasource_id=cls.datasource_id,
539+
type=cls.default_package_type,
540+
primary_language=cls.default_primary_language,
541+
description=cls.description,
542+
extra_data=extra_data,
543+
)
544+
yield models.PackageData.from_data(package_data, package_only)
503545

504546
def get_urls(namespace, name, version, **kwargs):
505547
return dict(
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
; sample .npmrc for tests
2+
# a comment line
3+
registry=https://registry.npmjs.org/
4+
cache=~/.npm
5+
strict-ssl=true
6+
//registry.npmjs.org/:_authToken="abc123"
7+
init.author.name=John Doe
8+
emptykey=
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
[
2+
{
3+
"type": "npm",
4+
"namespace": null,
5+
"name": null,
6+
"version": null,
7+
"qualifiers": {},
8+
"subpath": null,
9+
"primary_language": null,
10+
"description": "npm .npmrc configuration file",
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+
"cache": "~/.npm",
41+
"strict-ssl": "true",
42+
"//registry.npmjs.org/:_authToken": "abc123",
43+
"init.author.name": "John Doe",
44+
"emptykey": ""
45+
},
46+
"dependencies": [],
47+
"repository_homepage_url": null,
48+
"repository_download_url": null,
49+
"api_data_url": null,
50+
"datasource_id": "npmrc",
51+
"purl": null
52+
}
53+
]

tests/packagedcode/test_npmrc.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import os
2+
3+
from packagedcode import npm
4+
from packages_test_utils import PackageTester
5+
from scancode_config import REGEN_TEST_FIXTURES
6+
7+
8+
class TestNpmrc(PackageTester):
9+
test_data_dir = os.path.join(os.path.dirname(__file__), 'data')
10+
11+
def test_parse_basic_npmrc(self):
12+
test_file = self.get_test_loc('npm/basic/.npmrc')
13+
expected_loc = self.get_test_loc('npm/basic/.npmrc.expected')
14+
packages_data = npm.NpmrcHandler.parse(test_file)
15+
self.check_packages_data(packages_data, expected_loc, regen=REGEN_TEST_FIXTURES)

0 commit comments

Comments
 (0)