csscss 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## 1.2.0 - 4/14/2013 ##
2
+
3
+ * 0 and 0px are now reconciled as redundancies
4
+ * Disables color support by default for windows & ruby < 2.0
5
+ * Fixes bug where unquoted url(data...) isn't parsed correctly
6
+ * Adds support for LESS files
7
+
1
8
  ## 1.1.0 - 4/12/2013 ##
2
9
 
3
10
  * Fixes bug where CLI --no-color wasn't respected
data/CONTRIBUTORS.md CHANGED
@@ -2,3 +2,5 @@
2
2
  * Carson McDonald @carsonmcdonald
3
3
  * Martin Kuckert @MKuckert
4
4
  * Ivan Lazarevic @kopipejst
5
+ * Matt DuVall @mduvall twitter:@mduvall_
6
+ * Mekka Okereke @mekka @mekkaokereke
data/Gemfile CHANGED
@@ -6,6 +6,8 @@ gemspec
6
6
  # optional runtime dependencies
7
7
  gem "sass"
8
8
  gem "compass"
9
+ gem "less"
10
+ gem "therubyracer", :platform => :mri
9
11
 
10
12
  gem "rake", :require => false
11
13
  gem "debugger"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- csscss (1.1.0)
4
+ csscss (1.2.0)
5
5
  colorize
6
6
  parslet (~> 1.5)
7
7
 
@@ -12,6 +12,7 @@ GEM
12
12
  chunky_png (1.2.7)
13
13
  colorize (0.5.8)
14
14
  columnize (0.3.6)
15
+ commonjs (0.2.6)
15
16
  compass (0.12.2)
16
17
  chunky_png (~> 1.2)
17
18
  fssm (>= 0.2.7)
@@ -23,6 +24,9 @@ GEM
23
24
  debugger-linecache (1.2.0)
24
25
  debugger-ruby_core_source (1.2.0)
25
26
  fssm (0.2.10)
27
+ less (2.3.1)
28
+ commonjs (~> 0.2.6)
29
+ libv8 (3.11.8.17)
26
30
  m (1.3.1)
27
31
  method_source (>= 0.6.7)
28
32
  rake (>= 0.9.2.2)
@@ -32,8 +36,12 @@ GEM
32
36
  parslet (1.5.0)
33
37
  blankslate (~> 2.0)
34
38
  rake (10.0.3)
39
+ ref (1.0.4)
35
40
  ruby-prof (0.13.0)
36
41
  sass (3.2.7)
42
+ therubyracer (0.11.4)
43
+ libv8 (~> 3.11.8.12)
44
+ ref
37
45
 
38
46
  PLATFORMS
39
47
  ruby
@@ -42,9 +50,11 @@ DEPENDENCIES
42
50
  compass
43
51
  csscss!
44
52
  debugger
53
+ less
45
54
  m
46
55
  minitest
47
56
  minitest-rg
48
57
  rake
49
58
  ruby-prof
50
59
  sass
60
+ therubyracer
data/README.md CHANGED
@@ -45,11 +45,20 @@ rulesets that have fewer matches.
45
45
 
46
46
  $ csscss -n 10 -v path/to/style.css # ignores rulesets with < 10 matches
47
47
 
48
- If you prefer writing in sass, you can also parse your sass/scss files.
48
+ If you prefer writing in [sass](http://sass-lang.com/), you can also parse your sass/scss files.
49
49
 
50
50
  $ gem install sass
51
51
  $ csscss path/to/style.scss
52
52
 
53
+ If you prefer writing in [LESS](http://lesscss.org/), you can also parse your LESS files.
54
+
55
+ $ gem install less
56
+ $ csscss path/to/style.less
57
+
58
+ LESS requires an additional javascript runtime.
59
+ [v8/therubyracer](https://rubygems.org/gems/therubyracer) on most
60
+ rubies, and [therubyrhino](https://rubygems.org/gems/therubyrhino) on
61
+ jruby.
53
62
 
54
63
  ## I found bugs ##
55
64
 
data/lib/csscss/cli.rb CHANGED
@@ -3,12 +3,12 @@ module Csscss
3
3
  def initialize(argv)
4
4
  @argv = argv
5
5
  @verbose = false
6
- @color = true
6
+ @color = !windows_1_9
7
7
  @minimum = 3
8
8
  @compass = false
9
9
  @ignored_properties = []
10
10
  @ignored_selectors = []
11
- @match_shorthand = true
11
+ @match_shorthand = true
12
12
  end
13
13
 
14
14
  def run
@@ -16,31 +16,22 @@ module Csscss
16
16
  execute
17
17
  end
18
18
 
19
+ private
19
20
  def execute
20
21
  warn_old_debug_flag if ENV["CSSCSS_DEBUG"]
21
22
 
22
- all_contents = @argv.map do |filename|
23
- if %w(.scss .sass).include?(File.extname(filename).downcase) && !(filename =~ URI.regexp)
24
- begin
25
- require "sass"
26
- rescue LoadError
27
- abort "Must install sass gem before parsing sass/scss files"
28
- end
29
-
30
- sass_options = {cache:false}
31
- sass_options[:load_paths] = Compass.configuration.sass_load_paths if @compass
32
- begin
33
- Sass::Engine.for_file(filename, sass_options).render
34
- rescue Sass::SyntaxError => e
35
- if e.message =~ /compass/ && !@compass
36
- puts "Enable --compass option to use compass's extensions"
37
- exit 1
38
- else
39
- raise e
40
- end
41
- end
23
+ all_contents= @argv.map do |filename|
24
+ if filename =~ URI.regexp
25
+ load_css_file(filename)
42
26
  else
43
- open(filename) {|f| f.read }
27
+ case File.extname(filename).downcase
28
+ when ".scss", ".sass"
29
+ load_sass_file(filename)
30
+ when ".less"
31
+ load_less_file(filename)
32
+ else
33
+ load_css_file(filename)
34
+ end
44
35
  end
45
36
  end.join("\n")
46
37
 
@@ -80,7 +71,7 @@ module Csscss
80
71
  @verbose = v
81
72
  end
82
73
 
83
- opts.on("--[no-]color", "Colorize output (default is true)") do |c|
74
+ opts.on("--[no-]color", "Colorize output (default is #{@color})") do |c|
84
75
  @color = c
85
76
  end
86
77
 
@@ -133,7 +124,6 @@ module Csscss
133
124
  print_help(opts)
134
125
  end
135
126
 
136
- private
137
127
  def print_help(opts)
138
128
  puts opts
139
129
  exit
@@ -155,6 +145,51 @@ module Csscss
155
145
  abort "Must install compass gem before enabling its extensions"
156
146
  end
157
147
 
148
+ def windows_1_9
149
+ RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ && RUBY_VERSION =~ /^1\.9/
150
+ end
151
+
152
+ def gem_installed?(gem_name)
153
+ begin
154
+ require gem_name
155
+ true
156
+ rescue LoadError
157
+ false
158
+ end
159
+ end
160
+
161
+ def load_sass_file(filename)
162
+ if !gem_installed?("sass") then
163
+ abort 'Must install the "sass" gem before parsing sass/scss files'
164
+ end
165
+
166
+ sass_options = {cache:false}
167
+ sass_options[:load_paths] = Compass.configuration.sass_load_paths if @compass
168
+ begin
169
+ Sass::Engine.for_file(filename, sass_options).render
170
+ rescue Sass::SyntaxError => e
171
+ if e.message =~ /compass/ && !@compass
172
+ puts "Enable --compass option to use compass's extensions"
173
+ exit 1
174
+ else
175
+ raise e
176
+ end
177
+ end
178
+ end
179
+
180
+ def load_less_file(filename)
181
+ if !gem_installed?("less") then
182
+ abort 'Must install the "less" gem before parsing less files'
183
+ end
184
+
185
+ contents = load_css_file(filename)
186
+ Less::Parser.new.parse(contents).to_css
187
+ end
188
+
189
+ def load_css_file(filename)
190
+ open(filename) {|f| f.read }
191
+ end
192
+
158
193
  class << self
159
194
  def run(argv)
160
195
  new(argv).run
@@ -5,17 +5,19 @@ module Csscss
5
5
 
6
6
  UNITS = %w(px em ex in cm mm pt pc)
7
7
 
8
- rule(:space) { match['\s'].repeat(1) }
9
- rule(:space?) { space.maybe }
10
- rule(:number) { match["0-9"] }
11
- rule(:numbers) { number.repeat(1) }
12
- rule(:decimal) { numbers >> str(".").maybe >> numbers.maybe }
13
- rule(:percent) { decimal >> stri("%") >> space? }
14
- rule(:length) { decimal >> stri_list(UNITS) >> space? }
15
- rule(:identifier) { match["a-zA-Z"].repeat(1) }
16
- rule(:inherit) { stri("inherit") }
17
- rule(:eof) { any.absent? }
18
- rule(:nada) { any.repeat.as(:nada) }
8
+ rule(:space) { match['\s'].repeat(1) }
9
+ rule(:space?) { space.maybe }
10
+ rule(:number) { match["0-9"] }
11
+ rule(:numbers) { number.repeat(1) }
12
+ rule(:decimal) { numbers >> str(".").maybe >> numbers.maybe }
13
+ rule(:percent) { decimal >> stri("%") >> space? }
14
+ rule(:non_zero_length) { decimal >> stri_list(UNITS) >> space? }
15
+ rule(:zero_length) { match["0"] }
16
+ rule(:length) { zero_length | non_zero_length }
17
+ rule(:identifier) { match["a-zA-Z"].repeat(1) }
18
+ rule(:inherit) { stri("inherit") }
19
+ rule(:eof) { any.absent? }
20
+ rule(:nada) { any.repeat.as(:nada) }
19
21
 
20
22
  rule(:http) {
21
23
  (match['a-zA-Z0-9.:/\-'] | str('\(') | str('\)')).repeat >> space?
@@ -29,7 +31,7 @@ module Csscss
29
31
  stri("url") >> parens do
30
32
  (any_quoted { http } >> space?) |
31
33
  (any_quoted { data } >> space?) |
32
- http
34
+ data | http
33
35
  end
34
36
  }
35
37
 
@@ -25,7 +25,13 @@ module Csscss
25
25
  rule(:attribute) {
26
26
  match["^:{}"].repeat(1).as(:property) >>
27
27
  str(":") >>
28
- match["^;}"].repeat(1).as(:value) >>
28
+ (match["^;}"].repeat(1).capture(:stuff) >> dynamic {|source, context|
29
+ if context.captures[:stuff].to_s =~ /data:/
30
+ str(";") >> match["^;}"].repeat(1)
31
+ else
32
+ any.present?
33
+ end
34
+ }).as(:value) >>
29
35
  str(";").maybe >>
30
36
  space?
31
37
  }
@@ -65,7 +65,6 @@ module Csscss
65
65
  end
66
66
  end
67
67
 
68
-
69
68
  # trims any derivative declarations alongside shorthand
70
69
  inverted_matches.each do |selectors, declarations|
71
70
  redundant_derivatives = declarations.select do |dec|
data/lib/csscss/types.rb CHANGED
@@ -30,14 +30,15 @@ module Csscss
30
30
 
31
31
  def ==(other)
32
32
  if other.respond_to?(:property) && other.respond_to?(:value)
33
- property == other.property && value == other.value
33
+ # using eql? tanks performance
34
+ property == other.property && normalize_value(value) == normalize_value(other.value)
34
35
  else
35
36
  false
36
37
  end
37
38
  end
38
39
 
39
40
  def hash
40
- [property, value].hash
41
+ [property, normalize_value(value)].hash
41
42
  end
42
43
 
43
44
  def eql?(other)
@@ -67,6 +68,15 @@ module Csscss
67
68
  "<#{self.class} #{to_s}>"
68
69
  end
69
70
  end
71
+
72
+ private
73
+ def normalize_value(value)
74
+ if value =~ /^0(#{Csscss::Parser::Common::UNITS.join("|")}|%)$/
75
+ "0"
76
+ else
77
+ value
78
+ end
79
+ end
70
80
  end
71
81
 
72
82
  class Selector < Struct.new(:selectors)
@@ -1,3 +1,3 @@
1
1
  module Csscss
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -106,6 +106,8 @@ module Csscss::Parser
106
106
  @parser.length.must_parse "123px"
107
107
  @parser.length.must_parse "123EM"
108
108
  @parser.length.must_parse "1.23Pt"
109
+ @parser.length.must_parse "0"
110
+ @parser.length.wont_parse "1"
109
111
  end
110
112
  end
111
113
 
@@ -115,15 +117,16 @@ module Csscss::Parser
115
117
  @parser.http.must_parse 'foo\(bar\).jpg'
116
118
  @parser.http.must_parse 'http://foo\(bar\).jpg'
117
119
  @parser.http.must_parse 'http://foo.com/baz/\(bar\).jpg'
118
- @parser.http.must_parse '//foo.com/foo.jpg'
119
- @parser.http.must_parse 'https://foo.com/foo.jpg'
120
- @parser.http.must_parse 'http://foo100.com/foo.jpg'
121
- @parser.http.must_parse 'http://foo-bar.com/foo.jpg'
120
+ @parser.http.must_parse "//foo.com/foo.jpg"
121
+ @parser.http.must_parse "https://foo.com/foo.jpg"
122
+ @parser.http.must_parse "http://foo100.com/foo.jpg"
123
+ @parser.http.must_parse "http://foo-bar.com/foo.jpg"
122
124
  end
123
125
 
124
126
  it "parses data" do
125
- @parser.data.must_parse 'data:image/jpg;base64,IMGDATAGOESHERE=='
126
- @parser.data.must_parse 'data:image/svg+xml;base64,IMGDATAGOESHERE4/5/h/1+=='
127
+ @parser.data.must_parse "data:image/jpg;base64,IMGDATAGOESHERE=="
128
+ @parser.data.must_parse "data:image/svg+xml;base64,IMGDATAGOESHERE4/5/h/1+=="
129
+ @parser.data.must_parse "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAACECAYAAABRaEHiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHRJREFUeNqkUjESwCAIw+T/X/UHansdkLTQDnXgCAHNEW2tZbDz/Aq994bzqoY5Z8wEwiEcmmfwiRK+EGOMTVBrtz4mY9kEAyz6+E3sJ7MWBs1PaUy1lHLLmgTqElltNxLiINTBbWi0Vj5DZC9CaqZEOwQYAPhxY/7527NfAAAAAElFTkSuQmCC"
127
130
  end
128
131
 
129
132
  it "parses urls" do
@@ -133,6 +136,7 @@ module Csscss::Parser
133
136
  @parser.url.must_parse "url('foo.jpg')"
134
137
  @parser.url.must_parse "url('foo.jpg' )"
135
138
  @parser.url.must_parse 'url(foo\(bar\).jpg)'
139
+ @parser.url.must_parse "url(data:image/svg+xml;base64,IMGDATAGOESHERE4/5/h/1+==)"
136
140
  @parser.url.must_parse "url('data:image/svg+xml;base64,IMGDATAGOESHERE4/5/h/1+==')"
137
141
  end
138
142
  end
@@ -134,6 +134,39 @@ module Csscss::Parser
134
134
  rs(sel("h1"), [dec("display", "none")])
135
135
  ])
136
136
  end
137
+
138
+ it "parses attributes with encoded data that include semicolons" do
139
+ trans(%$
140
+ .foo1 {
141
+ background: rgb(123, 123, 123) url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAACECAYAAABRaEHiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHRJREFUeNqkUjESwCAIw+T/X/UHansdkLTQDnXgCAHNEW2tZbDz/Aq994bzqoY5Z8wEwiEcmmfwiRK+EGOMTVBrtz4mY9kEAyz6+E3sJ7MWBs1PaUy1lHLLmgTqElltNxLiINTBbWi0Vj5DZC9CaqZEOwQYAPhxY/7527NfAAAAAElFTkSuQmCC) repeat-x;
142
+ display: block;
143
+ }
144
+
145
+ .foo2 {
146
+ background: white url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAACECAYAAABRaEHiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHRJREFUeNqkUjESwCAIw+T/X/UHansdkLTQDnXgCAHNEW2tZbDz/Aq994bzqoY5Z8wEwiEcmmfwiRK+EGOMTVBrtz4mY9kEAyz6+E3sJ7MWBs1PaUy1lHLLmgTqElltNxLiINTBbWi0Vj5DZC9CaqZEOwQYAPhxY/7527NfAAAAAElFTkSuQmCC) repeat-x
147
+ }
148
+
149
+ .foo3 {
150
+ outline: 1px;
151
+ background: white url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAACECAYAAABRaEHiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHRJREFUeNqkUjESwCAIw+T/X/UHansdkLTQDnXgCAHNEW2tZbDz/Aq994bzqoY5Z8wEwiEcmmfwiRK+EGOMTVBrtz4mY9kEAyz6+E3sJ7MWBs1PaUy1lHLLmgTqElltNxLiINTBbWi0Vj5DZC9CaqZEOwQYAPhxY/7527NfAAAAAElFTkSuQmCC) repeat-x;
152
+ display: block;
153
+ }
154
+
155
+ .foo4 {
156
+ background: blue url(images/bg-bolt-inactive.png) no-repeat 99% 5px;
157
+ display: block;
158
+ }
159
+ $).must_equal([
160
+ rs(sel(".foo1"), [dec("background", "rgb(123, 123, 123) url(data:image/png;base64,ivborw0kggoaaaansuheugaaaaeaaacecayaaabraehiaaaagxrfwhrtb2z0d2fyzqbbzg9izsbjbwfnzvjlywr5ccllpaaaahrjrefuenqkujeswcaiw+t/x/uhansdkltqdnxgcahnew2tzbdz/aq994bzqoy5z8wewiecmmfwirk+egomtvbrtz4my9keayz6+e3sj7mwbs1pauy1lhllmgtqelltnxliintbbwi0vj5dzc9caqzeowqyaphxy/7527nfaaaaaelftksuqmcc) repeat-x"),
161
+ dec("display", "block")]),
162
+ rs(sel(".foo2"), [dec("background", "white url(data:image/png;base64,ivborw0kggoaaaansuheugaaaaeaaacecayaaabraehiaaaagxrfwhrtb2z0d2fyzqbbzg9izsbjbwfnzvjlywr5ccllpaaaahrjrefuenqkujeswcaiw+t/x/uhansdkltqdnxgcahnew2tzbdz/aq994bzqoy5z8wewiecmmfwirk+egomtvbrtz4my9keayz6+e3sj7mwbs1pauy1lhllmgtqelltnxliintbbwi0vj5dzc9caqzeowqyaphxy/7527nfaaaaaelftksuqmcc) repeat-x")]),
163
+ rs(sel(".foo3"), [dec("outline", "1px"),
164
+ dec("background", "white url(data:image/png;base64,ivborw0kggoaaaansuheugaaaaeaaacecayaaabraehiaaaagxrfwhrtb2z0d2fyzqbbzg9izsbjbwfnzvjlywr5ccllpaaaahrjrefuenqkujeswcaiw+t/x/uhansdkltqdnxgcahnew2tzbdz/aq994bzqoy5z8wewiecmmfwirk+egomtvbrtz4my9keayz6+e3sj7mwbs1pauy1lhllmgtqelltnxliintbbwi0vj5dzc9caqzeowqyaphxy/7527nfaaaaaelftksuqmcc) repeat-x"),
165
+ dec("display", "block")]),
166
+ rs(sel(".foo4"), [dec("background", "blue url(images/bg-bolt-inactive.png) no-repeat 99% 5px"),
167
+ dec("display", "block")])
168
+ ])
169
+ end
137
170
  end
138
171
  end
139
172
  end
@@ -275,6 +275,18 @@ module Csscss
275
275
  })
276
276
  end
277
277
 
278
+ it "matches 0 and 0px" do
279
+ css = %$
280
+ .bar { padding: 0; }
281
+ .foo { padding: 0px; }
282
+ $
283
+
284
+ RedundancyAnalyzer.new(css).redundancies.must_equal({
285
+ [sel(".bar"), sel(".foo")] => [dec("padding", "0")]
286
+ })
287
+ end
288
+
289
+
278
290
  # TODO: someday
279
291
  # it "reports duplication within the same selector" do
280
292
  # css = %$
@@ -69,5 +69,13 @@ module Csscss
69
69
  h[dec2].must_equal true
70
70
  h[dec3].must_equal true
71
71
  end
72
+
73
+ it "equates 0 length with and without units" do
74
+ Declaration.new("padding", "0px").must_equal Declaration.new("padding", "0")
75
+ Declaration.new("padding", "0%").must_equal Declaration.new("padding", "0")
76
+ Declaration.new("padding", "0").must_equal Declaration.new("padding", "0em")
77
+
78
+ Declaration.new("padding", "1").wont_equal Declaration.new("padding", "1px")
79
+ end
72
80
  end
73
81
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csscss
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-12 00:00:00.000000000 Z
12
+ date: 2013-04-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: parslet
@@ -127,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
127
  version: '0'
128
128
  segments:
129
129
  - 0
130
- hash: -506187209623006521
130
+ hash: 4177886489285701178
131
131
  requirements: []
132
132
  rubyforge_project:
133
133
  rubygems_version: 1.8.25