diff --git a/CHANGELOG.md b/CHANGELOG.md index 99b35e4e..57672727 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ master * Remove deprecated `request` instance * Remove old module-style extension support * Placed all `config.rb` evaluation inside the `ConfigContext` class +* Add :format and :keep_original options to :asset_hash 3.3.0-3.3.2 === diff --git a/middleman-core/features/asset_hash.feature b/middleman-core/features/asset_hash.feature index 59c92d29..936126e4 100644 --- a/middleman-core/features/asset_hash.feature +++ b/middleman-core/features/asset_hash.feature @@ -34,6 +34,60 @@ Feature: Assets get a file hash appended to their and references to them are upd And the file "other/index.html" should contain 'src="../javascripts/application-1d8d5276.js"' And the file "other/index.html" should contain 'src="../images/100px-5fd6fb90.jpg"' + Scenario: Keep Originals + Given a fixture app "asset-hash-app" + And a file named "config.rb" with: + """ + activate :asset_hash, :keep_original => true + """ + And a successfully built app at "asset-hash-app" + When I cd to "build" + Then the following files should exist: + | images/100px-1242c368.png | + | images/100px-5fd6fb90.jpg | + | images/100px-5fd6fb90.gif | + | javascripts/application-1d8d5276.js | + | stylesheets/site-171eb3c0.css | + | images/100px.png | + | images/100px.jpg | + | images/100px.gif | + | javascripts/application.js | + | stylesheets/site.css | + + Scenario: Custom format + Given a fixture app "asset-hash-app" + And a file named "config.rb" with: + """ + activate :asset_hash, :format => ':basename-.-:digest.:ext' + activate :directory_indexes + """ + And a successfully built app at "asset-hash-app" + When I cd to "build" + Then the following files should exist: + | images/100px-.-1242c368.png | + | images/100px-.-5fd6fb90.jpg | + | images/100px-.-5fd6fb90.gif | + | javascripts/application-.-1d8d5276.js | + | stylesheets/site-.-171eb3c0.css | + And the following files should not exist: + | images/100px.png | + | images/100px.jpg | + | images/100px.gif | + | javascripts/application.js | + | stylesheets/site.css | + + And the file "javascripts/application-.-1d8d5276.js" should contain "img.src = '/images/100px-.-5fd6fb90.jpg'" + And the file "stylesheets/site-.-171eb3c0.css" should contain "background-image: url('/images/100px-.-5fd6fb90.jpg')" + And the file "index.html" should contain 'href="/stylesheets/site-.-171eb3c0.css"' + And the file "index.html" should contain 'src="/javascripts/application-.-1d8d5276.js"' + And the file "index.html" should contain 'src="/images/100px-.-5fd6fb90.jpg"' + And the file "subdir/index.html" should contain 'href="/stylesheets/site-.-171eb3c0.css"' + And the file "subdir/index.html" should contain 'src="/javascripts/application-.-1d8d5276.js"' + And the file "subdir/index.html" should contain 'src="/images/100px-.-5fd6fb90.jpg"' + And the file "other/index.html" should contain 'href="/stylesheets/site-.-171eb3c0.css"' + And the file "other/index.html" should contain 'src="/javascripts/application-.-1d8d5276.js"' + And the file "other/index.html" should contain 'src="/images/100px-.-5fd6fb90.jpg"' + Scenario: Hashed assets work in preview server Given the Server is running at "asset-hash-app" When I go to "/" diff --git a/middleman-core/lib/middleman-core/extensions/asset_hash.rb b/middleman-core/lib/middleman-core/extensions/asset_hash.rb index 058b5efd..6673fec6 100644 --- a/middleman-core/lib/middleman-core/extensions/asset_hash.rb +++ b/middleman-core/lib/middleman-core/extensions/asset_hash.rb @@ -3,6 +3,8 @@ require 'middleman-core/util' class Middleman::Extensions::AssetHash < ::Middleman::Extension option :exts, %w(.jpg .jpeg .png .gif .js .css .otf .woff .eot .ttf .svg), 'List of extensions that get asset hashes appended to them.' option :ignore, [], 'Regexes of filenames to skip adding asset hashes to' + option :format, ':basename-:digest.:ext', 'Format of renamed file.' + option :keep_original, false, 'Whether the original file name should exist along side the hashed version.' def initialize(app, options_hash={}, &block) super @@ -24,10 +26,12 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension def manipulate_resource_list(resources) @rack_client = ::Rack::MockRequest.new(app.class.to_rack_app) + proxied_renames = [] + # Process resources in order: binary images and fonts, then SVG, then JS/CSS. # This is so by the time we get around to the text files (which may reference # images and fonts) the static assets' hashes are already calculated. - resources.sort_by do |a| + sorted_resources = resources.sort_by do |a| if %w(.svg).include? a.ext 0 elsif %w(.js .css).include? a.ext @@ -35,21 +39,47 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension else -1 end - end.each(&method(:manipulate_single_resource)) + end.each do |resource| + next unless options.exts.include?(resource.ext) + next if ignored_resource?(resource) + next if resource.ignored? + + new_name = hashed_filename(resource) + + if options.keep_original + p = ::Middleman::Sitemap::Resource.new( + app.sitemap, + new_name + ) + p.proxy_to(resource.path) + + proxied_renames << p + else + resource.destination_path = new_name + end + end + + sorted_resources + proxied_renames end - def manipulate_single_resource(resource) - return unless options.exts.include?(resource.ext) - return if ignored_resource?(resource) - return if resource.ignored? - + def hashed_filename(resource) # Render through the Rack interface so middleware and mounted apps get a shot response = @rack_client.get(URI.escape(resource.destination_path), { 'bypass_asset_hash' => 'true' }) raise "#{resource.path} should be in the sitemap!" unless response.status == 200 digest = Digest::SHA1.hexdigest(response.body)[0..7] - resource.destination_path = resource.destination_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" } + file_name = File.basename(resource.destination_path) + path = resource.destination_path.split(file_name).first + + ext_without_leading_period = resource.ext.sub(/^\./, '') + + base_name = File.basename(file_name, resource.ext) + + path + options.format.dup + .gsub(/:basename/, base_name) + .gsub(/:digest/, digest) + .gsub(/:ext/, ext_without_leading_period) end def ignored_resource?(resource)