Ensure preload_link_tag preloads modules correctly

Prior to this change, preload_link_tag always used rel=preload

This causes the browser to make a request to preload the script, but
for modules scripts the rel does not match the type, so the browser
can not reuse the prefetched script and discards it.

When passing type="module", it should use rel=modulepreload instead.

[0] developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content
[1] developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link
[2] developer.mozilla.org/en-US/docs/Web/HTML/Link_types/modulepreload
[3] developers.google.com/web/updates/2017/12/modulepreload
This commit is contained in:
Maximo Mussini 2022-01-18 17:29:30 -03:00
parent 2197814074
commit dd870eef11
2 changed files with 12 additions and 2 deletions

View File

@ -325,16 +325,17 @@ module ActionView
crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
integrity = options[:integrity]
nopush = options.delete(:nopush) || false
rel = mime_type == "module" ? "modulepreload" : "preload"
link_tag = tag.link(**{
rel: "preload",
rel: rel,
href: href,
as: as_type,
type: mime_type,
crossorigin: crossorigin
}.merge!(options.symbolize_keys))
preload_link = "<#{href}>; rel=preload; as=#{as_type}"
preload_link = "<#{href}>; rel=#{rel}; as=#{as_type}"
preload_link += "; type=#{mime_type}" if mime_type
preload_link += "; crossorigin=#{crossorigin}" if crossorigin
preload_link += "; integrity=#{integrity}" if integrity

View File

@ -246,6 +246,7 @@ class AssetTagHelperTest < ActionView::TestCase
}
PreloadLinkToTag = {
%(preload_link_tag '/application.js', type: 'module') => %(<link rel="modulepreload" href="/application.js" as="script" type="module" >),
%(preload_link_tag '/styles/custom_theme.css') => %(<link rel="preload" href="/styles/custom_theme.css" as="style" type="text/css" />),
%(preload_link_tag '/videos/video.webm') => %(<link rel="preload" href="/videos/video.webm" as="video" type="video/webm" />),
%(preload_link_tag '/posts.json', as: 'fetch') => %(<link rel="preload" href="/posts.json" as="fetch" type="application/json" />),
@ -605,6 +606,14 @@ class AssetTagHelperTest < ActionView::TestCase
end
end
def test_should_set_preload_early_hints_with_rel_modulepreload
with_preload_links_header do
preload_link_tag("http://example.com/all.js", type: "module")
expected = "<http://example.com/all.js>; rel=modulepreload; as=script; type=module"
assert_equal expected, @response.headers["Link"]
end
end
def test_should_set_preload_links_with_integrity_hashes
with_preload_links_header do
stylesheet_link_tag("http://example.com/style.css", integrity: "sha256-AbpHGcgLb+kRsJGnwFEktk7uzpZOCcBY74+YBdrKVGs")