blob: 25d90501a5d3b9b076116cf5bfb51b9daf07c035 [file]
"Utility functions for npm rules"
load("@aspect_bazel_lib//lib:paths.bzl", "relative_file")
load(":yaml.bzl", _parse_yaml = "parse")
def _bazel_name(name, version = None):
"Make a bazel friendly name from a package name and (optionally) a version that can be used in repository and target names"
escaped_name = name.replace("@", "at_").replace("/", "_")
if not version:
return escaped_name
version_segments = version.split("_")
escaped_version = version_segments[0].replace("@", "at_").replace("/", "_")
peer_version = "_".join(version_segments[1:])
if peer_version.startswith("@"):
peer_version = "at_" + peer_version[1:]
if peer_version:
escaped_version = "%s__%s" % (escaped_version, peer_version.replace("/", "_").replace("@", "_").replace("+", "_"))
return "%s__%s" % (escaped_name, escaped_version)
def _strip_peer_dep_version(version):
"Remove peer dependency syntax from version string"
# 21.1.0_rollup@2.70.2 becomes 21.1.0
index = version.find("_")
if index != -1:
return version[:index]
return version
def _pnpm_name(name, version):
"Make a name/version pnpm-style name for a package name and version"
return "%s/%s" % (name, version)
def _parse_pnpm_name(pnpmName):
# Parse a name/version or @scope/name/version string and return
# a [name, version] list
segments = pnpmName.rsplit("/", 1)
if len(segments) != 2:
fail("unexpected pnpm versioned name " + pnpmName)
return (segments[0], segments[1])
def _parse_pnpm_lock(lockfile_content):
"""Parse a pnpm lock file.
Args:
lockfile_content: yaml lockfile content
Returns:
dict containing parsed lockfile
"""
return _parse_yaml(lockfile_content)
def _assert_lockfile_version(version, testonly = False):
if type(version) != type(1.0):
fail("version should be passed as a float")
# Restrict the supported lock file versions to what this code has been tested with:
# 5.3 - pnpm v6.x.x
# 5.4 - pnpm v7.0.0 bumped the lockfile version to 5.4
min_lock_version = 5.3
max_lock_version = 5.4
msg = None
if version < min_lock_version:
msg = "npm_translate_lock requires lock_version at least {min}, but found {actual}. Please upgrade to pnpm v6 or greater.".format(
min = min_lock_version,
actual = version,
)
if version > max_lock_version:
msg = "npm_translate_lock currently supports a maximum lock_version of {max}, but found {actual}. Please file an issue on rules_js".format(
max = max_lock_version,
actual = version,
)
if msg and not testonly:
fail(msg)
return msg
def _friendly_name(name, version):
"Make a name@version developer-friendly name for a package name and version"
return "%s@%s" % (name, version)
def _virtual_store_name(name, version):
"Make a virtual store name for a given package and version"
escaped_name = name.replace("/", "+")
escaped_version = version.replace("/", "+")
return "%s@%s" % (escaped_name, escaped_version)
def _make_symlink(ctx, symlink_path, target_file):
files = []
if ctx.attr.use_declare_symlink:
symlink = ctx.actions.declare_symlink(symlink_path)
ctx.actions.symlink(
output = symlink,
target_path = relative_file(target_file.path, symlink.path),
)
files.append(target_file)
else:
symlink = ctx.actions.declare_file(symlink_path)
ctx.actions.symlink(
output = symlink,
target_file = target_file,
)
files.append(symlink)
return files
utils = struct(
bazel_name = _bazel_name,
pnpm_name = _pnpm_name,
assert_lockfile_version = _assert_lockfile_version,
parse_pnpm_name = _parse_pnpm_name,
parse_pnpm_lock = _parse_pnpm_lock,
friendly_name = _friendly_name,
virtual_store_name = _virtual_store_name,
strip_peer_dep_version = _strip_peer_dep_version,
make_symlink = _make_symlink,
# Symlinked node_modules structure virtual store path under node_modules
virtual_store_root = ".aspect_rules_js",
# Suffix for npm_import links repository
links_repo_suffix = "__links",
# Output group name for the package directory of a linked package
package_directory_output_group = "package_directory",
)