importmap-rails 2.0.1 → 2.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1409421a79c426cb82404b0c910dedb0c5ec2dc733304e5f907f92067cfb208
4
- data.tar.gz: 138353eac33ffcbbe8a80ce4a8202485792e4609122409478bc5dc9fa324650d
3
+ metadata.gz: 24bbd66810fcdeffe321841aa86a4308b0fa76749e9ec20105f8d74475634aa7
4
+ data.tar.gz: ea19b79e7701a7aafd6fe546edc88880aa5e68858999959d8e52005ce0be5a34
5
5
  SHA512:
6
- metadata.gz: 3f248fc4722407b75d81d533c52b368a8a12fceaf10ba9cd7d103c10a103d0722013910eaecdc824cd5076a0fd073fefa6c08020bd97a5a5d9948fb696091416
7
- data.tar.gz: 56ca2998ed2b6e73b50d691da2894593dbf31f2092cc16db5c26cb7aea46ebb71995660ee75945daaf5a32e355fe953d3d141b4612ab87e56744ce70e4cbcc65
6
+ metadata.gz: 741aab3cb6747eba7daf39dd5d311c126dfccfb55132c68d1e1cdaafe2b84a9e2596ea4746446cf6f1702d851a109cae3413b97a233889cd8855e347098ee592
7
+ data.tar.gz: 3b9ed94a737134bbfcd8be54f95fa5094539b2b6d56b7365362bdf7ac961bb1e727cef396eca0001629d45e2c50578c3af819559a521829f41b4e875733d3e80
data/README.md CHANGED
@@ -141,6 +141,25 @@ pin "md5", preload: false # file lives in vendor/javascript/md5.js
141
141
  ...
142
142
  ```
143
143
 
144
+ You can also specify which entry points to preload a particular dependency in by providing `preload:` a string or array of strings.
145
+
146
+ Example:
147
+
148
+ ```ruby
149
+ # config/importmap.rb
150
+ pin "@github/hotkey", to: "@github--hotkey.js", preload: 'application'
151
+ pin "md5", preload: ['application', 'alternate']
152
+
153
+ # app/views/layouts/application.html.erb
154
+ <%= javascript_importmap_tags 'alternate' %>
155
+
156
+ # will include the following link before the importmap is setup:
157
+ <link rel="modulepreload" href="/assets/javascript/md5.js">
158
+ ...
159
+ ```
160
+
161
+
162
+
144
163
  ## Composing import maps
145
164
 
146
165
  By default, Rails loads import map definition from the application's `config/importmap.rb` to the `Importmap::Map` object available at `Rails.application.importmap`.
@@ -207,14 +226,16 @@ Import your module on the specific page. Note: you'll likely want to use a `cont
207
226
 
208
227
  ## Include a digest of the import map in your ETag
209
228
 
210
- If you're using [ETags](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) generated by Rails helpers like `stale?` or `fresh_when`, you need to include the digest of the import map into this calculation. Otherwise your application will return 302 cache responses even when your JavaScript assets have changed. You can avoid this with something like:
229
+ If you're using [ETags](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) generated by Rails helpers like `stale?` or `fresh_when`, you need to include the digest of the import map into this calculation. Otherwise your application will return [304](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304) cache responses even when your JavaScript assets have changed. You can avoid this using the `stale_when_importmap_changes` method:
211
230
 
212
231
  ```ruby
213
232
  class ApplicationController < ActionController::Base
214
- etag { Rails.application.importmap.digest(resolver: helpers) if request.format&.html? }
233
+ stale_when_importmap_changes
215
234
  end
216
235
  ```
217
236
 
237
+ This will add the digest of the importmap to the etag calculation when the request format is HTML.
238
+
218
239
 
219
240
  ## Sweeping the cache in development and test
220
241
 
@@ -242,6 +263,15 @@ Importmap for Rails provides two commands to check your pinned packages:
242
263
  - `./bin/importmap outdated` checks the NPM registry for new versions
243
264
  - `./bin/importmap audit` checks the NPM registry for known security issues
244
265
 
266
+ ## Supporting legacy browsers such as Safari on iOS 15
267
+
268
+ If you want to support [legacy browsers that do not support import maps](https://caniuse.com/import-maps) such as [iOS 15.8.1 released on 22 Jan 2024](https://support.apple.com/en-us/HT201222), insert [`es-module-shims`](https://github.com/guybedford/es-module-shims) before `javascript_importmap_tags` as below.
269
+
270
+ ```erb
271
+ <script async src="https://ga.jspm.io/npm:es-module-shims@1.8.2/dist/es-module-shims.js" data-turbo-track="reload"></script>
272
+ <%= javascript_importmap_tags %>
273
+ ```
274
+
245
275
  ## License
246
276
 
247
277
  Importmap for Rails is released under the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,5 @@
1
+ module Importmap::Freshness
2
+ def stale_when_importmap_changes
3
+ etag { Rails.application.importmap.digest(resolver: helpers) if request.format&.html? }
4
+ end
5
+ end
@@ -3,7 +3,7 @@ module Importmap::ImportmapTagsHelper
3
3
  def javascript_importmap_tags(entry_point = "application", importmap: Rails.application.importmap)
4
4
  safe_join [
5
5
  javascript_inline_importmap_tag(importmap.to_json(resolver: self)),
6
- javascript_importmap_module_preload_tags(importmap),
6
+ javascript_importmap_module_preload_tags(importmap, entry_point:),
7
7
  javascript_import_module_tag(entry_point)
8
8
  ], "\n"
9
9
  end
@@ -24,8 +24,8 @@ module Importmap::ImportmapTagsHelper
24
24
  # Link tags for preloading all modules marked as preload: true in the `importmap`
25
25
  # (defaults to Rails.application.importmap), such that they'll be fetched
26
26
  # in advance by browsers supporting this link type (https://caniuse.com/?search=modulepreload).
27
- def javascript_importmap_module_preload_tags(importmap = Rails.application.importmap)
28
- javascript_module_preload_tag(*importmap.preloaded_module_paths(resolver: self))
27
+ def javascript_importmap_module_preload_tags(importmap = Rails.application.importmap, entry_point: "application")
28
+ javascript_module_preload_tag(*importmap.preloaded_module_paths(resolver: self, entry_point:, cache_key: entry_point))
29
29
  end
30
30
 
31
31
  # Link tag(s) for preloading the JavaScript module residing in `*paths`. Will return one link tag per path element.
@@ -46,6 +46,24 @@ class Importmap::Commands < Thor
46
46
  end
47
47
  end
48
48
 
49
+ desc "pristine", "Redownload all pinned packages"
50
+ option :env, type: :string, aliases: :e, default: "production"
51
+ option :from, type: :string, aliases: :f, default: "jspm"
52
+ def pristine
53
+ packages = npm.packages_with_versions.map do |p, v|
54
+ v.blank? ? p : [p, v].join("@")
55
+ end
56
+
57
+ if imports = packager.import(*packages, env: options[:env], from: options[:from])
58
+ imports.each do |package, url|
59
+ puts %(Downloading "#{package}" to #{packager.vendor_path}/#{package}.js from #{url})
60
+ packager.download(package, url)
61
+ end
62
+ else
63
+ puts "Couldn't find any packages in #{packages.inspect} on #{options[:from]}"
64
+ end
65
+ end
66
+
49
67
  desc "json", "Show the full importmap in json"
50
68
  def json
51
69
  require Rails.root.join("config/environment")
@@ -92,7 +110,7 @@ class Importmap::Commands < Thor
92
110
  desc "update", "Update outdated package pins"
93
111
  def update
94
112
  if (outdated_packages = npm.outdated_packages).any?
95
- pin outdated_packages.map(&:name)
113
+ pin(*outdated_packages.map(&:name))
96
114
  else
97
115
  puts "No outdated packages found"
98
116
  end
@@ -11,7 +11,7 @@ module Importmap
11
11
  config.importmap.cache_sweepers = []
12
12
  config.importmap.rescuable_asset_errors = []
13
13
 
14
- config.autoload_once_paths = %W( #{root}/app/helpers )
14
+ config.autoload_once_paths = %W( #{root}/app/helpers #{root}/app/controllers )
15
15
 
16
16
  initializer "importmap" do |app|
17
17
  app.importmap = Importmap::Map.new
@@ -48,6 +48,12 @@ module Importmap
48
48
  end
49
49
  end
50
50
 
51
+ initializer "importmap.concerns" do
52
+ ActiveSupport.on_load(:action_controller_base) do
53
+ extend Importmap::Freshness
54
+ end
55
+ end
56
+
51
57
  initializer "importmap.helpers" do
52
58
  ActiveSupport.on_load(:action_controller_base) do
53
59
  helper Importmap::ImportmapTagsHelper
data/lib/importmap/map.rb CHANGED
@@ -40,9 +40,9 @@ class Importmap::Map
40
40
  # resolver that has been configured for the `asset_host` you want these resolved paths to use. In case you need to
41
41
  # resolve for different asset hosts, you can pass in a custom `cache_key` to vary the cache used by this method for
42
42
  # the different cases.
43
- def preloaded_module_paths(resolver:, cache_key: :preloaded_module_paths)
43
+ def preloaded_module_paths(resolver:, entry_point: "application", cache_key: :preloaded_module_paths)
44
44
  cache_as(cache_key) do
45
- resolve_asset_paths(expanded_preloading_packages_and_directories, resolver: resolver).values
45
+ resolve_asset_paths(expanded_preloading_packages_and_directories(entry_point:), resolver:).values
46
46
  end
47
47
  end
48
48
 
@@ -118,8 +118,8 @@ class Importmap::Map
118
118
  end.compact
119
119
  end
120
120
 
121
- def expanded_preloading_packages_and_directories
122
- expanded_packages_and_directories.select { |name, mapping| mapping.preload }
121
+ def expanded_preloading_packages_and_directories(entry_point:)
122
+ expanded_packages_and_directories.select { |name, mapping| mapping.preload.in?([true, false]) ? mapping.preload : (Array(mapping.preload) & Array(entry_point)).any? }
123
123
  end
124
124
 
125
125
  def expanded_packages_and_directories
@@ -141,7 +141,17 @@ class Importmap::Map
141
141
  end
142
142
 
143
143
  def module_name_from(filename, mapping)
144
- [ mapping.under, filename.to_s.remove(filename.extname).remove(/\/?index$/).presence ].compact.join("/")
144
+ # Regex explanation:
145
+ # (?:\/|^) # Matches either / OR the start of the string
146
+ # index # Matches the word index
147
+ # $ # Matches the end of the string
148
+ #
149
+ # Sample matches
150
+ # index
151
+ # folder/index
152
+ index_regex = /(?:\/|^)index$/
153
+
154
+ [ mapping.under, filename.to_s.remove(filename.extname).remove(index_regex).presence ].compact.join("/")
145
155
  end
146
156
 
147
157
  def module_path_from(filename, mapping)
data/lib/importmap/npm.rb CHANGED
@@ -86,7 +86,7 @@ class Importmap::Npm
86
86
  end
87
87
 
88
88
  def find_latest_version(response)
89
- latest_version = response.dig('dist-tags', 'latest')
89
+ latest_version = response.is_a?(String) ? response : response.dig('dist-tags', 'latest')
90
90
  return latest_version if latest_version
91
91
 
92
92
  return unless response['versions']
@@ -19,10 +19,10 @@ class Importmap::Packager
19
19
 
20
20
  def import(*packages, env: "production", from: "jspm")
21
21
  response = post_json({
22
- "install" => Array(packages),
22
+ "install" => Array(packages),
23
23
  "flattenScope" => true,
24
24
  "env" => [ "browser", "module", env ],
25
- "provider" => from.to_s,
25
+ "provider" => normalize_provider(from)
26
26
  })
27
27
 
28
28
  case response.code
@@ -69,6 +69,10 @@ class Importmap::Packager
69
69
  raise HTTPError, "Unexpected transport error (#{error.class}: #{error.message})"
70
70
  end
71
71
 
72
+ def normalize_provider(name)
73
+ name.to_s == "jspm" ? "jspm.io" : name.to_s
74
+ end
75
+
72
76
  def extract_parsed_imports(response)
73
77
  JSON.parse(response.body).dig("map", "imports")
74
78
  end
@@ -80,7 +84,7 @@ class Importmap::Packager
80
84
  raise HTTPError, "Unexpected response code (#{response.code})"
81
85
  end
82
86
  end
83
-
87
+
84
88
  def parse_service_error(response)
85
89
  JSON.parse(response.body.to_s)["error"]
86
90
  rescue JSON::ParserError
@@ -113,14 +117,16 @@ class Importmap::Packager
113
117
  response = Net::HTTP.get_response(URI(url))
114
118
 
115
119
  if response.code == "200"
116
- save_vendored_package(package, response.body)
120
+ save_vendored_package(package, url, response.body)
117
121
  else
118
122
  handle_failure_response(response)
119
123
  end
120
124
  end
121
125
 
122
- def save_vendored_package(package, source)
126
+ def save_vendored_package(package, url, source)
123
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
+
124
130
  vendored_package.write remove_sourcemap_comment_from(source).force_encoding("UTF-8")
125
131
  end
126
132
  end
@@ -1,3 +1,3 @@
1
1
  module Importmap
2
- VERSION = "2.0.1"
2
+ VERSION = "2.1.0"
3
3
  end
@@ -1,6 +1,9 @@
1
1
  namespace :importmap do
2
2
  desc "Setup Importmap for the app"
3
3
  task :install do
4
- system RbConfig.ruby, "./bin/rails", "app:template", "LOCATION=#{File.expand_path("../install/install.rb", __dir__)}"
4
+ previous_location = ENV["LOCATION"]
5
+ ENV["LOCATION"] = File.expand_path("../install/install.rb", __dir__)
6
+ Rake::Task["app:template"].invoke
7
+ ENV["LOCATION"] = previous_location
5
8
  end
6
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: importmap-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-03 00:00:00.000000000 Z
11
+ date: 2024-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -61,6 +61,7 @@ files:
61
61
  - MIT-LICENSE
62
62
  - README.md
63
63
  - Rakefile
64
+ - app/controllers/importmap/freshness.rb
64
65
  - app/helpers/importmap/importmap_tags_helper.rb
65
66
  - lib/importmap-rails.rb
66
67
  - lib/importmap/commands.rb
@@ -88,14 +89,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
88
89
  requirements:
89
90
  - - ">="
90
91
  - !ruby/object:Gem::Version
91
- version: 2.7.0
92
+ version: 3.1.0
92
93
  required_rubygems_version: !ruby/object:Gem::Requirement
93
94
  requirements:
94
95
  - - ">="
95
96
  - !ruby/object:Gem::Version
96
97
  version: '0'
97
98
  requirements: []
98
- rubygems_version: 3.4.14
99
+ rubygems_version: 3.5.22
99
100
  signing_key:
100
101
  specification_version: 4
101
102
  summary: Use ESM with importmap to manage modern JavaScript in Rails without transpiling