Skip to content

Commit a7b3e4d

Browse files
committed
Refactor of Jammit.
* Upgraded development and test Gemfile * Able to switch to 4 different JS compressors: jsmin, yui, closure, or uglifier. * Able to switch to 3 different CSS compressors: cssmin, yui, or sass. * New attributes: Jammit.css_compressors and Jammit.javascript_compressors. * All tests now explicitly use :yui * New compressors: lib/jammit/cssmin_compressor.rb lib/jammit/jsmin_compressor.rb lib/jammit/sass_compressor.rb lib/jammit/yui_css_compressor.rb lib/jammit/yui_javascript_compressor.rb * Absorbed cssmin and jsmin. See: (https://github.com/rgrove/jsmin and https://github.com/rgrove/cssmin) * All tests pass.
1 parent adaa3de commit a7b3e4d

26 files changed

Lines changed: 609 additions & 60 deletions

Gemfile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ source 'http://rubygems.org'
33
gemspec
44

55
group :development, :test do
6-
gem "rake", "0.9.2"
7-
gem "rails", "2.3.11"
8-
gem "yui-compressor", "0.9.3"
9-
gem "closure-compiler", "1.1.5"
10-
gem "uglifier", "0.4.0"
6+
gem "rake", "0.9.2.2"
7+
gem "rails", "2.3.14"
8+
gem "yui-compressor", "0.9.6"
9+
gem "closure-compiler", "1.1.6"
10+
gem "uglifier", "1.2.4"
1111
end
1212

1313
group :development do

doc/Jammit/Compressor.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ <h2>Constant Summary</h2>
234234
<span class='rbrace token'>}</span>
235235
</pre></dd>
236236

237-
<dt id="DEFAULT_OPTIONS-constant" class="">DEFAULT_OPTIONS =
237+
<dt id="JAVASCRIPT_DEFAULT_OPTIONS-constant" class="">JAVASCRIPT_DEFAULT_OPTIONS =
238238

239239
</dt>
240240
<dd><pre class="code"><span class='lbrace token'>{</span>

lib/jammit.rb

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,32 @@
44
# to all of the configuration options.
55
module Jammit
66

7-
VERSION = "0.6.5"
7+
VERSION = "0.6.5"
88

9-
ROOT = File.expand_path(File.dirname(__FILE__) + '/..')
9+
ROOT = File.expand_path(File.dirname(__FILE__) + '/..')
1010

11-
ASSET_ROOT = File.expand_path((defined?(Rails) && Rails.root.to_s.length > 0) ? Rails.root : ENV['RAILS_ROOT'] || ".") unless defined?(ASSET_ROOT)
11+
ASSET_ROOT = File.expand_path((defined?(Rails) && Rails.root.to_s.length > 0) ? Rails.root : ENV['RAILS_ROOT'] || ".") unless defined?(ASSET_ROOT)
1212

13-
DEFAULT_PUBLIC_ROOT = (defined?(Rails) && Rails.public_path.to_s.length > 0) ? Rails.public_path : File.join(ASSET_ROOT, 'public') unless defined?(PUBLIC_ROOT)
13+
DEFAULT_PUBLIC_ROOT = (defined?(Rails) && Rails.public_path.to_s.length > 0) ? Rails.public_path : File.join(ASSET_ROOT, 'public') unless defined?(PUBLIC_ROOT)
1414

15-
DEFAULT_CONFIG_PATH = File.join(ASSET_ROOT, 'config', 'assets.yml')
15+
DEFAULT_CONFIG_PATH = File.join(ASSET_ROOT, 'config', 'assets.yml')
1616

17-
DEFAULT_PACKAGE_PATH = "assets"
17+
DEFAULT_PACKAGE_PATH = "assets"
1818

19-
DEFAULT_JST_SCRIPT = File.join(ROOT, 'lib/jammit/jst.js')
19+
DEFAULT_JST_SCRIPT = File.join(ROOT, 'lib/jammit/jst.js')
2020

21-
DEFAULT_JST_COMPILER = "template"
21+
DEFAULT_JST_COMPILER = "template"
2222

23-
DEFAULT_JST_NAMESPACE = "window.JST"
23+
DEFAULT_JST_NAMESPACE = "window.JST"
2424

25-
COMPRESSORS = [:yui, :closure, :uglifier]
25+
JAVASCRIPT_COMPRESSORS = [:jsmin, :yui, :closure, :uglifier]
26+
27+
DEFAULT_JAVASCRIPT_COMPRESSOR = :yui
28+
29+
CSS_COMPRESSORS = [:cssmin, :yui, :sass]
30+
31+
DEFAULT_CSS_COMPRESSOR = :yui
2632

27-
DEFAULT_COMPRESSOR = :yui
28-
2933
# Extension matchers for JavaScript and JST, which need to be disambiguated.
3034
JS_EXTENSION = /\.js\Z/
3135
DEFAULT_JST_EXTENSION = "jst"
@@ -50,17 +54,20 @@ class << self
5054
attr_reader :configuration, :template_function, :template_namespace,
5155
:embed_assets, :package_assets, :compress_assets, :gzip_assets,
5256
:package_path, :mhtml_enabled, :include_jst_script, :config_path,
53-
:javascript_compressor, :compressor_options, :css_compressor_options,
54-
:template_extension, :template_extension_matcher, :allow_debugging,
57+
:javascript_compressor, :compressor_options, :css_compressor,
58+
:css_compressor_options, :template_extension,
59+
:template_extension_matcher, :allow_debugging,
5560
:rewrite_relative_paths, :public_root
56-
attr_accessor :compressors
61+
attr_accessor :javascript_compressors, :css_compressors
5762
end
5863

5964
# The minimal required configuration.
6065
@configuration = {}
6166
@public_root = DEFAULT_PUBLIC_ROOT
6267
@package_path = DEFAULT_PACKAGE_PATH
63-
@compressors = COMPRESSORS
68+
69+
@javascript_compressors = JAVASCRIPT_COMPRESSORS
70+
@css_compressors = CSS_COMPRESSORS
6471

6572
# Load the complete asset configuration from the specified @config_path@.
6673
# If we're loading softly, don't let missing configuration error out.
@@ -149,7 +156,13 @@ def self.set_public_root(public_root=nil)
149156
# Ensure that the JavaScript compressor is a valid choice.
150157
def self.set_javascript_compressor(value)
151158
value = value && value.to_sym
152-
@javascript_compressor = compressors.include?(value) ? value : DEFAULT_COMPRESSOR
159+
@javascript_compressor = javascript_compressors.include?(value) ? value : DEFAULT_JAVASCRIPT_COMPRESSOR
160+
end
161+
162+
# Ensure that the CSS compressor is a valid choice.
163+
def self.set_css_compressor(value)
164+
value = value && value.to_sym
165+
@css_compressor = css_compressors.include?(value) ? value : DEFAULT_CSS_COMPRESSOR
153166
end
154167

155168
# Turn asset packaging on or off, depending on configuration and environment.

lib/jammit/compressor.rb

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,39 @@ class Compressor
4141
JST_START = "(function(){"
4242
JST_END = "})();"
4343

44-
COMPRESSORS = {
45-
:yui => YUI::JavaScriptCompressor,
46-
:closure => Jammit.compressors.include?(:closure) ? Closure::Compiler : nil,
47-
:uglifier => Jammit.compressors.include?(:uglifier) ? Jammit::Uglifier : nil
44+
JAVASCRIPT_COMPRESSORS = {
45+
:jsmin => Jammit.javascript_compressors.include?(:jsmin) ? Jammit::JsminCompressor : nil,
46+
:yui => Jammit.javascript_compressors.include?(:yui) ? Jammit::YUIJavaScriptCompressor : nil,
47+
:closure => Jammit.javascript_compressors.include?(:closure) ? Closure::Compiler : nil,
48+
:uglifier => Jammit.javascript_compressors.include?(:uglifier) ? Jammit::Uglifier : nil
4849
}
4950

50-
DEFAULT_OPTIONS = {
51+
CSS_COMPRESSORS = {
52+
:cssmin => Jammit.css_compressors.include?(:cssmin) ? Jammit::CssminCompressor : nil,
53+
:yui => Jammit.css_compressors.include?(:yui) ? Jammit::YUICssCompressor : nil,
54+
:sass => Jammit.css_compressors.include?(:sass) ? Jammit::SassCompressor : nil
55+
}
56+
57+
JAVASCRIPT_DEFAULT_OPTIONS = {
58+
:jsmin => {},
5159
:yui => {:munge => true},
5260
:closure => {},
5361
:uglifier => {:copyright => false}
5462
}
5563

56-
# The css compressor is always the YUI Compressor. JS compression can be
57-
# provided with YUI Compressor, Google Closure Compiler or UglifyJS.
64+
# CSS compression can be provided with YUI Compressor or sass. JS
65+
# compression can be provided with YUI Compressor, Google Closure
66+
# Compiler or UglifyJS.
5867
def initialize
59-
Jammit.check_java_version
60-
@css_compressor = YUI::CssCompressor.new(Jammit.css_compressor_options || {})
61-
flavor = Jammit.javascript_compressor || Jammit::DEFAULT_COMPRESSOR
62-
@options = DEFAULT_OPTIONS[flavor].merge(Jammit.compressor_options || {})
63-
@js_compressor = COMPRESSORS[flavor].new(@options)
68+
if Jammit.javascript_compressors.include?(:yui) || Jammit.javascript_compressors.include?(:closure) || Jammit.css_compressors.include?(:yui)
69+
Jammit.check_java_version
70+
end
71+
72+
css_flavor = Jammit.css_compressor || Jammit::DEFAULT_CSS_COMPRESSOR
73+
@css_compressor = CSS_COMPRESSORS[css_flavor].new(Jammit.css_compressor_options || {})
74+
js_flavor = Jammit.javascript_compressor || Jammit::DEFAULT_JAVASCRIPT_COMPRESSOR
75+
@options = JAVASCRIPT_DEFAULT_OPTIONS[js_flavor].merge(Jammit.compressor_options || {})
76+
@js_compressor = JAVASCRIPT_COMPRESSORS[js_flavor].new(@options)
6477
end
6578

6679
# Concatenate together a list of JavaScript paths, and pass them through the
@@ -172,7 +185,7 @@ def with_mhtml(css, asset_url)
172185
end
173186
[MHTML_START, mhtml, MHTML_END, css].flatten.join('')
174187
end
175-
188+
176189
# Return a rewritten asset URL for a new stylesheet -- the asset should
177190
# be tagged for embedding if embeddable, and referenced at the correct level
178191
# if relative.

lib/jammit/cssmin_compressor.rb

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#--
2+
# Copyright (c) 2008 Ryan Grove <[email protected]>
3+
# All rights reserved.
4+
#
5+
# Redistribution and use in source and binary forms, with or without
6+
# modification, are permitted provided that the following conditions are met:
7+
#
8+
# * Redistributions of source code must retain the above copyright notice,
9+
# this list of conditions and the following disclaimer.
10+
# * Redistributions in binary form must reproduce the above copyright notice,
11+
# this list of conditions and the following disclaimer in the documentation
12+
# and/or other materials provided with the distribution.
13+
# * Neither the name of this project nor the names of its contributors may be
14+
# used to endorse or promote products derived from this software without
15+
# specific prior written permission.
16+
#
17+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
#++
28+
29+
# = CSSMin
30+
#
31+
# Minifies CSS using a fast, safe routine adapted from Julien Lecomte's YUI
32+
# Compressor, which was in turn adapted from Isaac Schlueter's cssmin PHP
33+
# script.
34+
#
35+
# Author:: Ryan Grove (mailto:[email protected])
36+
# Version:: 1.0.2 (2008-08-23)
37+
# Copyright:: Copyright (c) 2008 Ryan Grove. All rights reserved.
38+
# License:: New BSD License (http://opensource.org/licenses/bsd-license.php)
39+
# Website:: http://github.com/rgrove/cssmin/
40+
#
41+
# == Example
42+
#
43+
# require 'rubygems'
44+
# require 'cssmin'
45+
#
46+
# File.open('example.css', 'r') {|file| puts CSSMin.minify(file) }
47+
#
48+
module CSSMin
49+
50+
# Reads CSS from +input+ (which can be a String or an IO object) and
51+
# returns a String containing minified CSS.
52+
def self.minify(input)
53+
css = input.is_a?(IO) ? input.read : input.dup.to_s
54+
55+
# Remove comments.
56+
css.gsub!(/\/\*[\s\S]*?\*\//, '')
57+
58+
# Compress all runs of whitespace to a single space to make things easier
59+
# to work with.
60+
css.gsub!(/\s+/, ' ')
61+
62+
# Replace box model hacks with placeholders.
63+
css.gsub!(/"\\"\}\\""/, '___BMH___')
64+
65+
# Remove unnecessary spaces, but be careful not to turn "p :link {...}"
66+
# into "p:link{...}".
67+
css.gsub!(/(?:^|\})[^\{:]+\s+:+[^\{]*\{/) do |match|
68+
match.gsub(':', '___PSEUDOCLASSCOLON___')
69+
end
70+
css.gsub!(/\s+([!\{\};:>+\(\)\],])/, '\1')
71+
css.gsub!('___PSEUDOCLASSCOLON___', ':')
72+
css.gsub!(/([!\{\}:;>+\(\[,])\s+/, '\1')
73+
74+
# Add missing semicolons.
75+
css.gsub!(/([^;\}])\}/, '\1;}')
76+
77+
# Replace 0(%, em, ex, px, in, cm, mm, pt, pc) with just 0.
78+
css.gsub!(/([\s:])([+-]?0)(?:%|em|ex|px|in|cm|mm|pt|pc)/i, '\1\2')
79+
80+
# Replace 0 0 0 0; with 0.
81+
css.gsub!(/:(?:0 )+0;/, ':0;')
82+
83+
# Replace background-position:0; with background-position:0 0;
84+
css.gsub!('background-position:0;', 'background-position:0 0;')
85+
86+
# Replace 0.6 with .6, but only when preceded by : or a space.
87+
css.gsub!(/(:|\s)0+\.(\d+)/, '\1.\2')
88+
89+
# Convert rgb color values to hex values.
90+
css.gsub!(/rgb\s*\(\s*([0-9,\s]+)\s*\)/) do |match|
91+
'#' << $1.scan(/\d+/).map{|n| n.to_i.to_s(16).rjust(2, '0') }.join
92+
end
93+
94+
# Compress color hex values, making sure not to touch values used in IE
95+
# filters, since they would break.
96+
css.gsub!(/([^"'=\s])(\s?)\s*#([0-9a-f])\3([0-9a-f])\4([0-9a-f])\5/i, '\1\2#\3\4\5')
97+
98+
# Remove empty rules.
99+
css.gsub!(/[^\}]+\{;\}\n/, '')
100+
101+
# Re-insert box model hacks.
102+
css.gsub!('___BMH___', '"\"}\""')
103+
104+
# Prevent redundant semicolons.
105+
css.gsub!(/;;+/, ';')
106+
107+
css.strip
108+
end
109+
110+
end
111+
112+
113+
# Wraps CSSMin compressor to use the same API as the rest of
114+
# Jammit's compressors.
115+
class Jammit::CssminCompressor
116+
def initialize(options = {})
117+
end
118+
119+
def compress(css)
120+
CSSMin.minify(css)
121+
end
122+
end

lib/jammit/dependencies.rb

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,45 @@
77
require 'pathname'
88
require 'fileutils'
99

10-
# Include YUI as the default
11-
require 'yui/compressor'
10+
# Try Uglifier.
11+
begin
12+
require 'uglifier'
13+
require 'jammit/uglifier'
14+
rescue LoadError
15+
Jammit.javascript_compressors.delete :uglifier
16+
end
17+
18+
# Try YUI
19+
begin
20+
require 'yui/compressor'
21+
require 'jammit/yui_css_compressor'
22+
require 'jammit/yui_javascript_compressor'
23+
rescue LoadError
24+
Jammit.javascript_compressors.delete :yui
25+
Jammit.css_compressors.delete :yui
26+
end
1227

1328
# Try Closure.
1429
begin
1530
require 'closure-compiler'
1631
rescue LoadError
17-
Jammit.compressors.delete :closure
32+
Jammit.javascript_compressors.delete :closure
1833
end
1934

20-
# Try Uglifier.
35+
# Try Sass
2136
begin
22-
require 'uglifier'
37+
require 'sass'
38+
require 'jammit/sass_compressor'
2339
rescue LoadError
24-
Jammit.compressors.delete :uglifier
40+
Jammit.css_compressors.delete :sass
2541
end
2642

2743
# Load initial configuration before the rest of Jammit.
2844
Jammit.load_configuration(Jammit::DEFAULT_CONFIG_PATH, true) if defined?(Rails)
2945

3046
# Jammit Core:
31-
require 'jammit/uglifier' if Jammit.compressors.include? :uglifier
47+
require 'jammit/jsmin_compressor'
48+
require 'jammit/cssmin_compressor'
3249
require 'jammit/compressor'
3350
require 'jammit/packager'
3451

0 commit comments

Comments
 (0)