importmap-rails 2.2.0 → 2.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +32 -78
- data/lib/importmap/commands.rb +33 -69
- data/lib/importmap/map.rb +48 -2
- data/lib/importmap/npm.rb +4 -4
- data/lib/importmap/packager.rb +55 -13
- data/lib/importmap/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91837e96107f59efa73a314dec3955a488f9f40826c27e116da43c5d9ef0a44a
|
4
|
+
data.tar.gz: ce9d44539cd6bb86b1e027c9c714dc8274b4a7b918ee0a67493b79b9eb58103f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95a4d846c8a808b8037e2425ad307e1cecd736051053e86f5c9739f9118375d81e0e967f64be3f6cee916d0ce7ca763fd7b236684f9b0889679b62f5718643a0
|
7
|
+
data.tar.gz: 5e20ed5b2d79945a603db1cf6e0e5d694a8a7ad7b96f5bb13e6e1083225034fcbf7ba2019acf397b004345bd4d54827895b9e4ffd5c831eb1fdfc2c051dcb063
|
data/README.md
CHANGED
@@ -81,12 +81,13 @@ If you want to import local js module files from `app/javascript/src` or other s
|
|
81
81
|
pin_all_from 'app/javascript/src', under: 'src', to: 'src'
|
82
82
|
|
83
83
|
# With automatic integrity calculation for enhanced security
|
84
|
+
enable_integrity!
|
84
85
|
pin_all_from 'app/javascript/controllers', under: 'controllers', integrity: true
|
85
86
|
```
|
86
87
|
|
87
88
|
The `:to` parameter is only required if you want to change the destination logical import name. If you drop the :to option, you must place the :under option directly after the first parameter.
|
88
89
|
|
89
|
-
The `integrity: true`
|
90
|
+
The `enable_integrity!` call enables integrity calculation globally, and `integrity: true` automatically calculates integrity hashes for all files in the directory, providing security benefits without manual hash management.
|
90
91
|
|
91
92
|
Allows you to:
|
92
93
|
|
@@ -138,73 +139,27 @@ Unpinning and removing "react"
|
|
138
139
|
|
139
140
|
## Subresource Integrity (SRI)
|
140
141
|
|
141
|
-
For enhanced security, importmap-rails
|
142
|
-
|
143
|
-
### Default behavior with integrity
|
144
|
-
|
145
|
-
When you pin a package, integrity hashes are automatically included:
|
146
|
-
|
147
|
-
```bash
|
148
|
-
./bin/importmap pin lodash
|
149
|
-
Pinning "lodash" to vendor/javascript/lodash.js via download from https://ga.jspm.io/npm:lodash@4.17.21/lodash.js
|
150
|
-
Using integrity: sha384-PkIkha4kVPRlGtFantHjuv+Y9mRefUHpLFQbgOYUjzy247kvi16kLR7wWnsAmqZF
|
151
|
-
```
|
152
|
-
|
153
|
-
This generates a pin in your `config/importmap.rb` with the integrity hash:
|
154
|
-
|
155
|
-
```ruby
|
156
|
-
pin "lodash", integrity: "sha384-PkIkha4kVPRlGtFantHjuv+Y9mRefUHpLFQbgOYUjzy247kvi16kLR7wWnsAmqZF" # @4.17.21
|
157
|
-
```
|
158
|
-
|
159
|
-
### Opting out of integrity
|
160
|
-
|
161
|
-
If you need to disable integrity checking (not recommended for security reasons), you can use the `--no-integrity` flag:
|
162
|
-
|
163
|
-
```bash
|
164
|
-
./bin/importmap pin lodash --no-integrity
|
165
|
-
Pinning "lodash" to vendor/javascript/lodash.js via download from https://ga.jspm.io/npm:lodash@4.17.21/lodash.js
|
166
|
-
```
|
167
|
-
|
168
|
-
This generates a pin without integrity:
|
169
|
-
|
170
|
-
```ruby
|
171
|
-
pin "lodash" # @4.17.21
|
172
|
-
```
|
173
|
-
|
174
|
-
### Adding integrity to existing pins
|
175
|
-
|
176
|
-
If you have existing pins without integrity hashes, you can add them using the `integrity` command:
|
177
|
-
|
178
|
-
```bash
|
179
|
-
# Add integrity to specific packages
|
180
|
-
./bin/importmap integrity lodash react
|
181
|
-
|
182
|
-
# Add integrity to all pinned packages
|
183
|
-
./bin/importmap integrity
|
184
|
-
|
185
|
-
# Update your importmap.rb file with integrity hashes
|
186
|
-
./bin/importmap integrity --update
|
187
|
-
```
|
142
|
+
For enhanced security, importmap-rails supports [Subresource Integrity (SRI)](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) hashes for packages loaded from external CDNs.
|
188
143
|
|
189
144
|
### Automatic integrity for local assets
|
190
145
|
|
191
|
-
|
146
|
+
To enable automatic integrity calculation for local assets served by the Rails asset pipeline, you must first call `enable_integrity!` in your importmap configuration:
|
192
147
|
|
193
148
|
```ruby
|
194
149
|
# config/importmap.rb
|
195
150
|
|
196
|
-
#
|
197
|
-
|
198
|
-
pin "admin", to: "admin.js", integrity: true
|
151
|
+
# Enable integrity calculation globally
|
152
|
+
enable_integrity!
|
199
153
|
|
200
|
-
#
|
201
|
-
|
202
|
-
|
154
|
+
# With integrity enabled, these will auto-calculate integrity hashes
|
155
|
+
pin "application" # Auto-calculated integrity
|
156
|
+
pin "admin", to: "admin.js" # Auto-calculated integrity
|
157
|
+
pin_all_from "app/javascript/controllers", under: "controllers" # Auto-calculated integrity
|
203
158
|
|
204
|
-
# Mixed usage
|
205
|
-
pin "
|
206
|
-
pin "
|
207
|
-
pin "
|
159
|
+
# Mixed usage - explicitly controlling integrity
|
160
|
+
pin "cdn_package", integrity: "sha384-abc123..." # Pre-calculated hash
|
161
|
+
pin "no_integrity_package", integrity: false # Explicitly disable integrity
|
162
|
+
pin "nil_integrity_package", integrity: nil # Explicitly disable integrity
|
208
163
|
```
|
209
164
|
|
210
165
|
This is particularly useful for:
|
@@ -212,13 +167,18 @@ This is particularly useful for:
|
|
212
167
|
* **Bulk operations** with `pin_all_from` where calculating hashes manually would be tedious
|
213
168
|
* **Development workflow** where asset contents change frequently
|
214
169
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
170
|
+
**Note:** Integrity calculation is opt-in and must be enabled with `enable_integrity!`. This behavior can be further controlled by setting `integrity: false` or `integrity: nil` on individual pins.
|
171
|
+
|
172
|
+
**Important for Propshaft users:** SRI support requires Propshaft 1.2+ and you must configure the integrity hash algorithm in your application:
|
173
|
+
|
174
|
+
```ruby
|
175
|
+
# config/application.rb or config/environments/*.rb
|
176
|
+
config.assets.integrity_hash_algorithm = 'sha256' # or 'sha384', 'sha512'
|
177
|
+
```
|
178
|
+
|
179
|
+
Without this configuration, integrity will be disabled by default when using Propshaft. Sprockets includes integrity support out of the box.
|
220
180
|
|
221
|
-
**Example output with `integrity: true`:**
|
181
|
+
**Example output with `enable_integrity!` and `integrity: true`:**
|
222
182
|
```json
|
223
183
|
{
|
224
184
|
"imports": {
|
@@ -240,10 +200,14 @@ The integrity hashes are automatically included in your import map and module pr
|
|
240
200
|
```json
|
241
201
|
{
|
242
202
|
"imports": {
|
243
|
-
"lodash": "https://ga.jspm.io/npm:lodash@4.17.21/lodash.js"
|
203
|
+
"lodash": "https://ga.jspm.io/npm:lodash@4.17.21/lodash.js",
|
204
|
+
"application": "/assets/application-abc123.js",
|
205
|
+
"controllers/hello_controller": "/assets/controllers/hello_controller-def456.js"
|
244
206
|
},
|
245
207
|
"integrity": {
|
246
208
|
"https://ga.jspm.io/npm:lodash@4.17.21/lodash.js": "sha384-PkIkha4kVPRlGtFantHjuv+Y9mRefUHpLFQbgOYUjzy247kvi16kLR7wWnsAmqZF"
|
209
|
+
"/assets/application-abc123.js": "sha256-xyz789...",
|
210
|
+
"/assets/controllers/hello_controller-def456.js": "sha256-uvw012..."
|
247
211
|
}
|
248
212
|
}
|
249
213
|
```
|
@@ -251,22 +215,12 @@ The integrity hashes are automatically included in your import map and module pr
|
|
251
215
|
**Module preload tags:**
|
252
216
|
```html
|
253
217
|
<link rel="modulepreload" href="https://ga.jspm.io/npm:lodash@4.17.21/lodash.js" integrity="sha384-PkIkha4kVPRlGtFantHjuv+Y9mRefUHpLFQbgOYUjzy247kvi16kLR7wWnsAmqZF">
|
218
|
+
<link rel="modulepreload" href="/assets/application-abc123.js" integrity="sha256-xyz789...">
|
219
|
+
<link rel="modulepreload" href="/assets/controllers/hello_controller-def456.js" integrity="sha256-uvw012...">
|
254
220
|
```
|
255
221
|
|
256
222
|
Modern browsers will automatically validate these integrity hashes when loading the JavaScript modules, ensuring the files haven't been modified.
|
257
223
|
|
258
|
-
### Redownloading packages with integrity
|
259
|
-
|
260
|
-
The `pristine` command also includes integrity by default:
|
261
|
-
|
262
|
-
```bash
|
263
|
-
# Redownload all packages with integrity (default)
|
264
|
-
./bin/importmap pristine
|
265
|
-
|
266
|
-
# Redownload packages without integrity
|
267
|
-
./bin/importmap pristine --no-integrity
|
268
|
-
```
|
269
|
-
|
270
224
|
## Preloading pinned modules
|
271
225
|
|
272
226
|
To avoid the waterfall effect where the browser has to load one file after another before it can get to the deepest nested import, importmap-rails uses [modulepreload links](https://developers.google.com/web/updates/2017/12/modulepreload) by default. If you don't want to preload a dependency, because you want to load it on-demand for efficiency, append `preload: false` to the pin.
|
data/lib/importmap/commands.rb
CHANGED
@@ -13,19 +13,9 @@ class Importmap::Commands < Thor
|
|
13
13
|
option :env, type: :string, aliases: :e, default: "production"
|
14
14
|
option :from, type: :string, aliases: :f, default: "jspm"
|
15
15
|
option :preload, type: :string, repeatable: true, desc: "Can be used multiple times"
|
16
|
-
option :integrity, type: :boolean, aliases: :i, default: true, desc: "Include integrity hash from JSPM"
|
17
16
|
def pin(*packages)
|
18
|
-
|
19
|
-
|
20
|
-
puts %(Pinning "#{package}" to #{packager.vendor_path}/#{package}.js via download from #{url})
|
21
|
-
|
22
|
-
packager.download(package, url)
|
23
|
-
|
24
|
-
pin = packager.vendored_pin_for(package, url, options[:preload], integrity: integrity_hash)
|
25
|
-
|
26
|
-
log_integrity_usage(integrity_hash)
|
27
|
-
update_importmap_with_pin(package, pin)
|
28
|
-
end
|
17
|
+
for_each_import(packages, env: options[:env], from: options[:from]) do |package, url|
|
18
|
+
pin_package(package, url, options[:preload])
|
29
19
|
end
|
30
20
|
end
|
31
21
|
|
@@ -33,12 +23,10 @@ class Importmap::Commands < Thor
|
|
33
23
|
option :env, type: :string, aliases: :e, default: "production"
|
34
24
|
option :from, type: :string, aliases: :f, default: "jspm"
|
35
25
|
def unpin(*packages)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
packager.remove(package)
|
41
|
-
end
|
26
|
+
for_each_import(packages, env: options[:env], from: options[:from]) do |package, url|
|
27
|
+
if packager.packaged?(package)
|
28
|
+
puts %(Unpinning and removing "#{package}")
|
29
|
+
packager.remove(package)
|
42
30
|
end
|
43
31
|
end
|
44
32
|
end
|
@@ -46,18 +34,13 @@ class Importmap::Commands < Thor
|
|
46
34
|
desc "pristine", "Redownload all pinned packages"
|
47
35
|
option :env, type: :string, aliases: :e, default: "production"
|
48
36
|
option :from, type: :string, aliases: :f, default: "jspm"
|
49
|
-
option :integrity, type: :boolean, aliases: :i, default: true, desc: "Include integrity hash from JSPM"
|
50
37
|
def pristine
|
51
38
|
packages = prepare_packages_with_versions
|
52
39
|
|
53
|
-
|
54
|
-
|
55
|
-
puts %(Downloading "#{package}" to #{packager.vendor_path}/#{package}.js from #{url})
|
56
|
-
|
57
|
-
packager.download(package, url)
|
40
|
+
for_each_import(packages, env: options[:env], from: options[:from]) do |package, url|
|
41
|
+
puts %(Downloading "#{package}" to #{packager.vendor_path}/#{package}.js from #{url})
|
58
42
|
|
59
|
-
|
60
|
-
end
|
43
|
+
packager.download(package, url)
|
61
44
|
end
|
62
45
|
end
|
63
46
|
|
@@ -107,7 +90,14 @@ class Importmap::Commands < Thor
|
|
107
90
|
desc "update", "Update outdated package pins"
|
108
91
|
def update
|
109
92
|
if (outdated_packages = npm.outdated_packages).any?
|
110
|
-
|
93
|
+
package_names = outdated_packages.map(&:name)
|
94
|
+
packages_with_options = packager.extract_existing_pin_options(package_names)
|
95
|
+
|
96
|
+
for_each_import(package_names, env: "production", from: "jspm") do |package, url|
|
97
|
+
options = packages_with_options[package] || {}
|
98
|
+
|
99
|
+
pin_package(package, url, options[:preload])
|
100
|
+
end
|
111
101
|
else
|
112
102
|
puts "No outdated packages found"
|
113
103
|
end
|
@@ -118,33 +108,6 @@ class Importmap::Commands < Thor
|
|
118
108
|
puts npm.packages_with_versions.map { |x| x.join(' ') }
|
119
109
|
end
|
120
110
|
|
121
|
-
desc "integrity [*PACKAGES]", "Download and add integrity hashes for packages"
|
122
|
-
option :env, type: :string, aliases: :e, default: "production"
|
123
|
-
option :from, type: :string, aliases: :f, default: "jspm"
|
124
|
-
option :update, type: :boolean, aliases: :u, default: false, desc: "Update importmap.rb with integrity hashes"
|
125
|
-
def integrity(*packages)
|
126
|
-
packages = prepare_packages_with_versions(packages)
|
127
|
-
|
128
|
-
with_import_response(packages, env: options[:env], from: options[:from], integrity: true) do |imports, integrity_hashes|
|
129
|
-
process_imports(imports, integrity_hashes) do |package, url, integrity_hash|
|
130
|
-
puts %(Getting integrity for "#{package}" from #{url})
|
131
|
-
|
132
|
-
if integrity_hash
|
133
|
-
puts %( #{package}: #{integrity_hash})
|
134
|
-
|
135
|
-
if options[:update]
|
136
|
-
pin_with_integrity = packager.pin_for(package, url, integrity: integrity_hash)
|
137
|
-
|
138
|
-
update_importmap_with_pin(package, pin_with_integrity)
|
139
|
-
puts %( Updated importmap.rb with integrity for "#{package}")
|
140
|
-
end
|
141
|
-
else
|
142
|
-
puts %( No integrity hash available for "#{package}")
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
111
|
private
|
149
112
|
def packager
|
150
113
|
@packager ||= Importmap::Packager.new
|
@@ -154,18 +117,26 @@ class Importmap::Commands < Thor
|
|
154
117
|
@npm ||= Importmap::Npm.new
|
155
118
|
end
|
156
119
|
|
120
|
+
def pin_package(package, url, preload)
|
121
|
+
puts %(Pinning "#{package}" to #{packager.vendor_path}/#{package}.js via download from #{url})
|
122
|
+
|
123
|
+
packager.download(package, url)
|
124
|
+
|
125
|
+
pin = packager.vendored_pin_for(package, url, preload)
|
126
|
+
|
127
|
+
update_importmap_with_pin(package, pin)
|
128
|
+
end
|
129
|
+
|
157
130
|
def update_importmap_with_pin(package, pin)
|
131
|
+
new_pin = "#{pin}\n"
|
132
|
+
|
158
133
|
if packager.packaged?(package)
|
159
|
-
gsub_file("config/importmap.rb",
|
134
|
+
gsub_file("config/importmap.rb", Importmap::Map.pin_line_regexp_for(package), pin, verbose: false)
|
160
135
|
else
|
161
|
-
append_to_file("config/importmap.rb",
|
136
|
+
append_to_file("config/importmap.rb", new_pin, verbose: false)
|
162
137
|
end
|
163
138
|
end
|
164
139
|
|
165
|
-
def log_integrity_usage(integrity_hash)
|
166
|
-
puts %( Using integrity: #{integrity_hash}) if integrity_hash
|
167
|
-
end
|
168
|
-
|
169
140
|
def handle_package_not_found(packages, from)
|
170
141
|
puts "Couldn't find any packages in #{packages.inspect} on #{from}"
|
171
142
|
end
|
@@ -205,18 +176,11 @@ class Importmap::Commands < Thor
|
|
205
176
|
end
|
206
177
|
end
|
207
178
|
|
208
|
-
def
|
209
|
-
imports.each do |package, url|
|
210
|
-
integrity_hash = integrity_hashes[url]
|
211
|
-
block.call(package, url, integrity_hash)
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
def with_import_response(packages, **options)
|
179
|
+
def for_each_import(packages, **options, &block)
|
216
180
|
response = packager.import(*packages, **options)
|
217
181
|
|
218
182
|
if response
|
219
|
-
|
183
|
+
response[:imports].each(&block)
|
220
184
|
else
|
221
185
|
handle_package_not_found(packages, options[:from])
|
222
186
|
end
|
data/lib/importmap/map.rb
CHANGED
@@ -3,9 +3,16 @@ require "pathname"
|
|
3
3
|
class Importmap::Map
|
4
4
|
attr_reader :packages, :directories
|
5
5
|
|
6
|
+
PIN_REGEX = /^pin\s+["']([^"']+)["']/.freeze # :nodoc:
|
7
|
+
|
8
|
+
def self.pin_line_regexp_for(package) # :nodoc:
|
9
|
+
/^.*pin\s+["']#{Regexp.escape(package)}["'].*$/.freeze
|
10
|
+
end
|
11
|
+
|
6
12
|
class InvalidFile < StandardError; end
|
7
13
|
|
8
14
|
def initialize
|
15
|
+
@integrity = false
|
9
16
|
@packages, @directories = {}, {}
|
10
17
|
@cache = {}
|
11
18
|
end
|
@@ -25,12 +32,49 @@ class Importmap::Map
|
|
25
32
|
self
|
26
33
|
end
|
27
34
|
|
28
|
-
|
35
|
+
# Enables automatic integrity hash calculation for all pinned modules.
|
36
|
+
#
|
37
|
+
# When enabled, integrity values are included in the importmap JSON for all
|
38
|
+
# pinned modules. For local assets served by the Rails asset pipeline,
|
39
|
+
# integrity hashes are automatically calculated when +integrity: true+ is
|
40
|
+
# specified. For modules with explicit integrity values, those values are
|
41
|
+
# included as provided. This provides Subresource Integrity (SRI) protection
|
42
|
+
# to ensure JavaScript modules haven't been tampered with.
|
43
|
+
#
|
44
|
+
# Clears the importmap cache when called to ensure fresh integrity hashes
|
45
|
+
# are generated.
|
46
|
+
#
|
47
|
+
# ==== Examples
|
48
|
+
#
|
49
|
+
# # config/importmap.rb
|
50
|
+
# enable_integrity!
|
51
|
+
#
|
52
|
+
# # These will now auto-calculate integrity hashes
|
53
|
+
# pin "application" # integrity: true by default
|
54
|
+
# pin "admin", to: "admin.js" # integrity: true by default
|
55
|
+
# pin_all_from "app/javascript/lib" # integrity: true by default
|
56
|
+
#
|
57
|
+
# # Manual control still works
|
58
|
+
# pin "no_integrity", integrity: false
|
59
|
+
# pin "custom_hash", integrity: "sha384-abc123..."
|
60
|
+
#
|
61
|
+
# ==== Notes
|
62
|
+
#
|
63
|
+
# * Integrity calculation is disabled by default and must be explicitly enabled
|
64
|
+
# * Requires asset pipeline support for integrity calculation (Sprockets or Propshaft 1.2+)
|
65
|
+
# * For Propshaft, you must configure +config.assets.integrity_hash_algorithm+
|
66
|
+
# * External CDN packages should provide their own integrity hashes
|
67
|
+
def enable_integrity!
|
68
|
+
clear_cache
|
69
|
+
@integrity = true
|
70
|
+
end
|
71
|
+
|
72
|
+
def pin(name, to: nil, preload: true, integrity: true)
|
29
73
|
clear_cache
|
30
74
|
@packages[name] = MappedFile.new(name: name, path: to || "#{name}.js", preload: preload, integrity: integrity)
|
31
75
|
end
|
32
76
|
|
33
|
-
def pin_all_from(dir, under: nil, to: nil, preload: true, integrity:
|
77
|
+
def pin_all_from(dir, under: nil, to: nil, preload: true, integrity: true)
|
34
78
|
clear_cache
|
35
79
|
@directories[dir] = MappedDir.new(dir: dir, under: under, path: to, preload: preload, integrity: integrity)
|
36
80
|
end
|
@@ -210,6 +254,8 @@ class Importmap::Map
|
|
210
254
|
end
|
211
255
|
|
212
256
|
def resolve_integrity_value(integrity, path, resolver:)
|
257
|
+
return unless @integrity
|
258
|
+
|
213
259
|
case integrity
|
214
260
|
when true
|
215
261
|
resolver.asset_integrity(path) if resolver.respond_to?(:asset_integrity)
|
data/lib/importmap/npm.rb
CHANGED
@@ -3,7 +3,7 @@ require "uri"
|
|
3
3
|
require "json"
|
4
4
|
|
5
5
|
class Importmap::Npm
|
6
|
-
PIN_REGEX =
|
6
|
+
PIN_REGEX = /#{Importmap::Map::PIN_REGEX}.*/.freeze # :nodoc:
|
7
7
|
|
8
8
|
Error = Class.new(StandardError)
|
9
9
|
HTTPError = Class.new(Error)
|
@@ -17,7 +17,7 @@ class Importmap::Npm
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def outdated_packages
|
20
|
-
packages_with_versions.
|
20
|
+
packages_with_versions.each_with_object([]) do |(package, current_version), outdated_packages|
|
21
21
|
outdated_package = OutdatedPackage.new(name: package, current_version: current_version)
|
22
22
|
|
23
23
|
if !(response = get_package(package))
|
@@ -51,7 +51,7 @@ class Importmap::Npm
|
|
51
51
|
def packages_with_versions
|
52
52
|
# We cannot use the name after "pin" because some dependencies are loaded from inside packages
|
53
53
|
# Eg. pin "buffer", to: "https://ga.jspm.io/npm:@jspm/core@2.0.0-beta.19/nodelibs/browser/buffer.js"
|
54
|
-
with_versions = importmap.scan(/^pin .*(?<=npm:|npm\/|skypack\.dev\/|unpkg\.com\/)(
|
54
|
+
with_versions = importmap.scan(/^pin .*(?<=npm:|npm\/|skypack\.dev\/|unpkg\.com\/)([^@\/]+)@(\d+\.\d+\.\d+(?:[^\/\s"']*))/) |
|
55
55
|
importmap.scan(/#{PIN_REGEX} #.*@(\d+\.\d+\.\d+(?:[^\s]*)).*$/)
|
56
56
|
|
57
57
|
vendored_packages_without_version(with_versions).each do |package, path|
|
@@ -147,7 +147,7 @@ class Importmap::Npm
|
|
147
147
|
end
|
148
148
|
|
149
149
|
def find_unversioned_vendored_package(line, versioned_packages)
|
150
|
-
regexp = line.include?("to:")? /#{PIN_REGEX}to: ["']([^
|
150
|
+
regexp = line.include?("to:")? /#{PIN_REGEX}to: ["']([^"']*)["'].*/ : PIN_REGEX
|
151
151
|
match = line.match(regexp)
|
152
152
|
|
153
153
|
return unless match
|
data/lib/importmap/packager.rb
CHANGED
@@ -3,6 +3,9 @@ require "uri"
|
|
3
3
|
require "json"
|
4
4
|
|
5
5
|
class Importmap::Packager
|
6
|
+
PIN_REGEX = /#{Importmap::Map::PIN_REGEX}(.*)/.freeze # :nodoc:
|
7
|
+
PRELOAD_OPTION_REGEXP = /preload:\s*(\[[^\]]+\]|true|false|["'][^"']*["'])/.freeze # :nodoc:
|
8
|
+
|
6
9
|
Error = Class.new(StandardError)
|
7
10
|
HTTPError = Class.new(Error)
|
8
11
|
ServiceError = Error.new(Error)
|
@@ -17,13 +20,12 @@ class Importmap::Packager
|
|
17
20
|
@vendor_path = Pathname.new(vendor_path)
|
18
21
|
end
|
19
22
|
|
20
|
-
def import(*packages, env: "production", from: "jspm"
|
23
|
+
def import(*packages, env: "production", from: "jspm")
|
21
24
|
response = post_json({
|
22
25
|
"install" => Array(packages),
|
23
26
|
"flattenScope" => true,
|
24
27
|
"env" => [ "browser", "module", env ],
|
25
28
|
"provider" => normalize_provider(from),
|
26
|
-
"integrity" => integrity
|
27
29
|
})
|
28
30
|
|
29
31
|
case response.code
|
@@ -36,24 +38,23 @@ class Importmap::Packager
|
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
|
-
def pin_for(package, url = nil, preloads: nil
|
41
|
+
def pin_for(package, url = nil, preloads: nil)
|
40
42
|
to = url ? %(, to: "#{url}") : ""
|
41
43
|
preload_param = preload(preloads)
|
42
|
-
integrity_param = integrity ? %(, integrity: "#{integrity}") : ""
|
43
44
|
|
44
|
-
%(pin "#{package}") + to + preload_param
|
45
|
+
%(pin "#{package}") + to + preload_param
|
45
46
|
end
|
46
47
|
|
47
|
-
def vendored_pin_for(package, url, preloads = nil
|
48
|
+
def vendored_pin_for(package, url, preloads = nil)
|
48
49
|
filename = package_filename(package)
|
49
50
|
version = extract_package_version_from(url)
|
50
51
|
to = "#{package}.js" != filename ? filename : nil
|
51
52
|
|
52
|
-
pin_for(package, to, preloads: preloads
|
53
|
+
pin_for(package, to, preloads: preloads) + %( # #{version})
|
53
54
|
end
|
54
55
|
|
55
56
|
def packaged?(package)
|
56
|
-
importmap.match(
|
57
|
+
importmap.match(Importmap::Map.pin_line_regexp_for(package))
|
57
58
|
end
|
58
59
|
|
59
60
|
def download(package, url)
|
@@ -67,14 +68,57 @@ class Importmap::Packager
|
|
67
68
|
remove_package_from_importmap(package)
|
68
69
|
end
|
69
70
|
|
71
|
+
def extract_existing_pin_options(packages)
|
72
|
+
return {} unless @importmap_path.exist?
|
73
|
+
|
74
|
+
packages = Array(packages)
|
75
|
+
|
76
|
+
all_package_options = build_package_options_lookup(importmap.lines)
|
77
|
+
|
78
|
+
packages.to_h do |package|
|
79
|
+
[package, all_package_options[package] || {}]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
70
83
|
private
|
84
|
+
def build_package_options_lookup(lines)
|
85
|
+
lines.each_with_object({}) do |line, package_options|
|
86
|
+
match = line.strip.match(PIN_REGEX)
|
87
|
+
|
88
|
+
if match
|
89
|
+
package_name = match[1]
|
90
|
+
options_part = match[2]
|
91
|
+
|
92
|
+
preload_match = options_part.match(PRELOAD_OPTION_REGEXP)
|
93
|
+
|
94
|
+
if preload_match
|
95
|
+
preload = preload_from_string(preload_match[1])
|
96
|
+
package_options[package_name] = { preload: preload }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def preload_from_string(value)
|
103
|
+
case value
|
104
|
+
when "true"
|
105
|
+
true
|
106
|
+
when "false"
|
107
|
+
false
|
108
|
+
when /^\[.*\]$/
|
109
|
+
JSON.parse(value)
|
110
|
+
else
|
111
|
+
value.gsub(/["']/, "")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
71
115
|
def preload(preloads)
|
72
116
|
case Array(preloads)
|
73
117
|
in []
|
74
118
|
""
|
75
|
-
in ["true"]
|
119
|
+
in ["true"] | [true]
|
76
120
|
%(, preload: true)
|
77
|
-
in ["false"]
|
121
|
+
in ["false"] | [false]
|
78
122
|
%(, preload: false)
|
79
123
|
in [string]
|
80
124
|
%(, preload: "#{string}")
|
@@ -96,11 +140,9 @@ class Importmap::Packager
|
|
96
140
|
def extract_parsed_response(response)
|
97
141
|
parsed = JSON.parse(response.body)
|
98
142
|
imports = parsed.dig("map", "imports")
|
99
|
-
integrity = parsed.dig("map", "integrity") || {}
|
100
143
|
|
101
144
|
{
|
102
145
|
imports: imports,
|
103
|
-
integrity: integrity
|
104
146
|
}
|
105
147
|
end
|
106
148
|
|
@@ -133,7 +175,7 @@ class Importmap::Packager
|
|
133
175
|
|
134
176
|
def remove_package_from_importmap(package)
|
135
177
|
all_lines = File.readlines(@importmap_path)
|
136
|
-
with_lines_removed = all_lines.grep_v(
|
178
|
+
with_lines_removed = all_lines.grep_v(Importmap::Map.pin_line_regexp_for(package))
|
137
179
|
|
138
180
|
File.open(@importmap_path, "w") do |file|
|
139
181
|
with_lines_removed.each { |line| file.write(line) }
|
data/lib/importmap/version.rb
CHANGED