#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright 2012 T.C. Hollingsworth # Copyright 2017 Tomas Tomecek # Copyright 2019 Jan Stanek # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. """Automatic provides generator for Node.js libraries. Metadata taken from package.json. See `man npm-json` for details. """ from __future__ import print_function, with_statement import json import os import sys from itertools import chain, groupby DEPENDENCY_TEMPLATE = "rh-nodejs14-npm(%(name)s) = %(version)s" BUNDLED_TEMPLATE = "bundled(nodejs-%(name)s) = %(version)s" NODE_MODULES = {"node_modules", "node_modules.bundled"} class PrivatePackage(RuntimeError): """Private package metadata that should not be listed.""" #: Something is wrong with the ``package.json`` file _INVALID_METADATA_FILE = (IOError, PrivatePackage, KeyError) def format_metadata(metadata, bundled=False): """Format ``package.json``-like metadata into RPM dependency. Arguments: metadata (dict): Package metadata, presumably read from ``package.json``. bundled (bool): Should the bundled dependency format be used? Returns: str: RPM dependency (i.e. ``npm(example) = 1.0.0``) Raises: KeyError: Expected key (i.e. ``name``, ``version``) missing in metadata. PrivatePackage: The metadata indicate private (unlisted) package. """ # Skip private packages if metadata.get("private", False): raise PrivatePackage(metadata) template = BUNDLED_TEMPLATE if bundled else DEPENDENCY_TEMPLATE return template % metadata def generate_dependencies(module_path, module_dir_set=NODE_MODULES): """Generate RPM dependency for a module and all it's dependencies. Arguments: module_path (str): Path to a module directory or it's ``package.json`` module_dir_set (set): Base names of directories to look into for bundled dependencies. Yields: str: RPM dependency for the module and each of it's (public) bundled dependencies. Raises: ValueError: module_path is not valid module or ``package.json`` file """ # Determine paths to root module directory and package.json if os.path.isdir(module_path): root_dir = module_path elif os.path.basename(module_path) == "package.json": root_dir = os.path.dirname(module_path) else: # Invalid metadata path raise ValueError("Invalid module path '%s'" % module_path) for dir_path, subdir_list, file_list in os.walk(root_dir): # We are only interested in directories that contain package.json if "package.json" not in file_list: continue # Read and format metadata metadata_path = os.path.join(dir_path, "package.json") bundled = dir_path != root_dir try: with open(metadata_path, mode="r") as metadata_file: metadata = json.load(metadata_file) yield format_metadata(metadata, bundled=bundled) except _INVALID_METADATA_FILE: pass # Ignore # Only visit subdirectories in module_dir_set subdir_list[:] = list(module_dir_set & set(subdir_list)) if __name__ == "__main__": module_paths = (path.strip() for path in sys.stdin) provides = chain.from_iterable(generate_dependencies(m) for m in module_paths) # sort|uniq for provide, __ in groupby(sorted(provides)): print(provide)