scss-lint 0.10.1 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sass/script.rb +71 -37
- data/lib/sass/tree.rb +11 -6
- data/lib/scss_lint/lint.rb +7 -2
- data/lib/scss_lint/linter.rb +15 -4
- data/lib/scss_lint/linter/color_keyword.rb +7 -14
- data/lib/scss_lint/linter/duplicate_property.rb +23 -0
- data/lib/scss_lint/linter/hex_format.rb +3 -3
- data/lib/scss_lint/linter/indentation.rb +71 -0
- data/lib/scss_lint/linter/leading_zero.rb +32 -0
- data/lib/scss_lint/linter/shorthand.rb +46 -26
- data/lib/scss_lint/linter/zero_unit.rb +4 -10
- data/lib/scss_lint/reporter/default_reporter.rb +3 -1
- data/lib/scss_lint/reporter/xml_reporter.rb +3 -1
- data/lib/scss_lint/runner.rb +1 -1
- data/lib/scss_lint/utils.rb +21 -0
- data/lib/scss_lint/version.rb +1 -1
- metadata +20 -17
- data/lib/scss_lint/linter/no_zero_before_decimal.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62980ca32775a70ec7170fc8b7399eb13b304c7b
|
4
|
+
data.tar.gz: 76fa2ea621edad2ad9ebc9d0e2b302b57c43a70d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce7dfa08c1c68044da63de6f376a3ce483e5145eaa7854668b616dbd0b1596c983e7dbc5f510c8cdbf3d27f47658b21cebe5f0caa63b9c9a888ef0d0df772729
|
7
|
+
data.tar.gz: dae5b62b186b24c851394b7e18c16e45f4f1281a2046c7f6a00513deb94d8300df30e8a5e7e1d3845c77f9efe68f1fef5c9e4f059239499b55e56cf4ff24f0c4
|
data/lib/sass/script.rb
CHANGED
@@ -1,12 +1,58 @@
|
|
1
|
-
# Contains extensions of Sass::Script::Nodes to add support for accessing
|
2
|
-
# various parts of the parse tree not provided out-of-the-box.
|
3
1
|
module Sass::Script
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def
|
9
|
-
|
2
|
+
# Redefine some of the lexer helpers in order to store the original string
|
3
|
+
# with the created object so that the original string can be inspected rather
|
4
|
+
# than a typically normalized version.
|
5
|
+
class Lexer
|
6
|
+
def color
|
7
|
+
return unless color_string = scan(REGULAR_EXPRESSIONS[:color])
|
8
|
+
|
9
|
+
unless [4, 7].include?(color_string.length)
|
10
|
+
raise ::Sass::SyntaxError,
|
11
|
+
"Colors must have either three or six digits: '#{color_string}'"
|
12
|
+
end
|
13
|
+
|
14
|
+
[:color, Value::Color.from_string(color_string)]
|
15
|
+
end
|
16
|
+
|
17
|
+
def number
|
18
|
+
return unless scan(REGULAR_EXPRESSIONS[:number])
|
19
|
+
value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i
|
20
|
+
value = -value if @scanner[1]
|
21
|
+
|
22
|
+
number = Value::Number.new(value, Array(@scanner[4])).tap do |num|
|
23
|
+
num.original_string = @scanner[0]
|
24
|
+
end
|
25
|
+
[:number, number]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Parser
|
30
|
+
# We redefine the ident parser to specially handle color keywords.
|
31
|
+
def ident
|
32
|
+
return funcall unless @lexer.peek && @lexer.peek.type == :ident
|
33
|
+
return if @stop_at && @stop_at.include?(@lexer.peek.value)
|
34
|
+
|
35
|
+
name = @lexer.next
|
36
|
+
if (color = Value::Color::COLOR_NAMES[name.value.downcase])
|
37
|
+
return literal_node(Value::Color.from_string(name.value, color), name.source_range)
|
38
|
+
end
|
39
|
+
literal_node(Value::String.new(name.value, :identifier), name.source_range)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Value::Base
|
44
|
+
attr_accessor :node_parent
|
45
|
+
|
46
|
+
def children
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
|
50
|
+
def line
|
51
|
+
@line || (node_parent && node_parent.line)
|
52
|
+
end
|
53
|
+
|
54
|
+
def source_range
|
55
|
+
@source_range || (node_parent && node_parent.source_range)
|
10
56
|
end
|
11
57
|
end
|
12
58
|
|
@@ -14,8 +60,8 @@ module Sass::Script
|
|
14
60
|
# color string. This adds an attribute to the Color to keep track of the
|
15
61
|
# original string and provides a method which the modified lexer can use to
|
16
62
|
# set it.
|
17
|
-
class Color
|
18
|
-
attr_accessor :
|
63
|
+
class Value::Color
|
64
|
+
attr_accessor :original_string
|
19
65
|
|
20
66
|
def self.from_string(string, rgb = nil)
|
21
67
|
unless rgb
|
@@ -24,41 +70,29 @@ module Sass::Script
|
|
24
70
|
map { |hex| hex.ljust(2, hex).to_i(16) }
|
25
71
|
end
|
26
72
|
|
27
|
-
color =
|
28
|
-
color.
|
73
|
+
color = new(rgb, false)
|
74
|
+
color.original_string = string
|
29
75
|
color
|
30
76
|
end
|
31
77
|
end
|
32
78
|
|
33
|
-
class
|
34
|
-
|
35
|
-
# `Color` object so that we can inspect the original string before it is
|
36
|
-
# normalized.
|
37
|
-
#
|
38
|
-
# This is an adapted version from the original Sass source code.
|
39
|
-
def color
|
40
|
-
return unless color_string = scan(REGULAR_EXPRESSIONS[:color])
|
41
|
-
|
42
|
-
unless [4, 7].include?(color_string.length)
|
43
|
-
raise ::Sass::SyntaxError,
|
44
|
-
"Colors must have either three or six digits: '#{color_string}'"
|
45
|
-
end
|
46
|
-
|
47
|
-
[:color, Color.from_string(color_string)]
|
48
|
-
end
|
79
|
+
class Value::Number
|
80
|
+
attr_accessor :original_string
|
49
81
|
end
|
50
82
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
83
|
+
# Contains extensions of Sass::Script::Tree::Nodes to add support for
|
84
|
+
# accessing various parts of the parse tree not provided out-of-the-box.
|
85
|
+
module Tree
|
86
|
+
class Node
|
87
|
+
attr_accessor :node_parent
|
88
|
+
end
|
56
89
|
|
57
|
-
|
58
|
-
|
59
|
-
|
90
|
+
class Literal
|
91
|
+
# Literals wrap their underlying values. For sake of convenience, consider
|
92
|
+
# the wrapped value a child of the Literal.
|
93
|
+
def children
|
94
|
+
[value]
|
60
95
|
end
|
61
|
-
node(Sass::Script::String.new(name.value, :identifier))
|
62
96
|
end
|
63
97
|
end
|
64
98
|
end
|
data/lib/sass/tree.rb
CHANGED
@@ -5,6 +5,9 @@
|
|
5
5
|
module Sass::Tree
|
6
6
|
# Define some common helper code for use in the various monkey patchings.
|
7
7
|
class Node
|
8
|
+
# Stores node for which this node is a direct child
|
9
|
+
attr_accessor :node_parent
|
10
|
+
|
8
11
|
# The `args` field of some Sass::Tree::Node classes returns
|
9
12
|
# Sass::Script::Variable nodes with no line numbers. This adds the line
|
10
13
|
# numbers back in so lint reporting works for those nodes.
|
@@ -17,7 +20,7 @@ module Sass::Tree
|
|
17
20
|
# The Sass parser sometimes doesn't assign line numbers in cases where it
|
18
21
|
# should. This is a helper to easily correct that.
|
19
22
|
def add_line_number(node)
|
20
|
-
node.line ||= line if node.is_a?(Sass::Script::Node)
|
23
|
+
node.line ||= line if node.is_a?(::Sass::Script::Tree::Node)
|
21
24
|
node
|
22
25
|
end
|
23
26
|
|
@@ -25,7 +28,7 @@ module Sass::Tree
|
|
25
28
|
# the name of the variable. This helper takes that name and turns it back
|
26
29
|
# into a Sass::Script::Variable that supports lint reporting.
|
27
30
|
def create_variable(var_name)
|
28
|
-
Sass::Script::Variable.new(var_name).tap do |v|
|
31
|
+
::Sass::Script::Tree::Variable.new(var_name).tap do |v|
|
29
32
|
v.line = line # Use line number of the containing parse tree node
|
30
33
|
end
|
31
34
|
end
|
@@ -34,7 +37,7 @@ module Sass::Tree
|
|
34
37
|
# Sass::Script::Nodes interspersed within them. This returns a filtered list
|
35
38
|
# of just those nodes.
|
36
39
|
def extract_script_nodes(list)
|
37
|
-
list.select { |item| item.is_a?
|
40
|
+
list.select { |item| item.is_a?(::Sass::Script::Tree::Node) }
|
38
41
|
end
|
39
42
|
|
40
43
|
# Takes a list of arguments, be they arrays or individual objects, and
|
@@ -70,7 +73,9 @@ module Sass::Tree
|
|
70
73
|
|
71
74
|
class EachNode
|
72
75
|
def children
|
73
|
-
|
76
|
+
loop_vars = vars.map { |var| create_variable(var) }
|
77
|
+
|
78
|
+
concat_expr_lists super, loop_vars, list
|
74
79
|
end
|
75
80
|
end
|
76
81
|
|
@@ -114,9 +119,9 @@ module Sass::Tree
|
|
114
119
|
|
115
120
|
# Keyword mapping is String -> Expr, so convert the string to a variable
|
116
121
|
# node that supports lint reporting
|
117
|
-
keyword_exprs = keywords.map do |var_name, var_expr|
|
122
|
+
keyword_exprs = keywords.as_stored.map do |var_name, var_expr|
|
118
123
|
[create_variable(var_name), var_expr]
|
119
|
-
end
|
124
|
+
end if keywords.any?
|
120
125
|
|
121
126
|
concat_expr_lists super, args, keyword_exprs, splat
|
122
127
|
end
|
data/lib/scss_lint/lint.rb
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
module SCSSLint
|
2
2
|
class Lint
|
3
|
-
attr_reader :filename, :line, :description
|
3
|
+
attr_reader :filename, :line, :description, :severity
|
4
4
|
|
5
|
-
def initialize(filename, line, description)
|
5
|
+
def initialize(filename, line, description, severity = :warning)
|
6
6
|
@filename = filename
|
7
7
|
@line = line
|
8
8
|
@description = description
|
9
|
+
@severity = severity
|
10
|
+
end
|
11
|
+
|
12
|
+
def error?
|
13
|
+
severity == :error
|
9
14
|
end
|
10
15
|
end
|
11
16
|
end
|
data/lib/scss_lint/linter.rb
CHANGED
@@ -20,17 +20,20 @@ module SCSSLint
|
|
20
20
|
end
|
21
21
|
|
22
22
|
# Helper for creating lint from a parse tree node
|
23
|
-
def add_lint(
|
23
|
+
def add_lint(node_or_line, message = nil)
|
24
|
+
line = node_or_line.respond_to?(:line) ? node_or_line.line : node_or_line
|
25
|
+
|
24
26
|
@lints << Lint.new(engine.filename,
|
25
|
-
|
27
|
+
line,
|
26
28
|
message || description)
|
27
29
|
end
|
28
30
|
|
29
31
|
# Monkey-patched implementation that adds support for traversing
|
30
32
|
# Sass::Script::Nodes (original implementation only supports
|
31
33
|
# Sass::Tree::Nodes).
|
32
|
-
def node_name(node)
|
33
|
-
|
34
|
+
def self.node_name(node)
|
35
|
+
case node
|
36
|
+
when Sass::Script::Tree::Node, Sass::Script::Value::Base
|
34
37
|
"script_#{node.class.name.gsub(/.*::(.*?)$/, '\\1').downcase}"
|
35
38
|
else
|
36
39
|
super
|
@@ -46,5 +49,13 @@ module SCSSLint
|
|
46
49
|
|
47
50
|
super
|
48
51
|
end
|
52
|
+
|
53
|
+
# Redefine so we can set the `node_parent` of each node
|
54
|
+
def visit_children(parent)
|
55
|
+
parent.children.each do |child|
|
56
|
+
child.node_parent = parent
|
57
|
+
visit(child)
|
58
|
+
end
|
59
|
+
end
|
49
60
|
end
|
50
61
|
end
|
@@ -3,7 +3,7 @@ module SCSSLint
|
|
3
3
|
include LinterRegistry
|
4
4
|
|
5
5
|
def visit_script_color(node)
|
6
|
-
add_color_lint(node, node.
|
6
|
+
add_color_lint(node, node.original_string) if color_keyword?(node.original_string)
|
7
7
|
end
|
8
8
|
|
9
9
|
def visit_script_string(node)
|
@@ -17,28 +17,21 @@ module SCSSLint
|
|
17
17
|
private
|
18
18
|
|
19
19
|
def add_color_lint(node, original)
|
20
|
-
hex_form = Sass::Script::Color.new(color_rgb(original)).
|
20
|
+
hex_form = Sass::Script::Value::Color.new(color_rgb(original)).tap do |color|
|
21
|
+
color.options = {} # `inspect` requires options to be set
|
22
|
+
end.inspect
|
23
|
+
|
21
24
|
add_lint(node,
|
22
25
|
"Color `#{original}` should be written in hexadecimal form " <<
|
23
26
|
"as `#{shortest_hex_form(hex_form)}`")
|
24
27
|
end
|
25
28
|
|
26
29
|
def color_keyword?(string)
|
27
|
-
!!color_rgb(string)
|
30
|
+
!!color_rgb(string) && string != 'transparent'
|
28
31
|
end
|
29
32
|
|
30
33
|
def color_rgb(string)
|
31
|
-
Sass::Script::Color::COLOR_NAMES[string]
|
32
|
-
end
|
33
|
-
|
34
|
-
# Takes a string like `hello "world" 'how are' you` and turns it into:
|
35
|
-
# `hello you`.
|
36
|
-
# This is useful for scanning for keywords in shorthand properties or lists
|
37
|
-
# which can contain quoted strings but for which you don't want to inspect
|
38
|
-
# quoted strings (e.g. you care about the actual color keyword `red`, not
|
39
|
-
# the string "red").
|
40
|
-
def remove_quoted_strings(string)
|
41
|
-
string.gsub(/"[^"]*"|'[^']*'/, '')
|
34
|
+
Sass::Script::Value::Color::COLOR_NAMES[string]
|
42
35
|
end
|
43
36
|
end
|
44
37
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
class Linter::DuplicateProperty < Linter
|
3
|
+
include LinterRegistry
|
4
|
+
|
5
|
+
def visit_rule(node)
|
6
|
+
properties = node.children
|
7
|
+
.select { |child| child.is_a?(Sass::Tree::PropNode) }
|
8
|
+
.reject { |prop| prop.name.any? { |item| item.is_a?(Sass::Script::Node) } }
|
9
|
+
|
10
|
+
prop_names = {}
|
11
|
+
|
12
|
+
properties.each do |prop|
|
13
|
+
name = prop.name.join
|
14
|
+
|
15
|
+
if existing_prop = prop_names[name]
|
16
|
+
add_lint(prop, "Property '#{name}' already defined on line #{existing_prop.line}")
|
17
|
+
else
|
18
|
+
prop_names[name] = prop
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -3,10 +3,10 @@ module SCSSLint
|
|
3
3
|
include LinterRegistry
|
4
4
|
|
5
5
|
def visit_script_color(node)
|
6
|
-
return unless node.
|
6
|
+
return unless node.original_string && node.original_string.match(HEX_REGEX)
|
7
7
|
|
8
|
-
unless valid_hex_format?(node.
|
9
|
-
add_hex_lint(node, node.
|
8
|
+
unless valid_hex_format?(node.original_string[HEX_REGEX, 1])
|
9
|
+
add_hex_lint(node, node.original_string)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
class Linter::Indentation < Linter
|
3
|
+
include LinterRegistry
|
4
|
+
|
5
|
+
def visit_root(node)
|
6
|
+
@indent = 0
|
7
|
+
yield
|
8
|
+
end
|
9
|
+
|
10
|
+
def check_and_visit_children(node)
|
11
|
+
# Don't continue checking children as the moment a parent's indentation is
|
12
|
+
# off it's likely the children will be as will. We don't display the child
|
13
|
+
# indentation problems as that would likely make the lint too noisy.
|
14
|
+
return if check_indentation(node)
|
15
|
+
|
16
|
+
@indent += INDENT_WIDTH
|
17
|
+
yield
|
18
|
+
@indent -= INDENT_WIDTH
|
19
|
+
end
|
20
|
+
|
21
|
+
def check_indentation(node)
|
22
|
+
return unless node.line
|
23
|
+
|
24
|
+
# Ignore the case where the node is on the same line as its previous
|
25
|
+
# sibling or its parent, as indentation isn't possible
|
26
|
+
return if (previous = previous_node(node)) && previous.line == node.line
|
27
|
+
|
28
|
+
actual_indent = engine.lines[node.line - 1][/^(\s*)/, 1]
|
29
|
+
|
30
|
+
if actual_indent.length != @indent
|
31
|
+
add_lint(node.line,
|
32
|
+
"Line should be indented #{@indent} spaces, " <<
|
33
|
+
"but was indented #{actual_indent.length} spaces")
|
34
|
+
return true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Deal with `else` statements
|
39
|
+
def visit_if(node, &block)
|
40
|
+
check_and_visit_children(node, &block)
|
41
|
+
visit(node.else) if node.else
|
42
|
+
end
|
43
|
+
|
44
|
+
# Define node types that increase indentation level
|
45
|
+
alias :visit_directive :check_and_visit_children
|
46
|
+
alias :visit_each :check_and_visit_children
|
47
|
+
alias :visit_for :check_and_visit_children
|
48
|
+
alias :visit_function :check_and_visit_children
|
49
|
+
alias :visit_media :check_and_visit_children
|
50
|
+
alias :visit_mixin :check_and_visit_children
|
51
|
+
alias :visit_mixindef :check_and_visit_children
|
52
|
+
alias :visit_prop :check_and_visit_children
|
53
|
+
alias :visit_rule :check_and_visit_children
|
54
|
+
alias :visit_supports :check_and_visit_children
|
55
|
+
alias :visit_while :check_and_visit_children
|
56
|
+
|
57
|
+
# Define node types to check indentation of (notice comments are left out)
|
58
|
+
alias :visit_charset :check_indentation
|
59
|
+
alias :visit_content :check_indentation
|
60
|
+
alias :visit_cssimport :check_indentation
|
61
|
+
alias :visit_extend :check_indentation
|
62
|
+
alias :visit_import :check_indentation
|
63
|
+
alias :visit_return :check_indentation
|
64
|
+
alias :visit_variable :check_indentation
|
65
|
+
alias :visit_warn :check_indentation
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
INDENT_WIDTH = 2
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
class Linter::LeadingZero < Linter
|
3
|
+
include LinterRegistry
|
4
|
+
|
5
|
+
def visit_script_string(node)
|
6
|
+
return unless node.type == :identifier
|
7
|
+
|
8
|
+
non_string_values = remove_quoted_strings(node.value).split
|
9
|
+
|
10
|
+
non_string_values.each do |value|
|
11
|
+
if number = value[/\b(0\.\d+)/, 1]
|
12
|
+
add_leading_zero_lint(node, number)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def visit_script_number(node)
|
18
|
+
if node.original_string =~ /^0\./
|
19
|
+
add_leading_zero_lint(node, node.original_string)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def add_leading_zero_lint(node, number)
|
26
|
+
trimmed_number = number[/^[^\.]+(.*)$/, 1]
|
27
|
+
|
28
|
+
add_lint(node, "`#{number}` should be written without a leading zero " <<
|
29
|
+
"as `#{trimmed_number}`")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -3,47 +3,67 @@ module SCSSLint
|
|
3
3
|
include LinterRegistry
|
4
4
|
|
5
5
|
def visit_prop(node)
|
6
|
-
|
6
|
+
property_name = node.name.join
|
7
|
+
return unless SHORTHANDABLE_PROPERTIES.include?(property_name)
|
7
8
|
|
8
9
|
case node.value
|
9
|
-
when Sass::Script::
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
when Sass::Script::
|
15
|
-
|
16
|
-
add_lint(node) unless valid_shorthand?(*$1.split(/\s+/))
|
17
|
-
end
|
10
|
+
when Sass::Script::Tree::Literal
|
11
|
+
# HACK: node_parent may not be initialized at this point, so we need to
|
12
|
+
# set it ourselves
|
13
|
+
node.value.value.node_parent = node.value
|
14
|
+
check_script_string(property_name, node.value.value)
|
15
|
+
when Sass::Script::Tree::ListLiteral
|
16
|
+
check_script_list(property_name, node.value)
|
18
17
|
end
|
19
18
|
end
|
20
19
|
|
21
|
-
|
22
|
-
|
20
|
+
private
|
21
|
+
|
22
|
+
SHORTHANDABLE_PROPERTIES = %w[
|
23
|
+
border-color
|
24
|
+
border-radius
|
25
|
+
border-style
|
26
|
+
border-width
|
27
|
+
margin
|
28
|
+
padding
|
29
|
+
]
|
30
|
+
|
31
|
+
def check_script_list(prop, list)
|
32
|
+
check_shorthand(prop, list, list.children.map(&:to_sass))
|
23
33
|
end
|
24
34
|
|
25
|
-
|
35
|
+
def check_script_string(prop, script_string)
|
36
|
+
return unless script_string.type == :identifier
|
37
|
+
|
38
|
+
if values = script_string.value.strip[/\A(\S+\s+\S+(\s+\S+){0,2})\z/, 1]
|
39
|
+
check_shorthand(prop, script_string, values.split)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_shorthand(prop, node, values)
|
44
|
+
return unless (2..4).member?(values.count)
|
26
45
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
46
|
+
shortest_form = condensed_shorthand(*values)
|
47
|
+
return if values == shortest_form
|
48
|
+
|
49
|
+
add_lint(node, "Shorthand form for property `#{prop}` should be " <<
|
50
|
+
"written more concisely as `#{shortest_form.join(' ')}` " <<
|
51
|
+
"instead of `#{values.join(' ')}`")
|
52
|
+
end
|
33
53
|
|
34
|
-
def
|
54
|
+
def condensed_shorthand(top, right, bottom = nil, left = nil)
|
35
55
|
if top == right && right == bottom && bottom == left
|
36
|
-
|
56
|
+
[top]
|
37
57
|
elsif top == right && bottom.nil? && left.nil?
|
38
|
-
|
58
|
+
[top]
|
39
59
|
elsif top == bottom && right == left
|
40
|
-
|
60
|
+
[top, right]
|
41
61
|
elsif top == bottom && left.nil?
|
42
|
-
|
62
|
+
top == right ? [top] : [top, right]
|
43
63
|
elsif right == left
|
44
|
-
|
64
|
+
[top, right, bottom]
|
45
65
|
else
|
46
|
-
|
66
|
+
[top, right, bottom, left].compact
|
47
67
|
end
|
48
68
|
end
|
49
69
|
end
|
@@ -2,21 +2,15 @@ module SCSSLint
|
|
2
2
|
class Linter::ZeroUnit < Linter
|
3
3
|
include LinterRegistry
|
4
4
|
|
5
|
-
def
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
node.value.value.scan(/\b(0[a-z]+)\b/i) do |match|
|
10
|
-
add_lint(node, MESSAGE_FORMAT % match.first)
|
11
|
-
end
|
5
|
+
def visit_script_string(node)
|
6
|
+
node.value.scan(/\b(0[a-z]+)\b/i) do |match|
|
7
|
+
add_lint(node, MESSAGE_FORMAT % match.first)
|
12
8
|
end
|
13
|
-
|
14
|
-
yield # Continue visiting children
|
15
9
|
end
|
16
10
|
|
17
11
|
def visit_script_number(node)
|
18
12
|
if node.value == 0 && !node.unitless?
|
19
|
-
add_lint(node, MESSAGE_FORMAT % node.
|
13
|
+
add_lint(node, MESSAGE_FORMAT % node.original_string)
|
20
14
|
end
|
21
15
|
end
|
22
16
|
|
@@ -5,7 +5,9 @@ module SCSSLint
|
|
5
5
|
def report_lints
|
6
6
|
if lints.any?
|
7
7
|
lints.map do |lint|
|
8
|
-
|
8
|
+
type = lint.error? ? '[E]'.red : '[W]'.yellow
|
9
|
+
"#{lint.filename.cyan}:" << "#{lint.line}".magenta <<
|
10
|
+
" #{type} #{lint.description}"
|
9
11
|
end.join("\n") + "\n"
|
10
12
|
end
|
11
13
|
end
|
@@ -8,7 +8,9 @@ module SCSSLint
|
|
8
8
|
output << "<file name='#{filename}'>"
|
9
9
|
|
10
10
|
file_lints.each do |lint|
|
11
|
-
output << "<issue line='#{lint.line}'
|
11
|
+
output << "<issue line='#{lint.line}' " <<
|
12
|
+
"severity='#{lint.severity}' " <<
|
13
|
+
"reason='#{lint.description}' />"
|
12
14
|
end
|
13
15
|
|
14
16
|
output << '</file>'
|
data/lib/scss_lint/runner.rb
CHANGED
data/lib/scss_lint/utils.rb
CHANGED
@@ -34,6 +34,27 @@ module SCSSLint
|
|
34
34
|
hex[5] == hex[6]
|
35
35
|
end
|
36
36
|
|
37
|
+
# Takes a string like `hello "world" 'how are' you` and turns it into:
|
38
|
+
# `hello you`.
|
39
|
+
# This is useful for scanning for keywords in shorthand properties or lists
|
40
|
+
# which can contain quoted strings but for which you don't want to inspect
|
41
|
+
# quoted strings (e.g. you care about the actual color keyword `red`, not
|
42
|
+
# the string "red").
|
43
|
+
def remove_quoted_strings(string)
|
44
|
+
string.gsub(/"[^"]*"|'[^']*'/, '')
|
45
|
+
end
|
46
|
+
|
47
|
+
def previous_node(node)
|
48
|
+
return unless node && parent = node.node_parent
|
49
|
+
index = parent.children.index(node)
|
50
|
+
|
51
|
+
if index == 0
|
52
|
+
parent
|
53
|
+
else
|
54
|
+
parent.children[index - 1]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
37
58
|
private
|
38
59
|
|
39
60
|
INVALID_NAME_CHARS = '[_A-Z]'
|
data/lib/scss_lint/version.rb
CHANGED
metadata
CHANGED
@@ -1,71 +1,72 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scss-lint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Causes Engineering
|
8
|
+
- Shane da Silva
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2013-
|
12
|
+
date: 2013-10-21 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: colorize
|
15
16
|
requirement: !ruby/object:Gem::Requirement
|
16
17
|
requirements:
|
17
|
-
- - '
|
18
|
+
- - '='
|
18
19
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
20
|
+
version: 0.5.8
|
20
21
|
type: :runtime
|
21
22
|
prerelease: false
|
22
23
|
version_requirements: !ruby/object:Gem::Requirement
|
23
24
|
requirements:
|
24
|
-
- - '
|
25
|
+
- - '='
|
25
26
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
27
|
+
version: 0.5.8
|
27
28
|
- !ruby/object:Gem::Dependency
|
28
29
|
name: sass
|
29
30
|
requirement: !ruby/object:Gem::Requirement
|
30
31
|
requirements:
|
31
32
|
- - '='
|
32
33
|
- !ruby/object:Gem::Version
|
33
|
-
version: 3.
|
34
|
+
version: 3.3.0.rc.1
|
34
35
|
type: :runtime
|
35
36
|
prerelease: false
|
36
37
|
version_requirements: !ruby/object:Gem::Requirement
|
37
38
|
requirements:
|
38
39
|
- - '='
|
39
40
|
- !ruby/object:Gem::Version
|
40
|
-
version: 3.
|
41
|
+
version: 3.3.0.rc.1
|
41
42
|
- !ruby/object:Gem::Dependency
|
42
43
|
name: nokogiri
|
43
44
|
requirement: !ruby/object:Gem::Requirement
|
44
45
|
requirements:
|
45
|
-
- - '
|
46
|
+
- - '='
|
46
47
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
48
|
+
version: 1.6.0
|
48
49
|
type: :development
|
49
50
|
prerelease: false
|
50
51
|
version_requirements: !ruby/object:Gem::Requirement
|
51
52
|
requirements:
|
52
|
-
- - '
|
53
|
+
- - '='
|
53
54
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
55
|
+
version: 1.6.0
|
55
56
|
- !ruby/object:Gem::Dependency
|
56
57
|
name: rspec
|
57
58
|
requirement: !ruby/object:Gem::Requirement
|
58
59
|
requirements:
|
59
|
-
- - '
|
60
|
+
- - '='
|
60
61
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
62
|
+
version: 2.13.0
|
62
63
|
type: :development
|
63
64
|
prerelease: false
|
64
65
|
version_requirements: !ruby/object:Gem::Requirement
|
65
66
|
requirements:
|
66
|
-
- - '
|
67
|
+
- - '='
|
67
68
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
69
|
+
version: 2.13.0
|
69
70
|
description: Opinionated tool to help write clean and consistent SCSS
|
70
71
|
email:
|
71
72
|
- eng@causes.com
|
@@ -87,19 +88,21 @@ files:
|
|
87
88
|
- lib/scss_lint/linter/property_format.rb
|
88
89
|
- lib/scss_lint/linter/space_before_brace.rb
|
89
90
|
- lib/scss_lint/linter/capitalization_in_selector.rb
|
91
|
+
- lib/scss_lint/linter/indentation.rb
|
90
92
|
- lib/scss_lint/linter/declared_name.rb
|
91
93
|
- lib/scss_lint/linter/sorted_properties.rb
|
92
94
|
- lib/scss_lint/linter/shorthand.rb
|
93
95
|
- lib/scss_lint/linter/id_with_extraneous_selector.rb
|
94
96
|
- lib/scss_lint/linter/declaration_order.rb
|
95
|
-
- lib/scss_lint/linter/no_zero_before_decimal.rb
|
96
97
|
- lib/scss_lint/linter/zero_unit.rb
|
97
98
|
- lib/scss_lint/linter/placeholder_in_extend.rb
|
98
99
|
- lib/scss_lint/linter/usage_name.rb
|
99
100
|
- lib/scss_lint/linter/border_zero.rb
|
100
101
|
- lib/scss_lint/linter/debug_statement.rb
|
102
|
+
- lib/scss_lint/linter/duplicate_property.rb
|
101
103
|
- lib/scss_lint/linter/hex_format.rb
|
102
104
|
- lib/scss_lint/linter/color_keyword.rb
|
105
|
+
- lib/scss_lint/linter/leading_zero.rb
|
103
106
|
- lib/scss_lint/linter/comment.rb
|
104
107
|
- lib/scss_lint/linter/empty_rule.rb
|
105
108
|
- lib/scss_lint/linter/single_line_per_selector.rb
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module SCSSLint
|
2
|
-
class Linter::NoZeroBeforeDecimal < Linter
|
3
|
-
include LinterRegistry
|
4
|
-
|
5
|
-
def visit_prop(node)
|
6
|
-
# Misleading, but anything that isn't Sass Script is considered an
|
7
|
-
# `identifier` in the context of a Sass::Script::String.
|
8
|
-
return unless node.value.is_a?(Sass::Script::String) && node.value.type == :identifier
|
9
|
-
|
10
|
-
# Remove string chunks (e.g. `"hello" 3 'world'` -> `3`
|
11
|
-
non_string_values = node.value.value.gsub(/"[^"]*"|'[^']'/, '').split
|
12
|
-
|
13
|
-
non_string_values.each do |value|
|
14
|
-
add_lint(node) if value =~ /\b0\.\d+/
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def description
|
19
|
-
'Leading zero should be omitted in fractional values'
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|