scss-lint 0.6 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,25 +1,27 @@
1
1
  module SCSSLint
2
- class Linter
2
+ class Linter < Sass::Tree::Visitors::Base
3
3
  include LinterRegistry
4
4
 
5
- class << self
6
- def run(engine)
7
- [] # No lints
8
- end
5
+ attr_reader :engine, :lints
9
6
 
10
- def create_lint(node)
11
- Lint.new(node.filename, node.line, description)
12
- end
7
+ def initialize
8
+ @lints = []
9
+ end
10
+
11
+ def run(engine)
12
+ @engine = engine
13
+ visit(engine.tree)
14
+ end
13
15
 
14
- def description
15
- nil
16
- end
16
+ def description
17
+ nil
18
+ end
17
19
 
18
- protected
20
+ protected
19
21
 
20
- def name
21
- self.class.name
22
- end
22
+ # Helper for creating lint from a parse tree node
23
+ def add_lint(node)
24
+ @lints << Lint.new(node.filename, node.line, description)
23
25
  end
24
26
  end
25
27
  end
@@ -4,18 +4,12 @@ module SCSSLint
4
4
  class Linter::DebugLinter < Linter
5
5
  include LinterRegistry
6
6
 
7
- class << self
8
- def run(engine)
9
- lints = []
10
- engine.tree.each do |node|
11
- lints << create_lint(node) if node.is_a?(Sass::Tree::DebugNode)
12
- end
13
- lints
14
- end
7
+ def visit_debug(node)
8
+ add_lint(node)
9
+ end
15
10
 
16
- def description
17
- '@debug line'
18
- end
11
+ def description
12
+ '@debug line'
19
13
  end
20
14
  end
21
15
  end
@@ -10,44 +10,30 @@ module SCSSLint
10
10
  Sass::Tree::RuleNode,
11
11
  ]
12
12
 
13
- class << self
14
- def run(engine)
15
- lints = []
16
- engine.tree.each do |node|
17
- if node.is_a?(Sass::Tree::RuleNode)
18
- lints << check_order_of_declarations(node)
19
- end
20
- end
21
- lints.compact
22
- end
13
+ def visit_rule(node)
14
+ children = node.children.select { |node| important_node?(node) }.
15
+ map { |node| node.class }
23
16
 
24
- def description
25
- 'Rule sets should start with @extend declarations, followed by ' <<
26
- 'properties and nested rule sets, in that order'
17
+ sorted_children = children.sort do |a, b|
18
+ DECLARATION_ORDER.index(a) <=> DECLARATION_ORDER.index(b)
27
19
  end
28
20
 
29
- private
30
-
31
- def important_node?(node)
32
- case node
33
- when *DECLARATION_ORDER
34
- true
35
- end
21
+ if children != sorted_children
22
+ add_lint(node.children.first)
36
23
  end
37
24
 
38
- def check_order_of_declarations(rule_node)
39
- children = rule_node.children.select { |node| important_node?(node) }.
40
- map { |node| node.class }
25
+ yield # Continue linting children
26
+ end
27
+
28
+ def description
29
+ 'Rule sets should start with @extend declarations, followed by ' <<
30
+ 'properties and nested rule sets, in that order'
31
+ end
41
32
 
42
- # Inefficient, but we're not sorting thousands of declarations
43
- sorted_children = children.sort do |x,y|
44
- DECLARATION_ORDER.index(x) <=> DECLARATION_ORDER.index(y)
45
- end
33
+ private
46
34
 
47
- if children != sorted_children
48
- return create_lint(rule_node.children.first)
49
- end
50
- end
35
+ def important_node?(node)
36
+ DECLARATION_ORDER.include? node.class
51
37
  end
52
38
  end
53
39
  end
@@ -4,26 +4,13 @@ module SCSSLint
4
4
  class Linter::EmptyRuleLinter < Linter
5
5
  include LinterRegistry
6
6
 
7
- class << self
8
- def run(engine)
9
- lints = []
10
- engine.tree.each do |node|
11
- if node.is_a?(Sass::Tree::RuleNode)
12
- lints << check_empty_rule(node)
13
- end
14
- end
15
- lints.compact
16
- end
17
-
18
- def description
19
- 'Empty rule'
20
- end
21
-
22
- private
7
+ def visit_rule(node)
8
+ add_lint(node) if node.children.empty?
9
+ yield # Continue linting children
10
+ end
23
11
 
24
- def check_empty_rule(rule_node)
25
- create_lint(rule_node) if rule_node.children.empty?
26
- end
12
+ def description
13
+ 'Empty rule'
27
14
  end
28
15
  end
29
16
  end
@@ -4,42 +4,30 @@ module SCSSLint
4
4
  class Linter::HexLinter < Linter
5
5
  include LinterRegistry
6
6
 
7
- class << self
8
- def run(engine)
9
- lints = []
10
- engine.tree.each do |node|
11
- if node.is_a?(Sass::Tree::PropNode)
12
- lints << check_valid_hex_value(node)
13
- end
14
- end
15
- lints.compact
16
- end
17
-
18
- def description
19
- 'Hexadecimal color codes should be lowercase and in 3-digit form where possible'
7
+ def visit_prop(node)
8
+ if node.value.is_a?(Sass::Script::String) &&
9
+ node.value.to_s =~ /#(\h{3,6})/
10
+ add_lint(node) unless valid_hex?($1)
20
11
  end
12
+ end
21
13
 
22
- private
14
+ def description
15
+ 'Hexadecimal color codes should be lowercase and in 3-digit form where possible'
16
+ end
23
17
 
24
- def check_valid_hex_value(prop_node)
25
- if prop_node.value.is_a?(Sass::Script::String) &&
26
- prop_node.value.to_s =~ /#(\h{3,6})/
27
- return create_lint(prop_node) unless valid_hex?($1)
28
- end
29
- end
18
+ private
30
19
 
31
- def valid_hex?(hex)
32
- [3,6].include?(hex.length) &&
33
- hex.downcase == hex &&
34
- !can_be_condensed(hex)
35
- end
20
+ def valid_hex?(hex)
21
+ [3,6].include?(hex.length) &&
22
+ hex.downcase == hex &&
23
+ !can_be_condensed(hex)
24
+ end
36
25
 
37
- def can_be_condensed(hex)
38
- hex.length == 6 &&
39
- hex[0] == hex[1] &&
40
- hex[2] == hex[3] &&
41
- hex[4] == hex[5]
42
- end
26
+ def can_be_condensed(hex)
27
+ hex.length == 6 &&
28
+ hex[0] == hex[1] &&
29
+ hex[2] == hex[3] &&
30
+ hex[4] == hex[5]
43
31
  end
44
32
  end
45
33
  end
@@ -4,38 +4,27 @@ module SCSSLint
4
4
  class Linter::PropertyFormatLinter < Linter
5
5
  include LinterRegistry
6
6
 
7
- class << self
8
- def run(engine)
9
- lints = []
10
- engine.tree.each do |node|
11
- if node.is_a?(Sass::Tree::PropNode)
12
- lints << check_property_format(node, engine.lines[node.line - 1]) if node.line
13
- end
14
- end
15
- lints.compact
16
- end
17
-
18
- def description
19
- 'Property declarations should always be on one line of the form ' <<
20
- '`name: value;` or `name: [value] {` ' <<
21
- '(are you missing a trailing semi-colon?)'
22
- end
7
+ def visit_prop(node)
8
+ line = engine.lines[node.line - 1] if node.line
9
+ add_lint(node) unless line =~ PROPERTY_RE
10
+ end
23
11
 
24
- private
12
+ def description
13
+ 'Property declarations should always be on one line of the form ' <<
14
+ '`name: value;` or `name: [value] {` ' <<
15
+ '(are you missing a trailing semi-colon?)'
16
+ end
25
17
 
26
- VALUE_RE = %r{(\S+\s)*\S+} # eg. "10px", "10px normal"
27
- PROPERTY_RE = %r{
28
- ^\s*[\w-]+:\s # property name, colon, one space
29
- ( # followed by
30
- #{VALUE_RE}; # property and terminating semi-colon eg. a b c;
31
- | # or
32
- ((#{VALUE_RE}\s)?\{) # nested property, optional value, trailing curly
33
- )
34
- }x
18
+ private
35
19
 
36
- def check_property_format(prop_node, line)
37
- return create_lint(prop_node) unless line =~ PROPERTY_RE
38
- end
39
- end
20
+ VALUE_RE = %r{(\S+\s)*\S+} # eg. "10px", "10px normal"
21
+ PROPERTY_RE = %r{
22
+ ^\s*[\w-]+:\s # property name, colon, one space
23
+ ( # followed by
24
+ #{VALUE_RE}; # property and terminating semi-colon eg. a b c;
25
+ | # or
26
+ ((#{VALUE_RE}\s)?\{) # nested property, optional value, trailing curly
27
+ )
28
+ }x
40
29
  end
41
30
  end
@@ -4,56 +4,45 @@ module SCSSLint
4
4
  class Linter::ShorthandLinter < Linter
5
5
  include LinterRegistry
6
6
 
7
- class << self
8
- def run(engine)
9
- lints = []
10
- engine.tree.each do |node|
11
- if node.is_a?(Sass::Tree::PropNode)
12
- lints << check_valid_shorthand_value(node)
13
- end
14
- end
15
- lints.compact
7
+ def visit_prop(node)
8
+ unless SHORTHANDABLE_PROPERTIES.include? node.name.first.to_s
9
+ return
16
10
  end
17
11
 
18
- def description
19
- 'Property values should use the shortest shorthand syntax allowed'
12
+ if node.value.to_sass.strip =~ /\A(\S+\s+\S+(\s+\S+){0,2})\Z/
13
+ add_lint(node) unless valid_shorthand?($1)
20
14
  end
15
+ end
21
16
 
22
- private
23
- SHORTHANDABLE_PROPERTIES = %w[border-color
24
- border-radius
25
- border-style
26
- border-width
27
- margin
28
- padding]
29
-
30
- def check_valid_shorthand_value(prop_node)
31
- unless SHORTHANDABLE_PROPERTIES.include? prop_node.name.first.to_s
32
- return
33
- end
34
-
35
- if prop_node.value.to_s.strip =~ /\A(\S+\s+\S+(\s+\S+){0,2})\Z/
36
- return create_lint(prop_node) unless valid_shorthand?($1)
37
- end
38
- end
17
+ def description
18
+ 'Property values should use the shortest shorthand syntax allowed'
19
+ end
39
20
 
40
- def valid_shorthand?(shorthand)
41
- values = shorthand.split(/\s+/)
42
- top, right, bottom, left = values
43
-
44
- if top == right && right == bottom && bottom == left
45
- false
46
- elsif top == right && bottom.nil? && left.nil?
47
- false
48
- elsif top == bottom && right == left
49
- false
50
- elsif top == bottom && left.nil?
51
- false
52
- elsif right == left
53
- false
54
- else
55
- true
56
- end
21
+ private
22
+
23
+ SHORTHANDABLE_PROPERTIES = %w[border-color
24
+ border-radius
25
+ border-style
26
+ border-width
27
+ margin
28
+ padding]
29
+
30
+ def valid_shorthand?(shorthand)
31
+ values = shorthand.split(/\s+/)
32
+ top, right, bottom, left = values
33
+
34
+ if top == right && right == bottom && bottom == left
35
+ false
36
+ elsif top == right && bottom.nil? && left.nil?
37
+ false
38
+ elsif top == bottom && right == left
39
+ false
40
+ elsif top == bottom && left.nil?
41
+ false
42
+ elsif right == left
43
+ false
44
+ else
45
+ true
57
46
  end
58
47
  end
59
48
  end
@@ -4,26 +4,13 @@ module SCSSLint
4
4
  class Linter::SingleLinePerSelector < Linter
5
5
  include LinterRegistry
6
6
 
7
- class << self
8
- def run(engine)
9
- lints = []
10
- engine.tree.each do |node|
11
- if node.is_a?(Sass::Tree::RuleNode)
12
- lints << check_selector_format(node)
13
- end
14
- end
15
- lints.compact
16
- end
17
-
18
- def description
19
- 'Each selector should be on its own line'
20
- end
21
-
22
- private
7
+ def visit_rule(node)
8
+ add_lint(node) unless node.rule.grep(/,[^\n]/).empty?
9
+ yield # Continue linting children
10
+ end
23
11
 
24
- def check_selector_format(rule_node)
25
- create_lint(rule_node) unless rule_node.rule.grep(/,[^\n]/).empty?
26
- end
12
+ def description
13
+ 'Each selector should be on its own line'
27
14
  end
28
15
  end
29
16
  end
@@ -4,36 +4,24 @@ module SCSSLint
4
4
  class Linter::SortedPropertiesLinter < Linter
5
5
  include LinterRegistry
6
6
 
7
- class << self
8
- def run(engine)
9
- lints = []
10
- engine.tree.each do |node|
11
- if node.is_a?(Sass::Tree::RuleNode)
12
- lints << check_properties_sorted(node)
13
- end
14
- end
15
- lints.compact
7
+ def visit_rule(node)
8
+ properties = node.children.select do |child|
9
+ child.is_a?(Sass::Tree::PropNode)
16
10
  end
17
11
 
18
- def description
19
- 'Properties should be sorted in alphabetical order'
12
+ prop_names = properties.map do |prop_node|
13
+ prop_node.name.first.to_s
20
14
  end
21
15
 
22
- private
23
-
24
- def check_properties_sorted(rule_node)
25
- properties = rule_node.children.select do |node|
26
- node.is_a?(Sass::Tree::PropNode)
27
- end
16
+ if prop_names.sort != prop_names
17
+ add_lint(properties.first)
18
+ end
28
19
 
29
- prop_names = properties.map do |prop_node|
30
- prop_node.name.first.to_s
31
- end
20
+ yield # Continue linting children
21
+ end
32
22
 
33
- if prop_names.sort != prop_names
34
- create_lint(properties.first)
35
- end
36
- end
23
+ def description
24
+ 'Properties should be sorted in alphabetical order'
37
25
  end
38
26
  end
39
27
  end
@@ -4,32 +4,17 @@ module SCSSLint
4
4
  class Linter::TypeInIdSelectorLinter < Linter
5
5
  include LinterRegistry
6
6
 
7
- class << self
8
- def run(engine)
9
- lints = []
10
- engine.tree.each do |node|
11
- if node.is_a?(Sass::Tree::RuleNode)
12
- lints << check_type_in_selector(node)
13
- end
14
- end
15
- lints.compact
7
+ def visit_rule(node)
8
+ selectors = node.rule.first.to_s.split(',')
9
+ selectors.each do |selector|
10
+ add_lint(node) if selector.strip =~ /^[a-z0-9]+#.*/i
16
11
  end
17
12
 
18
- def description
19
- 'Avoid ID names with unnecessary type selectors (e.g. prefer `#id` over `p#id`)'
20
- end
21
-
22
- private
23
-
24
- def check_type_in_selector(rule_node)
25
- selectors = rule_node.rule.first.to_s.split(',')
26
-
27
- selectors.each do |selector|
28
- return create_lint(rule_node) if selector.strip =~ /^[a-z0-9]+#.*/i
29
- end
13
+ yield # Continue linting children
14
+ end
30
15
 
31
- nil
32
- end
16
+ def description
17
+ 'Avoid ID names with unnecessary type selectors (e.g. prefer `#id` over `p#id`)'
33
18
  end
34
19
  end
35
20
  end
@@ -4,26 +4,13 @@ module SCSSLint
4
4
  class Linter::ZeroUnitLinter < Linter
5
5
  include LinterRegistry
6
6
 
7
- class << self
8
- def run(engine)
9
- lints = []
10
- engine.tree.each do |node|
11
- if node.is_a?(Sass::Tree::PropNode)
12
- lints << check_zero_unit(node, engine.lines[node.line - 1]) if node.line
13
- end
14
- end
15
- lints.compact
16
- end
17
-
18
- def description
19
- 'Properties with a value of zero should be unit-less'
20
- end
21
-
22
- private
7
+ def visit_prop(node)
8
+ line = engine.lines[node.line - 1] if node.line
9
+ add_lint(node) if line =~ /^\s*[\w-]+:\s*0[a-z]+;$/i
10
+ end
23
11
 
24
- def check_zero_unit(prop_node, line)
25
- return create_lint(prop_node) if line =~ /^\s*[\w-]+:\s*0[a-z]+;$/i
26
- end
12
+ def description
13
+ 'Properties with a value of zero should be unit-less, e.g. "0" instead of "0px"'
27
14
  end
28
15
  end
29
16
  end
@@ -16,6 +16,8 @@ module SCSSLint
16
16
 
17
17
  @linters = LinterRegistry.linters.reject do |linter|
18
18
  ignored_linters.include?(linter)
19
+ end.map do |linter_class|
20
+ linter_class.new
19
21
  end
20
22
  end
21
23
 
@@ -26,13 +28,17 @@ module SCSSLint
26
28
  files.each do |file|
27
29
  find_lints(file)
28
30
  end
31
+
32
+ linters.each do |linter|
33
+ @lints += linter.lints
34
+ end
29
35
  end
30
36
 
31
37
  def find_lints(file)
32
38
  engine = Engine.new(file)
33
39
 
34
40
  linters.each do |linter|
35
- @lints += linter.run(engine)
41
+ linter.run(engine)
36
42
  end
37
43
  rescue Sass::SyntaxError => ex
38
44
  @lints << Lint.new(ex.sass_filename, ex.sass_line, ex.to_s)
@@ -1,3 +1,3 @@
1
1
  module SCSSLint
2
- VERSION = '0.6'
2
+ VERSION = '0.6.5'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scss-lint
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.6'
4
+ version: 0.6.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-15 00:00:00.000000000 Z
12
+ date: 2013-03-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: colorize