Skip to content

Commit 54ee82e

Browse files
Merge pull request #4079 from aboutcode-org/fix-npm-workspace-parsing
Fix pnpm workspace parsing and udpate package detection
2 parents 1d0fe75 + f966c8f commit 54ee82e

File tree

9 files changed

+3523
-8
lines changed

9 files changed

+3523
-8
lines changed

src/packagedcode/npm.py

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,8 @@ def get_workspace_members(cls, workspaces, codebase, workspace_root_path):
444444
# Case 3: This is a complex glob pattern, we are doing a full codebase walk
445445
# and glob matching each resource
446446
else:
447-
for resource in workspace_root_path:
447+
workspace_root = codebase.get_resource(path=workspace_root_path)
448+
for resource in workspace_root.walk(codebase):
448449
if NpmPackageJsonHandler.is_datafile(resource.location) and fnmatch.fnmatch(
449450
name=resource.location, pat=workspace_path,
450451
):
@@ -1155,6 +1156,10 @@ def parse(cls, location, package_only=False):
11551156
yield models.PackageData.from_data(package_data, package_only)
11561157

11571158

1159+
class UnknownPnpmLockFormat(Exception):
1160+
pass
1161+
1162+
11581163
class BasePnpmLockHandler(BaseNpmHandler):
11591164

11601165
@classmethod
@@ -1181,31 +1186,62 @@ def parse(cls, location, package_only=False):
11811186
}
11821187
major_v, minor_v = lockfile_version.split(".")
11831188

1184-
resolved_packages = lock_data.get("packages", [])
1189+
resolved_packages = lock_data.get("packages", {})
1190+
dependency_relations = lock_data.get("snapshots", {})
1191+
dependency_relations_by_purl = {}
1192+
if dependency_relations:
1193+
for purl_fields, relations in dependency_relations.items():
1194+
clean_purl_fields = purl_fields.split("(")[0]
1195+
sections = clean_purl_fields.split("/")
1196+
namespace = None
1197+
if len(sections) == 2:
1198+
namespace, name_version = sections
1199+
elif len(sections) == 1:
1200+
name_version, = sections
1201+
name, version = name_version.split("@")
1202+
1203+
purl = PackageURL(
1204+
type=cls.default_package_type,
1205+
name=name,
1206+
namespace=namespace,
1207+
version=version,
1208+
).to_string()
1209+
dependency_relations_by_purl[purl] = relations
1210+
11851211
dependencies_by_purl = {}
11861212

11871213
for purl_fields, data in resolved_packages.items():
11881214
if major_v == "6":
11891215
clean_purl_fields = purl_fields.split("(")[0]
11901216
elif major_v == "5" or is_shrinkwrap:
11911217
clean_purl_fields = purl_fields.split("_")[0]
1192-
else:
1218+
elif major_v == "9":
11931219
clean_purl_fields = purl_fields
1194-
raise Exception(lockfile_version, purl_fields)
1220+
else:
1221+
message = f"Unknown pnpm lockfile format: {lockfile_version}"
1222+
raise UnknownPnpmLockFormat(message, purl_fields)
11951223

11961224
sections = clean_purl_fields.split("/")
1197-
name_version= None
1225+
name_version = None
1226+
namespace = None
11981227
if major_v == "6":
11991228
if len(sections) == 2:
1200-
namespace = None
12011229
_, name_version = sections
12021230
elif len(sections) == 3:
12031231
_, namespace, name_version = sections
1232+
elif len(sections) == 1:
1233+
name_version, = sections
1234+
1235+
name, version = name_version.split("@")
1236+
elif major_v == "9":
1237+
if len(sections) == 2:
1238+
namespace, name_version = sections
1239+
elif len(sections) == 1:
1240+
name_version, = sections
12041241

12051242
name, version = name_version.split("@")
12061243
elif major_v == "5" or is_shrinkwrap:
12071244
if len(sections) == 3:
1208-
namespace = None
12091245
_, name, version = sections
12101246
elif len(sections) == 4:
12111247
_, namespace, name, version = sections
@@ -1223,7 +1259,21 @@ def parse(cls, location, package_only=False):
12231259

12241260
dependencies = data.get('dependencies') or {}
12251261
optional_dependencies = data.get('optionalDependencies') or {}
1226-
transitive_peer_dependencies = data.get('transitivePeerDependencies') or {}
1262+
transitive_peer_dependencies = data.get('transitivePeerDependencies') or []
1263+
1264+
if purl in dependency_relations_by_purl:
1265+
dependency_relations = dependency_relations_by_purl.get(purl)
1266+
if dependency_relations:
1267+
deps = dependency_relations.get('dependencies')
1268+
if deps:
1269+
dependencies.update(deps)
1270+
optional_deps = dependency_relations.get('optionalDependencies')
1271+
if optional_deps:
1272+
optional_dependencies.update(optional_deps)
1273+
transitive_peer_deps = dependency_relations.get('transitivePeerDependencies')
1274+
if transitive_peer_deps:
1275+
transitive_peer_dependencies.extend(transitive_peer_deps)
1276+
12271277
peer_dependencies = data.get('peerDependencies') or {}
12281278
peer_dependencies_meta = data.get('peerDependenciesMeta') or {}
12291279

0 commit comments

Comments
 (0)