dead_css 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/bin/dead_css +58 -0
  3. data/lib/dead_css.rb +56 -0
  4. metadata +60 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6cc29741d322c19a5cd25d7cdb78e80a8c41e023
4
+ data.tar.gz: 278ef9a7fcad61e9375ff0fc1c747b4788c5dbca
5
+ SHA512:
6
+ metadata.gz: aac3fb05c60f6d3790f1bf86dd34799f223ffbd32cbe2a4de922219dd3032673d8407a567eb80d3e60ec248efff62c8f4fdd9aba7b8e440fd70514936743ed14
7
+ data.tar.gz: 643b099b04d9f054c64ca0574c94e236dfea1b1f244fbd2da76b9bc6542be0edcab087283b71164c7d6cf7d458577162c5791ec4bd9f2c013e86403ccf12faa1
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'logger'
4
+ require 'optparse'
5
+ require 'dead_css'
6
+
7
+
8
+ logger = Logger.new(STDOUT)
9
+ logger.level = Logger::INFO
10
+
11
+ options = {}
12
+ OptionParser.new do |opts|
13
+ opts.banner = 'Usage: dead_css -c <path/to/codebase>'
14
+
15
+ opts.on('-c', '--codebase PATH', 'root of the codebase to analize') { |v| options[:root] = v }
16
+
17
+ opts.on_tail('-h', '--help', 'Show this message') do
18
+ puts opts
19
+ exit
20
+ end
21
+
22
+ opts.on('-v', '--[no-]verbose', 'Run verbosely') { |v| options[:verbose] = v }
23
+ end.parse!
24
+
25
+ logger.level = Logger::DEBUG if options[:verbose]
26
+
27
+ unless options[:root]
28
+ logger.error('Missing required -c argument, exiting')
29
+ exit
30
+ end
31
+
32
+ unless File.directory?(options[:root])
33
+ logger.error('-c argument is an invalid path, exiting')
34
+ exit
35
+ end
36
+
37
+ root = File.expand_path(options[:root])
38
+ logger.info("Running dead_css in #{root}")
39
+
40
+ logger.info('Scanning css files')
41
+ css_files = UnusedCSS.files_by_extensions(root, UnusedCSS::CSS_EXTENSIONS)
42
+ logger.info("Found #{css_files.size} css files")
43
+ css_files.each { |css_file| logger.debug(css_file) }
44
+
45
+ css_selectors = css_files.map { |css_file| UnusedCSS.css_selectors(css_file) }.flatten.uniq
46
+ css_selectors = css_selectors.map { |css_selector| UnusedCSS.elementary_selectors(css_selector) }.flatten.uniq
47
+ logger.info("Found #{css_selectors.size} css selectors")
48
+ css_selectors.each { |css_selector| logger.debug(css_selector) }
49
+ css_selectors = css_selectors.map { |s| UnusedCSS::Selector.new(s) }
50
+
51
+ assets_files = UnusedCSS.files_by_extensions(root, UnusedCSS::ASSETS_EXTENSIONS)
52
+ logger.info("Found #{assets_files.size} assets files")
53
+ logger.info('Looking for selectors in assets files')
54
+ assets_files.each { |asset_file| logger.debug(asset_file) }
55
+ unmatched_selectors = UnusedCSS.unmatched_selectors(css_selectors, assets_files)
56
+
57
+ logger.info("The following selectors are potentially dead:")
58
+ unmatched_selectors.each { |selector| logger.info(selector.to_s) }
@@ -0,0 +1,56 @@
1
+ require 'css_parser'
2
+
3
+
4
+ module UnusedCSS
5
+ include CssParser
6
+
7
+ CSS_EXTENSIONS = ['css', 'scss']
8
+ ASSETS_EXTENSIONS = ['html', 'js', 'coffee', 'haml']
9
+ SPLIT_REGEX = /(?:\s|>|~)+/
10
+ SELECTOR_REGEX = /((\.|#)\w+)/
11
+
12
+ class Selector
13
+ def initialize(sel)
14
+ @sel = sel
15
+ end
16
+
17
+ def prefix
18
+ @sel[0]
19
+ end
20
+
21
+ def value
22
+ @sel[1..-1]
23
+ end
24
+
25
+ def to_s
26
+ @sel
27
+ end
28
+ end
29
+
30
+ def self.files_by_extensions(root, extensions)
31
+ extensions.map { |ext| Dir.glob("#{root}/**/*.#{ext}") }.flatten
32
+ end
33
+
34
+ def self.css_selectors(path)
35
+ parser = CssParser::Parser.new
36
+ parser.load_file!(path)
37
+ selectors = []
38
+ parser.each_selector { |selector, declarations, specificity| selectors << selector }
39
+ selectors.map(&:split).flatten.uniq
40
+ end
41
+
42
+ def self.elementary_selectors(composed_selector)
43
+ composed_selector.split(/(?:\s|>|~)+/).map do |s|
44
+ SELECTOR_REGEX.match(s)
45
+ end.flatten.compact.map(&:to_s)
46
+ end
47
+
48
+ def self.unmatched_selectors(selectors, files)
49
+ unmatched_selectors = Array.new(selectors)
50
+ files.each do |file|
51
+ lines = File.readlines(file)
52
+ unmatched_selectors.delete_if { |selector| lines.grep(Regexp.new(selector.value)).size > 0 }
53
+ end
54
+ unmatched_selectors
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dead_css
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Lucas Deschamps
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: css_parser
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ description: List potentially unused css selectors in a project
28
+ email: lucasdchamps@gmail.com
29
+ executables:
30
+ - dead_css
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - bin/dead_css
35
+ - lib/dead_css.rb
36
+ homepage: http://rubygems.org/gems/dead_css
37
+ licenses:
38
+ - MIT
39
+ metadata: {}
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 2.4.8
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: Reports unused css
60
+ test_files: []