Skip to content

Commit e2b104b

Browse files
committed
Download all listed dependency packages
1 parent ddf9be4 commit e2b104b

6 files changed

+274
-125
lines changed

lib/importmap/commands.rb

+7-14
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,9 @@ def self.exit_on_failure?
1414
option :from, type: :string, aliases: :f, default: "jspm"
1515
def pin(*packages)
1616
if imports = packager.import(*packages, env: options[:env], from: options[:from])
17-
imports.each do |package, url|
18-
puts %(Pinning "#{package}" to #{packager.vendor_path}/#{package}.js via download from #{url})
19-
packager.download(package, url)
20-
pin = packager.vendored_pin_for(package, url)
21-
22-
if packager.packaged?(package)
23-
gsub_file("config/importmap.rb", /^pin "#{package}".*$/, pin, verbose: false)
24-
else
25-
append_to_file("config/importmap.rb", "#{pin}\n", verbose: false)
26-
end
17+
imports.each do |package|
18+
puts %(Pinning "#{package.package_name}" to #{packager.vendor_path}/#{package.vendored_package_folder} via download from #{package.base_url})
19+
package.download
2720
end
2821
else
2922
puts "Couldn't find any packages in #{packages.inspect} on #{options[:from]}"
@@ -35,10 +28,10 @@ def pin(*packages)
3528
option :from, type: :string, aliases: :f, default: "jspm"
3629
def unpin(*packages)
3730
if imports = packager.import(*packages, env: options[:env], from: options[:from])
38-
imports.each do |package, url|
39-
if packager.packaged?(package)
40-
puts %(Unpinning and removing "#{package}")
41-
packager.remove(package)
31+
imports.each do |package|
32+
if packager.packaged?(package.package_name)
33+
puts %(Unpinning and removing "#{package.package_name}")
34+
package.remove
4235
end
4336
end
4437
else

lib/importmap/package.rb

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
class Importmap::Package
2+
attr_reader :base_url, :main_url, :package_name
3+
4+
def initialize(
5+
unfiltered_dependencies:,
6+
package_name:,
7+
main_url:,
8+
packager:
9+
)
10+
@unfiltered_dependencies = unfiltered_dependencies
11+
@package_name = package_name
12+
@main_url = main_url
13+
@packager = packager
14+
15+
@base_url = extract_base_url_from(main_url)
16+
17+
dependencies = unfiltered_dependencies.select { _1.start_with?(base_url) }
18+
@dependency_files = dependencies.map { _1[(base_url.size + 1)..] } # @main_file is included in this list
19+
20+
@main_file = main_url[(base_url.size + 1)..]
21+
end
22+
23+
def download
24+
@packager.ensure_vendor_directory_exists
25+
remove_existing_package_files
26+
27+
@dependency_files.each do |file|
28+
download_file(file)
29+
end
30+
31+
@packager.pin_package_in_importmap(@package_name, vendored_pin)
32+
end
33+
34+
def remove
35+
remove_existing_package_files
36+
@packager.remove_package_from_importmap(@package_name)
37+
end
38+
39+
def vendored_pin
40+
filename = vendored_package_path_for_file(@main_file)
41+
version = extract_package_version_from(@main_url)
42+
43+
%(pin "#{package_name}", to: "#{filename}" # #{version})
44+
end
45+
46+
def vendored_package_folder
47+
@packager.vendor_path.join(folder_name)
48+
end
49+
50+
private
51+
def download_file(file)
52+
response = Net::HTTP.get_response(URI("#{base_url}/#{file}"))
53+
54+
if response.code == "200"
55+
save_vendored_file(file, response.body)
56+
else
57+
handle_failure_response(response)
58+
end
59+
end
60+
61+
def save_vendored_file(file, source)
62+
url = "#{base_url}/#{file}"
63+
file_name = vendored_package_path_for_file(file)
64+
ensure_parent_directories_exist_for(file_name)
65+
File.open(file_name, "w+") do |vendored_file|
66+
vendored_file.write "// #{package_name}#{extract_package_version_from(url)}/#{file} downloaded from #{url}\n\n"
67+
68+
vendored_file.write remove_sourcemap_comment_from(source).force_encoding("UTF-8")
69+
end
70+
end
71+
72+
def ensure_parent_directories_exist_for(file)
73+
dir_name = File.dirname(file)
74+
75+
unless File.directory?(dir_name)
76+
FileUtils.mkdir_p(dir_name)
77+
end
78+
end
79+
80+
def remove_sourcemap_comment_from(source)
81+
source.gsub(/^\/\/# sourceMappingURL=.*/, "")
82+
end
83+
84+
def vendored_package_path_for_file(file)
85+
vendored_package_folder.join(file)
86+
end
87+
88+
def handle_failure_response(response)
89+
if error_message = parse_service_error(response)
90+
raise ServiceError, error_message
91+
else
92+
raise HTTPError, "Unexpected response code (#{response.code})"
93+
end
94+
end
95+
96+
def parse_service_error(response)
97+
JSON.parse(response.body.to_s)["error"]
98+
rescue JSON::ParserError
99+
nil
100+
end
101+
102+
def remove_existing_package_files
103+
FileUtils.rm_rf vendored_package_folder
104+
end
105+
106+
def folder_name
107+
@package_name.gsub("/", "--")
108+
end
109+
110+
def extract_base_url_from(url)
111+
url.match(/^.+@\d+\.\d+\.\d+/)&.to_a&.first
112+
end
113+
114+
def extract_package_version_from(url)
115+
url.match(/@\d+\.\d+\.\d+/)&.to_a&.first
116+
end
117+
end

lib/importmap/packager.rb

+33-74
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require "net/http"
22
require "uri"
33
require "json"
4+
require "importmap/package"
45

56
class Importmap::Packager
67
Error = Class.new(StandardError)
@@ -10,7 +11,7 @@ class Importmap::Packager
1011
singleton_class.attr_accessor :endpoint
1112
self.endpoint = URI("https://api.jspm.io/generate")
1213

13-
attr_reader :vendor_path
14+
attr_reader :vendor_path, :importmap_path
1415

1516
def initialize(importmap_path = "config/importmap.rb", vendor_path: "vendor/javascript")
1617
@importmap_path = Pathname.new(importmap_path)
@@ -32,34 +33,29 @@ def import(*packages, env: "production", from: "jspm")
3233
end
3334
end
3435

35-
def pin_for(package, url)
36-
%(pin "#{package}", to: "#{url}")
36+
def packaged?(package_name)
37+
importmap.match(/^pin ["']#{package_name}["'].*$/)
3738
end
3839

39-
def vendored_pin_for(package, url)
40-
filename = package_filename(package)
41-
version = extract_package_version_from(url)
40+
def remove_package_from_importmap(package_name)
41+
all_lines = File.readlines(@importmap_path)
42+
with_lines_removed = all_lines.grep_v(/pin ["']#{package_name}["']/)
4243

43-
if "#{package}.js" == filename
44-
%(pin "#{package}" # #{version})
45-
else
46-
%(pin "#{package}", to: "#{filename}" # #{version})
44+
File.open(@importmap_path, "w") do |file|
45+
with_lines_removed.each { |line| file.write(line) }
4746
end
4847
end
4948

50-
def packaged?(package)
51-
importmap.match(/^pin ["']#{package}["'].*$/)
52-
end
53-
54-
def download(package, url)
55-
ensure_vendor_directory_exists
56-
remove_existing_package_file(package)
57-
download_package_file(package, url)
49+
def pin_package_in_importmap(package_name, pin)
50+
if packaged?(package_name)
51+
gsub_file(@importmap_path, /^pin "#{package_name}".*$/, pin, verbose: false)
52+
else
53+
File.write(@importmap_path, "#{pin}\n", mode: 'a+')
54+
end
5855
end
5956

60-
def remove(package)
61-
remove_existing_package_file(package)
62-
remove_package_from_importmap(package)
57+
def ensure_vendor_directory_exists
58+
FileUtils.mkdir_p @vendor_path
6359
end
6460

6561
private
@@ -74,7 +70,22 @@ def normalize_provider(name)
7470
end
7571

7672
def extract_parsed_imports(response)
77-
JSON.parse(response.body).dig("map", "imports")
73+
parsed_response = JSON.parse(response.body)
74+
75+
imports = parsed_response.dig("map", "imports")
76+
static_dependencies = parsed_response["staticDeps"] || []
77+
dynamic_dependencies = parsed_response["dynamicDeps"] || []
78+
79+
dependencies = static_dependencies + dynamic_dependencies
80+
81+
imports.map do |package, url|
82+
Importmap::Package.new(
83+
unfiltered_dependencies: dependencies,
84+
package_name: package,
85+
main_url: url,
86+
packager: self
87+
)
88+
end
7889
end
7990

8091
def handle_failure_response(response)
@@ -94,56 +105,4 @@ def parse_service_error(response)
94105
def importmap
95106
@importmap ||= File.read(@importmap_path)
96107
end
97-
98-
99-
def ensure_vendor_directory_exists
100-
FileUtils.mkdir_p @vendor_path
101-
end
102-
103-
def remove_existing_package_file(package)
104-
FileUtils.rm_rf vendored_package_path(package)
105-
end
106-
107-
def remove_package_from_importmap(package)
108-
all_lines = File.readlines(@importmap_path)
109-
with_lines_removed = all_lines.grep_v(/pin ["']#{package}["']/)
110-
111-
File.open(@importmap_path, "w") do |file|
112-
with_lines_removed.each { |line| file.write(line) }
113-
end
114-
end
115-
116-
def download_package_file(package, url)
117-
response = Net::HTTP.get_response(URI(url))
118-
119-
if response.code == "200"
120-
save_vendored_package(package, url, response.body)
121-
else
122-
handle_failure_response(response)
123-
end
124-
end
125-
126-
def save_vendored_package(package, url, source)
127-
File.open(vendored_package_path(package), "w+") do |vendored_package|
128-
vendored_package.write "// #{package}#{extract_package_version_from(url)} downloaded from #{url}\n\n"
129-
130-
vendored_package.write remove_sourcemap_comment_from(source).force_encoding("UTF-8")
131-
end
132-
end
133-
134-
def remove_sourcemap_comment_from(source)
135-
source.gsub(/^\/\/# sourceMappingURL=.*/, "")
136-
end
137-
138-
def vendored_package_path(package)
139-
@vendor_path.join(package_filename(package))
140-
end
141-
142-
def package_filename(package)
143-
package.gsub("/", "--") + ".js"
144-
end
145-
146-
def extract_package_version_from(url)
147-
url.match(/@\d+\.\d+\.\d+/)&.to_a&.first
148-
end
149108
end

0 commit comments

Comments
 (0)