Blame SOURCES/nodejs.prov

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