Class: Importmap::Map
- Inherits:
-
Object
- Object
- Importmap::Map
- Defined in:
- lib/importmap/map.rb
Defined Under Namespace
Classes: InvalidFile, MappedDir, MappedFile
Constant Summary collapse
- PIN_REGEX =
:nodoc:
/^pin\s+["']([^"']+)["']/.freeze
Instance Attribute Summary collapse
-
#directories ⇒ Object
readonly
Returns the value of attribute directories.
-
#packages ⇒ Object
readonly
Returns the value of attribute packages.
Class Method Summary collapse
Instance Method Summary collapse
-
#cache_sweeper(watches: nil) ⇒ Object
Returns an instance of ActiveSupport::EventedFileUpdateChecker configured to clear the cache of the map when the directories passed on initialization via ‘watches:` have changes.
-
#digest(resolver:) ⇒ Object
Returns a SHA1 digest of the import map json that can be used as a part of a page etag to ensure that a html cache is invalidated when the import map is changed.
- #draw(path = nil, &block) ⇒ Object
-
#enable_integrity! ⇒ Object
Enables automatic integrity hash calculation for all pinned modules.
-
#initialize ⇒ Map
constructor
A new instance of Map.
- #pin(name, to: nil, preload: true, integrity: true) ⇒ Object
- #pin_all_from(dir, under: nil, to: nil, preload: true, integrity: true) ⇒ Object
-
#preloaded_module_packages(resolver:, entry_point: "application", cache_key: :preloaded_module_packages) ⇒ Object
Returns a hash of resolved module paths to their corresponding package objects for all pinned packages that are marked for preloading.
-
#preloaded_module_paths(resolver:, entry_point: "application", cache_key: :preloaded_module_paths) ⇒ Object
Returns an array of all the resolved module paths of the pinned packages.
-
#to_json(resolver:, cache_key: :json) ⇒ Object
Returns a JSON hash (as a string) of all the resolved module paths of the pinned packages in the import map format.
Constructor Details
#initialize ⇒ Map
Returns a new instance of Map.
14 15 16 17 18 |
# File 'lib/importmap/map.rb', line 14 def initialize @integrity = false @packages, @directories = {}, {} @cache = {} end |
Instance Attribute Details
#directories ⇒ Object (readonly)
Returns the value of attribute directories.
4 5 6 |
# File 'lib/importmap/map.rb', line 4 def directories @directories end |
#packages ⇒ Object (readonly)
Returns the value of attribute packages.
4 5 6 |
# File 'lib/importmap/map.rb', line 4 def packages @packages end |
Class Method Details
.pin_line_regexp_for(package) ⇒ Object
:nodoc:
8 9 10 |
# File 'lib/importmap/map.rb', line 8 def self.pin_line_regexp_for(package) # :nodoc: /^.*pin\s+["']#{Regexp.escape(package)}["'].*$/.freeze end |
Instance Method Details
#cache_sweeper(watches: nil) ⇒ Object
Returns an instance of ActiveSupport::EventedFileUpdateChecker configured to clear the cache of the map when the directories passed on initialization via ‘watches:` have changes. This is used in development and test to ensure the map caches are reset when javascript files are changed.
185 186 187 188 189 190 191 192 193 194 |
# File 'lib/importmap/map.rb', line 185 def cache_sweeper(watches: nil) if watches @cache_sweeper = Rails.application.config.file_watcher.new([], Array(watches).collect { |dir| [ dir.to_s, "js"] }.to_h) do clear_cache end else @cache_sweeper end end |
#digest(resolver:) ⇒ Object
Returns a SHA1 digest of the import map json that can be used as a part of a page etag to ensure that a html cache is invalidated when the import map is changed.
Example:
class ApplicationController < ActionController::Base
etag { Rails.application.importmap.digest(resolver: helpers) if request.format&.html? }
end
178 179 180 |
# File 'lib/importmap/map.rb', line 178 def digest(resolver:) Digest::SHA1.hexdigest(to_json(resolver: resolver).to_s) end |
#draw(path = nil, &block) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/importmap/map.rb', line 20 def draw(path = nil, &block) if path && File.exist?(path) begin instance_eval(File.read(path), path.to_s) rescue StandardError => e Rails.logger.error "Unable to parse import map from #{path}: #{e.}" raise InvalidFile, "Unable to parse import map from #{path}: #{e.}" end elsif block_given? instance_eval(&block) end self end |
#enable_integrity! ⇒ Object
Enables automatic integrity hash calculation for all pinned modules.
When enabled, integrity values are included in the importmap JSON for all pinned modules. For local assets served by the Rails asset pipeline, integrity hashes are automatically calculated when integrity: true is specified. For modules with explicit integrity values, those values are included as provided. This provides Subresource Integrity (SRI) protection to ensure JavaScript modules haven’t been tampered with.
Clears the importmap cache when called to ensure fresh integrity hashes are generated.
Examples
# config/importmap.rb
enable_integrity!
# These will now auto-calculate integrity hashes
pin "application" # integrity: true by default
pin "admin", to: "admin.js" # integrity: true by default
pin_all_from "app/javascript/lib" # integrity: true by default
# Manual control still works
pin "no_integrity", integrity: false
pin "custom_hash", integrity: "sha384-abc123..."
Notes
-
Integrity calculation is disabled by default and must be explicitly enabled
-
Requires asset pipeline support for integrity calculation (Sprockets or Propshaft 1.2+)
-
For Propshaft, you must configure
config.assets.integrity_hash_algorithm
-
External CDN packages should provide their own integrity hashes
67 68 69 70 |
# File 'lib/importmap/map.rb', line 67 def enable_integrity! clear_cache @integrity = true end |
#pin(name, to: nil, preload: true, integrity: true) ⇒ Object
72 73 74 75 |
# File 'lib/importmap/map.rb', line 72 def pin(name, to: nil, preload: true, integrity: true) clear_cache @packages[name] = MappedFile.new(name: name, path: to || "#{name}.js", preload: preload, integrity: integrity) end |
#pin_all_from(dir, under: nil, to: nil, preload: true, integrity: true) ⇒ Object
77 78 79 80 |
# File 'lib/importmap/map.rb', line 77 def pin_all_from(dir, under: nil, to: nil, preload: true, integrity: true) clear_cache @directories[dir] = MappedDir.new(dir: dir, under: under, path: to, preload: preload, integrity: integrity) end |
#preloaded_module_packages(resolver:, entry_point: "application", cache_key: :preloaded_module_packages) ⇒ Object
Returns a hash of resolved module paths to their corresponding package objects for all pinned packages that are marked for preloading. The hash keys are the resolved asset paths, and the values are the MappedFile
objects containing package metadata including name, path, preload setting, and integrity.
The resolver
must respond to path_to_asset
, such as ActionController::Base.helpers
or ApplicationController.helpers
. You’ll want to use the resolver that has been configured for the asset_host
you want these resolved paths to use.
Parameters
resolver
-
An object that responds to
path_to_asset
for resolving asset paths. entry_point
-
The entry point name or array of entry point names to determine which packages should be preloaded. Defaults to “application”. Packages with preload: true are always included regardless of entry point. Packages with specific entry point names (e.g., preload: “admin”) are only included when that entry point is specified.
cache_key
-
A custom cache key to vary the cache used by this method for different cases, such as resolving for different asset hosts. Defaults to
:preloaded_module_packages
.
Returns
A hash where:
-
Keys are resolved asset paths (strings)
-
Values are
MappedFile
objects withname
,path
,preload
, andintegrity
attributes
Missing assets are gracefully handled and excluded from the returned hash.
Examples
# Get all preloaded packages for the default "application" entry point
packages = importmap.preloaded_module_packages(resolver: ApplicationController.helpers)
# => { "/assets/application-abc123.js" => #<struct name="application", path="application.js", preload=true, integrity=nil>,
# "https://cdn.skypack.dev/react" => #<struct name="react", path="https://cdn.skypack.dev/react", preload=true, integrity="sha384-..."> }
# Get preloaded packages for a specific entry point
packages = importmap.preloaded_module_packages(resolver: helpers, entry_point: "admin")
# Get preloaded packages for multiple entry points
packages = importmap.preloaded_module_packages(resolver: helpers, entry_point: ["application", "admin"])
# Use a custom cache key for different asset hosts
packages = importmap.preloaded_module_packages(resolver: helpers, cache_key: "cdn_host")
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/importmap/map.rb', line 137 def preloaded_module_packages(resolver:, entry_point: "application", cache_key: :preloaded_module_packages) cache_as(cache_key) do (entry_point:).filter_map do |_, package| resolved_path = resolve_asset_path(package.path, resolver: resolver) next unless resolved_path resolved_integrity = resolve_integrity_value(package.integrity, package.path, resolver: resolver) package = MappedFile.new( name: package.name, path: package.path, preload: package.preload, integrity: resolved_integrity ) [resolved_path, package] end.to_h end end |
#preloaded_module_paths(resolver:, entry_point: "application", cache_key: :preloaded_module_paths) ⇒ Object
Returns an array of all the resolved module paths of the pinned packages. The ‘resolver` must respond to `path_to_asset`, such as `ActionController::Base.helpers` or `ApplicationController.helpers`. You’ll want to use the resolver that has been configured for the ‘asset_host` you want these resolved paths to use. In case you need to resolve for different asset hosts, you can pass in a custom `cache_key` to vary the cache used by this method for the different cases.
87 88 89 |
# File 'lib/importmap/map.rb', line 87 def preloaded_module_paths(resolver:, entry_point: "application", cache_key: :preloaded_module_paths) preloaded_module_packages(resolver: resolver, entry_point: entry_point, cache_key: cache_key).keys end |
#to_json(resolver:, cache_key: :json) ⇒ Object
Returns a JSON hash (as a string) of all the resolved module paths of the pinned packages in the import map format. The ‘resolver` must respond to `path_to_asset`, such as `ActionController::Base.helpers` or `ApplicationController.helpers`. You’ll want to use the resolver that has been configured for the ‘asset_host` you want these resolved paths to use. In case you need to resolve for different asset hosts, you can pass in a custom `cache_key` to vary the cache used by this method for the different cases.
162 163 164 165 166 167 168 |
# File 'lib/importmap/map.rb', line 162 def to_json(resolver:, cache_key: :json) cache_as(cache_key) do packages = map = build_import_map(packages, resolver: resolver) JSON.pretty_generate(map) end end |