Blame SOURCES/list_bundled_nodejs_packages.py

3d91f6
#!/usr/bin/env python3
3d91f6
#
3d91f6
# generates Provides: bundled(npm(...)) = ... lines for each declared dependency and devDependency of package.json
3d91f6
#
91b47b
import os
3d91f6
import sys
3d91f6
import json
05d305
import yaml
3d91f6
from packaging import version
3d91f6
3d91f6
91b47b
def scan_package_json(package_dir):
91b47b
    for root, dirs, files in os.walk(package_dir, topdown=True):
91b47b
        dirs[:] = [d for d in dirs if d not in ["node_modules", "vendor"]]
91b47b
        if "package.json" in files:
91b47b
            yield os.path.join(root, "package.json")
91b47b
91b47b
3d91f6
def read_declared_pkgs(package_json_path):
3d91f6
    with open(package_json_path) as f:
3d91f6
        package_json = json.load(f)
91b47b
        return list(package_json.get("dependencies", {}).keys()) + list(
91b47b
            package_json.get("devDependencies", {}).keys()
91b47b
        )
3d91f6
3d91f6
3d91f6
def read_installed_pkgs(yarn_lock_path):
3d91f6
    with open(yarn_lock_path) as f:
05d305
        lockfile = yaml.safe_load(f)
05d305
        for pkg_decl, meta in lockfile.items():
05d305
            for pkg in pkg_decl.split(", "):
05d305
                if ":" not in pkg:
05d305
                    continue
05d305
                pkg_name = pkg[: pkg.index("@", 1)]
05d305
                pkg_version = meta["version"]
05d305
                yield (pkg_name, pkg_version)
3d91f6
3d91f6
3d91f6
def list_provides(declared_pkgs, installed_pkgs):
3d91f6
    for declared_pkg in declared_pkgs:
3d91f6
        # there can be multiple versions installed of one package (transitive dependencies)
3d91f6
        # but rpm doesn't support Provides: with a single package and multiple versions
3d91f6
        # so let's declare the oldest version here
91b47b
        versions = [
91b47b
            version.parse(pkg_version)
91b47b
            for pkg_name, pkg_version in installed_pkgs
91b47b
            if pkg_name == declared_pkg
91b47b
        ]
91b47b
91b47b
        if not versions:
91b47b
            print(f"warning: {declared_pkg} missing in yarn.lock", file=sys.stderr)
91b47b
            continue
91b47b
3d91f6
        oldest_version = sorted(versions)[0]
3d91f6
        yield f"Provides: bundled(npm({declared_pkg})) = {oldest_version}"
3d91f6
3d91f6
3d91f6
if __name__ == "__main__":
3d91f6
    if len(sys.argv) != 2:
3d91f6
        print(f"usage: {sys.argv[0]} package-X.Y.Z/", file=sys.stdout)
3d91f6
        sys.exit(1)
3d91f6
3d91f6
    package_dir = sys.argv[1]
05d305
    declared_pkgs = set()
91b47b
    for package_json_path in scan_package_json(package_dir):
05d305
        declared_pkgs.update(read_declared_pkgs(package_json_path))
05d305
    installed_pkgs = list(read_installed_pkgs(f"{package_dir}/yarn.lock"))
3d91f6
    provides = list_provides(declared_pkgs, installed_pkgs)
3d91f6
    for provide in sorted(provides):
3d91f6
        print(provide)