importmap-rails 2.2.0 → 2.2.1
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 +27 -77
- data/lib/importmap/commands.rb +14 -63
- data/lib/importmap/map.rb +2 -2
- data/lib/importmap/packager.rb +5 -9
- 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: c286a2385b19e8a170d991478ce228cfb1c0e27eb02fbda6349a889798caa85f
|
4
|
+
data.tar.gz: a40cea607756433bf36ad49e291e342f324a303c80e0286e980ee27c6c9001f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71fc6847c3860c86d611dad36554feab750059128eee5d72961da0cd6bc2ba6ec08dc96358bb7919f487243d7463ca304efbbbf68874d7b58c9b85580d652f75
|
7
|
+
data.tar.gz: 59b0d5ce601ef182abd5f1017f414eec8fea35d1e48e270b0edd10bcf0c59cd7c5e52c1af0dab4f98f0ea81972ce82004af3011d1f3399d152ce077fbbd7a7e1
|
data/README.md
CHANGED
@@ -138,73 +138,24 @@ Unpinning and removing "react"
|
|
138
138
|
|
139
139
|
## Subresource Integrity (SRI)
|
140
140
|
|
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
|
-
```
|
141
|
+
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
142
|
|
189
143
|
### Automatic integrity for local assets
|
190
144
|
|
191
|
-
|
145
|
+
Starting with importmap-rails, **`integrity: true` is the default** for all pins. This automatically calculates integrity hashes for local assets served by the Rails asset pipeline:
|
192
146
|
|
193
147
|
```ruby
|
194
148
|
# config/importmap.rb
|
195
149
|
|
196
|
-
#
|
197
|
-
pin "application"
|
198
|
-
pin "admin", to: "admin.js"
|
199
|
-
|
200
|
-
# Works with pin_all_from too
|
201
|
-
pin_all_from "app/javascript/controllers", under: "controllers", integrity: true
|
202
|
-
pin_all_from "app/javascript/lib", under: "lib", integrity: true
|
150
|
+
# These all use integrity: true by default
|
151
|
+
pin "application" # Auto-calculated integrity
|
152
|
+
pin "admin", to: "admin.js" # Auto-calculated integrity
|
153
|
+
pin_all_from "app/javascript/controllers", under: "controllers" # Auto-calculated integrity
|
203
154
|
|
204
|
-
# Mixed usage
|
205
|
-
pin "
|
206
|
-
pin "
|
207
|
-
pin "
|
155
|
+
# Mixed usage - explicitly controlling integrity
|
156
|
+
pin "cdn_package", integrity: "sha384-abc123..." # Pre-calculated hash
|
157
|
+
pin "no_integrity_package", integrity: false # Explicitly disable integrity
|
158
|
+
pin "nil_integrity_package", integrity: nil # Explicitly disable integrity
|
208
159
|
```
|
209
160
|
|
210
161
|
This is particularly useful for:
|
@@ -212,11 +163,16 @@ This is particularly useful for:
|
|
212
163
|
* **Bulk operations** with `pin_all_from` where calculating hashes manually would be tedious
|
213
164
|
* **Development workflow** where asset contents change frequently
|
214
165
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
166
|
+
This behavior can be disabled by setting `integrity: false` or `integrity: nil`
|
167
|
+
|
168
|
+
**Important for Propshaft users:** SRI support requires Propshaft 1.2+ and you must configure the integrity hash algorithm in your application:
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
# config/application.rb or config/environments/*.rb
|
172
|
+
config.assets.integrity_hash_algorithm = 'sha256' # or 'sha384', 'sha512'
|
173
|
+
```
|
174
|
+
|
175
|
+
Without this configuration, integrity will be disabled by default when using Propshaft. Sprockets includes integrity support out of the box.
|
220
176
|
|
221
177
|
**Example output with `integrity: true`:**
|
222
178
|
```json
|
@@ -240,10 +196,14 @@ The integrity hashes are automatically included in your import map and module pr
|
|
240
196
|
```json
|
241
197
|
{
|
242
198
|
"imports": {
|
243
|
-
"lodash": "https://ga.jspm.io/npm:lodash@4.17.21/lodash.js"
|
199
|
+
"lodash": "https://ga.jspm.io/npm:lodash@4.17.21/lodash.js",
|
200
|
+
"application": "/assets/application-abc123.js",
|
201
|
+
"controllers/hello_controller": "/assets/controllers/hello_controller-def456.js"
|
244
202
|
},
|
245
203
|
"integrity": {
|
246
204
|
"https://ga.jspm.io/npm:lodash@4.17.21/lodash.js": "sha384-PkIkha4kVPRlGtFantHjuv+Y9mRefUHpLFQbgOYUjzy247kvi16kLR7wWnsAmqZF"
|
205
|
+
"/assets/application-abc123.js": "sha256-xyz789...",
|
206
|
+
"/assets/controllers/hello_controller-def456.js": "sha256-uvw012..."
|
247
207
|
}
|
248
208
|
}
|
249
209
|
```
|
@@ -251,22 +211,12 @@ The integrity hashes are automatically included in your import map and module pr
|
|
251
211
|
**Module preload tags:**
|
252
212
|
```html
|
253
213
|
<link rel="modulepreload" href="https://ga.jspm.io/npm:lodash@4.17.21/lodash.js" integrity="sha384-PkIkha4kVPRlGtFantHjuv+Y9mRefUHpLFQbgOYUjzy247kvi16kLR7wWnsAmqZF">
|
214
|
+
<link rel="modulepreload" href="/assets/application-abc123.js" integrity="sha256-xyz789...">
|
215
|
+
<link rel="modulepreload" href="/assets/controllers/hello_controller-def456.js" integrity="sha256-uvw012...">
|
254
216
|
```
|
255
217
|
|
256
218
|
Modern browsers will automatically validate these integrity hashes when loading the JavaScript modules, ensuring the files haven't been modified.
|
257
219
|
|
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
220
|
## Preloading pinned modules
|
271
221
|
|
272
222
|
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,15 @@ 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})
|
17
|
+
for_each_import(packages, env: options[:env], from: options[:from]) do |package, url|
|
18
|
+
puts %(Pinning "#{package}" to #{packager.vendor_path}/#{package}.js via download from #{url})
|
21
19
|
|
22
|
-
|
20
|
+
packager.download(package, url)
|
23
21
|
|
24
|
-
|
22
|
+
pin = packager.vendored_pin_for(package, url, options[:preload])
|
25
23
|
|
26
|
-
|
27
|
-
update_importmap_with_pin(package, pin)
|
28
|
-
end
|
24
|
+
update_importmap_with_pin(package, pin)
|
29
25
|
end
|
30
26
|
end
|
31
27
|
|
@@ -33,12 +29,10 @@ class Importmap::Commands < Thor
|
|
33
29
|
option :env, type: :string, aliases: :e, default: "production"
|
34
30
|
option :from, type: :string, aliases: :f, default: "jspm"
|
35
31
|
def unpin(*packages)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
packager.remove(package)
|
41
|
-
end
|
32
|
+
for_each_import(packages, env: options[:env], from: options[:from]) do |package, url|
|
33
|
+
if packager.packaged?(package)
|
34
|
+
puts %(Unpinning and removing "#{package}")
|
35
|
+
packager.remove(package)
|
42
36
|
end
|
43
37
|
end
|
44
38
|
end
|
@@ -46,18 +40,13 @@ class Importmap::Commands < Thor
|
|
46
40
|
desc "pristine", "Redownload all pinned packages"
|
47
41
|
option :env, type: :string, aliases: :e, default: "production"
|
48
42
|
option :from, type: :string, aliases: :f, default: "jspm"
|
49
|
-
option :integrity, type: :boolean, aliases: :i, default: true, desc: "Include integrity hash from JSPM"
|
50
43
|
def pristine
|
51
44
|
packages = prepare_packages_with_versions
|
52
45
|
|
53
|
-
|
54
|
-
|
55
|
-
puts %(Downloading "#{package}" to #{packager.vendor_path}/#{package}.js from #{url})
|
46
|
+
for_each_import(packages, env: options[:env], from: options[:from]) do |package, url|
|
47
|
+
puts %(Downloading "#{package}" to #{packager.vendor_path}/#{package}.js from #{url})
|
56
48
|
|
57
|
-
|
58
|
-
|
59
|
-
log_integrity_usage(integrity_hash)
|
60
|
-
end
|
49
|
+
packager.download(package, url)
|
61
50
|
end
|
62
51
|
end
|
63
52
|
|
@@ -118,33 +107,6 @@ class Importmap::Commands < Thor
|
|
118
107
|
puts npm.packages_with_versions.map { |x| x.join(' ') }
|
119
108
|
end
|
120
109
|
|
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
110
|
private
|
149
111
|
def packager
|
150
112
|
@packager ||= Importmap::Packager.new
|
@@ -162,10 +124,6 @@ class Importmap::Commands < Thor
|
|
162
124
|
end
|
163
125
|
end
|
164
126
|
|
165
|
-
def log_integrity_usage(integrity_hash)
|
166
|
-
puts %( Using integrity: #{integrity_hash}) if integrity_hash
|
167
|
-
end
|
168
|
-
|
169
127
|
def handle_package_not_found(packages, from)
|
170
128
|
puts "Couldn't find any packages in #{packages.inspect} on #{from}"
|
171
129
|
end
|
@@ -205,18 +163,11 @@ class Importmap::Commands < Thor
|
|
205
163
|
end
|
206
164
|
end
|
207
165
|
|
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)
|
166
|
+
def for_each_import(packages, **options, &block)
|
216
167
|
response = packager.import(*packages, **options)
|
217
168
|
|
218
169
|
if response
|
219
|
-
|
170
|
+
response[:imports].each(&block)
|
220
171
|
else
|
221
172
|
handle_package_not_found(packages, options[:from])
|
222
173
|
end
|
data/lib/importmap/map.rb
CHANGED
@@ -25,12 +25,12 @@ class Importmap::Map
|
|
25
25
|
self
|
26
26
|
end
|
27
27
|
|
28
|
-
def pin(name, to: nil, preload: true, integrity:
|
28
|
+
def pin(name, to: nil, preload: true, integrity: true)
|
29
29
|
clear_cache
|
30
30
|
@packages[name] = MappedFile.new(name: name, path: to || "#{name}.js", preload: preload, integrity: integrity)
|
31
31
|
end
|
32
32
|
|
33
|
-
def pin_all_from(dir, under: nil, to: nil, preload: true, integrity:
|
33
|
+
def pin_all_from(dir, under: nil, to: nil, preload: true, integrity: true)
|
34
34
|
clear_cache
|
35
35
|
@directories[dir] = MappedDir.new(dir: dir, under: under, path: to, preload: preload, integrity: integrity)
|
36
36
|
end
|
data/lib/importmap/packager.rb
CHANGED
@@ -17,13 +17,12 @@ class Importmap::Packager
|
|
17
17
|
@vendor_path = Pathname.new(vendor_path)
|
18
18
|
end
|
19
19
|
|
20
|
-
def import(*packages, env: "production", from: "jspm"
|
20
|
+
def import(*packages, env: "production", from: "jspm")
|
21
21
|
response = post_json({
|
22
22
|
"install" => Array(packages),
|
23
23
|
"flattenScope" => true,
|
24
24
|
"env" => [ "browser", "module", env ],
|
25
25
|
"provider" => normalize_provider(from),
|
26
|
-
"integrity" => integrity
|
27
26
|
})
|
28
27
|
|
29
28
|
case response.code
|
@@ -36,20 +35,19 @@ class Importmap::Packager
|
|
36
35
|
end
|
37
36
|
end
|
38
37
|
|
39
|
-
def pin_for(package, url = nil, preloads: nil
|
38
|
+
def pin_for(package, url = nil, preloads: nil)
|
40
39
|
to = url ? %(, to: "#{url}") : ""
|
41
40
|
preload_param = preload(preloads)
|
42
|
-
integrity_param = integrity ? %(, integrity: "#{integrity}") : ""
|
43
41
|
|
44
|
-
%(pin "#{package}") + to + preload_param
|
42
|
+
%(pin "#{package}") + to + preload_param
|
45
43
|
end
|
46
44
|
|
47
|
-
def vendored_pin_for(package, url, preloads = nil
|
45
|
+
def vendored_pin_for(package, url, preloads = nil)
|
48
46
|
filename = package_filename(package)
|
49
47
|
version = extract_package_version_from(url)
|
50
48
|
to = "#{package}.js" != filename ? filename : nil
|
51
49
|
|
52
|
-
pin_for(package, to, preloads: preloads
|
50
|
+
pin_for(package, to, preloads: preloads) + %( # #{version})
|
53
51
|
end
|
54
52
|
|
55
53
|
def packaged?(package)
|
@@ -96,11 +94,9 @@ class Importmap::Packager
|
|
96
94
|
def extract_parsed_response(response)
|
97
95
|
parsed = JSON.parse(response.body)
|
98
96
|
imports = parsed.dig("map", "imports")
|
99
|
-
integrity = parsed.dig("map", "integrity") || {}
|
100
97
|
|
101
98
|
{
|
102
99
|
imports: imports,
|
103
|
-
integrity: integrity
|
104
100
|
}
|
105
101
|
end
|
106
102
|
|
data/lib/importmap/version.rb
CHANGED