blob: 4a4beb6ad62933d71d2ef9188b73dfe815566bd6 [file]
"""Helper functions for checking npm package visibility rules."""
_PACKAGE_VISIBILITY_ERROR_TEMPLATE = """
Package visibility violation:
Package: {}
Requested by: {}
This package is not visible from {}.
Check the package_visibility configuration in your npm_translate_lock rule.
For more information, see: https://registry.bazel.build/docs/aspect_rules_js#module_extension-translate_lock
"""
def validate_npm_package_visibility(accessing_package, package_locations, visibility_config):
"""Validate that accessing_package can access all packages available at its location.
Args:
accessing_package: The bazel package trying to access npm packages
package_locations: Dictionary mapping locations to lists of package names available at each location
visibility_config: Dictionary mapping package names/patterns to visibility rules
"""
# Direct lookup of packages available at this location
packages_at_location = package_locations.get(accessing_package, [])
# Validate each package
for package_name in packages_at_location:
if not check_package_visibility(accessing_package, package_name, visibility_config):
fail(_PACKAGE_VISIBILITY_ERROR_TEMPLATE.format(package_name, accessing_package, accessing_package))
def check_package_visibility(accessing_package, package_name, visibility_config):
"""Check if accessing_package can access package_name based on visibility_config.
Args:
accessing_package: The bazel package trying to access the npm package
package_name: The name of the npm package being accessed
visibility_config: Dictionary mapping package names/patterns to visibility rules
Returns:
True if access is allowed, False otherwise
"""
# Get visibility rules for this package
visibility_rules = []
if package_name in visibility_config:
visibility_rules = visibility_config[package_name]
elif "*" in visibility_config:
visibility_rules = visibility_config["*"]
else:
# Default to public if not specified
return True
# Check each visibility rule
for rule in visibility_rules:
if rule == "//visibility:public":
return True
# Package-specific access: //packages/foo:__pkg__
if rule.endswith(":__pkg__") and rule.startswith("//"):
# Extract package part: //some/package:__pkg__ -> some/package
if rule[2:-8] == accessing_package:
return True
# Subpackage access: //packages/foo:__subpackages__
if rule.endswith(":__subpackages__"):
rule_package = rule[2:-16] # Remove "//" and ":__subpackages__"
if accessing_package.startswith(rule_package + "/") or accessing_package == rule_package:
return True
# Target-specific access: //packages/foo:target
if rule.startswith("//"):
colon_pos = rule.find(":", 2) # Find first colon after "//"
if colon_pos > 2 and rule[2:colon_pos] == accessing_package:
return True
return False