blob: f07b18990ccaf19301f259c8b1211bf11027e721 [file] [log] [blame]
"""pnpm extension logic (the extension itself is in npm/extensions.bzl)."""
load("@aspect_bazel_lib//lib:lists.bzl", "unique")
load(":pnpm_repository.bzl", "DEFAULT_PNPM_VERSION", "LATEST_PNPM_VERSION")
DEFAULT_PNPM_REPO_NAME = "pnpm"
# copied from https://github.com/bazelbuild/bazel-skylib/blob/b459822483e05da514b539578f81eeb8a705d600/lib/versions.bzl#L60
# to avoid taking a dependency on skylib here
def _parse_version(version):
return tuple([int(n) for n in version.split(".")])
def resolve_pnpm_repositories(mctx):
"""Resolves pnpm tags in all `modules`
Args:
mctx: module context
Returns:
A struct with the following fields:
- `repositories`: dict (name -> pnpm_version) to invoke `pnpm_repository` with.
- `notes`: list of notes to print to the user.
"""
registrations = {}
integrity = {}
result = struct(
notes = [],
repositories = {},
)
for mod in mctx.modules:
for attr in mod.tags.pnpm:
if attr.name != DEFAULT_PNPM_REPO_NAME and not mod.is_root:
fail("""\
Only the root module may override the default name for the pnpm repository.
This prevents conflicting registrations in the global namespace of external repos.
""")
if not registrations.get(attr.name, False):
registrations[attr.name] = []
if attr.pnpm_version_from and attr.pnpm_version and attr.pnpm_version != DEFAULT_PNPM_VERSION:
fail("Cannot specify both pnpm_version = {} and pnpm_version_from = {}".format(attr.pnpm_version, attr.pnpm_version_from))
# Handle pnpm_version_from by reading package.json
if attr.pnpm_version_from != None:
# Read package.json and extract packageManager field
package_json_content = mctx.read(attr.pnpm_version_from)
package_json = json.decode(package_json_content)
if "packageManager" not in package_json:
fail("packageManager field not found in package.json file: " + str(attr.pnpm_version_from))
package_manager = package_json["packageManager"]
if not package_manager.startswith("pnpm@"):
fail("packageManager field must specify pnpm, got: " + package_manager)
# Extract version from "pnpm@8.15.9" format
v = package_manager[5:] # Remove "pnpm@" prefix
elif attr.pnpm_version == "latest":
v = LATEST_PNPM_VERSION
else:
v = attr.pnpm_version
# Avoid inserting the default version from a non-root module
# (likely rules_js itself) if the root module already has a version.
if mod.is_root or len(registrations[attr.name]) == 0:
registrations[attr.name].append(v)
if attr.pnpm_version_integrity:
integrity[attr.pnpm_version] = attr.pnpm_version_integrity
for name, version_list in registrations.items():
# Disregard repeated version numbers
versions = unique(version_list)
# Use "Minimal Version Selection" like bzlmod does for resolving module conflicts
# Note, the 'sorted(list)' function in starlark doesn't allow us to provide a custom comparator
if len(versions) > 1:
selected = versions[0]
selected_tuple = _parse_version(selected)
for idx in range(1, len(versions)):
if _parse_version(versions[idx]) > selected_tuple:
selected = versions[idx]
selected_tuple = _parse_version(selected)
result.notes.append("NOTE: repo '{}' has multiple versions {}; selected {}".format(name, versions, selected))
else:
selected = versions[0]
result.repositories[name] = (selected, integrity[selected]) if selected in integrity.keys() else selected
return result