Skip to content

Commit 6d43623

Browse files
committed
refactor: validate parsed pnpm-lock file/link: references
1 parent ec41261 commit 6d43623

File tree

1 file changed

+51
-20
lines changed

1 file changed

+51
-20
lines changed

npm/private/pnpm.bzl

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,32 @@ def _convert_pnpm_v6_v9_version_peer_dep(version):
7373

7474
######################### Lockfile v9 #########################
7575

76-
def _convert_pnpm_v9_package_dependency_version(snapshots, name, version):
76+
def _v9_snapshot_key_to_package_key(snapshot_key):
77+
peer_meta_index = snapshot_key.find("(")
78+
package_key = snapshot_key[:peer_meta_index] if peer_meta_index > 0 else snapshot_key
79+
return package_key
80+
81+
def _v9_resolve_link_version(packages, snapshot_key, name, link):
82+
package_key = _v9_snapshot_key_to_package_key(snapshot_key)
83+
package = packages[package_key]
84+
resolution = package["resolution"]
85+
86+
# :link dep from file:package will be relative to the file:package and not the workspace root
87+
if resolution.get("type", None) == "directory":
88+
# ... unless that link: dep is resolved from a peerDependency, then it is already resolved to workspace-relative
89+
if "peerDependencies" in package and name in package["peerDependencies"]:
90+
return link
91+
92+
return paths.normalize(paths.join(resolution["directory"], link))
93+
94+
# in the standard case snapshot link: deps are already relative to the workspace root
95+
return link
96+
97+
def _convert_pnpm_v9_package_dependency_version(packages, snapshots, snapshot_key, name, version):
98+
if version.startswith("link:"):
99+
# Resolve link: deps to be workspace root relative
100+
version = "link:" + _v9_resolve_link_version(packages, snapshot_key, name, version[5:])
101+
77102
# Detect when an alias is just a direct reference to another snapshot
78103
is_alias = version in snapshots
79104

@@ -82,10 +107,10 @@ def _convert_pnpm_v9_package_dependency_version(snapshots, name, version):
82107

83108
return "npm:{}".format(version) if is_alias else version
84109

85-
def _convert_pnpm_v9_package_dependency_map(snapshots, deps):
110+
def _convert_pnpm_v9_package_dependency_map(packages, snapshots, snapshot_key, deps):
86111
result = {}
87112
for name, version in deps.items():
88-
result[name] = _convert_pnpm_v9_package_dependency_version(snapshots, name, version)
113+
result[name] = _convert_pnpm_v9_package_dependency_version(packages, snapshots, snapshot_key, name, version)
89114
return result
90115

91116
def _convert_pnpm_v9_importer_dependency_map(import_path, deps):
@@ -156,9 +181,8 @@ def _convert_v9_packages(packages, snapshots):
156181
result = {}
157182

158183
# Snapshots contains the packages with the keys (which include peers) to return
159-
for package_key, package_snapshot in snapshots.items():
160-
peer_meta_index = package_key.find("(")
161-
static_key = package_key[:peer_meta_index] if peer_meta_index > 0 else package_key
184+
for snapshot_key, package_snapshot in snapshots.items():
185+
static_key = _v9_snapshot_key_to_package_key(snapshot_key)
162186
if not static_key in packages:
163187
msg = "package {} not found in pnpm 'packages'".format(static_key)
164188
fail(msg)
@@ -173,7 +197,7 @@ def _convert_v9_packages(packages, snapshots):
173197
version_index = static_key.index("@", 1)
174198
name = static_key[:version_index]
175199

176-
package_key = _convert_pnpm_v6_v9_version_peer_dep(package_key)
200+
package_key = _convert_pnpm_v6_v9_version_peer_dep(snapshot_key)
177201

178202
# Extract the version including peerDeps+patch from the key
179203
version = _convert_pnpm_v6_v9_version_peer_dep(package_key[package_key.index("@", 1) + 1:])
@@ -185,8 +209,8 @@ def _convert_v9_packages(packages, snapshots):
185209
name = name,
186210
version = version,
187211
friendly_version = friendly_version,
188-
dependencies = _convert_pnpm_v9_package_dependency_map(snapshots, package_snapshot.get("dependencies", {})),
189-
optional_dependencies = _convert_pnpm_v9_package_dependency_map(snapshots, package_snapshot.get("optionalDependencies", {})),
212+
dependencies = _convert_pnpm_v9_package_dependency_map(packages, snapshots, snapshot_key, package_snapshot.get("dependencies", {})),
213+
optional_dependencies = _convert_pnpm_v9_package_dependency_map(packages, snapshots, snapshot_key, package_snapshot.get("optionalDependencies", {})),
190214
has_bin = package_data.get("hasBin", False),
191215
optional = package_snapshot.get("optional", False),
192216
resolution = package_data["resolution"],
@@ -258,31 +282,38 @@ def _parse_lockfile(parsed, err):
258282

259283
def _validate_lockfile_data(importers, packages):
260284
for name, deps in importers.items():
261-
_validate_lockfile_deps(packages, "importer", name, deps["dependencies"])
262-
_validate_lockfile_deps(packages, "importer", name, deps["dev_dependencies"])
263-
_validate_lockfile_deps(packages, "importer", name, deps["optional_dependencies"])
285+
_validate_lockfile_deps(importers, packages, "importer", name, deps["dependencies"])
286+
_validate_lockfile_deps(importers, packages, "importer", name, deps["dev_dependencies"])
287+
_validate_lockfile_deps(importers, packages, "importer", name, deps["optional_dependencies"])
264288

265289
for name, info in packages.items():
266-
_validate_lockfile_deps(packages, "package", name, info["dependencies"])
267-
_validate_lockfile_deps(packages, "package", name, info["optional_dependencies"])
290+
_validate_lockfile_deps(importers, packages, "package", name, info["dependencies"])
291+
_validate_lockfile_deps(importers, packages, "package", name, info["optional_dependencies"])
268292

269-
def _validate_lockfile_deps(packages, importer_type, importer, deps):
293+
def _validate_lockfile_deps(importers, packages, importer_type, importer, deps):
270294
for dep, version in deps.items():
271295
if version.startswith("npm:"):
272296
version = version[4:]
273297

274-
if version not in packages and not (version.startswith("file:") or version.startswith("link:")) and not ("{}@{}".format(dep, version) in packages):
298+
if version.startswith("link:"):
299+
if version[5:] not in importers:
300+
msg = "ERROR: {} '{}' depends on package '{}' at link path '{}' which is not in the importers: {}".format(
301+
importer_type,
302+
importer,
303+
dep,
304+
version,
305+
importers.keys(),
306+
)
307+
fail(msg)
308+
elif version not in packages and not ("{}@{}".format(dep, version) in packages):
275309
msg = "ERROR: {} '{}' depends on package '{}' at version '{}' which is not in the packages: {}".format(
276310
importer_type,
277311
importer,
278312
dep,
279313
version,
280314
packages.keys(),
281315
)
282-
283-
# TODO(3.0): fail instead of print
284-
# buildifier: disable=print
285-
print(msg)
316+
fail(msg)
286317

287318
def _assert_lockfile_version(version, testonly = False):
288319
if type(version) != type(1.0):

0 commit comments

Comments
 (0)