Compare commits
6 commits
master
...
feature/pi
Author | SHA1 | Date | |
---|---|---|---|
|
439ecb1887 | ||
|
87e0f240ff | ||
|
2804a61c61 | ||
|
0d3030f28c | ||
|
029de6613b | ||
|
11e478fad9 |
47 changed files with 379 additions and 618 deletions
|
@ -1,8 +1,9 @@
|
|||
master
|
||||
===
|
||||
|
||||
# Next
|
||||
# 4.2.0
|
||||
|
||||
* Remove Rack support in favor of `resource.filters << proc { |oldbody| newbody }`
|
||||
* Expose `development?` and `production?` helpers to template context.
|
||||
|
||||
# 4.1.8
|
||||
|
|
|
@ -188,23 +188,6 @@ Feature: Assets get file hashes appended to them and references to them are upda
|
|||
When I go to "/partials/"
|
||||
Then I should see 'href="../stylesheets/uses_partials-ec347271.css'
|
||||
|
||||
Scenario: The asset hash should change when a Rack-based filter changes
|
||||
Given a fixture app "asset-hash-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
activate :asset_hash
|
||||
activate :relative_assets
|
||||
activate :directory_indexes
|
||||
require 'lib/middleware.rb'
|
||||
use ::Middleware
|
||||
"""
|
||||
Given the Server is running at "asset-hash-app"
|
||||
When I go to "/"
|
||||
Then I should see 'href="stylesheets/site-5ad7def0.css'
|
||||
When I go to "stylesheets/site-5ad7def0.css"
|
||||
Then I should see 'background-image: url("../images/100px-5fd6fb90.jpg")'
|
||||
Then I should see 'Added by Rack filter'
|
||||
|
||||
Scenario: Hashed-asset files are not produced for ignored paths
|
||||
Given a fixture app "asset-hash-app"
|
||||
And a file named "config.rb" with:
|
||||
|
|
17
middleman-core/features/endpoints.feature
Normal file
17
middleman-core/features/endpoints.feature
Normal file
|
@ -0,0 +1,17 @@
|
|||
Feature: Generic block based pages
|
||||
|
||||
Scenario: Static Ruby Endpoints
|
||||
Given an empty app
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
endpoint "hello.html" do
|
||||
"world"
|
||||
end
|
||||
"""
|
||||
And a file named "source/index.html.erb" with:
|
||||
"""
|
||||
Hi
|
||||
"""
|
||||
And the Server is running at "empty_app"
|
||||
When I go to "/hello.html"
|
||||
Then I should see "world"
|
|
@ -3,8 +3,9 @@ Feature: Extension author could use some hooks
|
|||
Scenario: When build
|
||||
Given a fixture app "extension-api-deprecations-app"
|
||||
When I run `middleman build`
|
||||
Then the exit status should be 0
|
||||
And the exit status should be 0
|
||||
And the output should contain "`set :layout` is deprecated"
|
||||
And the output should contain "Project built successfully"
|
||||
And the file "build/index.html" should contain "In Index"
|
||||
And the file "build/index.html" should not contain "In Layout"
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ Feature: Extension author could use some hooks
|
|||
And the output should contain "/// after_configuration ///"
|
||||
And the output should contain "/// ready ///"
|
||||
And the output should contain "/// before_build ///"
|
||||
And the output should contain "/// before ///"
|
||||
And the output should contain "/// before_render ///"
|
||||
And the output should contain "/// after_render ///"
|
||||
And the output should contain "/// after_build ///"
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
Feature: Support Rack apps mounted using map
|
||||
|
||||
Scenario: Mounted Rack App at /sinatra
|
||||
Given the Server is running at "sinatra-app"
|
||||
When I go to "/"
|
||||
Then I should see "Hello World (Middleman)"
|
||||
When I go to "/sinatra/"
|
||||
Then I should see "Hello World (Sinatra)"
|
||||
|
||||
Scenario: Built Mounted Rack App at /sinatra
|
||||
Given a successfully built app at "sinatra-app"
|
||||
When I cd to "build"
|
||||
Then the following files should exist:
|
||||
| index.html |
|
||||
Then the following files should not exist:
|
||||
| sinatra/index.html |
|
||||
| sinatra/index2.html |
|
||||
|
||||
Scenario: Static Ruby Endpoints
|
||||
Given a fixture app "sinatra-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
endpoint "hello.html" do
|
||||
"world"
|
||||
end
|
||||
"""
|
||||
And the Server is running at "sinatra-app"
|
||||
When I go to "/hello.html"
|
||||
Then I should see "world"
|
||||
|
||||
Scenario: Built Mounted Rack App at /sinatra (including rack endpoints)
|
||||
Given a fixture app "sinatra-app"
|
||||
And a file named "config.rb" with:
|
||||
"""
|
||||
require "sinatra"
|
||||
|
||||
class MySinatra < Sinatra::Base
|
||||
get "/" do
|
||||
"Hello World (Sinatra)"
|
||||
end
|
||||
get "/derp.html" do
|
||||
"De doo"
|
||||
end
|
||||
end
|
||||
|
||||
map "/sinatra" do
|
||||
run MySinatra
|
||||
end
|
||||
|
||||
endpoint "sinatra/index2.html", path: "/sinatra/"
|
||||
|
||||
endpoint "dedoo.html", path: "/sinatra/derp.html"
|
||||
|
||||
endpoint "hello.html" do
|
||||
"world"
|
||||
end
|
||||
"""
|
||||
And a successfully built app at "sinatra-app"
|
||||
When I cd to "build"
|
||||
Then the following files should exist:
|
||||
| index.html |
|
||||
| sinatra/index2.html |
|
||||
| dedoo.html |
|
||||
And the file "sinatra/index2.html" should contain 'Hello World (Sinatra)'
|
||||
And the file "dedoo.html" should contain 'De doo'
|
|
@ -1,14 +0,0 @@
|
|||
require "sinatra"
|
||||
|
||||
class MySinatra < Sinatra::Base
|
||||
get "/" do
|
||||
"Hello World (Sinatra)"
|
||||
end
|
||||
get "/derp.html" do
|
||||
"De doo"
|
||||
end
|
||||
end
|
||||
|
||||
map "/sinatra" do
|
||||
run MySinatra
|
||||
end
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
layout: false
|
||||
---
|
||||
|
||||
Hello World (Middleman)
|
|
@ -157,12 +157,12 @@ module Middleman
|
|||
define_setting :layout, :_auto_layout, 'Default layout name'
|
||||
|
||||
# Which file extensions have a layout by default.
|
||||
# @return [Array.<String>]
|
||||
define_setting :extensions_with_layout, %w(.htm .html .xhtml .php), 'Which file extensions have a layout by default.'
|
||||
# @return [Set.<String>]
|
||||
define_setting :extensions_with_layout, %w(.htm .html .xhtml .php), 'Which file extensions have a layout by default.', set: true
|
||||
|
||||
# Which file extensions are "assets."
|
||||
# @return [Array.<String>]
|
||||
define_setting :asset_extensions, %w(.css .png .jpg .jpeg .webp .svg .svgz .js .gif .ttf .otf .woff .woff2 .eot .ico .map), 'Which file extensions are treated as assets.'
|
||||
define_setting :asset_extensions, %w(.css .png .jpg .jpeg .webp .svg .svgz .js .gif .ttf .otf .woff .woff2 .eot .ico .map), 'Which file extensions are treated as assets.', set: true
|
||||
|
||||
# Default string encoding for templates and output.
|
||||
# @return [String]
|
||||
|
|
|
@ -2,7 +2,6 @@ require 'pathname'
|
|||
require 'fileutils'
|
||||
require 'tempfile'
|
||||
require 'parallel'
|
||||
require 'middleman-core/rack'
|
||||
require 'middleman-core/callback_manager'
|
||||
require 'middleman-core/contracts'
|
||||
|
||||
|
@ -39,9 +38,6 @@ module Middleman
|
|||
@cleaning = opts.fetch(:clean)
|
||||
@parallel = opts.fetch(:parallel, true)
|
||||
|
||||
rack_app = ::Middleman::Rack.new(@app).to_app
|
||||
@rack = ::Rack::MockRequest.new(rack_app)
|
||||
|
||||
@callbacks = ::Middleman::CallbackManager.new
|
||||
@callbacks.install_methods!(self, [:on_build_event])
|
||||
end
|
||||
|
@ -227,15 +223,7 @@ module Middleman
|
|||
if resource.binary?
|
||||
export_file!(output_file, resource.file_descriptor[:full_path])
|
||||
else
|
||||
response = @rack.get(::URI.escape(resource.request_path))
|
||||
|
||||
# If we get a response, save it to a tempfile.
|
||||
if response.status == 200
|
||||
export_file!(output_file, binary_encode(response.body))
|
||||
else
|
||||
trigger(:error, output_file, response.body)
|
||||
return false
|
||||
end
|
||||
export_file!(output_file, binary_encode(resource.render))
|
||||
end
|
||||
rescue => e
|
||||
trigger(:error, output_file, "#{e}\n#{e.backtrace.join("\n")}")
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'set'
|
||||
|
||||
module Middleman
|
||||
module Configuration
|
||||
# A class that manages a collection of documented settings.
|
||||
|
@ -129,23 +131,37 @@ module Middleman
|
|||
|
||||
def initialize(key, default, description, options={})
|
||||
@value_set = false
|
||||
@array_wrapped_value = nil
|
||||
@array_wrapped_default = nil
|
||||
self.key = key
|
||||
self.default = default
|
||||
self.description = description
|
||||
self.options = options
|
||||
|
||||
@array_wrapped_default = if self.default && options[:set] && self.default.is_a?(Array)
|
||||
Set.new(self.default)
|
||||
end
|
||||
end
|
||||
|
||||
# The user-supplied value for this setting, overriding the default
|
||||
def value=(value)
|
||||
@value = value
|
||||
@value_set = true
|
||||
|
||||
@array_wrapped_value = if @value && options[:set] && @value.is_a?(Array)
|
||||
Set.new(@value)
|
||||
end
|
||||
end
|
||||
|
||||
# The effective value of the setting, which may be the default
|
||||
# if the user has not set a value themselves. Note that even if the
|
||||
# user sets the value to nil it will override the default.
|
||||
def value
|
||||
value_set? ? @value : default
|
||||
if value_set?
|
||||
@array_wrapped_value ? @array_wrapped_value : @value
|
||||
else
|
||||
@array_wrapped_default ? @array_wrapped_default : default
|
||||
end
|
||||
end
|
||||
|
||||
# Whether or not there has been a value set beyond the default
|
||||
|
|
|
@ -19,12 +19,6 @@ Middleman::Extensions.register :data, auto_activate: :before_sitemap do
|
|||
Middleman::CoreExtensions::Data
|
||||
end
|
||||
|
||||
# Rewrite embedded URLs via Rack
|
||||
Middleman::Extensions.register :inline_url_rewriter, auto_activate: :before_sitemap do
|
||||
require 'middleman-core/core_extensions/inline_url_rewriter'
|
||||
Middleman::CoreExtensions::InlineURLRewriter
|
||||
end
|
||||
|
||||
# Catch and show exceptions at the Rack level
|
||||
Middleman::Extensions.register :show_exceptions, auto_activate: :before_configuration, modes: [:server] do
|
||||
require 'middleman-core/core_extensions/show_exceptions'
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
require 'rack'
|
||||
require 'rack/response'
|
||||
require 'memoist'
|
||||
require 'middleman-core/util'
|
||||
require 'middleman-core/contracts'
|
||||
|
||||
module Middleman
|
||||
module CoreExtensions
|
||||
class InlineURLRewriter < ::Middleman::Extension
|
||||
include Contracts
|
||||
|
||||
expose_to_application rewrite_inline_urls: :add
|
||||
|
||||
REWRITER_DESCRIPTOR = {
|
||||
id: Symbol,
|
||||
proc: Or[Proc, Method],
|
||||
url_extensions: ArrayOf[String],
|
||||
source_extensions: ArrayOf[String],
|
||||
ignore: ArrayOf[::Middleman::Util::IGNORE_DESCRIPTOR],
|
||||
after: Maybe[Symbol]
|
||||
}.freeze
|
||||
|
||||
def initialize(app, options_hash={}, &block)
|
||||
super
|
||||
|
||||
@rewriters = {}
|
||||
end
|
||||
|
||||
Contract REWRITER_DESCRIPTOR => Any
|
||||
def add(options)
|
||||
@rewriters[options] = options
|
||||
end
|
||||
|
||||
def after_configuration
|
||||
return if @rewriters.empty?
|
||||
|
||||
rewriters = @rewriters.values.sort do |a, b|
|
||||
if b[:after] && b[:after] == a[:id]
|
||||
1
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
|
||||
app.use Rack, rewriters: rewriters, middleman_app: @app
|
||||
end
|
||||
|
||||
class Rack
|
||||
extend Memoist
|
||||
include Contracts
|
||||
|
||||
Contract RespondTo[:call], {
|
||||
middleman_app: IsA['Middleman::Application'],
|
||||
rewriters: ArrayOf[REWRITER_DESCRIPTOR]
|
||||
} => Any
|
||||
def initialize(app, options={})
|
||||
@rack_app = app
|
||||
@middleman_app = options.fetch(:middleman_app)
|
||||
@rewriters = options.fetch(:rewriters)
|
||||
|
||||
all_source_exts = @rewriters
|
||||
.reduce([]) { |sum, rewriter| sum + rewriter[:source_extensions] }
|
||||
.flatten
|
||||
.uniq
|
||||
@source_exts_regex_text = Regexp.union(all_source_exts).to_s
|
||||
|
||||
@all_asset_exts = @rewriters
|
||||
.reduce([]) { |sum, rewriter| sum + rewriter[:url_extensions] }
|
||||
.flatten
|
||||
.uniq
|
||||
end
|
||||
|
||||
def call(env)
|
||||
status, headers, response = @rack_app.call(env)
|
||||
|
||||
# Allow configuration or upstream request to skip all rewriting
|
||||
return [status, headers, response] if env['bypass_inline_url_rewriter'] == 'true'
|
||||
|
||||
path = ::Middleman::Util.full_path(env['PATH_INFO'], @middleman_app)
|
||||
|
||||
return [status, headers, response] unless path =~ /(^\/$)|(#{@source_exts_regex_text}$)/
|
||||
return [status, headers, response] unless body = ::Middleman::Util.extract_response_text(response)
|
||||
|
||||
dirpath = ::Pathname.new(File.dirname(path))
|
||||
|
||||
rewritten = ::Middleman::Util.instrument 'inline_url_rewriter', path: path do
|
||||
::Middleman::Util.rewrite_paths(body, path, @all_asset_exts, @middleman_app) do |asset_path|
|
||||
uri = ::Middleman::Util.parse_uri(asset_path)
|
||||
|
||||
relative_path = uri.host.nil?
|
||||
|
||||
full_asset_path = if relative_path
|
||||
dirpath.join(asset_path).to_s
|
||||
else
|
||||
asset_path
|
||||
end
|
||||
|
||||
@rewriters.each do |rewriter|
|
||||
uid = rewriter.fetch(:id)
|
||||
|
||||
# Allow upstream request to skip this specific rewriting
|
||||
next if env["bypass_inline_url_rewriter_#{uid}"] == 'true'
|
||||
|
||||
exts = rewriter.fetch(:url_extensions)
|
||||
next unless exts.include?(::File.extname(asset_path))
|
||||
|
||||
source_exts = rewriter.fetch(:source_extensions)
|
||||
next unless source_exts.include?(::File.extname(path))
|
||||
|
||||
ignore = rewriter.fetch(:ignore)
|
||||
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) }
|
||||
|
||||
proc = rewriter.fetch(:proc)
|
||||
|
||||
result = proc.call(asset_path, dirpath, path)
|
||||
asset_path = result if result
|
||||
end
|
||||
|
||||
asset_path
|
||||
end
|
||||
end
|
||||
|
||||
::Rack::Response.new(
|
||||
rewritten,
|
||||
status,
|
||||
headers
|
||||
).finish
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -60,7 +60,7 @@ module Middleman
|
|||
# There are also some less common hooks that can be listened to from within an extension's `initialize` method:
|
||||
#
|
||||
# * `app.before_render {|body, path, locs, template_class| ... }` - Manipulate template sources before they are rendered.
|
||||
# * `app.after_render {|content, path, locs, template_class| ... }` - Manipulate output text after a template has been rendered. It is also common to install a Rack middleware to do this instead.
|
||||
# * `app.after_render {|content, path, locs, template_class| ... }` - Manipulate output text after a template has been rendered.
|
||||
# * `app.ready { ... }` - Run code once Middleman is ready to serve or build files (after `after_configuration`).
|
||||
|
||||
#
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
require 'middleman-core/util'
|
||||
require 'middleman-core/rack'
|
||||
|
||||
class Middleman::Extensions::AssetHash < ::Middleman::Extension
|
||||
option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for hashable assets.'
|
||||
option :exts, nil, 'List of extensions that get asset hashes appended to them.'
|
||||
option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for hashable assets.', set: true
|
||||
option :exts, nil, 'List of extensions that get asset hashes appended to them.', set: true
|
||||
option :ignore, [], 'Regexes of filenames to skip adding asset hashes to'
|
||||
option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites'
|
||||
|
||||
def initialize(app, options_hash={}, &block)
|
||||
super
|
||||
|
||||
require 'addressable/uri'
|
||||
require 'digest/sha1'
|
||||
require 'rack/mock'
|
||||
|
||||
# Allow specifying regexes to ignore, plus always ignore apple touch icons
|
||||
@ignore = Array(options.ignore) + [/^apple-touch-icon/]
|
||||
|
@ -20,14 +17,6 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
|
|||
# Exclude .ico from the default list because browsers expect it
|
||||
# to be named "favicon.ico"
|
||||
@exts = options.exts || (app.config[:asset_extensions] - %w(.ico))
|
||||
|
||||
app.rewrite_inline_urls id: :asset_hash,
|
||||
url_extensions: @exts.sort.reverse,
|
||||
source_extensions: options.sources,
|
||||
ignore: @ignore,
|
||||
rewrite_ignore: options.rewrite_ignore,
|
||||
proc: method(:rewrite_url),
|
||||
after: :asset_host
|
||||
end
|
||||
|
||||
Contract String, Or[String, Pathname], Any => Maybe[String]
|
||||
|
@ -45,7 +34,6 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
|
|||
|
||||
replacement_path = "/#{asset_page.destination_path}"
|
||||
replacement_path = Pathname.new(replacement_path).relative_path_from(dirpath).to_s if relative_path
|
||||
|
||||
replacement_path
|
||||
end
|
||||
|
||||
|
@ -53,9 +41,18 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
|
|||
# @return Array<Middleman::Sitemap::Resource>
|
||||
Contract ResourceList => ResourceList
|
||||
def manipulate_resource_list(resources)
|
||||
@rack_client ||= begin
|
||||
rack_app = ::Middleman::Rack.new(app).to_app
|
||||
::Rack::MockRequest.new(rack_app)
|
||||
resources.each do |r|
|
||||
next unless r.destination_path.end_with?('/', *options.sources)
|
||||
next if Array(options.rewrite_ignore || []).any? do |i|
|
||||
::Middleman::Util.path_match(i, "/#{r.destination_path}")
|
||||
end
|
||||
|
||||
r.filters << ::Middleman::InlineURLRewriter.new(:asset_hash,
|
||||
app,
|
||||
r,
|
||||
url_extensions: @exts,
|
||||
ignore: options.ignore,
|
||||
proc: method(:rewrite_url))
|
||||
end
|
||||
|
||||
# Process resources in order: binary images and fonts, then SVG, then JS/CSS.
|
||||
|
@ -81,15 +78,9 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
|
|||
digest = if resource.binary?
|
||||
::Digest::SHA1.file(resource.source_file).hexdigest[0..7]
|
||||
else
|
||||
# Render through the Rack interface so middleware and mounted apps get a shot
|
||||
response = @rack_client.get(
|
||||
::URI.escape(resource.destination_path),
|
||||
'bypass_inline_url_rewriter_asset_hash' => 'true'
|
||||
)
|
||||
|
||||
raise "#{resource.path} should be in the sitemap!" unless response.status == 200
|
||||
|
||||
::Digest::SHA1.hexdigest(response.body)[0..7]
|
||||
# Render without asset hash
|
||||
body = resource.render { |f| !f.respond_to?(:filter_name) || f.filter_name != :asset_hash }
|
||||
::Digest::SHA1.hexdigest(body)[0..7]
|
||||
end
|
||||
|
||||
resource.destination_path = resource.destination_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
require 'addressable/uri'
|
||||
|
||||
class Middleman::Extensions::AssetHost < ::Middleman::Extension
|
||||
option :host, nil, 'The asset host to use or a Proc to determine asset host', required: true
|
||||
option :exts, nil, 'List of extensions that get cache busters strings appended to them.'
|
||||
|
@ -7,15 +5,22 @@ class Middleman::Extensions::AssetHost < ::Middleman::Extension
|
|||
option :ignore, [], 'Regexes of filenames to skip adding query strings to'
|
||||
option :rewrite_ignore, [], 'Regexes of filenames to skip processing for host rewrites'
|
||||
|
||||
def initialize(app, options_hash={}, &block)
|
||||
super
|
||||
Contract ResourceList => ResourceList
|
||||
def manipulate_resource_list(resources)
|
||||
resources.each do |r|
|
||||
next unless r.destination_path.end_with?('/', *options.sources)
|
||||
next if Array(options.rewrite_ignore || []).any? do |i|
|
||||
::Middleman::Util.path_match(i, "/#{r.destination_path}")
|
||||
end
|
||||
|
||||
app.rewrite_inline_urls id: :asset_host,
|
||||
url_extensions: options.exts || app.config[:asset_extensions],
|
||||
source_extensions: options.sources,
|
||||
ignore: options.ignore,
|
||||
rewrite_ignore: options.rewrite_ignore,
|
||||
proc: method(:rewrite_url)
|
||||
r.filters << ::Middleman::InlineURLRewriter.new(:asset_host,
|
||||
app,
|
||||
r,
|
||||
after_filter: :asset_hash,
|
||||
url_extensions: options.exts || app.config[:asset_extensions],
|
||||
ignore: options.ignore,
|
||||
proc: method(:rewrite_url))
|
||||
end
|
||||
end
|
||||
|
||||
Contract String, Or[String, Pathname], Any => String
|
||||
|
|
|
@ -1,19 +1,25 @@
|
|||
# The Cache Buster extension
|
||||
class Middleman::Extensions::CacheBuster < ::Middleman::Extension
|
||||
option :exts, nil, 'List of extensions that get cache busters strings appended to them.'
|
||||
option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for bustable assets.'
|
||||
option :exts, nil, 'List of extensions that get cache busters strings appended to them.', set: true
|
||||
option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for bustable assets.', set: true
|
||||
option :ignore, [], 'Regexes of filenames to skip adding query strings to'
|
||||
option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites'
|
||||
|
||||
def initialize(app, options_hash={}, &block)
|
||||
super
|
||||
Contract ResourceList => ResourceList
|
||||
def manipulate_resource_list(resources)
|
||||
resources.each do |r|
|
||||
next unless r.destination_path.end_with?('/', *options.sources)
|
||||
next if Array(options.rewrite_ignore || []).any? do |i|
|
||||
::Middleman::Util.path_match(i, "/#{r.destination_path}")
|
||||
end
|
||||
|
||||
app.rewrite_inline_urls id: :cache_buster,
|
||||
url_extensions: options.exts || app.config[:asset_extensions],
|
||||
source_extensions: options.sources,
|
||||
ignore: options.ignore,
|
||||
rewrite_ignore: options.rewrite_ignore,
|
||||
proc: method(:rewrite_url)
|
||||
r.filters << ::Middleman::InlineURLRewriter.new(:cache_buster,
|
||||
app,
|
||||
r,
|
||||
url_extensions: options.exts || app.config[:asset_extensions],
|
||||
ignore: options.ignore,
|
||||
proc: method(:rewrite_url))
|
||||
end
|
||||
end
|
||||
|
||||
Contract String, Or[String, Pathname], Any => String
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'set'
|
||||
|
||||
# Directory Indexes extension
|
||||
class Middleman::Extensions::DirectoryIndexes < ::Middleman::Extension
|
||||
# This should run after most other sitemap manipulators so that it
|
||||
|
@ -11,7 +13,7 @@ class Middleman::Extensions::DirectoryIndexes < ::Middleman::Extension
|
|||
index_file = app.config[:index_file]
|
||||
new_index_path = "/#{index_file}"
|
||||
|
||||
extensions = %w(.htm .html .php .xhtml)
|
||||
extensions = Set.new(%w(.htm .html .php .xhtml))
|
||||
|
||||
resources.each do |resource|
|
||||
# Check if it would be pointless to reroute
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
# to .css, .htm, .html, .js, and .xhtml
|
||||
#
|
||||
class Middleman::Extensions::Gzip < ::Middleman::Extension
|
||||
option :exts, %w(.css .htm .html .js .svg .xhtml), 'File extensions to Gzip when building.'
|
||||
option :exts, %w(.css .htm .html .js .svg .xhtml), 'File extensions to Gzip when building.', set: true
|
||||
option :ignore, [], 'Patterns to avoid gzipping'
|
||||
option :overwrite, false, 'Overwrite original files instead of adding .gz extension.'
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require 'memoist'
|
||||
require 'middleman-core/contracts'
|
||||
require 'rack/mime'
|
||||
|
||||
# Minify CSS Extension
|
||||
class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
||||
|
@ -9,17 +9,10 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
|||
require 'sass'
|
||||
SassCompressor
|
||||
}, 'Set the CSS compressor to use.'
|
||||
option :content_types, %w(text/css), 'Content types of resources that contain CSS'
|
||||
option :inline_content_types, %w(text/html text/php), 'Content types of resources that contain inline CSS'
|
||||
option :content_types, %w(text/css), 'Content types of resources that contain CSS', set: true
|
||||
option :inline_content_types, %w(text/html text/php), 'Content types of resources that contain inline CSS', set: true
|
||||
|
||||
def ready
|
||||
# Setup Rack middleware to minify CSS
|
||||
app.use Rack, compressor: options[:compressor],
|
||||
ignore: Array(options[:ignore]) + [/\.min\./],
|
||||
inline: options[:inline],
|
||||
content_types: options[:content_types],
|
||||
inline_content_types: options[:inline_content_types]
|
||||
end
|
||||
INLINE_CSS_REGEX = /(<style[^>]*>\s*(?:\/\*<!\[CDATA\[\*\/\n)?)(.*?)((?:(?:\n\s*)?\/\*\]\]>\*\/)?\s*<\/style>)/m
|
||||
|
||||
class SassCompressor
|
||||
def self.compress(style, options={})
|
||||
|
@ -29,97 +22,61 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
|||
end
|
||||
end
|
||||
|
||||
# Rack middleware to look for CSS and compress it
|
||||
class Rack
|
||||
extend Memoist
|
||||
include Contracts
|
||||
INLINE_CSS_REGEX = /(<style[^>]*>\s*(?:\/\*<!\[CDATA\[\*\/\n)?)(.*?)((?:(?:\n\s*)?\/\*\]\]>\*\/)?\s*<\/style>)/m
|
||||
def initialize(app, options_hash={}, &block)
|
||||
super
|
||||
|
||||
# Init
|
||||
# @param [Class] app
|
||||
# @param [Hash] options
|
||||
Contract RespondTo[:call], {
|
||||
ignore: ArrayOf[PATH_MATCHER],
|
||||
inline: Bool,
|
||||
compressor: Or[Proc, RespondTo[:to_proc], RespondTo[:compress]]
|
||||
} => Any
|
||||
def initialize(app, options={})
|
||||
@app = app
|
||||
@ignore = options.fetch(:ignore)
|
||||
@inline = options.fetch(:inline)
|
||||
|
||||
@compressor = options.fetch(:compressor)
|
||||
@compressor = @compressor.to_proc if @compressor.respond_to? :to_proc
|
||||
@compressor = @compressor.call if @compressor.is_a? Proc
|
||||
@content_types = options[:content_types]
|
||||
@inline_content_types = options[:inline_content_types]
|
||||
end
|
||||
|
||||
# Rack interface
|
||||
# @param [Rack::Environmemt] env
|
||||
# @return [Array]
|
||||
def call(env)
|
||||
status, headers, response = @app.call(env)
|
||||
|
||||
content_type = headers['Content-Type'].try(:slice, /^[^;]*/)
|
||||
path = env['PATH_INFO']
|
||||
|
||||
minified = if @inline && minifiable_inline?(content_type)
|
||||
minify_inline(::Middleman::Util.extract_response_text(response))
|
||||
elsif minifiable?(content_type) && !ignore?(path)
|
||||
minify(::Middleman::Util.extract_response_text(response))
|
||||
end
|
||||
|
||||
if minified
|
||||
headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s
|
||||
response = [minified]
|
||||
end
|
||||
|
||||
[status, headers, response]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Whether the path should be ignored
|
||||
# @param [String] path
|
||||
# @return [Boolean]
|
||||
def ignore?(path)
|
||||
@ignore.any? { |ignore| ::Middleman::Util.path_match(ignore, path) }
|
||||
end
|
||||
memoize :ignore?
|
||||
|
||||
# Whether this type of content can be minified
|
||||
# @param [String, nil] content_type
|
||||
# @return [Boolean]
|
||||
def minifiable?(content_type)
|
||||
@content_types.include?(content_type)
|
||||
end
|
||||
memoize :minifiable?
|
||||
|
||||
# Whether this type of content contains inline content that can be minified
|
||||
# @param [String, nil] content_type
|
||||
# @return [Boolean]
|
||||
def minifiable_inline?(content_type)
|
||||
@inline_content_types.include?(content_type)
|
||||
end
|
||||
memoize :minifiable_inline?
|
||||
|
||||
# Minify the content
|
||||
# @param [String] content
|
||||
# @return [String]
|
||||
def minify(content)
|
||||
@compressor.compress(content)
|
||||
end
|
||||
memoize :minify
|
||||
|
||||
# Detect and minify inline content
|
||||
# @param [String] content
|
||||
# @return [String]
|
||||
def minify_inline(content)
|
||||
content.gsub(INLINE_CSS_REGEX) do
|
||||
$1 + minify($2) + $3
|
||||
end
|
||||
end
|
||||
memoize :minify_inline
|
||||
@ignore = Array(options[:ignore]) + [/\.min\./]
|
||||
@compressor = options[:compressor]
|
||||
@compressor = @compressor.to_proc if @compressor.respond_to? :to_proc
|
||||
@compressor = @compressor.call if @compressor.is_a? Proc
|
||||
end
|
||||
|
||||
Contract ResourceList => ResourceList
|
||||
def manipulate_resource_list(resources)
|
||||
resources.each do |r|
|
||||
type = r.content_type.try(:slice, /^[^;]*/)
|
||||
if options[:inline] && minifiable_inline?(type)
|
||||
r.filters << method(:minify_inline)
|
||||
elsif minifiable?(type) && !ignore?(r.destination_path)
|
||||
r.filters << method(:minify)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Whether the path should be ignored
|
||||
Contract String => Bool
|
||||
def ignore?(path)
|
||||
@ignore.any? { |ignore| ::Middleman::Util.path_match(ignore, path) }
|
||||
end
|
||||
memoize :ignore?
|
||||
|
||||
# Whether this type of content can be minified
|
||||
Contract Maybe[String] => Bool
|
||||
def minifiable?(content_type)
|
||||
options[:content_types].include?(content_type)
|
||||
end
|
||||
memoize :minifiable?
|
||||
|
||||
# Whether this type of content contains inline content that can be minified
|
||||
Contract Maybe[String] => Bool
|
||||
def minifiable_inline?(content_type)
|
||||
options[:inline_content_types].include?(content_type)
|
||||
end
|
||||
memoize :minifiable_inline?
|
||||
|
||||
# Minify the content
|
||||
Contract String => String
|
||||
def minify(content)
|
||||
@compressor.compress(content)
|
||||
end
|
||||
memoize :minify
|
||||
|
||||
# Detect and minify inline content
|
||||
Contract String => String
|
||||
def minify_inline(content)
|
||||
content.gsub(INLINE_CSS_REGEX) do
|
||||
$1 + minify($2) + $3
|
||||
end
|
||||
end
|
||||
memoize :minify_inline
|
||||
end
|
||||
|
|
|
@ -9,122 +9,79 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
|
|||
require 'uglifier'
|
||||
::Uglifier.new
|
||||
}, 'Set the JS compressor to use.'
|
||||
option :content_types, %w(application/javascript), 'Content types of resources that contain JS'
|
||||
option :inline_content_types, %w(text/html text/php), 'Content types of resources that contain inline JS'
|
||||
option :content_types, %w(application/javascript), 'Content types of resources that contain JS', set: true
|
||||
option :inline_content_types, %w(text/html text/php), 'Content types of resources that contain inline JS', set: true
|
||||
|
||||
def ready
|
||||
# Setup Rack middleware to minify JS
|
||||
app.use Rack, compressor: options[:compressor],
|
||||
ignore: Array(options[:ignore]) + [/\.min\./],
|
||||
inline: options[:inline],
|
||||
content_types: options[:content_types],
|
||||
inline_content_types: options[:inline_content_types]
|
||||
INLINE_JS_REGEX = /(<script[^>]*>\s*(?:\/\/(?:(?:<!--)|(?:<!\[CDATA\[))\n)?)(.*?)((?:(?:\n\s*)?\/\/(?:(?:-->)|(?:\]\]>)))?\s*<\/script>)/m
|
||||
|
||||
def initialize(app, options_hash={}, &block)
|
||||
super
|
||||
|
||||
@ignore = Array(options[:ignore]) + [/\.min\./]
|
||||
@compressor = options[:compressor]
|
||||
@compressor = @compressor.to_proc if @compressor.respond_to? :to_proc
|
||||
@compressor = @compressor.call if @compressor.is_a? Proc
|
||||
end
|
||||
|
||||
# Rack middleware to look for JS and compress it
|
||||
class Rack
|
||||
extend Memoist
|
||||
include Contracts
|
||||
INLINE_JS_REGEX = /(<script[^>]*>\s*(?:\/\/(?:(?:<!--)|(?:<!\[CDATA\[))\n)?)(.*?)((?:(?:\n\s*)?\/\/(?:(?:-->)|(?:\]\]>)))?\s*<\/script>)/m
|
||||
|
||||
# Init
|
||||
# @param [Class] app
|
||||
# @param [Hash] options
|
||||
Contract RespondTo[:call], {
|
||||
ignore: ArrayOf[PATH_MATCHER],
|
||||
inline: Bool,
|
||||
compressor: Or[Proc, RespondTo[:to_proc], RespondTo[:compress]]
|
||||
} => Any
|
||||
def initialize(app, options={})
|
||||
@app = app
|
||||
@ignore = options.fetch(:ignore)
|
||||
@inline = options.fetch(:inline)
|
||||
|
||||
@compressor = options.fetch(:compressor)
|
||||
@compressor = @compressor.to_proc if @compressor.respond_to? :to_proc
|
||||
@compressor = @compressor.call if @compressor.is_a? Proc
|
||||
@content_types = options[:content_types]
|
||||
@inline_content_types = options[:inline_content_types]
|
||||
end
|
||||
|
||||
# Rack interface
|
||||
# @param [Rack::Environmemt] env
|
||||
# @return [Array]
|
||||
def call(env)
|
||||
status, headers, response = @app.call(env)
|
||||
|
||||
type = headers['Content-Type'].try(:slice, /^[^;]*/)
|
||||
@path = env['PATH_INFO']
|
||||
|
||||
minified = if @inline && minifiable_inline?(type)
|
||||
minify_inline(::Middleman::Util.extract_response_text(response))
|
||||
elsif minifiable?(type) && !ignore?(@path)
|
||||
minify(::Middleman::Util.extract_response_text(response))
|
||||
end
|
||||
|
||||
if minified
|
||||
headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s
|
||||
response = [minified]
|
||||
end
|
||||
|
||||
[status, headers, response]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Whether the path should be ignored
|
||||
# @param [String] path
|
||||
# @return [Boolean]
|
||||
def ignore?(path)
|
||||
@ignore.any? { |ignore| Middleman::Util.path_match(ignore, path) }
|
||||
end
|
||||
memoize :ignore?
|
||||
|
||||
# Whether this type of content can be minified
|
||||
# @param [String, nil] content_type
|
||||
# @return [Boolean]
|
||||
def minifiable?(content_type)
|
||||
@content_types.include?(content_type)
|
||||
end
|
||||
memoize :minifiable?
|
||||
|
||||
# Whether this type of content contains inline content that can be minified
|
||||
# @param [String, nil] content_type
|
||||
# @return [Boolean]
|
||||
def minifiable_inline?(content_type)
|
||||
@inline_content_types.include?(content_type)
|
||||
end
|
||||
memoize :minifiable_inline?
|
||||
|
||||
# Minify the content
|
||||
# @param [String] content
|
||||
# @return [String]
|
||||
def minify(content)
|
||||
@compressor.compress(content)
|
||||
rescue ExecJS::ProgramError => e
|
||||
warn "WARNING: Couldn't compress JavaScript in #{@path}: #{e.message}"
|
||||
content
|
||||
end
|
||||
memoize :minify
|
||||
|
||||
# Detect and minify inline content
|
||||
# @param [String] content
|
||||
# @return [String]
|
||||
def minify_inline(content)
|
||||
content.gsub(INLINE_JS_REGEX) do |match|
|
||||
first = $1
|
||||
inline_content = $2
|
||||
last = $3
|
||||
|
||||
# Only compress script tags that contain JavaScript (as opposed to
|
||||
# something like jQuery templates, identified with a "text/html" type).
|
||||
if !first.include?('type=') || first.include?('text/javascript')
|
||||
first + minify(inline_content) + last
|
||||
else
|
||||
match
|
||||
end
|
||||
Contract ResourceList => ResourceList
|
||||
def manipulate_resource_list(resources)
|
||||
resources.each do |r|
|
||||
type = r.content_type.try(:slice, /^[^;]*/)
|
||||
if options[:inline] && minifiable_inline?(type)
|
||||
r.filters << method(:minify_inline)
|
||||
elsif minifiable?(type) && !ignore?(r.destination_path)
|
||||
r.filters << method(:minify)
|
||||
end
|
||||
end
|
||||
memoize :minify_inline
|
||||
end
|
||||
|
||||
# Whether the path should be ignored
|
||||
Contract String => Bool
|
||||
def ignore?(path)
|
||||
@ignore.any? { |ignore| ::Middleman::Util.path_match(ignore, path) }
|
||||
end
|
||||
memoize :ignore?
|
||||
|
||||
# Whether this type of content can be minified
|
||||
Contract Maybe[String] => Bool
|
||||
def minifiable?(content_type)
|
||||
options[:content_types].include?(content_type)
|
||||
end
|
||||
memoize :minifiable?
|
||||
|
||||
# Whether this type of content contains inline content that can be minified
|
||||
Contract Maybe[String] => Bool
|
||||
def minifiable_inline?(content_type)
|
||||
options[:inline_content_types].include?(content_type)
|
||||
end
|
||||
memoize :minifiable_inline?
|
||||
|
||||
# Minify the content
|
||||
Contract String => String
|
||||
def minify(content)
|
||||
@compressor.compress(content)
|
||||
rescue ::ExecJS::ProgramError => e
|
||||
warn "WARNING: Couldn't compress JavaScript in #{@path}: #{e.message}"
|
||||
content
|
||||
end
|
||||
memoize :minify
|
||||
|
||||
# Detect and minify inline content
|
||||
Contract String => String
|
||||
def minify_inline(content)
|
||||
content.gsub(INLINE_JS_REGEX) do |match|
|
||||
first = $1
|
||||
inline_content = $2
|
||||
last = $3
|
||||
|
||||
# Only compress script tags that contain JavaScript (as opposed to
|
||||
# something like jQuery templates, identified with a "text/html" type).
|
||||
if !first.include?('type=') || first.include?('text/javascript')
|
||||
first + minify(inline_content) + last
|
||||
else
|
||||
match
|
||||
end
|
||||
end
|
||||
end
|
||||
memoize :minify_inline
|
||||
end
|
||||
|
|
|
@ -1,26 +1,28 @@
|
|||
require 'addressable/uri'
|
||||
|
||||
# Relative Assets extension
|
||||
class Middleman::Extensions::RelativeAssets < ::Middleman::Extension
|
||||
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 :exts, nil, 'List of extensions that get converted to relative paths.', set: true
|
||||
option :sources, %w(.css .htm .html .xhtml), 'List of extensions that are searched for relative assets.', set: true
|
||||
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
|
||||
Contract ResourceList => ResourceList
|
||||
def manipulate_resource_list(resources)
|
||||
return resources if options[:helpers_only]
|
||||
|
||||
if options[:helpers_only]
|
||||
return
|
||||
resources.each do |r|
|
||||
next unless r.destination_path.end_with?('/', *options.sources)
|
||||
next if Array(options.rewrite_ignore || []).any? do |i|
|
||||
::Middleman::Util.path_match(i, "/#{r.destination_path}")
|
||||
end
|
||||
|
||||
r.filters << ::Middleman::InlineURLRewriter.new(:relative_assets,
|
||||
app,
|
||||
r,
|
||||
url_extensions: options.exts || app.config[:asset_extensions],
|
||||
ignore: options.ignore,
|
||||
proc: method(:rewrite_url))
|
||||
end
|
||||
|
||||
app.rewrite_inline_urls id: :relative_assets,
|
||||
url_extensions: options.exts || app.config[:asset_extensions],
|
||||
source_extensions: options.sources,
|
||||
ignore: options.ignore,
|
||||
rewrite_ignore: options.rewrite_ignore,
|
||||
proc: method(:rewrite_url)
|
||||
end
|
||||
|
||||
def mark_as_relative(file_path, opts, current_resource)
|
||||
|
@ -53,7 +55,7 @@ class Middleman::Extensions::RelativeAssets < ::Middleman::Extension
|
|||
end
|
||||
|
||||
def asset_path(kind, source, options={})
|
||||
super(kind, source, app.extensions[:relative_assets].mark_as_relative(super, options, current_resource))
|
||||
super(kind, source, app.extensions[:relative_assets].mark_as_relative(super, options, current_resource))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
49
middleman-core/lib/middleman-core/inline_url_rewriter.rb
Normal file
49
middleman-core/lib/middleman-core/inline_url_rewriter.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
require 'middleman-core/util'
|
||||
require 'middleman-core/contracts'
|
||||
|
||||
module Middleman
|
||||
class InlineURLRewriter
|
||||
include Contracts
|
||||
|
||||
attr_reader :filter_name
|
||||
attr_reader :after_filter
|
||||
|
||||
def initialize(filter_name, app, resource, options={})
|
||||
@filter_name = filter_name
|
||||
@app = app
|
||||
@resource = resource
|
||||
@options = options
|
||||
|
||||
@after_filter = @options.fetch(:after_filter, nil)
|
||||
end
|
||||
|
||||
Contract String => String
|
||||
def execute_filter(body)
|
||||
path = "/#{@resource.destination_path}"
|
||||
dirpath = ::Pathname.new(File.dirname(path))
|
||||
|
||||
::Middleman::Util.instrument 'inline_url_rewriter', path: path do
|
||||
::Middleman::Util.rewrite_paths(body, path, @options.fetch(:url_extensions), @app) do |asset_path|
|
||||
uri = ::Middleman::Util.parse_uri(asset_path)
|
||||
|
||||
relative_path = uri.host.nil?
|
||||
full_asset_path = if relative_path
|
||||
dirpath.join(asset_path).to_s
|
||||
else
|
||||
asset_path
|
||||
end
|
||||
|
||||
exts = @options.fetch(:url_extensions)
|
||||
next unless exts.include?(::File.extname(asset_path))
|
||||
|
||||
next if @options.fetch(:ignore).any? { |r| ::Middleman::Util.should_ignore?(r, full_asset_path) }
|
||||
|
||||
result = @options.fetch(:proc).call(asset_path, dirpath, path)
|
||||
asset_path = result if result
|
||||
|
||||
asset_path
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -93,9 +93,6 @@ module Middleman
|
|||
request_path = ::Middleman::Util.full_path(request_path, @middleman)
|
||||
full_request_path = File.join(env['SCRIPT_NAME'], request_path) # Path including rack mount
|
||||
|
||||
# Run before callbacks
|
||||
@middleman.execute_callbacks(:before)
|
||||
|
||||
# Get the resource object for this path
|
||||
resource = @middleman.sitemap.find_resource_by_destination_path(request_path.gsub(' ', '%20'))
|
||||
|
||||
|
|
|
@ -9,14 +9,13 @@ module Middleman
|
|||
# Expose `endpoint`
|
||||
expose_to_config :endpoint
|
||||
|
||||
EndpointDescriptor = Struct.new(:path, :request_path, :block) do
|
||||
EndpointDescriptor = Struct.new(:path, :block) do
|
||||
def execute_descriptor(app, resources)
|
||||
r = EndpointResource.new(
|
||||
r = ::Middleman::Sitemap::CallbackResource.new(
|
||||
app.sitemap,
|
||||
path,
|
||||
request_path
|
||||
&block
|
||||
)
|
||||
r.output = block if block
|
||||
|
||||
resources + [r]
|
||||
end
|
||||
|
@ -24,43 +23,10 @@ module Middleman
|
|||
|
||||
# Setup a proxy from a path to a target
|
||||
# @param [String] path
|
||||
# @param [Hash] opts The :path value gives a request path if it
|
||||
# differs from the output path
|
||||
Contract String, Or[{ path: String }, Proc] => EndpointDescriptor
|
||||
def endpoint(path, opts={}, &block)
|
||||
if block_given?
|
||||
EndpointDescriptor.new(path, path, block)
|
||||
else
|
||||
EndpointDescriptor.new(path, opts[:path] || path, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class EndpointResource < ::Middleman::Sitemap::Resource
|
||||
Contract Maybe[Proc]
|
||||
attr_accessor :output
|
||||
|
||||
def initialize(store, path, request_path)
|
||||
super(store, path)
|
||||
@request_path = ::Middleman::Util.normalize_path(request_path)
|
||||
end
|
||||
|
||||
Contract String
|
||||
attr_reader :request_path
|
||||
|
||||
Contract Bool
|
||||
def template?
|
||||
true
|
||||
end
|
||||
|
||||
Contract Args[Any] => String
|
||||
def render(*)
|
||||
return output.call if output
|
||||
end
|
||||
|
||||
Contract Bool
|
||||
def ignored?
|
||||
false
|
||||
Contract String, Proc => EndpointDescriptor
|
||||
def endpoint(path, &block)
|
||||
EndpointDescriptor.new(path, block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,7 @@ require 'middleman-core/sitemap/extensions/traversal'
|
|||
require 'middleman-core/file_renderer'
|
||||
require 'middleman-core/template_renderer'
|
||||
require 'middleman-core/contracts'
|
||||
require 'middleman-core/inline_url_rewriter'
|
||||
|
||||
module Middleman
|
||||
# Sitemap namespace
|
||||
|
@ -39,24 +40,26 @@ module Middleman
|
|||
attr_reader :metadata
|
||||
|
||||
attr_accessor :ignored
|
||||
attr_accessor :filters
|
||||
|
||||
# Initialize resource with parent store and URL
|
||||
# @param [Middleman::Sitemap::Store] store
|
||||
# @param [String] path
|
||||
# @param [String] source
|
||||
Contract IsA['Middleman::Sitemap::Store'], String, Maybe[Or[IsA['Middleman::SourceFile'], String]] => Any
|
||||
def initialize(store, path, source=nil)
|
||||
Contract IsA['Middleman::Sitemap::Store'], String, Maybe[Any] => Any
|
||||
def initialize(store, path, source_file=nil)
|
||||
@store = store
|
||||
@app = @store.app
|
||||
@path = path
|
||||
@ignored = false
|
||||
@filters = []
|
||||
|
||||
source = Pathname(source) if source && source.is_a?(String)
|
||||
source_file = Pathname(source_file) if source_file && source_file.is_a?(String)
|
||||
|
||||
@file_descriptor = if source && source.is_a?(Pathname)
|
||||
::Middleman::SourceFile.new(source.relative_path_from(@app.source_dir), source, @app.source_dir, Set.new([:source]), 0)
|
||||
@file_descriptor = if source_file && source_file.is_a?(Pathname)
|
||||
::Middleman::SourceFile.new(source_file.relative_path_from(@app.source_dir), source_file, @app.source_dir, Set.new([:source]), 0)
|
||||
else
|
||||
source
|
||||
source_file
|
||||
end
|
||||
|
||||
@destination_path = @path
|
||||
|
@ -138,8 +141,45 @@ module Middleman
|
|||
|
||||
# Render this resource
|
||||
# @return [String]
|
||||
Contract Hash, Hash => String
|
||||
# Contract Maybe[Hash], Maybe[Hash], Maybe[Proc] => String
|
||||
def render(opts={}, locs={})
|
||||
body = render_without_filters(opts, locs)
|
||||
|
||||
return body if @filters.empty?
|
||||
|
||||
sortable_filters = @filters.select { |f| f.respond_to?(:filter_name) }.sort do |a, b|
|
||||
if b.after_filter == a.filter_name
|
||||
1
|
||||
else
|
||||
-1
|
||||
end
|
||||
end.reverse
|
||||
|
||||
n = 0
|
||||
sorted_filters = @filters.sort_by do |m|
|
||||
n += 1
|
||||
idx = sortable_filters.index(m)
|
||||
|
||||
[idx.nil? ? 0 : idx, n]
|
||||
end
|
||||
|
||||
sorted_filters.reduce(body) do |output, filter|
|
||||
if block_given? && !yield(filter)
|
||||
output
|
||||
elsif filter.respond_to?(:execute_filter)
|
||||
filter.execute_filter(output)
|
||||
elsif filter.respond_to?(:call)
|
||||
filter.call(output)
|
||||
else
|
||||
output
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Render this resource without content filters
|
||||
# @return [String]
|
||||
Contract Hash, Hash => String
|
||||
def render_without_filters(opts={}, locs={})
|
||||
return ::Middleman::FileRenderer.new(@app, file_descriptor[:full_path].to_s).template_data_for_file unless template?
|
||||
|
||||
md = metadata
|
||||
|
@ -151,7 +191,7 @@ module Middleman
|
|||
opts[:layout] = false if !opts.key?(:layout) && !@app.config.extensions_with_layout.include?(ext)
|
||||
|
||||
renderer = ::Middleman::TemplateRenderer.new(@app, file_descriptor[:full_path].to_s)
|
||||
renderer.render(locs, opts)
|
||||
renderer.render(locs, opts).to_str
|
||||
end
|
||||
|
||||
# A path without the directory index - so foo/index.html becomes
|
||||
|
@ -204,9 +244,10 @@ module Middleman
|
|||
end
|
||||
|
||||
class StringResource < Resource
|
||||
def initialize(store, path, contents=nil, &block)
|
||||
Contract IsA['Middleman::Sitemap::Store'], String, Maybe[Or[String, Proc]] => Any
|
||||
def initialize(store, path, contents)
|
||||
@request_path = path
|
||||
@contents = block_given? ? block : contents
|
||||
@contents = contents
|
||||
super(store, path)
|
||||
end
|
||||
|
||||
|
@ -215,7 +256,28 @@ module Middleman
|
|||
end
|
||||
|
||||
def render(*)
|
||||
@contents.respond_to?(:call) ? @contents.call : @contents
|
||||
@contents
|
||||
end
|
||||
|
||||
def binary?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
class CallbackResource < Resource
|
||||
Contract IsA['Middleman::Sitemap::Store'], String, Proc => Any
|
||||
def initialize(store, path, &block)
|
||||
@request_path = path
|
||||
@contents = block
|
||||
super(store, path)
|
||||
end
|
||||
|
||||
def template?
|
||||
true
|
||||
end
|
||||
|
||||
def render(*)
|
||||
@contents.call
|
||||
end
|
||||
|
||||
def binary?
|
||||
|
|
|
@ -94,7 +94,7 @@ module Middleman
|
|||
.transpose
|
||||
.map(&::Regexp.method(:union))
|
||||
|
||||
match = /
|
||||
/
|
||||
\A(?:[^\r\n]*coding:[^\r\n]*\r?\n)?
|
||||
(?<start>#{start_delims})[ ]*\r?\n
|
||||
(?<frontmatter>.*?)[ ]*\r?\n?
|
||||
|
|
|
@ -56,10 +56,8 @@ module Middleman
|
|||
|
||||
Contract String => String
|
||||
def step_through_extensions(path)
|
||||
while ::Middleman::Util.tilt_class(path)
|
||||
ext = ::File.extname(path)
|
||||
break if ext.empty?
|
||||
|
||||
while ext = File.extname(path)
|
||||
break if ext.empty? || !::Middleman::Util.tilt_class(ext)
|
||||
yield ext if block_given?
|
||||
|
||||
# Strip templating extensions as long as Tilt knows them
|
||||
|
|
|
@ -25,13 +25,15 @@ module Middleman
|
|||
def tilt_class(path)
|
||||
::Tilt[path]
|
||||
end
|
||||
memoize :tilt_class
|
||||
# memoize :tilt_class
|
||||
|
||||
# Normalize a path to not include a leading slash
|
||||
# @param [String] path
|
||||
# @return [String]
|
||||
Contract String => String
|
||||
Contract Any => String
|
||||
def normalize_path(path)
|
||||
return path unless path.is_a?(String)
|
||||
|
||||
# The tr call works around a bug in Ruby's Unicode handling
|
||||
::URI.decode(path).sub(%r{^/}, '').tr('', '')
|
||||
end
|
||||
|
|
|
@ -6,22 +6,9 @@ module Middleman
|
|||
|
||||
module_function
|
||||
|
||||
# Extract the text of a Rack response as a string.
|
||||
# Useful for extensions implemented as Rack middleware.
|
||||
# @param response The response from #call
|
||||
# @return [String] The whole response as a string.
|
||||
Contract RespondTo[:each] => String
|
||||
def extract_response_text(response)
|
||||
# The rack spec states all response bodies must respond to each
|
||||
result = ''
|
||||
response.each do |part, _|
|
||||
result << part
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
Contract String, String, ArrayOf[String], IsA['::Middleman::Application'], Proc => String
|
||||
Contract String, String, SetOf[String], IsA['::Middleman::Application'], Proc => String
|
||||
def rewrite_paths(body, path, exts, app, &_block)
|
||||
exts = exts.sort_by(&:length).reverse
|
||||
matcher = /([\'\"\(,]\s*|# sourceMappingURL=)([^\s\'\"\)\(>]+(#{::Regexp.union(exts)}))/
|
||||
|
||||
url_fn_prefix = 'url('
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module Middleman
|
||||
# Current Version
|
||||
# @return [String]
|
||||
VERSION = '4.1.7'.freeze unless const_defined?(:VERSION)
|
||||
VERSION = '4.2.0'.freeze unless const_defined?(:VERSION)
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue