More WIP
This commit is contained in:
parent
11e478fad9
commit
029de6613b
4 changed files with 78 additions and 129 deletions
|
@ -1,5 +1,6 @@
|
||||||
require 'memoist'
|
require 'memoist'
|
||||||
require 'middleman-core/contracts'
|
require 'middleman-core/contracts'
|
||||||
|
require 'rack/mime'
|
||||||
|
|
||||||
# Minify CSS Extension
|
# Minify CSS Extension
|
||||||
class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
||||||
|
@ -12,14 +13,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
||||||
option :content_types, %w(text/css), 'Content types of resources that contain CSS'
|
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 :inline_content_types, %w(text/html text/php), 'Content types of resources that contain inline CSS'
|
||||||
|
|
||||||
def ready
|
INLINE_CSS_REGEX = /(<style[^>]*>\s*(?:\/\*<!\[CDATA\[\*\/\n)?)(.*?)((?:(?:\n\s*)?\/\*\]\]>\*\/)?\s*<\/style>)/m
|
||||||
# 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
|
|
||||||
|
|
||||||
class SassCompressor
|
class SassCompressor
|
||||||
def self.compress(style, options={})
|
def self.compress(style, options={})
|
||||||
|
@ -29,92 +23,57 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Rack middleware to look for CSS and compress it
|
def initialize(app, options_hash={}, &block)
|
||||||
class Rack
|
super
|
||||||
extend Memoist
|
|
||||||
include Contracts
|
|
||||||
INLINE_CSS_REGEX = /(<style[^>]*>\s*(?:\/\*<!\[CDATA\[\*\/\n)?)(.*?)((?:(?:\n\s*)?\/\*\]\]>\*\/)?\s*<\/style>)/m
|
|
||||||
|
|
||||||
# Init
|
@ignore = Array(options[:ignore]) + [/\.min\./]
|
||||||
# @param [Class] app
|
@compressor = options[:compressor]
|
||||||
# @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.to_proc if @compressor.respond_to? :to_proc
|
||||||
@compressor = @compressor.call if @compressor.is_a? Proc
|
@compressor = @compressor.call if @compressor.is_a? Proc
|
||||||
@content_types = options[:content_types]
|
|
||||||
@inline_content_types = options[:inline_content_types]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Rack interface
|
Contract ResourceList => ResourceList
|
||||||
# @param [Rack::Environmemt] env
|
def manipulate_resource_list(resources)
|
||||||
# @return [Array]
|
resources.each do |r|
|
||||||
def call(env)
|
type = r.content_type.try(:slice, /^[^;]*/)
|
||||||
status, headers, response = @app.call(env)
|
if options[:inline] && minifiable_inline?(type)
|
||||||
|
r.filters << method(:minify_inline)
|
||||||
content_type = headers['Content-Type'].try(:slice, /^[^;]*/)
|
elsif minifiable?(type) && !ignore?(r.destination_path)
|
||||||
path = env['PATH_INFO']
|
r.filters << method(:minify)
|
||||||
|
|
||||||
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
|
end
|
||||||
|
|
||||||
if minified
|
|
||||||
headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s
|
|
||||||
response = [minified]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
[status, headers, response]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# Whether the path should be ignored
|
# Whether the path should be ignored
|
||||||
# @param [String] path
|
Contract String => Bool
|
||||||
# @return [Boolean]
|
|
||||||
def ignore?(path)
|
def ignore?(path)
|
||||||
@ignore.any? { |ignore| ::Middleman::Util.path_match(ignore, path) }
|
@ignore.any? { |ignore| ::Middleman::Util.path_match(ignore, path) }
|
||||||
end
|
end
|
||||||
memoize :ignore?
|
memoize :ignore?
|
||||||
|
|
||||||
# Whether this type of content can be minified
|
# Whether this type of content can be minified
|
||||||
# @param [String, nil] content_type
|
Contract Maybe[String] => Bool
|
||||||
# @return [Boolean]
|
|
||||||
def minifiable?(content_type)
|
def minifiable?(content_type)
|
||||||
@content_types.include?(content_type)
|
options[:content_types].include?(content_type)
|
||||||
end
|
end
|
||||||
memoize :minifiable?
|
memoize :minifiable?
|
||||||
|
|
||||||
# Whether this type of content contains inline content that can be minified
|
# Whether this type of content contains inline content that can be minified
|
||||||
# @param [String, nil] content_type
|
Contract Maybe[String] => Bool
|
||||||
# @return [Boolean]
|
|
||||||
def minifiable_inline?(content_type)
|
def minifiable_inline?(content_type)
|
||||||
@inline_content_types.include?(content_type)
|
options[:inline_content_types].include?(content_type)
|
||||||
end
|
end
|
||||||
memoize :minifiable_inline?
|
memoize :minifiable_inline?
|
||||||
|
|
||||||
# Minify the content
|
# Minify the content
|
||||||
# @param [String] content
|
Contract String => String
|
||||||
# @return [String]
|
|
||||||
def minify(content)
|
def minify(content)
|
||||||
@compressor.compress(content)
|
@compressor.compress(content)
|
||||||
end
|
end
|
||||||
memoize :minify
|
memoize :minify
|
||||||
|
|
||||||
# Detect and minify inline content
|
# Detect and minify inline content
|
||||||
# @param [String] content
|
Contract String => String
|
||||||
# @return [String]
|
|
||||||
def minify_inline(content)
|
def minify_inline(content)
|
||||||
content.gsub(INLINE_CSS_REGEX) do
|
content.gsub(INLINE_CSS_REGEX) do
|
||||||
$1 + minify($2) + $3
|
$1 + minify($2) + $3
|
||||||
|
@ -122,4 +81,3 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
|
||||||
end
|
end
|
||||||
memoize :minify_inline
|
memoize :minify_inline
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
require 'forwardable'
|
|
||||||
|
|
||||||
module Middleman
|
|
||||||
class Pipeline
|
|
||||||
extend Forwardable
|
|
||||||
attr_reader :filters
|
|
||||||
attr_reader :resource
|
|
||||||
|
|
||||||
def_delegators :filters, :<<, :push, :unshift, :insert, :shift, :pop, :first, :clear
|
|
||||||
|
|
||||||
def initialize(resource)
|
|
||||||
@resource = resource
|
|
||||||
@filters = []
|
|
||||||
end
|
|
||||||
|
|
||||||
def render(*args)
|
|
||||||
process resource.render *args
|
|
||||||
end
|
|
||||||
|
|
||||||
def process(body)
|
|
||||||
filters.inject(body){ |body, app| app.call(body) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -109,7 +109,7 @@ module Middleman
|
||||||
|
|
||||||
begin
|
begin
|
||||||
# Write out the contents of the page
|
# Write out the contents of the page
|
||||||
res.write resource.pipeline.render({}, rack: { request: req })
|
res.write resource.render({}, rack: { request: req })
|
||||||
|
|
||||||
# Valid content is a 200 status
|
# Valid content is a 200 status
|
||||||
res.status = 200
|
res.status = 200
|
||||||
|
|
|
@ -3,7 +3,6 @@ require 'middleman-core/sitemap/extensions/traversal'
|
||||||
require 'middleman-core/file_renderer'
|
require 'middleman-core/file_renderer'
|
||||||
require 'middleman-core/template_renderer'
|
require 'middleman-core/template_renderer'
|
||||||
require 'middleman-core/contracts'
|
require 'middleman-core/contracts'
|
||||||
require 'middleman-core/pipeline'
|
|
||||||
|
|
||||||
module Middleman
|
module Middleman
|
||||||
# Sitemap namespace
|
# Sitemap namespace
|
||||||
|
@ -40,8 +39,7 @@ module Middleman
|
||||||
attr_reader :metadata
|
attr_reader :metadata
|
||||||
|
|
||||||
attr_accessor :ignored
|
attr_accessor :ignored
|
||||||
|
attr_accessor :filters
|
||||||
attr_reader :pipeline
|
|
||||||
|
|
||||||
# Initialize resource with parent store and URL
|
# Initialize resource with parent store and URL
|
||||||
# @param [Middleman::Sitemap::Store] store
|
# @param [Middleman::Sitemap::Store] store
|
||||||
|
@ -53,7 +51,7 @@ module Middleman
|
||||||
@app = @store.app
|
@app = @store.app
|
||||||
@path = path
|
@path = path
|
||||||
@ignored = false
|
@ignored = false
|
||||||
@pipeline = Pipeline.new(self)
|
@filters = []
|
||||||
|
|
||||||
source = Pathname(source) if source && source.is_a?(String)
|
source = Pathname(source) if source && source.is_a?(String)
|
||||||
|
|
||||||
|
@ -144,6 +142,23 @@ module Middleman
|
||||||
# @return [String]
|
# @return [String]
|
||||||
Contract Hash, Hash => String
|
Contract Hash, Hash => String
|
||||||
def render(opts={}, locs={})
|
def render(opts={}, locs={})
|
||||||
|
body = render_without_filters(opts, locs)
|
||||||
|
|
||||||
|
@filters.reduce(body) do |output, filter|
|
||||||
|
if 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?
|
return ::Middleman::FileRenderer.new(@app, file_descriptor[:full_path].to_s).template_data_for_file unless template?
|
||||||
|
|
||||||
md = metadata
|
md = metadata
|
||||||
|
@ -155,7 +170,7 @@ module Middleman
|
||||||
opts[:layout] = false if !opts.key?(:layout) && !@app.config.extensions_with_layout.include?(ext)
|
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 = ::Middleman::TemplateRenderer.new(@app, file_descriptor[:full_path].to_s)
|
||||||
renderer.render(locs, opts)
|
renderer.render(locs, opts).to_str
|
||||||
end
|
end
|
||||||
|
|
||||||
# A path without the directory index - so foo/index.html becomes
|
# A path without the directory index - so foo/index.html becomes
|
||||||
|
|
Loading…
Add table
Reference in a new issue