diff --git a/src/packagedcode/npm.py b/src/packagedcode/npm.py index 8b2e7864184..5fa2292b34a 100644 --- a/src/packagedcode/npm.py +++ b/src/packagedcode/npm.py @@ -376,6 +376,18 @@ def update_dependencies_by_purl( if '_' in metadata: requirement, _extra = metadata.split('_') + if ':' in requirement and '@' in requirement: + # dependencies with requirements like this are aliases and should be reported + aliased_package, _, constraint = requirement.rpartition('@') + _, _, aliased_package_name = aliased_package.rpartition(':') + sdns, _ , sdname = aliased_package_name.rpartition('/') + dep_purl = PackageURL( + type=cls.default_package_type, + namespace=sdns, + name=sdname + ).to_string() + requirement = constraint + dep_package = models.DependentPackage( purl=dep_purl, scope=scope, @@ -1014,13 +1026,12 @@ def parse(cls, location, package_only=False): ns_name = ns_name.replace('"', '') ns, _ , name = ns_name.rpartition('/') - # sometimes constraints appear in the form of - # wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - if '@' in constraint: - # "npm:wrap-ansi" should be appended to `name`, joined - # with an "@" - constraint_package, _, constraint = constraint.partition('@') - name = f'{name}@{constraint_package}' + if ':' in constraint and '@' in constraint: + # dependencies with requirements like this are aliases and should be reported + aliased_package, _, constraint = constraint.rpartition('@') + _, _, aliased_package_name = aliased_package.rpartition(':') + ns, _ , name = aliased_package_name.rpartition('/') + sub_dependencies.append((ns, name, constraint,)) elif line.startswith(' ' * 2): @@ -1780,9 +1791,13 @@ def deps_mapper(deps, package, field_name, is_direct=True): ns, name = split_scoped_package_name(fqname) if not name: continue - if '@' in requirement: - requirement_package, _, requirement = requirement.partition('@') - name = f'{name}@{requirement_package}' + + if ':' in requirement and '@' in requirement: + # dependencies with requirements like this are aliases and should be reported + aliased_package, _, requirement = requirement.rpartition('@') + _, _, aliased_package_name = aliased_package.rpartition(':') + ns, _ , name = aliased_package_name.rpartition('/') + purl = PackageURL(type='npm', namespace=ns, name=name).to_string() # optionalDependencies override the dependencies with the same name diff --git a/tests/packagedcode/data/npm/special_extracted_requirements/package.json.expected b/tests/packagedcode/data/npm/special_extracted_requirements/package.json.expected index 07e89067ca4..89bb0db8d8b 100755 --- a/tests/packagedcode/data/npm/special_extracted_requirements/package.json.expected +++ b/tests/packagedcode/data/npm/special_extracted_requirements/package.json.expected @@ -68,7 +68,7 @@ }, "dependencies": [ { - "purl": "pkg:npm/strip-ansi-cjs%40npm:strip-ansi", + "purl": "pkg:npm/strip-ansi", "extracted_requirement": "^6.0.1", "scope": "devDependencies", "is_runtime": false, diff --git a/tests/packagedcode/data/npm/workspace/crystal.expected.json b/tests/packagedcode/data/npm/workspace/crystal.expected.json index fd30da26963..09e6eece4fa 100644 --- a/tests/packagedcode/data/npm/workspace/crystal.expected.json +++ b/tests/packagedcode/data/npm/workspace/crystal.expected.json @@ -9960,8 +9960,8 @@ "datasource_id": "npm_package_json" }, { - "purl": "pkg:npm/%40localrepo/prettier2-for-jest", - "extracted_requirement": "npm:prettier@^2", + "purl": "pkg:npm/prettier", + "extracted_requirement": "^2", "scope": "devDependencies", "is_runtime": false, "is_optional": true, @@ -9969,7 +9969,7 @@ "is_direct": true, "resolved_package": {}, "extra_data": {}, - "dependency_uid": "pkg:npm/%40localrepo/prettier2-for-jest?uuid=fixed-uid-done-for-testing-5642512d1758", + "dependency_uid": "pkg:npm/prettier?uuid=fixed-uid-done-for-testing-5642512d1758", "for_package_uid": null, "datafile_path": "crystal/package.json", "datasource_id": "npm_package_json" @@ -18249,8 +18249,8 @@ "extra_data": {} }, { - "purl": "pkg:npm/%40localrepo/prettier2-for-jest", - "extracted_requirement": "npm:prettier@^2", + "purl": "pkg:npm/prettier", + "extracted_requirement": "^2", "scope": "devDependencies", "is_runtime": false, "is_optional": true, diff --git a/tests/packagedcode/data/npm/yarn-lock/v1-other-constraint/yarn.lock-expected b/tests/packagedcode/data/npm/yarn-lock/v1-other-constraint/yarn.lock-expected index ac6cf2adffc..5c6d955a7eb 100644 --- a/tests/packagedcode/data/npm/yarn-lock/v1-other-constraint/yarn.lock-expected +++ b/tests/packagedcode/data/npm/yarn-lock/v1-other-constraint/yarn.lock-expected @@ -95,7 +95,7 @@ "extra_data": {} }, { - "purl": "pkg:npm/string-width-cjs%40%22npm:string-width", + "purl": "pkg:npm/string-width", "extracted_requirement": "^4.2.0", "scope": "dependencies", "is_runtime": true, @@ -117,7 +117,7 @@ "extra_data": {} }, { - "purl": "pkg:npm/strip-ansi-cjs%40%22npm:strip-ansi", + "purl": "pkg:npm/strip-ansi", "extracted_requirement": "^6.0.1", "scope": "dependencies", "is_runtime": true,