diff --git a/vite_rails/lib/vite_rails/tag_helpers.rb b/vite_rails/lib/vite_rails/tag_helpers.rb index 3c4b4ebe..b8bd5460 100644 --- a/vite_rails/lib/vite_rails/tag_helpers.rb +++ b/vite_rails/lib/vite_rails/tag_helpers.rb @@ -26,15 +26,27 @@ def vite_asset_path(name, **options) def vite_javascript_tag(*names, type: 'module', asset_type: :javascript, + integrity: false, skip_preload_tags: false, skip_style_tags: false, crossorigin: 'anonymous', media: 'screen', **options) entries = vite_manifest.resolve_entries(*names, type: asset_type) - tags = javascript_include_tag(*entries.fetch(:scripts), crossorigin: crossorigin, type: type, extname: false, **options) - tags << vite_preload_tag(*entries.fetch(:imports), crossorigin: crossorigin, **options) unless skip_preload_tags + tags = ''.html_safe + + entries.fetch(:main).each do |src, attrs| + tags << javascript_include_tag(src, crossorigin: crossorigin, type: type, extname: false, **attrs, **options) + end + + unless skip_preload_tags + entries.fetch(:imports).each do |href, attrs| + tags << vite_preload_tag(href, crossorigin: crossorigin, **attrs, **options) + end + end + tags << stylesheet_link_tag(*entries.fetch(:stylesheets), media: media, crossorigin: crossorigin, **options) unless skip_style_tags + tags end @@ -44,9 +56,12 @@ def vite_typescript_tag(*names, **options) end # Public: Renders a tag for the specified Vite entrypoints. - def vite_stylesheet_tag(*names, **options) - style_paths = names.map { |name| vite_asset_path(name, type: :stylesheet) } - stylesheet_link_tag(*style_paths, **options) + def vite_stylesheet_tag(*names, integrity: false, **options) + ''.html_safe.tap do |tags| + vite_manifest.resolve_entries(*names, type: :stylesheet).fetch(:main).each do |href, attrs| + tags << stylesheet_link_tag(href, **attrs, **options) + end + end end # Public: Renders an tag for the specified Vite asset. @@ -68,11 +83,13 @@ def vite_manifest end # Internal: Renders a modulepreload link tag. - def vite_preload_tag(*sources, crossorigin:, **options) - sources.map { |source| - href = path_to_asset(source) - try(:request).try(:send_early_hints, 'Link' => %(<#{ href }>; rel=modulepreload; as=script; crossorigin=#{ crossorigin })) - tag.link(rel: 'modulepreload', href: href, as: 'script', crossorigin: crossorigin, **options) - }.join("\n").html_safe + def vite_preload_tag(source, crossorigin:, **options) + href = path_to_asset(source) + try(:request).try(:send_early_hints, 'Link' => %(<#{ href }>; rel=modulepreload; as=script; crossorigin=#{ crossorigin }).tap { |hint| + if integrity = options[:integrity] + hint << "; integrity: #{ integrity }" + end + }) + tag.link(rel: 'modulepreload', href: href, as: 'script', type: 'module', crossorigin: crossorigin, **options) end end diff --git a/vite_ruby/lib/vite_ruby/manifest.rb b/vite_ruby/lib/vite_ruby/manifest.rb index 5c5877af..ceea255a 100644 --- a/vite_ruby/lib/vite_ruby/manifest.rb +++ b/vite_ruby/lib/vite_ruby/manifest.rb @@ -22,16 +22,15 @@ def path_for(name, **options) lookup!(name, **options).fetch('file') end - # Public: Returns scripts, imported modules, and stylesheets for the specified + # Public: Returns entries, imported modules, and stylesheets for the specified # entrypoint files. def resolve_entries(*names, **options) entries = names.map { |name| lookup!(name, **options) } - script_paths = entries.map { |entry| entry.fetch('file') } imports = dev_server_running? ? [] : entries.flat_map { |entry| entry['imports'] }.compact.uniq { - scripts: script_paths, - imports: imports.map { |entry| entry.fetch('file') }.uniq, + main: entries.map(&TO_ENTRY), + imports: imports.map(&TO_ENTRY).uniq, stylesheets: dev_server_running? ? [] : (entries + imports).flat_map { |entry| entry['css'] }.compact.uniq, } end @@ -63,6 +62,9 @@ def react_refresh_preamble protected + # Internal: Returns a [src, attrs] entry. + TO_ENTRY = ->(entry) { [entry.fetch('file'), entry.slice('integrity').symbolize_keys] } + # Internal: Strict version of lookup. # # Returns a relative path for the asset, or raises an error if not found.