class Tailwindcss::Purger
def escape_class_selector(class_name)
def escape_class_selector(class_name) class_name.gsub(/\A\d|[^-_a-z0-9]/, '\\\\\0') end
def extract_class_names(string)
def extract_class_names(string) string.scan(CLASS_NAME_PATTERN).uniq.sort! end
def extract_class_names_from(files)
def extract_class_names_from(files) Array(files).flat_map { |file| extract_class_names(file.read) }.uniq.sort! end
def initialize(keep_these_class_names)
def initialize(keep_these_class_names) @keep_these_class_names = keep_these_class_names end
def keep_these_selectors_pattern
def keep_these_selectors_pattern @keep_these_selectors_pattern ||= begin escaped_classes = @keep_these_class_names.map { |name| Regexp.escape self.class.escape_class_selector(name) } /(?:\A|,)[^.,{]*(?:[.](?:#{escaped_classes.join("|")})#{CLASS_BREAK}[^.,{]*)*(?=[,{])/ end end
def purge(input, keeping_class_names_from_files:)
def purge(input, keeping_class_names_from_files:) new(extract_class_names_from(keeping_class_names_from_files)).purge(input) end
def purge(input)
def purge(input) conveyor = Conveyor.new(input) until conveyor.done? conveyor.discard(COMMENTS_AND_BLANK_LINES) \ or conveyor.conditionally_keep(PROPERTIES) { conveyor.staged_output.last != "" } \ or conveyor.conditionally_keep(END_OF_BLOCK) { not conveyor.staged_output.pop } \ or conveyor.stage_output(CLASSLESS_BEGINNING_OF_BLOCK) \ or conveyor.stage_output(BEGINNING_OF_BLOCK) { |match| purge_beginning_of_block(match.to_s) } \ or raise "infinite loop" end conveyor.output end
def purge_beginning_of_block(string)
def purge_beginning_of_block(string) purged = string.scan(keep_these_selectors_pattern).join unless purged.empty? purged.sub!(/\A,\s*/, "") purged.rstrip! purged << " {\n" end purged end