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.
- data/lib/scss_lint/linter.rb +17 -15
- data/lib/scss_lint/linter/debug_linter.rb +5 -11
- data/lib/scss_lint/linter/declaration_order_linter.rb +17 -31
- data/lib/scss_lint/linter/empty_rule_linter.rb +6 -19
- data/lib/scss_lint/linter/hex_linter.rb +19 -31
- data/lib/scss_lint/linter/property_format_linter.rb +19 -30
- data/lib/scss_lint/linter/shorthand_linter.rb +34 -45
- data/lib/scss_lint/linter/single_line_per_selector_linter.rb +6 -19
- data/lib/scss_lint/linter/sorted_properties_linter.rb +12 -24
- data/lib/scss_lint/linter/type_in_id_selector_linter.rb +8 -23
- data/lib/scss_lint/linter/zero_unit_linter.rb +6 -19
- data/lib/scss_lint/runner.rb +7 -1
- data/lib/scss_lint/version.rb +1 -1
- metadata +2 -2
data/lib/scss_lint/linter.rb
CHANGED
@@ -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
|
-
|
6
|
-
def run(engine)
|
7
|
-
[] # No lints
|
8
|
-
end
|
5
|
+
attr_reader :engine, :lints
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
def initialize
|
8
|
+
@lints = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(engine)
|
12
|
+
@engine = engine
|
13
|
+
visit(engine.tree)
|
14
|
+
end
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
def description
|
17
|
+
nil
|
18
|
+
end
|
17
19
|
|
18
|
-
|
20
|
+
protected
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
17
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
25
|
-
|
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
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
43
|
-
sorted_children = children.sort do |x,y|
|
44
|
-
DECLARATION_ORDER.index(x) <=> DECLARATION_ORDER.index(y)
|
45
|
-
end
|
33
|
+
private
|
46
34
|
|
47
|
-
|
48
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
25
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
14
|
+
def description
|
15
|
+
'Hexadecimal color codes should be lowercase and in 3-digit form where possible'
|
16
|
+
end
|
23
17
|
|
24
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
19
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
25
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
19
|
-
|
12
|
+
prop_names = properties.map do |prop_node|
|
13
|
+
prop_node.name.first.to_s
|
20
14
|
end
|
21
15
|
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
30
|
-
|
31
|
-
end
|
20
|
+
yield # Continue linting children
|
21
|
+
end
|
32
22
|
|
33
|
-
|
34
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
19
|
-
|
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
|
-
|
32
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
25
|
-
|
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
|
data/lib/scss_lint/runner.rb
CHANGED
@@ -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
|
-
|
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)
|
data/lib/scss_lint/version.rb
CHANGED
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:
|
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-
|
12
|
+
date: 2013-03-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: colorize
|