continuing
This commit is contained in:
parent
2804a61c61
commit
87e0f240ff
34 changed files with 129 additions and 299 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
|
||||
|
|
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)
|
|
@ -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")}")
|
||||
|
|
|
@ -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,4 +1,3 @@
|
|||
require 'memoist'
|
||||
require 'middleman-core/contracts'
|
||||
require 'rack/mime'
|
||||
|
||||
|
|
|
@ -12,119 +12,76 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
|
|||
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'
|
||||
|
||||
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,19 +0,0 @@
|
|||
class Middleman::Extensions::Pipeline < Middleman::Extension
|
||||
expose_to_config :pipeline
|
||||
|
||||
def initialize(app, options_hash={}, &block)
|
||||
super
|
||||
@pipelines = []
|
||||
end
|
||||
|
||||
def pipeline(&block)
|
||||
@pipelines << block
|
||||
end
|
||||
|
||||
def manipulate_resource_list(resources)
|
||||
@pipelines.each do |pipeline|
|
||||
pipeline.call resources
|
||||
end
|
||||
resources
|
||||
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
|
||||
|
|
|
@ -46,20 +46,20 @@ module Middleman
|
|||
# @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
|
||||
|
@ -244,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
|
||||
|
||||
|
@ -255,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?
|
||||
|
|
|
@ -25,7 +25,7 @@ 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
|
||||
|
|
|
@ -6,20 +6,6 @@ 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
|
||||
def rewrite_paths(body, path, exts, app, &_block)
|
||||
exts = exts.sort_by(&:length).reverse
|
||||
|
|
|
@ -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