Created
April 22, 2014 10:42
-
-
Save vaicine/11173681 to your computer and use it in GitHub Desktop.
Count CSS selectors (ruby)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CSSCount | |
MAX_SELECTORS_DEFAULT = 4095 | |
# returns the specified split of the passed css_string | |
def self.split_string(css_string, split = 1, max_selectors = MAX_SELECTORS_DEFAULT) | |
rules = split_string_into_rules(css_string) | |
extract_part rules, split, max_selectors | |
end | |
# splits string into array of rules (also strips comments) | |
def self.split_string_into_rules(css_string) | |
strip_comments(css_string).chomp.scan /[^}]*}/ | |
end | |
# extracts the specified part of an overlong CSS string | |
def self.extract_part(rules, part = 1, max_selectors = MAX_SELECTORS_DEFAULT) | |
return if rules.first.nil? | |
charset_statement, rules[0] = extract_charset(rules.first) | |
return if rules.nil? | |
output = charset_statement || "" | |
selectors_count = 0 | |
selector_range = max_selectors * (part - 1) + 1 .. max_selectors * part # e.g (4096..8190) | |
current_media = nil | |
selectors_in_media = 0 | |
first_hit = true | |
rules.each do |rule| | |
media_part = extract_media(rule) | |
if media_part | |
current_media = media_part | |
selectors_in_media = 0 | |
end | |
rule_selectors_count = count_selectors_of_rule rule | |
selectors_count += rule_selectors_count | |
if selector_range.cover? selectors_count # add rule to current output if within selector_range | |
if rule =~ /^\s*}$/ | |
current_media = nil | |
# skip the line if the close bracket is the first rule for the new file | |
next if first_hit | |
end | |
if media_part | |
output << media_part | |
elsif first_hit && current_media | |
output << current_media | |
end | |
selectors_in_media += rule_selectors_count if current_media.present? | |
output << rule | |
first_hit = false | |
elsif selectors_count > selector_range.end # stop writing to output | |
break | |
end | |
end | |
if current_media.present? and selectors_in_media > 0 | |
output << '}' | |
end | |
output | |
end | |
# count selectors of one individual CSS rule | |
def self.count_selectors_of_rule(rule) | |
strip_comments(rule).partition(/\{/).first.scan(/,/).count.to_i + 1 | |
end | |
# count selectors of a CSS stylesheet (not used by SprocketsEngine) | |
def self.count_selectors(css_file) | |
raise "file could not be found" unless File.exists? css_file | |
rules = split_string_into_rules(File.read css_file) | |
return if rules.first.nil? | |
rules.each { |rule| count_selectors_of_rule(rule) }.count | |
end | |
private | |
def self.extract_media(rule) | |
if rule.sub!(/^\s*(@media[^{]*{)([^{}]*{[^}]*})$/) { $2 } | |
$1 | |
end | |
end | |
# extracts potential charset declaration from the first rule | |
def self.extract_charset(rule) | |
if rule.include?('charset') | |
rule.partition(/^\@charset[^;]+;/)[1,2] | |
else | |
[nil, rule] | |
end | |
end | |
def self.strip_comments(s) | |
s.gsub(/\/\*.*?\*\//m, "") | |
end | |
end | |
Dir["*.css"].each do |file| | |
css_file = File.open(file) | |
puts "#{file} has #{CSSCount.count_selectors(css_file)} css selectors" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment