Fix the asset_hash extension to operate on the hash of the rendered output rather than just the source file. This prevents generating the same hash for a file when partials it uses change, or if a helper it uses produces different output. As part of this change I removed the caching from Sitemap::Page#destination_path and asset_hash since they were preventing recalculation of path/hash when partials changed, and I rewrote the Sprockets extension to expose the Sprockets environment to other extension, which included consolidating the JS and CSS Sprockets environments into one.
This commit is contained in:
parent
30c41df909
commit
fc79459e61
|
@ -98,10 +98,10 @@ module Middleman::Cli
|
||||||
# Render a page to a file.
|
# Render a page to a file.
|
||||||
#
|
#
|
||||||
# @param [Middleman::Sitemap::Page] page
|
# @param [Middleman::Sitemap::Page] page
|
||||||
# @return [void]
|
# @return [String] The full path of the file that was written
|
||||||
def render_to_file(page)
|
def render_to_file(page)
|
||||||
build_dir = self.class.shared_instance.build_dir
|
build_dir = self.class.shared_instance.build_dir
|
||||||
output_file = File.join(self.class.shared_instance.build_dir, page.destination_path)
|
output_file = File.join(build_dir, page.destination_path)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
response = self.class.shared_rack.get(page.request_path.gsub(/\s/, "%20"))
|
response = self.class.shared_rack.get(page.request_path.gsub(/\s/, "%20"))
|
||||||
|
@ -114,6 +114,8 @@ module Middleman::Cli
|
||||||
say_status :error, output_file, :red
|
say_status :error, output_file, :red
|
||||||
raise Thor::Error.new $!
|
raise Thor::Error.new $!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
output_file
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -219,9 +221,8 @@ module Middleman::Cli
|
||||||
next if page.ignored?
|
next if page.ignored?
|
||||||
next if @config[:glob] && !File.fnmatch(@config[:glob], page.path)
|
next if @config[:glob] && !File.fnmatch(@config[:glob], page.path)
|
||||||
|
|
||||||
base.render_to_file(page)
|
output_path = base.render_to_file(page)
|
||||||
|
|
||||||
output_path = File.join(@destination, page.destination_path)
|
|
||||||
@cleaning_queue.delete(Pathname.new(output_path).realpath) if cleaning?
|
@cleaning_queue.delete(Pathname.new(output_path).realpath) if cleaning?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -116,15 +116,17 @@ module Middleman::Sitemap
|
||||||
# Render this page
|
# Render this page
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def render(*args, &block)
|
def render(*args, &block)
|
||||||
return unless template?
|
if template?
|
||||||
|
if proxy?
|
||||||
if proxy?
|
t = store.page(proxied_to).template
|
||||||
t = store.page(proxied_to).template
|
t.request_path = path
|
||||||
t.request_path = path
|
t.render(*args)
|
||||||
t.render(*args)
|
else
|
||||||
else
|
template.request_path = path
|
||||||
template.request_path = path
|
template.render(*args, &block)
|
||||||
template.render(*args, &block)
|
end
|
||||||
|
else # just a static file
|
||||||
|
File.open(source_file).read
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -167,9 +169,7 @@ module Middleman::Sitemap
|
||||||
# This path can be affected by proxy callbacks.
|
# This path can be affected by proxy callbacks.
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def destination_path
|
def destination_path
|
||||||
# memoizing this means that reroute callbacks should be in place before the sitemap
|
store.reroute_callbacks.inject(self.path) do |destination, callback|
|
||||||
# gets built
|
|
||||||
@destination_path ||= store.reroute_callbacks.inject(self.path) do |destination, callback|
|
|
||||||
callback.call(destination, self)
|
callback.call(destination, self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -256,7 +256,7 @@ module Middleman::Sitemap
|
||||||
# Clear the cache if the file is deleted
|
# Clear the cache if the file is deleted
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def delete
|
def delete
|
||||||
cache.clear
|
touch
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
|
@ -94,7 +94,6 @@ module Middleman::Sitemap
|
||||||
# @param [String] The destination (output) path of a page.
|
# @param [String] The destination (output) path of a page.
|
||||||
# @return [Middleman::Sitemap::Page]
|
# @return [Middleman::Sitemap::Page]
|
||||||
def page_by_destination(destination_path)
|
def page_by_destination(destination_path)
|
||||||
# TODO: memoize this
|
|
||||||
destination_path = normalize_path(destination_path)
|
destination_path = normalize_path(destination_path)
|
||||||
pages.find do |p|
|
pages.find do |p|
|
||||||
p.destination_path == destination_path ||
|
p.destination_path == destination_path ||
|
||||||
|
|
|
@ -7,8 +7,8 @@ Feature: Assets get a file hash appended to their and references to them are upd
|
||||||
| images/100px-1242c368.png |
|
| images/100px-1242c368.png |
|
||||||
| images/100px-5fd6fb90.jpg |
|
| images/100px-5fd6fb90.jpg |
|
||||||
| images/100px-5fd6fb90.gif |
|
| images/100px-5fd6fb90.gif |
|
||||||
| javascripts/application-1d8d5276.js |
|
| javascripts/application-570e5d45.js |
|
||||||
| stylesheets/site-8c28fde3.css |
|
| stylesheets/site-d9d84711.css |
|
||||||
| index.html |
|
| index.html |
|
||||||
| subdir/index.html |
|
| subdir/index.html |
|
||||||
| other/index.html |
|
| other/index.html |
|
||||||
|
@ -19,48 +19,66 @@ Feature: Assets get a file hash appended to their and references to them are upd
|
||||||
| javascripts/application.js |
|
| javascripts/application.js |
|
||||||
| stylesheets/site.css |
|
| stylesheets/site.css |
|
||||||
|
|
||||||
And the file "javascripts/application-1d8d5276.js" should contain "img.src = '/images/100px-5fd6fb90.jpg'"
|
And the file "javascripts/application-570e5d45.js" should contain "img.src = '/images/100px-5fd6fb90.jpg'"
|
||||||
And the file "stylesheets/site-8c28fde3.css" should contain "background-image: url('../images/100px-5fd6fb90.jpg')"
|
And the file "stylesheets/site-d9d84711.css" should contain "background-image: url('../images/100px-5fd6fb90.jpg')"
|
||||||
And the file "index.html" should contain 'href="stylesheets/site-8c28fde3.css"'
|
And the file "index.html" should contain 'href="stylesheets/site-d9d84711.css"'
|
||||||
And the file "index.html" should contain 'src="javascripts/application-1d8d5276.js"'
|
And the file "index.html" should contain 'src="javascripts/application-570e5d45.js"'
|
||||||
And the file "index.html" should contain 'src="images/100px-5fd6fb90.jpg"'
|
And the file "index.html" should contain 'src="images/100px-5fd6fb90.jpg"'
|
||||||
And the file "subdir/index.html" should contain 'href="../stylesheets/site-8c28fde3.css"'
|
And the file "subdir/index.html" should contain 'href="../stylesheets/site-d9d84711.css"'
|
||||||
And the file "subdir/index.html" should contain 'src="../javascripts/application-1d8d5276.js"'
|
And the file "subdir/index.html" should contain 'src="../javascripts/application-570e5d45.js"'
|
||||||
And the file "subdir/index.html" should contain 'src="../images/100px-5fd6fb90.jpg"'
|
And the file "subdir/index.html" should contain 'src="../images/100px-5fd6fb90.jpg"'
|
||||||
And the file "other/index.html" should contain 'href="../stylesheets/site-8c28fde3.css"'
|
And the file "other/index.html" should contain 'href="../stylesheets/site-d9d84711.css"'
|
||||||
And the file "other/index.html" should contain 'src="../javascripts/application-1d8d5276.js"'
|
And the file "other/index.html" should contain 'src="../javascripts/application-570e5d45.js"'
|
||||||
And the file "other/index.html" should contain 'src="../images/100px-5fd6fb90.jpg"'
|
And the file "other/index.html" should contain 'src="../images/100px-5fd6fb90.jpg"'
|
||||||
|
|
||||||
Scenario: Hashed assets work in preview server
|
Scenario: Hashed assets work in preview server
|
||||||
Given the Server is running at "asset-hash-app"
|
Given the Server is running at "asset-hash-app"
|
||||||
When I go to "/"
|
When I go to "/"
|
||||||
Then I should see 'href="stylesheets/site-8c28fde3.css"'
|
Then I should see 'href="stylesheets/site-d9d84711.css"'
|
||||||
And I should see 'src="javascripts/application-1d8d5276.js"'
|
And I should see 'src="javascripts/application-570e5d45.js"'
|
||||||
And I should see 'src="images/100px-5fd6fb90.jpg"'
|
And I should see 'src="images/100px-5fd6fb90.jpg"'
|
||||||
When I go to "/subdir/"
|
When I go to "/subdir/"
|
||||||
Then I should see 'href="../stylesheets/site-8c28fde3.css"'
|
Then I should see 'href="../stylesheets/site-d9d84711.css"'
|
||||||
And I should see 'src="../javascripts/application-1d8d5276.js"'
|
And I should see 'src="../javascripts/application-570e5d45.js"'
|
||||||
And I should see 'src="../images/100px-5fd6fb90.jpg"'
|
And I should see 'src="../images/100px-5fd6fb90.jpg"'
|
||||||
When I go to "/other/"
|
When I go to "/other/"
|
||||||
Then I should see 'href="../stylesheets/site-8c28fde3.css"'
|
Then I should see 'href="../stylesheets/site-d9d84711.css"'
|
||||||
And I should see 'src="../javascripts/application-1d8d5276.js"'
|
And I should see 'src="../javascripts/application-570e5d45.js"'
|
||||||
And I should see 'src="../images/100px-5fd6fb90.jpg"'
|
And I should see 'src="../images/100px-5fd6fb90.jpg"'
|
||||||
When I go to "/javascripts/application-1d8d5276.js"
|
When I go to "/javascripts/application-570e5d45.js"
|
||||||
Then I should see "img.src = '/images/100px-5fd6fb90.jpg'"
|
Then I should see "img.src = '/images/100px-5fd6fb90.jpg'"
|
||||||
When I go to "/stylesheets/site-8c28fde3.css"
|
When I go to "/stylesheets/site-d9d84711.css"
|
||||||
Then I should see "background-image: url('../images/100px-5fd6fb90.jpg')"
|
Then I should see "background-image: url('../images/100px-5fd6fb90.jpg')"
|
||||||
|
|
||||||
Scenario: Enabling an asset host still produces hashed files and references
|
Scenario: Enabling an asset host still produces hashed files and references
|
||||||
Given the Server is running at "asset-hash-host-app"
|
Given the Server is running at "asset-hash-host-app"
|
||||||
When I go to "/"
|
When I go to "/"
|
||||||
Then I should see 'href="http://middlemanapp.com/stylesheets/site-8c28fde3.css"'
|
Then I should see 'href="http://middlemanapp.com/stylesheets/site-0ac82771.css"'
|
||||||
And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"'
|
And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"'
|
||||||
When I go to "/subdir/"
|
When I go to "/subdir/"
|
||||||
Then I should see 'href="http://middlemanapp.com/stylesheets/site-8c28fde3.css"'
|
Then I should see 'href="http://middlemanapp.com/stylesheets/site-0ac82771.css"'
|
||||||
And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"'
|
And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"'
|
||||||
When I go to "/other/"
|
When I go to "/other/"
|
||||||
Then I should see 'href="http://middlemanapp.com/stylesheets/site-8c28fde3.css"'
|
Then I should see 'href="http://middlemanapp.com/stylesheets/site-0ac82771.css"'
|
||||||
And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"'
|
And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"'
|
||||||
# Asset helpers don't appear to work from Compass right now
|
# Asset helpers don't appear to work from Compass right now
|
||||||
# When I go to "/stylesheets/site-8c28fde3.css"
|
# When I go to "/stylesheets/site-0ac82771.css"
|
||||||
# Then I should see "background-image: url('http://middlemanapp.com/images/100px-5fd6fb90.jpg')"
|
# Then I should see "background-image: url('http://middlemanapp.com/images/100px-5fd6fb90.jpg')"
|
||||||
|
|
||||||
|
Scenario: The asset hash should change when a SASS or Sprockets partial changes
|
||||||
|
Given the Server is running at "asset-hash-app"
|
||||||
|
And the file "source/stylesheets/_partial.sass" has the contents
|
||||||
|
"""
|
||||||
|
body
|
||||||
|
font-size: 14px
|
||||||
|
"""
|
||||||
|
When I go to "/partials/"
|
||||||
|
Then I should see 'href="../stylesheets/uses_partials-e81dd9b4.css'
|
||||||
|
And the file "source/stylesheets/_partial.sass" has the contents
|
||||||
|
"""
|
||||||
|
body
|
||||||
|
font-size: 18px !important
|
||||||
|
"""
|
||||||
|
When I go to "/partials/"
|
||||||
|
Then I should see 'href="../stylesheets/uses_partials-ba3ef309.css'
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<%= stylesheet_link_tag 'uses_partials' %>
|
|
@ -0,0 +1,2 @@
|
||||||
|
body
|
||||||
|
font-size: 18px
|
|
@ -0,0 +1,4 @@
|
||||||
|
@import partial.sass
|
||||||
|
|
||||||
|
red
|
||||||
|
color: blue
|
|
@ -13,44 +13,51 @@ module Middleman::CoreExtensions::Sprockets
|
||||||
app.set :js_compressor, false
|
app.set :js_compressor, false
|
||||||
app.set :css_compressor, false
|
app.set :css_compressor, false
|
||||||
|
|
||||||
|
# Add class methods to context
|
||||||
|
app.send :include, InstanceMethods
|
||||||
|
|
||||||
# Once Middleman is setup
|
# Once Middleman is setup
|
||||||
app.ready do
|
app.ready do
|
||||||
# Create sprockets env for JS and CSS
|
|
||||||
js_env = Middleman::CoreExtensions::Sprockets::JavascriptEnvironment.new(self)
|
|
||||||
css_env = Middleman::CoreExtensions::Sprockets::StylesheetEnvironment.new(self)
|
|
||||||
|
|
||||||
# Add any gems with (vendor|app|.)/assets/javascripts to paths
|
# Add any gems with (vendor|app|.)/assets/javascripts to paths
|
||||||
# also add similar directories from project root (like in rails)
|
# also add similar directories from project root (like in rails)
|
||||||
root_paths = [%w{ app }, %w{ assets }, %w{ vendor }, %w{ app assets }, %w{ vendor assets }]
|
root_paths = [%w{ app }, %w{ assets }, %w{ vendor }, %w{ app assets }, %w{ vendor assets }]
|
||||||
try_js_paths = root_paths.map{|rp| File.join(rp, 'javascripts')}
|
try_paths = root_paths.map {|rp| File.join(rp, 'javascripts') } +
|
||||||
try_css_paths = root_paths.map{|rp| File.join(rp, 'stylesheets')}
|
root_paths.map {|rp| File.join(rp, 'stylesheets') }
|
||||||
|
|
||||||
{ try_js_paths => js_env, try_css_paths => css_env }.each do |paths, env|
|
([root] + ::Middleman.rubygems_latest_specs.map(&:full_gem_path)).each do |root_path|
|
||||||
([root] + ::Middleman.rubygems_latest_specs.map(&:full_gem_path)).each do |root_path|
|
try_paths.map {|p| File.join(root_path, p) }.
|
||||||
paths.map{|p| File.join(root_path, p)}.
|
select {|p| File.directory?(p) }.
|
||||||
select{|p| File.directory?(p)}.
|
each {|path| sprockets.append_path(path) }
|
||||||
each{|path| env.append_path(path)}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Setup Sprockets Sass options
|
# Setup Sprockets Sass options
|
||||||
sass.each { |k, v| ::Sprockets::Sass.options[k] = v }
|
sass.each { |k, v| ::Sprockets::Sass.options[k] = v }
|
||||||
|
|
||||||
# Intercept requests to /javascripts and /stylesheets and pass to sprockets
|
# Intercept requests to /javascripts and /stylesheets and pass to sprockets
|
||||||
map("/#{js_dir}") { run js_env }
|
our_sprockets = sprockets
|
||||||
map("/#{css_dir}"){ run css_env }
|
map("/#{js_dir}") { run our_sprockets }
|
||||||
|
map("/#{css_dir}") { run our_sprockets }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
alias :included :registered
|
alias :included :registered
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module InstanceMethods
|
||||||
|
# @return [Middleman::CoreExtensions::Sprockets::MiddlemanSprocketsEnvironment]
|
||||||
|
def sprockets
|
||||||
|
@sprockets ||= MiddlemanSprocketsEnvironment.new(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Generic Middleman Sprockets env
|
# Generic Middleman Sprockets env
|
||||||
class MiddlemanEnvironment < ::Sprockets::Environment
|
class MiddlemanSprocketsEnvironment < ::Sprockets::Environment
|
||||||
# Setup
|
# Setup
|
||||||
def initialize(app)
|
def initialize(app)
|
||||||
@app = app
|
@app = app
|
||||||
super app.source_dir
|
super app.source_dir
|
||||||
|
|
||||||
|
digest = Digest::SHA1
|
||||||
|
|
||||||
# Make the app context available to Sprockets
|
# Make the app context available to Sprockets
|
||||||
context_class.send(:define_method, :app) { app }
|
context_class.send(:define_method, :app) { app }
|
||||||
context_class.class_eval do
|
context_class.class_eval do
|
||||||
|
@ -62,26 +69,10 @@ module Middleman::CoreExtensions::Sprockets
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# During development, don't use the asset cache
|
# Remove old compressors
|
||||||
def find_asset(path, options = {})
|
|
||||||
expire_index! if @app.development?
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Javascript specific environment
|
|
||||||
class JavascriptEnvironment < MiddlemanEnvironment
|
|
||||||
|
|
||||||
# Init
|
|
||||||
def initialize(app)
|
|
||||||
super
|
|
||||||
|
|
||||||
expire_index!
|
|
||||||
|
|
||||||
# Remove old compressor
|
|
||||||
unregister_bundle_processor 'application/javascript', :js_compressor
|
unregister_bundle_processor 'application/javascript', :js_compressor
|
||||||
|
unregister_bundle_processor 'text/css', :css_compressor
|
||||||
|
|
||||||
# Register compressor from config
|
# Register compressor from config
|
||||||
register_bundle_processor 'application/javascript', :js_compressor do |context, data|
|
register_bundle_processor 'application/javascript', :js_compressor do |context, data|
|
||||||
|
@ -92,29 +83,6 @@ module Middleman::CoreExtensions::Sprockets
|
||||||
end
|
end
|
||||||
end if app.js_compressor
|
end if app.js_compressor
|
||||||
|
|
||||||
# configure search paths
|
|
||||||
append_path app.js_dir
|
|
||||||
end
|
|
||||||
|
|
||||||
# Clear cache on error
|
|
||||||
def javascript_exception_response(exception)
|
|
||||||
expire_index!
|
|
||||||
super(exception)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# CSS specific environment
|
|
||||||
class StylesheetEnvironment < MiddlemanEnvironment
|
|
||||||
|
|
||||||
# Init
|
|
||||||
def initialize(app)
|
|
||||||
super
|
|
||||||
|
|
||||||
expire_index!
|
|
||||||
|
|
||||||
# Remove old compressor
|
|
||||||
unregister_bundle_processor 'text/css', :css_compressor
|
|
||||||
|
|
||||||
# Register compressor from config
|
# Register compressor from config
|
||||||
register_bundle_processor 'text/css', :css_compressor do |context, data|
|
register_bundle_processor 'text/css', :css_compressor do |context, data|
|
||||||
if context.pathname.to_s =~ /\.min\./
|
if context.pathname.to_s =~ /\.min\./
|
||||||
|
@ -125,13 +93,23 @@ module Middleman::CoreExtensions::Sprockets
|
||||||
end if app.css_compressor
|
end if app.css_compressor
|
||||||
|
|
||||||
# configure search paths
|
# configure search paths
|
||||||
|
append_path app.js_dir
|
||||||
append_path app.css_dir
|
append_path app.css_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# During development, don't use the asset cache
|
||||||
|
def find_asset(path, options = {})
|
||||||
|
expire_index! if @app.development?
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
# Clear cache on error
|
# Clear cache on error
|
||||||
def css_exception_response(exception)
|
def javascript_exception_response(exception)
|
||||||
expire_index!
|
expire_index!
|
||||||
super(exception)
|
super(exception)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Clear cache on error
|
||||||
|
alias :css_exception_response :javascript_exception_response
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,10 +8,25 @@ module Middleman::Extensions
|
||||||
app.after_configuration do
|
app.after_configuration do
|
||||||
sitemap.reroute do |destination, page|
|
sitemap.reroute do |destination, page|
|
||||||
if exts.include? page.ext
|
if exts.include? page.ext
|
||||||
page.cache.fetch(:asset_hash) do
|
# figure out the path Sprockets would use for this asset
|
||||||
digest = Digest::SHA1.file(page.source_file).hexdigest[0..7]
|
if page.ext == '.js'
|
||||||
destination.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
|
sprockets_path = page.path.sub(js_dir,'').sub(/^\//,'')
|
||||||
|
elsif page.ext == '.css'
|
||||||
|
sprockets_path = page.path.sub(css_dir,'').sub(/^\//,'')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# See if Sprockets knows about the file
|
||||||
|
asset = sprockets.find_asset(sprockets_path) if sprockets_path
|
||||||
|
|
||||||
|
if asset # if it's a Sprockets asset, ask sprockets for its digest
|
||||||
|
digest = asset.digest[0..7]
|
||||||
|
elsif page.template? # if it's a template, render it out
|
||||||
|
digest = Digest::SHA1.hexdigest(page.render)[0..7]
|
||||||
|
else # if it's a static file, just hash it
|
||||||
|
digest = Digest::SHA1.file(page.source_file).hexdigest[0..7]
|
||||||
|
end
|
||||||
|
|
||||||
|
destination.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
|
||||||
else
|
else
|
||||||
destination
|
destination
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue