Skip to content

Instantly share code, notes, and snippets.

@seafarer
Created October 7, 2014 20:49
Show Gist options
  • Save seafarer/b1b94c58ee3f688568e1 to your computer and use it in GitHub Desktop.
Save seafarer/b1b94c58ee3f688568e1 to your computer and use it in GitHub Desktop.
# Requre a specific version in this file:
# gem 'zurb-foundation', '=4.3.1'
require 'zurb-foundation'
# Require any additional compass plugins here.
class Splitter
MAX_SELECTORS_DEFAULT = 4095
def self.split(infile, outdir = File.dirname(infile), max_selectors = MAX_SELECTORS_DEFAULT)
raise "infile could not be found" unless File.exists? infile
rules = IO.readlines(infile, "}")
return if rules.first.nil?
charset_statement, rules[0] = rules.first.partition(/^\@charset[^;]+;/)[1,2]
return if rules.nil?
# The infile remains the first file
file_id = 1
selectors_count = 0
output = nil
rules.each do |rule|
rule_selectors_count = count_selectors_of_rule rule
selectors_count += rule_selectors_count
# Nothing happens until the selectors limit is reached for the first time
if selectors_count > max_selectors
# Close current file if there is already one
output.close if output
# Prepare next file
file_id += 1
filename = File.join(outdir, File.basename(infile, File.extname(infile)) + "_#{file_id.to_s}" + File.extname(infile))
output = File.new(filename, "w")
output.write charset_statement
# Reset count with current rule count
selectors_count = rule_selectors_count
end
output.write rule if output
end
end
# 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 rule =~ /\A\s*}\z$/
current_media = nil
# skip the line if the close bracket is the first rule for the new file
next if first_hit
end
if selector_range.cover? selectors_count # add rule to current output if within selector_range
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)
parts = strip_comments(rule).partition(/\{/)
parts[1].empty? ? 0 : parts.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.sum{ |rule| count_selectors_of_rule(rule) }
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
# Set this to the root of your project when deployed:
http_path = "../"
css_dir = "css"
sass_dir = "scss"
images_dir = "images"
javascripts_dir = "js"
fonts_dir = "fonts"
# You can select your preferred output style here (can be overridden via the command line):
# output_style = :expanded or :nested or :compact or :compressed
output_style = :expanded
# To enable relative paths to assets via compass helper functions. Uncomment:
# relative_assets = true
# To disable debugging comments that display the original location of your selectors. Uncomment:
# line_comments = false
line_comments = true
# If you prefer the indented syntax, you might want to regenerate this
# project again passing --syntax sass, or you can uncomment this:
# preferred_syntax = :sass
# and then run:
# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
on_stylesheet_saved do |path|
Splitter.split(path) unless path[/\d+$/]
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment