diff --git a/middleman-core/features/relative_assets_helpers_only.feature b/middleman-core/features/relative_assets_helpers_only.feature new file mode 100644 index 00000000..9d508977 --- /dev/null +++ b/middleman-core/features/relative_assets_helpers_only.feature @@ -0,0 +1,123 @@ +Feature: Relative Assets (Helpers Only) + + Scenario: Rendering css with the feature enabled + Given a fixture app "relative-assets-app" + And a file named "config.rb" with: + """ + activate :relative_assets, helpers_only: true + """ + And a file named "source/stylesheets/relative_assets.css.sass.erb" with: + """ + h1 + background: url("<%= asset_url('images/blank.gif') %>") + h2 + background: url("<%= asset_url('/images/blank2.gif') %>") + """ + And a file named "source/javascripts/application.js.erb" with: + """ + function foo() { + var img = document.createElement('img'); + img.src = '<%= asset_url("images/100px.jpg") %>'; + var body = document.getElementsByTagName('body')[0]; + body.insertBefore(img, body.firstChild); + } + + window.onload = foo; + """ + And a file named "source/stylesheets/fonts3.css.erb" with: + """ + @font-face { + font-family: 'Roboto2'; + src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot") %>); + src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot?#iefix") %>) format('embedded-opentype'), + url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.woff") %>) format('woff'), + url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.ttf") %>) format('truetype'), + url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.svg#robotoregular") %>) format('svg'); + font-weight: normal; + font-style: normal; + } + """ + And the Server is running at "relative-assets-app" + When I go to "/stylesheets/relative_assets.css" + Then I should see 'url("../images/blank.gif' + And I should see 'url("../images/blank2.gif' + When I go to "/javascripts/application.js" + Then I should not see "../" + When I go to "/stylesheets/fonts3.css" + Then I should see 'url(../fonts/roboto/roboto-regular-webfont.eot' + And I should see 'url(../fonts/roboto/roboto-regular-webfont.woff' + And I should see 'url(../fonts/roboto/roboto-regular-webfont.ttf' + And I should see 'url(../fonts/roboto/roboto-regular-webfont.svg' + + Scenario: Relative css reference with directory indexes + Given a fixture app "relative-assets-app" + And a file named "config.rb" with: + """ + activate :directory_indexes + activate :relative_assets, helpers_only: true + """ + And the Server is running at "relative-assets-app" + When I go to "/relative_image/index.html" + Then I should see "../stylesheets/relative_assets.css" + + Scenario: Relative assets via image_tag + Given a fixture app "relative-assets-app" + And a file named "config.rb" with: + """ + activate :relative_assets, helpers_only: true + """ + And a file named "source/sub/image_tag.html.erb" with: + """ + <%= image_tag '/img/blank.gif' %> + """ + And the Server is running at "relative-assets-app" + When I go to "/sub/image_tag.html" + Then I should see '' + + Scenario: Relative assets should not break data URIs in image_tag + Given a fixture app "relative-assets-app" + And a file named "config.rb" with: + """ + activate :relative_assets, helpers_only: true + """ + And a file named "source/sub/image_tag.html.erb" with: + """ + <%= image_tag "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" %> + """ + And the Server is running at "relative-assets-app" + When I go to "/sub/image_tag.html" + Then I should see '' + + Scenario: URLs are not rewritten for rewrite ignored paths + Given a fixture app "relative-assets-app" + And a file named "config.rb" with: + """ + activate :relative_assets, rewrite_ignore: [ + '/stylesheets/fonts3.css', + ], helpers_only: true + """ + And a file named "source/stylesheets/relative_assets.css.sass.erb" with: + """ + h1 + background: url("<%= asset_url('images/blank.gif') %>") + h2 + background: url("<%= asset_url('/images/blank2.gif') %>") + """ + And a file named "source/stylesheets/fonts3.css.erb" with: + """ + @font-face { + font-family: 'Roboto2'; + src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot") %>); + src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot?#iefix") %>) format('embedded-opentype'), + url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.woff") %>) format('woff'), + url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.ttf") %>) format('truetype'), + url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.svg#robotoregular") %>) format('svg'); + font-weight: normal; + font-style: normal; + } + """ + And the Server is running at "relative-assets-app" + When I go to "/stylesheets/relative_assets.css" + Then I should see 'url("../images/blank.gif' + When I go to "/stylesheets/fonts3.css" + Then I should see 'url(/fonts/roboto/roboto-regular-webfont.eot' diff --git a/middleman-core/fixtures/relative-assets-app/source/images/blank2.gif b/middleman-core/fixtures/relative-assets-app/source/images/blank2.gif new file mode 100644 index 00000000..e69de29b diff --git a/middleman-core/lib/middleman-core/core_extensions/default_helpers.rb b/middleman-core/lib/middleman-core/core_extensions/default_helpers.rb index 33b411e1..774522db 100644 --- a/middleman-core/lib/middleman-core/core_extensions/default_helpers.rb +++ b/middleman-core/lib/middleman-core/core_extensions/default_helpers.rb @@ -1,4 +1,5 @@ require 'padrino-helpers' +require 'middleman-core/contracts' # Don't fail on invalid locale, that's not what our current # users expect. diff --git a/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb b/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb index 24d541fa..43354e97 100644 --- a/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb +++ b/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb @@ -11,13 +11,12 @@ module Middleman expose_to_application rewrite_inline_urls: :add - IGNORE_DESCRIPTOR = Or[Regexp, RespondTo[:call], String] REWRITER_DESCRIPTOR = { id: Symbol, proc: Or[Proc, Method], url_extensions: ArrayOf[String], source_extensions: ArrayOf[String], - ignore: ArrayOf[IGNORE_DESCRIPTOR], + ignore: ArrayOf[::Middleman::Util::IGNORE_DESCRIPTOR], after: Maybe[Symbol] }.freeze @@ -109,7 +108,7 @@ module Middleman next unless source_exts.include?(::File.extname(path)) ignore = rewriter.fetch(:ignore) - next if ignore.any? { |r| should_ignore?(r, full_asset_path) } + next if ignore.any? { |r| ::Middleman::Util.should_ignore?(r, full_asset_path) } rewrite_ignore = Array(rewriter[:rewrite_ignore] || []) next if rewrite_ignore.any? { |i| ::Middleman::Util.path_match(i, path) } @@ -130,24 +129,6 @@ module Middleman headers ).finish end - - Contract IGNORE_DESCRIPTOR, String => Bool - def should_ignore?(validator, value) - if validator.is_a? Regexp - # Treat as Regexp - !!(value =~ validator) - elsif validator.respond_to? :call - # Treat as proc - validator.call(value) - elsif validator.is_a? String - # Treat as glob - File.fnmatch(value, validator) - else - # If some unknown thing, don't ignore - false - end - end - memoize :should_ignore? end end end diff --git a/middleman-core/lib/middleman-core/extensions/relative_assets.rb b/middleman-core/lib/middleman-core/extensions/relative_assets.rb index 33ac420e..853a7005 100644 --- a/middleman-core/lib/middleman-core/extensions/relative_assets.rb +++ b/middleman-core/lib/middleman-core/extensions/relative_assets.rb @@ -2,15 +2,20 @@ require 'addressable/uri' # Relative Assets extension class Middleman::Extensions::RelativeAssets < ::Middleman::Extension - option :exts, nil, 'List of extensions that get cache busters strings appended to them.' + option :exts, nil, 'List of extensions that get converted to relative paths.' option :sources, %w(.css .htm .html .xhtml), 'List of extensions that are searched for relative assets.' - option :ignore, [], 'Regexes of filenames to skip adding query strings to' - option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites' + option :ignore, [], 'Regexes of filenames to skip converting to relative paths.' + option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites.' + option :helpers_only, false, 'Allow only Ruby helpers to change paths.' def initialize(app, options_hash={}, &block) super - app.rewrite_inline_urls id: :asset_hash, + if options[:helpers_only] + return + end + + app.rewrite_inline_urls id: :relative_assets, url_extensions: options.exts || app.config[:asset_extensions], source_extensions: options.sources, ignore: options.ignore, @@ -18,16 +23,37 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension proc: method(:rewrite_url) end - helpers do - # asset_url override for relative assets - # @param [String] path - # @param [String] prefix - # @param [Hash] options Additional options. - # @return [String] - def asset_url(path, prefix='', options={}) - options[:relative] = true unless options.key?(:relative) + def mark_as_relative(file_path, opts, current_resource) + result = opts.dup - super(path, prefix, options) + valid_exts = options.sources + + return result unless current_resource + return result unless valid_exts.include?(current_resource.ext) + + rewrite_ignores = Array(options.rewrite_ignore || []) + + path = current_resource.destination_path + return result if rewrite_ignores.any? do |i| + ::Middleman::Util.path_match(i, path) || ::Middleman::Util.path_match(i, "/#{path}") + end + + return result if Array(options.ignore || []).any? do |r| + ::Middleman::Util.should_ignore?(r, file_path) + end + + result[:relative] = true unless result.key?(:relative) + + result + end + + helpers do + def asset_url(path, prefix='', options={}) + super(path, prefix, app.extensions[:relative_assets].mark_as_relative(super, options, current_resource)) + end + + def asset_path(kind, source, options={}) + super(kind, source, app.extensions[:relative_assets].mark_as_relative(super, options, current_resource)) end end diff --git a/middleman-core/lib/middleman-core/util/paths.rb b/middleman-core/lib/middleman-core/util/paths.rb index fa25e818..26f29442 100644 --- a/middleman-core/lib/middleman-core/util/paths.rb +++ b/middleman-core/lib/middleman-core/util/paths.rb @@ -45,6 +45,25 @@ module Middleman end memoize :strip_leading_slash + IGNORE_DESCRIPTOR = Or[Regexp, RespondTo[:call], String] + Contract IGNORE_DESCRIPTOR, String => Bool + def should_ignore?(validator, value) + if validator.is_a? Regexp + # Treat as Regexp + !!(value =~ validator) + elsif validator.respond_to? :call + # Treat as proc + validator.call(value) + elsif validator.is_a? String + # Treat as glob + File.fnmatch(value, validator) + else + # If some unknown thing, don't ignore + false + end + end + memoize :should_ignore? + # Get the path of a file of a given type # # @param [Middleman::Application] app The app.