2013-04-20 23:27:25 +02:00
# Minify CSS Extension
class Middleman :: Extensions :: MinifyCss < :: Middleman :: Extension
option :compressor , nil , 'Set the CSS compressor to use.'
option :inline , false , 'Whether to minify CSS inline within HTML files'
option :ignore , [ ] , 'Patterns to avoid minifying'
2014-10-13 00:44:16 +02:00
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'
2012-08-14 00:39:06 +02:00
2013-04-20 23:27:25 +02:00
def initialize ( app , options_hash = { } , & block )
super
2012-08-14 00:39:06 +02:00
2013-04-20 23:27:25 +02:00
app . config . define_setting :css_compressor , nil , 'Set the CSS compressor to use. Deprecated in favor of the :compressor option when activating :minify_css'
end
2012-04-08 05:00:24 +02:00
2013-04-20 23:27:25 +02:00
def after_configuration
chosen_compressor = app . config [ :css_compressor ] || options [ :compressor ] || SassCompressor
2012-08-14 00:39:06 +02:00
2013-04-20 23:27:25 +02:00
# Setup Rack middleware to minify CSS
2014-04-29 19:44:24 +02:00
app . use Rack , compressor : chosen_compressor ,
ignore : Array ( options [ :ignore ] ) + [ / \ .min \ . / ] ,
2014-10-13 00:44:16 +02:00
inline : options [ :inline ] ,
content_types : options [ :content_types ] ,
inline_content_types : options [ :inline_content_types ]
2013-04-20 23:27:25 +02:00
end
2013-03-20 00:21:49 +01:00
2013-04-20 23:27:25 +02:00
class SassCompressor
2014-04-29 01:02:18 +02:00
def self . compress ( style , options = { } )
2013-04-20 23:27:25 +02:00
root_node = :: Sass :: SCSS :: CssParser . new ( style , 'middleman-css-input' , 1 ) . parse
2014-04-29 19:44:24 +02:00
root_node . options = options . merge ( style : :compressed )
2013-04-20 23:27:25 +02:00
root_node . render . strip
end
end
2012-04-08 05:00:24 +02:00
2013-04-20 23:27:25 +02:00
# Rack middleware to look for CSS and compress it
class Rack
2013-05-25 02:11:46 +02:00
INLINE_CSS_REGEX = / (<style[^>]*> \ s*(?: \/ \ *<! \ [CDATA \ [ \ * \/ \ n)?)(.*?)((?:(?: \ n \ s*)? \/ \ * \ ] \ ]> \ * \/ )? \ s*< \/ style>) /m
2013-04-20 23:27:25 +02:00
# Init
# @param [Class] app
# @param [Hash] options
def initialize ( app , options = { } )
@app = app
@compressor = options [ :compressor ]
@ignore = options [ :ignore ]
@inline = options [ :inline ]
2014-10-13 00:44:16 +02:00
@content_types = options [ :content_types ]
@inline_content_types = options [ :inline_content_types ]
2013-04-20 23:27:25 +02:00
end
2012-04-08 05:00:24 +02:00
2013-04-20 23:27:25 +02:00
# Rack interface
# @param [Rack::Environmemt] env
# @return [Array]
def call ( env )
status , headers , response = @app . call ( env )
2012-04-08 05:00:24 +02:00
2014-10-13 00:44:16 +02:00
content_type = headers [ 'Content-Type' ] . try ( :slice , / ^[^;]* / )
path = env [ 'PATH_INFO' ]
2012-04-08 05:00:24 +02:00
2014-10-13 00:44:16 +02:00
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
2013-12-28 01:26:31 +01:00
headers [ 'Content-Length' ] = :: Rack :: Utils . bytesize ( minified ) . to_s
2013-04-20 23:27:25 +02:00
response = [ minified ]
2012-04-08 05:00:24 +02:00
end
2013-04-20 23:27:25 +02:00
[ status , headers , response ]
2012-04-08 05:00:24 +02:00
end
2013-05-25 02:11:46 +02:00
2014-04-29 01:02:18 +02:00
private
2014-10-13 00:44:16 +02:00
# Whether the path should be ignored
# @param [String] path
# @return [Boolean]
def ignore? ( path )
@ignore . any? { | ignore | Middleman :: Util . path_match ( ignore , path ) }
end
# Whether this type of content can be minified
# @param [String, nil] content_type
# @return [Boolean]
def minifiable? ( content_type )
@content_types . include? ( content_type )
2013-05-25 02:11:46 +02:00
end
2014-10-13 00:44:16 +02:00
# 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
# Minify the content
# @param [String] content
# @return [String]
def minify ( content )
@compressor . compress ( content )
end
# 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
2013-05-25 02:11:46 +02:00
end
2012-04-08 05:00:24 +02:00
end
2012-04-27 06:55:07 +02:00
end