New compass subcommand: stats. Emits details about your stylesheets.
This commit is contained in:
parent
0d45a3b4aa
commit
d1e1c1756d
@ -5,7 +5,7 @@ require 'compass/commands/registry'
|
||||
|
||||
%w(base generate_grid_background help list_frameworks project_base
|
||||
update_project watch_project create_project installer_command
|
||||
print_version stamp_pattern validate_project
|
||||
print_version project_stats stamp_pattern validate_project
|
||||
write_configuration).each do |lib|
|
||||
require "compass/commands/#{lib}"
|
||||
end
|
||||
|
141
lib/compass/commands/project_stats.rb
Normal file
141
lib/compass/commands/project_stats.rb
Normal file
@ -0,0 +1,141 @@
|
||||
require 'compass/commands/project_base'
|
||||
require 'compass/commands/update_project'
|
||||
|
||||
module Compass
|
||||
module Commands
|
||||
module StatsOptionsParser
|
||||
def set_options(opts)
|
||||
opts.banner = %Q{
|
||||
Usage: compass stats [path/to/project] [options]
|
||||
|
||||
Description:
|
||||
Compile project at the path specified (or the current
|
||||
directory if not specified) and then compute statistics
|
||||
for the sass and css files in the project.
|
||||
|
||||
Options:
|
||||
}.strip.split("\n").map{|l| l.gsub(/^ {0,10}/,'')}.join("\n")
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
class ProjectStats < UpdateProject
|
||||
|
||||
register :stats
|
||||
|
||||
def initialize(working_path, options)
|
||||
super
|
||||
assert_project_directory_exists!
|
||||
end
|
||||
|
||||
def perform
|
||||
super
|
||||
require 'compass/stats'
|
||||
compiler = new_compiler_instance
|
||||
sass_files = sorted_sass_files(compiler)
|
||||
rows = [[ :-, :-, :-, :-, :- ],
|
||||
[ 'Filename', 'Rules', 'Properties', 'Mixins Defs', 'Mixins Used' ],
|
||||
[ :-, :-, :-, :-, :- ]]
|
||||
maximums = [ 8, 5, 10, 14, 11 ]
|
||||
alignments = [ :left, :right, :right, :right, :right ]
|
||||
delimiters = [ ['| ', ' |'], [' ', ' |'], [' ', ' |'], [' ', ' |'], [' ', ' |'] ]
|
||||
totals = [ "Total (#{sass_files.size} files):", 0, 0, 0, 0 ]
|
||||
|
||||
sass_files.each do |sass_file|
|
||||
css_file = compiler.corresponding_css_file(sass_file) unless sass_file[0..0] == '_'
|
||||
row = filename_columns(sass_file)
|
||||
row += sass_columns(sass_file)
|
||||
row.each_with_index do |c, i|
|
||||
maximums[i] = [maximums[i].to_i, c.size].max
|
||||
totals[i] = totals[i] + c.to_i if i > 0
|
||||
end
|
||||
rows << row
|
||||
end
|
||||
rows << [:-, :-, :-, :-, :-]
|
||||
rows << totals.map{|t| t.to_s}
|
||||
rows << [:-, :-, :-, :-, :-]
|
||||
rows.each do |row|
|
||||
row.each_with_index do |col, i|
|
||||
print pad(col, maximums[i], :align => alignments[i], :left => delimiters[i].first, :right => delimiters[i].last)
|
||||
end
|
||||
print "\n"
|
||||
end
|
||||
end
|
||||
|
||||
def pad(c, max, options = {})
|
||||
options[:align] ||= :left
|
||||
if c == :-
|
||||
filler = '-'
|
||||
c = ''
|
||||
else
|
||||
filler = ' '
|
||||
end
|
||||
spaces = max - c.size
|
||||
filled = filler * [spaces,0].max
|
||||
"#{options[:left]}#{filled if options[:align] == :right}#{c}#{filled if options[:align] == :left}#{options[:right]}"
|
||||
end
|
||||
|
||||
def sorted_sass_files(compiler)
|
||||
sass_files = compiler.sass_files(:exclude_partials => false)
|
||||
sass_files.map! do |s|
|
||||
filename = Compass.deprojectize(s, File.join(Compass.configuration.project_path, Compass.configuration.sass_dir))
|
||||
[s, File.dirname(filename), File.basename(filename)]
|
||||
end
|
||||
sass_files = sass_files.sort_by do |s,d,f|
|
||||
File.join(d, f[0] == ?_ ? f[1..-1] : f)
|
||||
end
|
||||
sass_files.map!{|s,d,f| s}
|
||||
end
|
||||
|
||||
def filename_columns(sass_file)
|
||||
filename = Compass.deprojectize(sass_file, working_path)
|
||||
[filename]
|
||||
end
|
||||
|
||||
def sass_columns(sass_file)
|
||||
sf = Compass::Stats::SassFile.new(sass_file)
|
||||
sf.analyze!
|
||||
%w(rule_count prop_count mixin_def_count mixin_count).map do |t|
|
||||
sf.send(t).to_s
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
|
||||
def option_parser(arguments)
|
||||
parser = Compass::Exec::CommandOptionParser.new(arguments)
|
||||
parser.extend(Compass::Exec::GlobalOptionsParser)
|
||||
parser.extend(Compass::Exec::ProjectOptionsParser)
|
||||
parser.extend(StatsOptionsParser)
|
||||
end
|
||||
|
||||
def usage
|
||||
option_parser([]).to_s
|
||||
end
|
||||
|
||||
def description(command)
|
||||
"Report statistics about your stylesheets"
|
||||
end
|
||||
|
||||
def parse!(arguments)
|
||||
parser = option_parser(arguments)
|
||||
parser.parse!
|
||||
parse_arguments!(parser, arguments)
|
||||
parser.options
|
||||
end
|
||||
|
||||
def parse_arguments!(parser, arguments)
|
||||
if arguments.size == 1
|
||||
parser.options[:project_name] = arguments.shift
|
||||
elsif arguments.size == 0
|
||||
# default to the current directory.
|
||||
else
|
||||
raise Compass::Error, "Too many arguments were specified."
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
@ -13,8 +13,9 @@ module Compass
|
||||
self.options[:cache_location] ||= File.join(from, ".sass-cache")
|
||||
end
|
||||
|
||||
def sass_files
|
||||
@sass_files || Dir.glob(separate("#{from}/**/[^_]*.sass"))
|
||||
def sass_files(options = {})
|
||||
exclude_partials = options.fetch(:exclude_partials, true)
|
||||
@sass_files || Dir.glob(separate("#{from}/**/#{'[^_]' if exclude_partials}*.sass"))
|
||||
end
|
||||
|
||||
def stylesheet_name(sass_file)
|
||||
@ -51,4 +52,4 @@ module Compass
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -66,6 +66,15 @@ module Compass
|
||||
File.join(project_path, *path.split('/'))
|
||||
end
|
||||
|
||||
def deprojectize(path, project_path = nil)
|
||||
project_path ||= configuration.project_path
|
||||
if path[0..(project_path.size - 1)] == project_path
|
||||
path[(project_path.size + 1)..-1]
|
||||
else
|
||||
path
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Deprecate the src/config.rb location.
|
||||
KNOWN_CONFIG_LOCATIONS = [".compass/config.rb", "config/compass.config", "config.rb", "src/config.rb"]
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
%w(stylesheet_updating).each do |patch|
|
||||
%w(stylesheet_updating traversal).each do |patch|
|
||||
require "compass/sass_extensions/monkey_patches/#{patch}"
|
||||
end
|
||||
end
|
||||
|
23
lib/compass/sass_extensions/monkey_patches/traversal.rb
Normal file
23
lib/compass/sass_extensions/monkey_patches/traversal.rb
Normal file
@ -0,0 +1,23 @@
|
||||
module Sass
|
||||
module Tree
|
||||
class Node
|
||||
unless method_defined?(:visit_depth_first)
|
||||
def visit_depth_first(visitor)
|
||||
visitor.visit(self)
|
||||
visitor.down(self) if children.any? and visitor.respond_to?(:down)
|
||||
if is_a?(ImportNode) && visitor.import?(self)
|
||||
root = Sass::Files.tree_for(import, @options)
|
||||
imported_children = root.children
|
||||
end
|
||||
|
||||
(imported_children || children).each do |child|
|
||||
break if visitor.respond_to?(:stop?) && visitor.stop?
|
||||
child.visit_depth_first(visitor)
|
||||
end
|
||||
visitor.up(self) if children.any?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
78
lib/compass/stats.rb
Normal file
78
lib/compass/stats.rb
Normal file
@ -0,0 +1,78 @@
|
||||
module Compass
|
||||
module Stats
|
||||
class StatsVisitor
|
||||
attr_accessor :rule_count, :prop_count, :mixin_def_count, :mixin_count
|
||||
def initialize
|
||||
self.rule_count = 0
|
||||
self.prop_count = 0
|
||||
self.mixin_def_count = 0
|
||||
self.mixin_count = 0
|
||||
end
|
||||
def visit(node)
|
||||
self.prop_count += 1 if node.is_a?(Sass::Tree::PropNode) && !node.children.any?
|
||||
if node.is_a?(Sass::Tree::RuleNode)
|
||||
self.rule_count += node.rules.map{|r| r.split(/,/)}.flatten.compact.size
|
||||
end
|
||||
self.mixin_def_count += 1 if node.is_a?(Sass::Tree::MixinDefNode)
|
||||
self.mixin_count += 1 if node.is_a?(Sass::Tree::MixinNode)
|
||||
end
|
||||
def up(node)
|
||||
end
|
||||
def down(node)
|
||||
end
|
||||
def import?(node)
|
||||
return false
|
||||
full_filename = node.send(:import)
|
||||
full_filename != Compass.deprojectize(full_filename)
|
||||
end
|
||||
end
|
||||
class CssFile
|
||||
attr_accessor :path
|
||||
def initialize(path)
|
||||
self.path = path
|
||||
end
|
||||
def contents
|
||||
@contents ||= File.read(path)
|
||||
end
|
||||
def lines
|
||||
contents.inject(0){|m,c| m + 1 }
|
||||
end
|
||||
end
|
||||
class SassFile
|
||||
attr_accessor :path
|
||||
attr_reader :visitor
|
||||
def initialize(path)
|
||||
self.path = path
|
||||
end
|
||||
def contents
|
||||
@contents ||= File.read(path)
|
||||
end
|
||||
def tree
|
||||
@tree = Sass::Engine.new(contents, Compass.configuration.to_sass_engine_options).to_tree
|
||||
end
|
||||
def visit_tree!
|
||||
@visitor = StatsVisitor.new
|
||||
tree.visit_depth_first(@visitor)
|
||||
@visitor
|
||||
end
|
||||
def analyze!
|
||||
visit_tree!
|
||||
end
|
||||
def lines
|
||||
contents.inject(0){|m,c| m + 1 }
|
||||
end
|
||||
def rule_count
|
||||
visitor.rule_count
|
||||
end
|
||||
def prop_count
|
||||
visitor.prop_count
|
||||
end
|
||||
def mixin_def_count
|
||||
visitor.mixin_def_count
|
||||
end
|
||||
def mixin_count
|
||||
visitor.mixin_count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user