├── Gemfile ├── test ├── fixtures │ ├── comments.css.min │ ├── media_queries.css.min │ ├── media_queries.css │ └── comments.css └── cssmin_test.rb ├── Gemfile.lock ├── Rakefile.rb ├── HISTORY.md ├── cssmin.gemspec ├── LICENSE └── lib └── cssmin.rb /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /test/fixtures/comments.css.min: -------------------------------------------------------------------------------- 1 | .classname{font-weight:normal} 2 | -------------------------------------------------------------------------------- /test/fixtures/media_queries.css.min: -------------------------------------------------------------------------------- 1 | @media screen and (-webkit-min-device-pixel-ratio:0){some-css:here} 2 | -------------------------------------------------------------------------------- /test/fixtures/media_queries.css: -------------------------------------------------------------------------------- 1 | @media screen and (-webkit-min-device-pixel-ratio:0) { 2 | some-css : here 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/comments.css: -------------------------------------------------------------------------------- 1 | /***** 2 | Multi-line comment 3 | before a new class name 4 | *****/ 5 | .classname { 6 | /* comment in declaration block */ 7 | font-weight: normal; 8 | } 9 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | cssmin (1.0.3) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | json (1.8.1) 10 | minitest (5.2.2) 11 | rake (10.1.1) 12 | rdoc (4.1.1) 13 | json (~> 1.4) 14 | 15 | PLATFORMS 16 | ruby 17 | 18 | DEPENDENCIES 19 | cssmin! 20 | minitest 21 | rake 22 | rdoc 23 | -------------------------------------------------------------------------------- /Rakefile.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rdoc/task' 3 | require 'rake/testtask' 4 | 5 | RDoc::Task.new do |rd| 6 | rd.main = 'CSSMin' 7 | rd.title = 'CSSMin' 8 | rd.rdoc_dir = 'doc' 9 | 10 | rd.rdoc_files.include('lib/**/*.rb') 11 | end 12 | 13 | Rake::TestTask.new do |t| 14 | t.libs.push 'lib' 15 | t.test_files = FileList['test/*_test.rb'] 16 | t.verbose = true 17 | end 18 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | CSSMin History 2 | ============== 3 | 4 | ## 1.0.3 (2013-03-14) 5 | 6 | * Fixed a bug that broke media queries. [Rob] 7 | 8 | * Fixed a bug that caused the input string to be modified when modifications 9 | should only have been made to a copy. 10 | 11 | ## 1.0.2 (2008-08-23) 12 | 13 | * Fixed a bug that could have resulted in a necessary space being removed if 14 | it preceded a compressible color value, such as in the property 15 | "border:1px solid #cccccc". 16 | 17 | ## 1.0.1 (2008-07-25) 18 | 19 | * Fixed a rare bug that could result in redundant semicolons. 20 | 21 | ## 1.0.0 (2008-03-22) 22 | 23 | * First release. 24 | -------------------------------------------------------------------------------- /test/cssmin_test.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'cssmin' 3 | 4 | gem 'minitest' 5 | require 'minitest/autorun' 6 | 7 | describe CSSMin do 8 | subject { CSSMin.minify(css) } 9 | 10 | describe 'stripping comments and whitespace that are not required' do 11 | let(:css) { IO.read('test/fixtures/comments.css') } 12 | let(:expected_minified_css) { IO.read('test/fixtures/comments.css.min').strip } 13 | 14 | it { subject.must_equal expected_minified_css } 15 | end 16 | 17 | describe 'preserving media queries significant whitespace' do 18 | let(:css) { IO.read('test/fixtures/media_queries.css') } 19 | let(:expected_minified_css) { IO.read('test/fixtures/media_queries.css.min').strip } 20 | 21 | it { subject.must_equal expected_minified_css } 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /cssmin.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | Gem::Specification.new do |s| 4 | s.name = 'cssmin' 5 | s.version = '1.0.3' 6 | s.author = 'Ryan Grove' 7 | s.email = 'ryan@wonko.com' 8 | s.homepage = 'https://github.com/rgrove/cssmin/' 9 | s.platform = Gem::Platform::RUBY 10 | s.summary = 'Ruby library for minifying CSS.' 11 | s.description = 'Ruby library for minifying CSS. Inspired by cssmin.js and YUI Compressor.' 12 | s.license = 'FreeBSD' 13 | 14 | s.files = `git ls-files`.split($/) 15 | s.test_files = s.files.grep(%r{^(test|spec|features)/}) 16 | s.require_path = 'lib' 17 | s.has_rdoc = true 18 | 19 | s.required_ruby_version = '>= 1.8.6' 20 | 21 | s.add_development_dependency "rake" 22 | s.add_development_dependency "rdoc" 23 | s.add_development_dependency "minitest" 24 | end 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 Ryan Grove 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of this project nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /lib/cssmin.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright (c) 2008 Ryan Grove 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:ryan@wonko.com) 36 | # Version:: 1.0.3 (2013-03-14) 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 = 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 = css.gsub(/\s+/, ' ') 61 | 62 | # Replace box model hacks with placeholders. 63 | css = css.gsub(/"\\"\}\\""/, '___BMH___') 64 | 65 | # Remove unnecessary spaces, but be careful not to turn "p :link {...}" 66 | # into "p:link{...}". 67 | css = css.gsub(/(?:^|\})[^\{:]+\s+:+[^\{]*\{/) do |match| 68 | match.gsub(':', '___PSEUDOCLASSCOLON___') 69 | end 70 | css = css.gsub(/\s+([!\{\};:>+\(\)\],])/, '\1') 71 | css = css.gsub('___PSEUDOCLASSCOLON___', ':') 72 | css = css.gsub(/([!\{\}:;>+\(\[,])\s+/, '\1') 73 | 74 | # Add missing semicolons. 75 | css = css.gsub(/([^;\}])\}/, '\1;}') 76 | 77 | # Replace 0(%, em, ex, px, in, cm, mm, pt, pc) with just 0. 78 | css = 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 = css.gsub(/:(?:0 )+0;/, ':0;') 82 | 83 | # Replace background-position:0; with background-position:0 0; 84 | css = 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 = css.gsub(/(:|\s)0+\.(\d+)/, '\1.\2') 88 | 89 | # Convert rgb color values to hex values. 90 | css = 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 = 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 = css.gsub(/[^\}]+\{;\}\n/, '') 100 | 101 | # Re-insert box model hacks. 102 | css = css.gsub('___BMH___', '"\"}\""') 103 | 104 | # Put the space back in for media queries 105 | css = css.gsub(/\band\(/, 'and (') 106 | 107 | # Prevent redundant semicolons. 108 | css = css.gsub(/;+\}/, '}') 109 | 110 | css.strip 111 | end 112 | 113 | end 114 | --------------------------------------------------------------------------------