Merge pull request #39939 from Shopify/link-preload-headers

Automatically set Link header for each stylesheet and script
This commit is contained in:
Eileen M. Uchitelle 2020-08-17 08:37:22 -04:00 committed by GitHub
commit 443c8470d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 22 deletions

View File

@ -86,11 +86,11 @@ module ActionView
def javascript_include_tag(*sources)
options = sources.extract_options!.stringify_keys
path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
early_hints_links = []
preload_links = []
sources_tags = sources.uniq.map { |source|
href = path_to_javascript(source, path_options)
early_hints_links << "<#{href}>; rel=preload; as=script"
preload_links << "<#{href}>; rel=preload; as=script"
tag_options = {
"src" => href
}.merge!(options)
@ -100,7 +100,7 @@ module ActionView
content_tag("script", "", tag_options)
}.join("\n").html_safe
request.send_early_hints("Link" => early_hints_links.join("\n")) if respond_to?(:request) && request
send_preload_links_header(preload_links)
sources_tags
end
@ -136,11 +136,11 @@ module ActionView
def stylesheet_link_tag(*sources)
options = sources.extract_options!.stringify_keys
path_options = options.extract!("protocol", "host", "skip_pipeline").symbolize_keys
early_hints_links = []
preload_links = []
sources_tags = sources.uniq.map { |source|
href = path_to_stylesheet(source, path_options)
early_hints_links << "<#{href}>; rel=preload; as=style"
preload_links << "<#{href}>; rel=preload; as=style"
tag_options = {
"rel" => "stylesheet",
"media" => "screen",
@ -149,7 +149,7 @@ module ActionView
tag(:link, tag_options)
}.join("\n").html_safe
request.send_early_hints("Link" => early_hints_links.join("\n")) if respond_to?(:request) && request
send_preload_links_header(preload_links)
sources_tags
end
@ -281,12 +281,12 @@ module ActionView
crossorigin: crossorigin
}.merge!(options.symbolize_keys))
early_hints_link = "<#{href}>; rel=preload; as=#{as_type}"
early_hints_link += "; type=#{mime_type}" if mime_type
early_hints_link += "; crossorigin=#{crossorigin}" if crossorigin
early_hints_link += "; nopush" if nopush
preload_link = "<#{href}>; rel=preload; as=#{as_type}"
preload_link += "; type=#{mime_type}" if mime_type
preload_link += "; crossorigin=#{crossorigin}" if crossorigin
preload_link += "; nopush" if nopush
request.send_early_hints("Link" => early_hints_link) if respond_to?(:request) && request
send_preload_links_header([preload_link])
link_tag
end
@ -482,6 +482,16 @@ module ActionView
type
end
end
def send_preload_links_header(preload_links)
if respond_to?(:request) && request
request.send_early_hints("Link" => preload_links.join("\n"))
end
if respond_to?(:response) && response
response.headers["Link"] = [response.headers["Link"].presence, *preload_links].compact.join(",")
end
end
end
end
end

View File

@ -84,7 +84,7 @@ class RoutedRackApp
end
class BasicController
attr_accessor :request
attr_accessor :request, :response
def config
@config ||= ActiveSupport::InheritableOptions.new(ActionController::Base.config).tap do |config|

View File

@ -9,23 +9,33 @@ ActionView::Template::Types.delegate_to Mime
class AssetTagHelperTest < ActionView::TestCase
tests ActionView::Helpers::AssetTagHelper
attr_reader :request
attr_reader :request, :response
class FakeRequest
attr_accessor :script_name
def protocol() "http://" end
def ssl?() false end
def host_with_port() "localhost" end
def base_url() "http://www.example.com" end
def send_early_hints(links) end
end
class FakeResponse
def headers
@headers ||= {}
end
end
def setup
super
@controller = BasicController.new
@request = Class.new do
attr_accessor :script_name
def protocol() "http://" end
def ssl?() false end
def host_with_port() "localhost" end
def base_url() "http://www.example.com" end
def send_early_hints(links) end
end.new
@request = FakeRequest.new
@controller.request = @request
@response = FakeResponse.new
@controller.response = @response
end
def url_for(*args)
@ -499,6 +509,14 @@ class AssetTagHelperTest < ActionView::TestCase
assert_dom_equal %(<script src="/javascripts/foo.js"></script>), javascript_include_tag("foo.js")
end
def test_should_set_preload_links
stylesheet_link_tag("http://example.com/style.css")
javascript_include_tag("http://example.com/all.js")
expected = "<http://example.com/style.css>; rel=preload; as=style,<http://example.com/all.js>; rel=preload; as=script"
assert_equal expected, @response.headers["Link"]
end
def test_image_path
ImagePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end