scss-lint 0.6 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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