mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Automatically set Link header for each stylesheet and script
<link rel="preload"> elements[0] can be serialized in `Link` headers[1] to allow the browser to preload them before it parsed the HTML body. It is particularly useful for scripts included at the bottom of the document. [0] https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content [1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link
This commit is contained in:
parent
70c9f39039
commit
a9012af688
3 changed files with 50 additions and 22 deletions
|
@ -86,11 +86,11 @@ module ActionView
|
||||||
def javascript_include_tag(*sources)
|
def javascript_include_tag(*sources)
|
||||||
options = sources.extract_options!.stringify_keys
|
options = sources.extract_options!.stringify_keys
|
||||||
path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
|
path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
|
||||||
early_hints_links = []
|
preload_links = []
|
||||||
|
|
||||||
sources_tags = sources.uniq.map { |source|
|
sources_tags = sources.uniq.map { |source|
|
||||||
href = path_to_javascript(source, path_options)
|
href = path_to_javascript(source, path_options)
|
||||||
early_hints_links << "<#{href}>; rel=preload; as=script"
|
preload_links << "<#{href}>; rel=preload; as=script"
|
||||||
tag_options = {
|
tag_options = {
|
||||||
"src" => href
|
"src" => href
|
||||||
}.merge!(options)
|
}.merge!(options)
|
||||||
|
@ -100,7 +100,7 @@ module ActionView
|
||||||
content_tag("script", "", tag_options)
|
content_tag("script", "", tag_options)
|
||||||
}.join("\n").html_safe
|
}.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
|
sources_tags
|
||||||
end
|
end
|
||||||
|
@ -136,11 +136,11 @@ module ActionView
|
||||||
def stylesheet_link_tag(*sources)
|
def stylesheet_link_tag(*sources)
|
||||||
options = sources.extract_options!.stringify_keys
|
options = sources.extract_options!.stringify_keys
|
||||||
path_options = options.extract!("protocol", "host", "skip_pipeline").symbolize_keys
|
path_options = options.extract!("protocol", "host", "skip_pipeline").symbolize_keys
|
||||||
early_hints_links = []
|
preload_links = []
|
||||||
|
|
||||||
sources_tags = sources.uniq.map { |source|
|
sources_tags = sources.uniq.map { |source|
|
||||||
href = path_to_stylesheet(source, path_options)
|
href = path_to_stylesheet(source, path_options)
|
||||||
early_hints_links << "<#{href}>; rel=preload; as=style"
|
preload_links << "<#{href}>; rel=preload; as=style"
|
||||||
tag_options = {
|
tag_options = {
|
||||||
"rel" => "stylesheet",
|
"rel" => "stylesheet",
|
||||||
"media" => "screen",
|
"media" => "screen",
|
||||||
|
@ -149,7 +149,7 @@ module ActionView
|
||||||
tag(:link, tag_options)
|
tag(:link, tag_options)
|
||||||
}.join("\n").html_safe
|
}.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
|
sources_tags
|
||||||
end
|
end
|
||||||
|
@ -281,12 +281,12 @@ module ActionView
|
||||||
crossorigin: crossorigin
|
crossorigin: crossorigin
|
||||||
}.merge!(options.symbolize_keys))
|
}.merge!(options.symbolize_keys))
|
||||||
|
|
||||||
early_hints_link = "<#{href}>; rel=preload; as=#{as_type}"
|
preload_link = "<#{href}>; rel=preload; as=#{as_type}"
|
||||||
early_hints_link += "; type=#{mime_type}" if mime_type
|
preload_link += "; type=#{mime_type}" if mime_type
|
||||||
early_hints_link += "; crossorigin=#{crossorigin}" if crossorigin
|
preload_link += "; crossorigin=#{crossorigin}" if crossorigin
|
||||||
early_hints_link += "; nopush" if nopush
|
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
|
link_tag
|
||||||
end
|
end
|
||||||
|
@ -482,6 +482,16 @@ module ActionView
|
||||||
type
|
type
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -84,7 +84,7 @@ class RoutedRackApp
|
||||||
end
|
end
|
||||||
|
|
||||||
class BasicController
|
class BasicController
|
||||||
attr_accessor :request
|
attr_accessor :request, :response
|
||||||
|
|
||||||
def config
|
def config
|
||||||
@config ||= ActiveSupport::InheritableOptions.new(ActionController::Base.config).tap do |config|
|
@config ||= ActiveSupport::InheritableOptions.new(ActionController::Base.config).tap do |config|
|
||||||
|
|
|
@ -9,23 +9,33 @@ ActionView::Template::Types.delegate_to Mime
|
||||||
class AssetTagHelperTest < ActionView::TestCase
|
class AssetTagHelperTest < ActionView::TestCase
|
||||||
tests ActionView::Helpers::AssetTagHelper
|
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
|
def setup
|
||||||
super
|
super
|
||||||
|
|
||||||
@controller = BasicController.new
|
@controller = BasicController.new
|
||||||
|
|
||||||
@request = Class.new do
|
@request = FakeRequest.new
|
||||||
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
|
|
||||||
|
|
||||||
@controller.request = @request
|
@controller.request = @request
|
||||||
|
|
||||||
|
@response = FakeResponse.new
|
||||||
|
@controller.response = @response
|
||||||
end
|
end
|
||||||
|
|
||||||
def url_for(*args)
|
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")
|
assert_dom_equal %(<script src="/javascripts/foo.js"></script>), javascript_include_tag("foo.js")
|
||||||
end
|
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
|
def test_image_path
|
||||||
ImagePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
|
ImagePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue