3
3
require "json"
4
4
5
5
class Importmap ::Npm
6
+ PIN_REGEX = /^pin ["']([^["']]*)["'].*/
7
+
6
8
Error = Class . new ( StandardError )
7
9
HTTPError = Class . new ( Error )
8
10
9
11
singleton_class . attr_accessor :base_uri
10
12
self . base_uri = URI ( "https://registry.npmjs.org" )
11
13
12
- def initialize ( importmap_path = "config/importmap.rb" )
14
+ def initialize ( importmap_path = "config/importmap.rb" , vendor_path : "vendor/javascript" )
13
15
@importmap_path = Pathname . new ( importmap_path )
16
+ @vendor_path = Pathname . new ( vendor_path )
14
17
end
15
18
16
19
def outdated_packages
17
20
packages_with_versions . each . with_object ( [ ] ) do |( package , current_version ) , outdated_packages |
18
- outdated_package = OutdatedPackage . new ( name : package ,
19
- current_version : current_version )
21
+ outdated_package = OutdatedPackage . new ( name : package , current_version : current_version )
20
22
21
23
if !( response = get_package ( package ) )
22
24
outdated_package . error = 'Response error'
@@ -36,28 +38,33 @@ def outdated_packages
36
38
def vulnerable_packages
37
39
get_audit . flat_map do |package , vulnerabilities |
38
40
vulnerabilities . map do |vulnerability |
39
- VulnerablePackage . new ( name : package ,
40
- severity : vulnerability [ 'severity' ] ,
41
- vulnerable_versions : vulnerability [ 'vulnerable_versions' ] ,
42
- vulnerability : vulnerability [ 'title' ] )
41
+ VulnerablePackage . new (
42
+ name : package ,
43
+ severity : vulnerability [ 'severity' ] ,
44
+ vulnerable_versions : vulnerability [ 'vulnerable_versions' ] ,
45
+ vulnerability : vulnerability [ 'title' ]
46
+ )
43
47
end
44
48
end . sort_by { |p | [ p . name , p . severity ] }
45
49
end
46
50
47
51
def packages_with_versions
48
52
# We cannot use the name after "pin" because some dependencies are loaded from inside packages
49
53
# Eg. pin "buffer", to: "https://ga.jspm.io/npm:@jspm/[email protected] /nodelibs/browser/buffer.js"
54
+ with_versions = importmap . scan ( /^pin .*(?<=npm:|npm\/ |skypack\. dev\/ |unpkg\. com\/ )(.*)(?=@\d +\. \d +\. \d +)@(\d +\. \d +\. \d +(?:[^\/ \s ["']]*)).*$/ ) |
55
+ importmap . scan ( /#{ PIN_REGEX } #.*@(\d +\. \d +\. \d +(?:[^\s ]*)).*$/ )
56
+
57
+ vendored_packages_without_version ( with_versions ) . each do |package , path |
58
+ $stdout. puts "Ignoring #{ package } (#{ path } ) since no version is specified in the importmap"
59
+ end
50
60
51
- importmap . scan ( /^pin .*(?<=npm:|npm\/ |skypack\. dev\/ |unpkg\. com\/ )(.*)(?=@\d +\. \d +\. \d +)@(\d +\. \d +\. \d +(?:[^\/ \s ["']]*)).*$/ ) |
52
- importmap . scan ( /^pin ["']([^["']]*)["'].* #.*@(\d +\. \d +\. \d +(?:[^\s ]*)).*$/ )
61
+ with_versions
53
62
end
54
63
55
64
private
56
65
OutdatedPackage = Struct . new ( :name , :current_version , :latest_version , :error , keyword_init : true )
57
66
VulnerablePackage = Struct . new ( :name , :severity , :vulnerable_versions , :vulnerability , keyword_init : true )
58
67
59
-
60
-
61
68
def importmap
62
69
@importmap ||= File . read ( @importmap_path )
63
70
end
@@ -130,4 +137,27 @@ def post_json(uri, body)
130
137
rescue => error
131
138
raise HTTPError , "Unexpected transport error (#{ error . class } : #{ error . message } )"
132
139
end
140
+
141
+ def vendored_packages_without_version ( packages_with_versions )
142
+ versioned_packages = packages_with_versions . map ( &:first ) . to_set
143
+
144
+ importmap
145
+ . lines
146
+ . filter_map { |line | find_unversioned_vendored_package ( line , versioned_packages ) }
147
+ end
148
+
149
+ def find_unversioned_vendored_package ( line , versioned_packages )
150
+ regexp = line . include? ( "to:" ) ? /#{ PIN_REGEX } to: ["']([^["']]*)["'].*/ : PIN_REGEX
151
+ match = line . match ( regexp )
152
+
153
+ return unless match
154
+
155
+ package , filename = match . captures
156
+ filename ||= "#{ package } .js"
157
+
158
+ return if versioned_packages . include? ( package )
159
+
160
+ path = File . join ( @vendor_path , filename )
161
+ [ package , path ] if File . exist? ( path )
162
+ end
133
163
end
0 commit comments