From da4cca4b4c33c4a1779c708a9cfaaa6413536939 Mon Sep 17 00:00:00 2001
From: Tomas Tomecek <ttomecek@redhat.com>
Date: Aug 26 2017 13:46:03 +0000
Subject: [PATCH 1/7] nodejs.prov: account for bundled libraries


Signed-off-by: Tomas Tomecek <ttomecek@redhat.com>

---

diff --git a/nodejs.prov b/nodejs.prov
index 7c8dac2..ac4ac71 100755
--- a/nodejs.prov
+++ b/nodejs.prov
@@ -6,6 +6,7 @@ Automatic provides generator for Node.js libraries.
 Taken from package.json.  See `man npm-json` for details.
 """
 # Copyright 2012 T.C. Hollingsworth <tchollingsworth@gmail.com>
+# Copyright 2017 Tomas Tomecek <ttomecek@redhat.com>
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
 # of this software and associated documentation files (the "Software"), to
@@ -25,21 +26,51 @@ Taken from package.json.  See `man npm-json` for details.
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
 
-import json
+from __future__ import print_function
+
+import os
 import sys
+import json
+
+
+def handle_package_json(path, bundled=False):
+    """
+    process package.json file available on path, print RPM dependency based on name and version
+    """
+    if not path.endswith('package.json') or not os.path.isfile(path):
+        return
+    fh = open(path)
+    metadata = json.load(fh)
+    fh.close()
+
+    if 'name' in metadata and not ('private' in metadata and metadata['private']):
+        if bundled:
+            value = "bundled(nodejs-%s) = %s" % (metadata["name"], metadata["version"])
+        else:
+            value = "npm(%s) = %s" % (metadata["name"], metadata["version"])
+        print(value)
+
+
+def handle_module(path, bundled):
+    """
+    process npm module and all its bundled dependencies
+    """
+    handle_package_json(path, bundled=bundled)
+    if not os.path.isdir(path):
+        path = os.path.dirname(path)
+    node_modules_dir_candidate = os.path.join(path, "node_modules")
+    if os.path.isdir(node_modules_dir_candidate):
+        for module_path in os.listdir(node_modules_dir_candidate):
+            p_json_file = os.path.join(node_modules_dir_candidate, module_path, "package.json")
+            handle_module(p_json_file, bundled=True)
+
 
-paths = [path.rstrip() for path in sys.stdin.readlines()]
+def main():
+    paths = [path.rstrip() for path in sys.stdin.readlines()]
 
-for path in paths:
-    if path.endswith('package.json'):
-        fh = open(path)
-        metadata = json.load(fh)
-        fh.close()
+    for path in paths:
+        handle_module(path, bundled=False)
 
-        if 'name' in metadata and not ('private' in metadata and metadata['private']):
-            print 'npm(' + metadata['name'] + ')',
 
-            if 'version' in metadata:
-                print '= ' + metadata['version']
-            else:
-                print
+if __name__ == '__main__':
+    main()

From 1bd5ec9e26fddf0f0653317bf9a5c6ef3ebc65b7 Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Sep 10 2017 08:48:43 +0000
Subject: [PATCH 2/7] Add sorting and uniqueness for bundled provides


---

diff --git a/nodejs.prov b/nodejs.prov
index ac4ac71..1d06fbd 100755
--- a/nodejs.prov
+++ b/nodejs.prov
@@ -32,6 +32,7 @@ import os
 import sys
 import json
 
+provides = set()
 
 def handle_package_json(path, bundled=False):
     """
@@ -48,7 +49,7 @@ def handle_package_json(path, bundled=False):
             value = "bundled(nodejs-%s) = %s" % (metadata["name"], metadata["version"])
         else:
             value = "npm(%s) = %s" % (metadata["name"], metadata["version"])
-        print(value)
+        provides.add(value)
 
 
 def handle_module(path, bundled):
@@ -71,6 +72,9 @@ def main():
     for path in paths:
         handle_module(path, bundled=False)
 
+    for provide in sorted(provides):
+        print(provide)
+
 
 if __name__ == '__main__':
     main()

From 1ddf945027f2093d04af1b23fee38bc82f546674 Mon Sep 17 00:00:00 2001
From: Tomas Tomecek <ttomecek@redhat.com>
Date: Sep 10 2017 08:54:08 +0000
Subject: [PATCH 3/7] skip dep if name or version is not specified


Signed-off-by: Tomas Tomecek <ttomecek@redhat.com>

---

diff --git a/nodejs.prov b/nodejs.prov
index 1d06fbd..74b07ae 100755
--- a/nodejs.prov
+++ b/nodejs.prov
@@ -44,12 +44,26 @@ def handle_package_json(path, bundled=False):
     metadata = json.load(fh)
     fh.close()
 
-    if 'name' in metadata and not ('private' in metadata and metadata['private']):
-        if bundled:
-            value = "bundled(nodejs-%s) = %s" % (metadata["name"], metadata["version"])
-        else:
-            value = "npm(%s) = %s" % (metadata["name"], metadata["version"])
-        provides.add(value)
+    try:
+        if metadata['private']:
+            return
+    except KeyError:
+        pass
+
+    try:
+        name = metadata["name"]
+    except KeyError:
+        return
+    try:
+        version = metadata["version"]
+    except KeyError:
+        return
+
+    if bundled:
+        value = "bundled(nodejs-%s) = %s" % (name, version)
+    else:
+        value = "npm(%s) = %s" % (name, version)
+    provides.add(value)
 
 
 def handle_module(path, bundled):

From 97bf5dfd83446604c0bf7430b4af1e8a71bea5d1 Mon Sep 17 00:00:00 2001
From: Tomas Tomecek <ttomecek@redhat.com>
Date: Sep 10 2017 09:04:07 +0000
Subject: [PATCH 4/7] nodejs.prov: document input of the script


Signed-off-by: Tomas Tomecek <ttomecek@redhat.com>

---

diff --git a/nodejs.prov b/nodejs.prov
index 74b07ae..fd25f8a 100755
--- a/nodejs.prov
+++ b/nodejs.prov
@@ -81,6 +81,7 @@ def handle_module(path, bundled):
 
 
 def main():
+    """ read list of package.json paths from stdin """
     paths = [path.rstrip() for path in sys.stdin.readlines()]
 
     for path in paths:

From f378cabb787568f3c6d010da6b7791cad4e63d86 Mon Sep 17 00:00:00 2001
From: Tomas Tomecek <ttomecek@redhat.com>
Date: Sep 10 2017 10:37:12 +0000
Subject: [PATCH 5/7] nodejs.req: whitespace


Signed-off-by: Tomas Tomecek <ttomecek@redhat.com>

---

diff --git a/nodejs.req b/nodejs.req
index e4b1e30..f60def3 100755
--- a/nodejs.req
+++ b/nodejs.req
@@ -17,7 +17,7 @@ Parsed from package.json.  See `man npm-json` for details.
 #
 # 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
@@ -84,21 +84,21 @@ def main():
 
 def process_dep(req, version):
     """Converts an individual npm dependency into RPM dependencies"""
-    
+
     deps = []
-    
+
     #there's no way RPM can do anything like an OR dependency
     if '||' in version:
         sys.stderr.write("WARNING: The {0} dependency contains an ".format(req) +
             "OR (||) dependency: '{0}.\nPlease manually include ".format(version) +
             "a versioned dependency in your spec file if necessary")
         deps.append(req)
-            
+
     elif ' - ' in version:
         gt, lt = version.split(' - ')
         deps.append(req + ' >= ' + gt)
         deps.append(req + ' <= ' + lt)
-        
+
     else:
         m = re.match(RE_VERSION, version)
 
@@ -115,10 +115,10 @@ def process_dep(req, version):
             deps.append(req)
 
     return deps
-            
+
 def convert_dep(req, operator, version):
     """Converts one of the two possibly listed versions into an RPM dependency"""
-    
+
     deps = []
 
     #any version will do
@@ -164,14 +164,14 @@ def convert_dep(req, operator, version):
         elif operator == '~':
             deps.append('{0} >= {1}'.format(req, version))
             deps.append('{0} < {1}'.format(req, parts[0]+1))
-        
+
         #^1.2
         elif operator == '^':
             deps.append('{0} >= {1}'.format(req, version))
             deps.append('{0} < {1}'.format(req, parts[0]+1))
-            
+
 
     return deps
-            
+
 if __name__ == '__main__':
     main()

From 1d6bd8412cf0e4c73fdd843b600dbca880cc019a Mon Sep 17 00:00:00 2001
From: Tomas Tomecek <ttomecek@redhat.com>
Date: Sep 10 2017 10:51:05 +0000
Subject: [PATCH 6/7] nodejs.{prov,req}: skip if bundled module is a symlink


This commit accounts for 2 use cases:
* all modules in node_modules/ are symlinks -- nodejs.prov and .req
  correctly require appropriate rpm and don't print any of the modules as
  bundled
* modules in node_modules/ bundled -- nodejs.prov prints these modules
  as bundled

Signed-off-by: Tomas Tomecek <ttomecek@redhat.com>

---

diff --git a/nodejs.prov b/nodejs.prov
index fd25f8a..811e7cc 100755
--- a/nodejs.prov
+++ b/nodejs.prov
@@ -76,8 +76,11 @@ def handle_module(path, bundled):
     node_modules_dir_candidate = os.path.join(path, "node_modules")
     if os.path.isdir(node_modules_dir_candidate):
         for module_path in os.listdir(node_modules_dir_candidate):
-            p_json_file = os.path.join(node_modules_dir_candidate, module_path, "package.json")
-            handle_module(p_json_file, bundled=True)
+            module_abs_path = os.path.join(node_modules_dir_candidate, module_path)
+            # skip modules which are linked against system module
+            if not os.path.islink(module_abs_path):
+                p_json_file = os.path.join(module_abs_path, "package.json")
+                handle_module(p_json_file, bundled=True)
 
 
 def main():
diff --git a/nodejs.req b/nodejs.req
index f60def3..aa36bcf 100755
--- a/nodejs.req
+++ b/nodejs.req
@@ -28,11 +28,23 @@ Parsed from package.json.  See `man npm-json` for details.
 
 from __future__ import unicode_literals
 import json
+import os
 import re
 import sys
 
 RE_VERSION = re.compile(r'\s*v?([<>=~^]{0,2})\s*([0-9][0-9\.\-]*)\s*')
 
+def has_all_bundled(path):
+    # remove 'package.json'
+    path = os.path.dirname(path)
+    node_modules_dir_candidate = os.path.join(path, "node_modules")
+    if os.path.isdir(node_modules_dir_candidate):
+        modules_abs_path = map(lambda x: os.path.join(node_modules_dir_candidate, x),
+                               os.listdir(node_modules_dir_candidate))
+        any_link = any([os.path.islink(x) for x in modules_abs_path])
+        return not any_link
+
+
 def main():
     #npm2rpm uses functions here to write BuildRequires so don't print anything
     #until the very end
@@ -45,7 +57,10 @@ def main():
     for path in paths:
         if not path.endswith('package.json'):
             continue
-        
+
+        if has_all_bundled(path):
+            continue
+
         fh = open(path)
         metadata = json.load(fh)
         fh.close()

From af7f4fa9aac78baf6dc82f93aadbef3dcd4f2a09 Mon Sep 17 00:00:00 2001
From: Tomas Tomecek <ttomecek@redhat.com>
Date: Sep 10 2017 10:53:13 +0000
Subject: [PATCH 7/7] nodejs.req: use python 3 syntax


Signed-off-by: Tomas Tomecek <ttomecek@redhat.com>

---

diff --git a/nodejs.req b/nodejs.req
index aa36bcf..c89a2cd 100755
--- a/nodejs.req
+++ b/nodejs.req
@@ -26,7 +26,7 @@ Parsed from package.json.  See `man npm-json` for details.
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
 
-from __future__ import unicode_literals
+from __future__ import unicode_literals, print_function
 import json
 import os
 import re
@@ -95,7 +95,7 @@ def main():
             else:
                 raise TypeError('invalid package.json: dependencies not a valid type')
 
-    print '\n'.join(deps)
+    print('\n'.join(deps))
 
 def process_dep(req, version):
     """Converts an individual npm dependency into RPM dependencies"""

