scss-lint 0.25.1 → 0.26.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/default.yml +10 -1
- data/data/property-sort-orders/concentric.txt +99 -0
- data/lib/scss_lint.rb +1 -0
- data/lib/scss_lint/cli.rb +9 -3
- data/lib/scss_lint/exceptions.rb +4 -0
- data/lib/scss_lint/linter.rb +10 -1
- data/lib/scss_lint/linter/capitalization_in_selector.rb +14 -6
- data/lib/scss_lint/linter/compass/property_with_mixin.rb +9 -2
- data/lib/scss_lint/linter/indentation.rb +28 -6
- data/lib/scss_lint/linter/property_sort_order.rb +61 -9
- data/lib/scss_lint/linter/single_line_per_property.rb +53 -0
- data/lib/scss_lint/linter/single_line_per_selector.rb +6 -1
- data/lib/scss_lint/linter/space_after_comma.rb +27 -19
- data/lib/scss_lint/linter/space_before_brace.rb +5 -4
- data/lib/scss_lint/linter/trailing_semicolon.rb +53 -0
- data/lib/scss_lint/linter/unnecessary_parent_reference.rb +36 -0
- data/lib/scss_lint/reporter/default_reporter.rb +7 -2
- data/lib/scss_lint/reporter/xml_reporter.rb +2 -1
- data/lib/scss_lint/runner.rb +7 -3
- data/lib/scss_lint/version.rb +1 -1
- data/spec/scss_lint/cli_spec.rb +314 -0
- data/spec/scss_lint/config_spec.rb +439 -0
- data/spec/scss_lint/engine_spec.rb +24 -0
- data/spec/scss_lint/linter/border_zero_spec.rb +84 -0
- data/spec/scss_lint/linter/capitalization_in_selector_spec.rb +71 -0
- data/spec/scss_lint/linter/color_keyword_spec.rb +83 -0
- data/spec/scss_lint/linter/comment_spec.rb +55 -0
- data/spec/scss_lint/linter/compass/property_with_mixin_spec.rb +55 -0
- data/spec/scss_lint/linter/debug_statement_spec.rb +21 -0
- data/spec/scss_lint/linter/declaration_order_spec.rb +94 -0
- data/spec/scss_lint/linter/duplicate_property_spec.rb +176 -0
- data/spec/scss_lint/linter/else_placement_spec.rb +106 -0
- data/spec/scss_lint/linter/empty_line_between_blocks_spec.rb +263 -0
- data/spec/scss_lint/linter/empty_rule_spec.rb +27 -0
- data/spec/scss_lint/linter/final_newline_spec.rb +49 -0
- data/spec/scss_lint/linter/hex_length_spec.rb +104 -0
- data/spec/scss_lint/linter/hex_notation_spec.rb +104 -0
- data/spec/scss_lint/linter/hex_validation_spec.rb +36 -0
- data/spec/scss_lint/linter/id_with_extraneous_selector_spec.rb +139 -0
- data/spec/scss_lint/linter/indentation_spec.rb +242 -0
- data/spec/scss_lint/linter/leading_zero_spec.rb +233 -0
- data/spec/scss_lint/linter/mergeable_selector_spec.rb +283 -0
- data/spec/scss_lint/linter/name_format_spec.rb +206 -0
- data/spec/scss_lint/linter/placeholder_in_extend_spec.rb +63 -0
- data/spec/scss_lint/linter/property_sort_order_spec.rb +246 -0
- data/spec/scss_lint/linter/property_spelling_spec.rb +57 -0
- data/spec/scss_lint/linter/selector_depth_spec.rb +159 -0
- data/spec/scss_lint/linter/shorthand_spec.rb +172 -0
- data/spec/scss_lint/linter/single_line_per_property_spec.rb +73 -0
- data/spec/scss_lint/linter/single_line_per_selector_spec.rb +121 -0
- data/spec/scss_lint/linter/space_after_comma_spec.rb +315 -0
- data/spec/scss_lint/linter/space_after_property_colon_spec.rb +238 -0
- data/spec/scss_lint/linter/space_after_property_name_spec.rb +23 -0
- data/spec/scss_lint/linter/space_before_brace_spec.rb +447 -0
- data/spec/scss_lint/linter/space_between_parens_spec.rb +263 -0
- data/spec/scss_lint/linter/string_quotes_spec.rb +303 -0
- data/spec/scss_lint/linter/trailing_semicolon_spec.rb +188 -0
- data/spec/scss_lint/linter/unnecessary_mantissa_spec.rb +67 -0
- data/spec/scss_lint/linter/unnecessary_parent_reference_spec.rb +67 -0
- data/spec/scss_lint/linter/url_format_spec.rb +55 -0
- data/spec/scss_lint/linter/url_quotes_spec.rb +63 -0
- data/spec/scss_lint/linter/zero_unit_spec.rb +113 -0
- data/spec/scss_lint/linter_registry_spec.rb +50 -0
- data/spec/scss_lint/location_spec.rb +42 -0
- data/spec/scss_lint/reporter/config_reporter_spec.rb +42 -0
- data/spec/scss_lint/reporter/default_reporter_spec.rb +73 -0
- data/spec/scss_lint/reporter/files_reporter_spec.rb +38 -0
- data/spec/scss_lint/reporter/xml_reporter_spec.rb +103 -0
- data/spec/scss_lint/reporter_spec.rb +11 -0
- data/spec/scss_lint/runner_spec.rb +132 -0
- data/spec/scss_lint/selector_visitor_spec.rb +264 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/isolated_environment.rb +25 -0
- data/spec/support/matchers/report_lint.rb +48 -0
- metadata +126 -8
- data/lib/scss_lint/linter/trailing_semicolon_after_property_value.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e07cf784e2e32fc05bf8294608db2d3bcf2951b2
|
4
|
+
data.tar.gz: d1577b62811661ad24efa01dfeeffc6ca19a573b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3179febbb5fce02dee1044be6ea7b05f312e833e8bb61d4c8cb23f9ab84eb07c9458f9492f6918a2766a8c42e10ae89debd3a20959ca7778bc38787eccc96e2
|
7
|
+
data.tar.gz: e50715f6dbbf827a29b1f079d520ec6011e8318f3b89ea9d7d31afb09c8bf68a32f3cea66ab7a8da926ab2556c4a34fb3023a8393ae10aea89ef3889e5a34f19
|
data/config/default.yml
CHANGED
@@ -52,6 +52,7 @@ linters:
|
|
52
52
|
|
53
53
|
Indentation:
|
54
54
|
enabled: true
|
55
|
+
character: space # or 'tab'
|
55
56
|
width: 2
|
56
57
|
|
57
58
|
LeadingZero:
|
@@ -71,6 +72,7 @@ linters:
|
|
71
72
|
|
72
73
|
PropertySortOrder:
|
73
74
|
enabled: true
|
75
|
+
ignore_unspecified: false
|
74
76
|
|
75
77
|
PropertySpelling:
|
76
78
|
enabled: true
|
@@ -83,6 +85,10 @@ linters:
|
|
83
85
|
Shorthand:
|
84
86
|
enabled: true
|
85
87
|
|
88
|
+
SingleLinePerProperty:
|
89
|
+
enabled: true
|
90
|
+
allow_single_line_rule_sets: true
|
91
|
+
|
86
92
|
SingleLinePerSelector:
|
87
93
|
enabled: true
|
88
94
|
|
@@ -108,12 +114,15 @@ linters:
|
|
108
114
|
enabled: true
|
109
115
|
style: single_quotes # or double_quotes
|
110
116
|
|
111
|
-
|
117
|
+
TrailingSemicolon:
|
112
118
|
enabled: true
|
113
119
|
|
114
120
|
UnnecessaryMantissa:
|
115
121
|
enabled: true
|
116
122
|
|
123
|
+
UnnecessaryParentReference:
|
124
|
+
enabled: true
|
125
|
+
|
117
126
|
UrlFormat:
|
118
127
|
enabled: true
|
119
128
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# Concentric CSS
|
2
|
+
# http://rhodesmill.org/brandon/2011/concentric-css/
|
3
|
+
|
4
|
+
display
|
5
|
+
position
|
6
|
+
top
|
7
|
+
right
|
8
|
+
bottom
|
9
|
+
left
|
10
|
+
|
11
|
+
float
|
12
|
+
clear
|
13
|
+
|
14
|
+
visibility
|
15
|
+
opacity
|
16
|
+
z-index
|
17
|
+
|
18
|
+
margin
|
19
|
+
margin-top
|
20
|
+
margin-right
|
21
|
+
margin-bottom
|
22
|
+
margin-left
|
23
|
+
|
24
|
+
outline
|
25
|
+
|
26
|
+
border
|
27
|
+
border-top
|
28
|
+
border-right
|
29
|
+
border-bottom
|
30
|
+
border-left
|
31
|
+
border-width
|
32
|
+
border-top-width
|
33
|
+
border-right-width
|
34
|
+
border-bottom-width
|
35
|
+
border-left-width
|
36
|
+
|
37
|
+
border-style
|
38
|
+
border-top-style
|
39
|
+
border-right-style
|
40
|
+
border-bottom-style
|
41
|
+
border-left-style
|
42
|
+
|
43
|
+
border-color
|
44
|
+
border-top-color
|
45
|
+
border-right-color
|
46
|
+
border-bottom-color
|
47
|
+
border-left-color
|
48
|
+
|
49
|
+
background
|
50
|
+
background-color
|
51
|
+
background-image
|
52
|
+
background-repeat
|
53
|
+
background-position
|
54
|
+
cursor
|
55
|
+
|
56
|
+
padding
|
57
|
+
padding-top
|
58
|
+
padding-right
|
59
|
+
padding-bottom
|
60
|
+
padding-left
|
61
|
+
|
62
|
+
width
|
63
|
+
min-width
|
64
|
+
max-width
|
65
|
+
|
66
|
+
height
|
67
|
+
min-height
|
68
|
+
max-height
|
69
|
+
|
70
|
+
overflow
|
71
|
+
|
72
|
+
list-style
|
73
|
+
caption-side
|
74
|
+
|
75
|
+
table-layout
|
76
|
+
border-collapse
|
77
|
+
border-spacing
|
78
|
+
empty-cells
|
79
|
+
|
80
|
+
vertical-align
|
81
|
+
|
82
|
+
text-align
|
83
|
+
text-indent
|
84
|
+
text-transform
|
85
|
+
text-decoration
|
86
|
+
|
87
|
+
line-height
|
88
|
+
word-spacing
|
89
|
+
letter-spacing
|
90
|
+
white-space
|
91
|
+
color
|
92
|
+
|
93
|
+
font
|
94
|
+
font-family
|
95
|
+
font-size
|
96
|
+
font-weight
|
97
|
+
|
98
|
+
content
|
99
|
+
quotes
|
data/lib/scss_lint.rb
CHANGED
data/lib/scss_lint/cli.rb
CHANGED
@@ -12,8 +12,9 @@ module SCSSLint
|
|
12
12
|
# Subset of semantic exit codes conforming to `sysexits` documentation.
|
13
13
|
EXIT_CODES = {
|
14
14
|
ok: 0,
|
15
|
+
warning: 1, # One or more warnings (but no errors) were reported
|
16
|
+
error: 2, # One or more errors were reported
|
15
17
|
usage: 64, # Command line usage error
|
16
|
-
data: 65, # User input was incorrect (i.e. contains lints)
|
17
18
|
no_input: 66, # Input file did not exist or was not readable
|
18
19
|
software: 70, # Internal software error
|
19
20
|
config: 78, # Configuration error
|
@@ -93,11 +94,16 @@ module SCSSLint
|
|
93
94
|
end
|
94
95
|
end
|
95
96
|
|
96
|
-
def run
|
97
|
+
def run # rubocop:disable MethodLength
|
97
98
|
runner = Runner.new(@config)
|
98
99
|
runner.run(files_to_lint)
|
99
100
|
report_lints(runner.lints)
|
100
|
-
|
101
|
+
|
102
|
+
if runner.lints.any? { |lint| lint.error? }
|
103
|
+
halt :error
|
104
|
+
elsif runner.lints.any?
|
105
|
+
halt :warning
|
106
|
+
end
|
101
107
|
rescue InvalidConfiguration => ex
|
102
108
|
puts ex
|
103
109
|
halt :config
|
data/lib/scss_lint/linter.rb
CHANGED
@@ -10,6 +10,8 @@ module SCSSLint
|
|
10
10
|
@lints = []
|
11
11
|
end
|
12
12
|
|
13
|
+
# Run this linter against a parsed document with a given configuration.
|
14
|
+
#
|
13
15
|
# @param engine [Engine]
|
14
16
|
# @param config [Config]
|
15
17
|
def run(engine, config)
|
@@ -18,6 +20,12 @@ module SCSSLint
|
|
18
20
|
visit(engine.tree)
|
19
21
|
end
|
20
22
|
|
23
|
+
# Return the human-friendly name of this linter as specified in the
|
24
|
+
# configuration file and in lint descriptions.
|
25
|
+
def name
|
26
|
+
self.class.name.split('::')[2..-1].join('::')
|
27
|
+
end
|
28
|
+
|
21
29
|
protected
|
22
30
|
|
23
31
|
# Helper for creating lint from a parse tree node
|
@@ -28,7 +36,8 @@ module SCSSLint
|
|
28
36
|
@lints << Lint.new(self,
|
29
37
|
engine.filename,
|
30
38
|
extract_location(node_or_line_or_location),
|
31
|
-
message
|
39
|
+
message,
|
40
|
+
@config.fetch('severity', :warning).to_sym)
|
32
41
|
end
|
33
42
|
|
34
43
|
# Extract {SCSSLint::Location} from a {Sass::Source::Range}.
|
@@ -3,34 +3,42 @@ module SCSSLint
|
|
3
3
|
class Linter::CapitalizationInSelector < Linter
|
4
4
|
include LinterRegistry
|
5
5
|
|
6
|
+
def visit_root(_node)
|
7
|
+
@ignored_names = Array(config['ignored_names']).to_set
|
8
|
+
@ignored_types = Array(config['ignored_types']).to_set
|
9
|
+
yield
|
10
|
+
end
|
11
|
+
|
6
12
|
def visit_attribute(attribute)
|
7
|
-
check(attribute)
|
13
|
+
check(attribute) unless @ignored_types.include?('attribute')
|
8
14
|
end
|
9
15
|
|
10
16
|
def visit_class(klass)
|
11
|
-
check(klass)
|
17
|
+
check(klass) unless @ignored_types.include?('class')
|
12
18
|
end
|
13
19
|
|
14
20
|
def visit_element(element)
|
15
|
-
check(element)
|
21
|
+
check(element) unless @ignored_types.include?('element')
|
16
22
|
end
|
17
23
|
|
18
24
|
def visit_id(id)
|
19
|
-
check(id)
|
25
|
+
check(id) unless @ignored_types.include?('id')
|
20
26
|
end
|
21
27
|
|
22
28
|
def visit_placeholder(placeholder)
|
23
|
-
check(placeholder)
|
29
|
+
check(placeholder) unless @ignored_types.include?('placeholder')
|
24
30
|
end
|
25
31
|
|
26
32
|
def visit_pseudo(pseudo)
|
27
|
-
check(pseudo, 'Pseudo-selector')
|
33
|
+
check(pseudo, 'Pseudo-selector') unless @ignored_types.include?('pseudo-selector')
|
28
34
|
end
|
29
35
|
|
30
36
|
private
|
31
37
|
|
32
38
|
def check(node, selector_name = nil)
|
33
39
|
name = node.name.join
|
40
|
+
|
41
|
+
return if @ignored_names.include?(name)
|
34
42
|
return unless name =~ /[A-Z]/
|
35
43
|
|
36
44
|
selector_name ||= node.class.name.split('::').last
|
@@ -24,17 +24,24 @@ module SCSSLint
|
|
24
24
|
|
25
25
|
def check_for_properties_with_mixins(node)
|
26
26
|
prop_name = node.name.join
|
27
|
-
return unless PROPERTIES_WITH_MIXINS.include?(prop_name)
|
27
|
+
return unless PROPERTIES_WITH_MIXINS.include?(prop_name) &&
|
28
|
+
!ignore_compass_mixin?(prop_name)
|
28
29
|
|
29
30
|
add_lint node, "Use the Compass `#{prop_name}` mixin instead of the property"
|
30
31
|
end
|
31
32
|
|
32
33
|
def check_for_inline_block(node)
|
33
34
|
prop_name = node.name.join
|
34
|
-
return unless prop_name == 'display' &&
|
35
|
+
return unless prop_name == 'display' &&
|
36
|
+
node.value.to_sass == 'inline-block' &&
|
37
|
+
!ignore_compass_mixin?('inline-block')
|
35
38
|
|
36
39
|
add_lint node,
|
37
40
|
'Use the Compass `inline-block` mixin instead of `display: inline-block`'
|
38
41
|
end
|
42
|
+
|
43
|
+
def ignore_compass_mixin?(prop_name)
|
44
|
+
config.fetch('ignore', []).include?(prop_name)
|
45
|
+
end
|
39
46
|
end
|
40
47
|
end
|
@@ -4,7 +4,8 @@ module SCSSLint
|
|
4
4
|
include LinterRegistry
|
5
5
|
|
6
6
|
def visit_root(_node)
|
7
|
-
@indent_width = config['width']
|
7
|
+
@indent_width = config['width'].to_i
|
8
|
+
@indent_character = config['character'] || 'space'
|
8
9
|
@indent = 0
|
9
10
|
yield
|
10
11
|
end
|
@@ -27,14 +28,35 @@ module SCSSLint
|
|
27
28
|
# sibling or its parent, as indentation isn't possible
|
28
29
|
return if nodes_on_same_line?(previous_node(node), node)
|
29
30
|
|
31
|
+
if @indent_character == 'tab'
|
32
|
+
other_character = ' '
|
33
|
+
other_character_name = 'space'
|
34
|
+
else
|
35
|
+
other_character = "\t"
|
36
|
+
other_character_name = 'tab'
|
37
|
+
end
|
38
|
+
|
39
|
+
check_indent_width(node, other_character, @indent_character, other_character_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_indent_width(node, other_character, character_name, other_character_name)
|
30
43
|
actual_indent = engine.lines[node.line - 1][/^(\s*)/, 1]
|
31
44
|
|
32
|
-
|
45
|
+
if actual_indent.include?(other_character)
|
46
|
+
add_lint(node.line,
|
47
|
+
"Line should be indented with #{character_name}s, " \
|
48
|
+
"not #{other_character_name}s")
|
49
|
+
return true
|
50
|
+
end
|
51
|
+
|
52
|
+
unless actual_indent.length == @indent
|
53
|
+
add_lint(node.line,
|
54
|
+
"Line should be indented #{@indent} #{character_name}s, " \
|
55
|
+
"but was indented #{actual_indent.length} #{character_name}s")
|
56
|
+
return true
|
57
|
+
end
|
33
58
|
|
34
|
-
|
35
|
-
"Line should be indented #{@indent} spaces, " \
|
36
|
-
"but was indented #{actual_indent.length} spaces")
|
37
|
-
true
|
59
|
+
false
|
38
60
|
end
|
39
61
|
|
40
62
|
# Deal with `else` statements
|
@@ -3,11 +3,14 @@ module SCSSLint
|
|
3
3
|
class Linter::PropertySortOrder < Linter
|
4
4
|
include LinterRegistry
|
5
5
|
|
6
|
+
def visit_root(_node)
|
7
|
+
@preferred_order = extract_preferred_order_from_config
|
8
|
+
yield
|
9
|
+
end
|
10
|
+
|
6
11
|
def visit_rule(node)
|
7
|
-
# Ignore properties that contain interpolation
|
8
12
|
sortable_props = node.children.select do |child|
|
9
|
-
child.is_a?(Sass::Tree::PropNode) &&
|
10
|
-
child.name.all? { |part| part.is_a?(String) }
|
13
|
+
child.is_a?(Sass::Tree::PropNode) && !ignore_property?(child)
|
11
14
|
end
|
12
15
|
|
13
16
|
sortable_prop_info = sortable_props
|
@@ -23,7 +26,7 @@ module SCSSLint
|
|
23
26
|
sorted_props.each_with_index do |prop, index|
|
24
27
|
next unless prop != sortable_prop_info[index]
|
25
28
|
|
26
|
-
add_lint(sortable_props[index],
|
29
|
+
add_lint(sortable_props[index], lint_message)
|
27
30
|
break
|
28
31
|
end
|
29
32
|
|
@@ -32,9 +35,6 @@ module SCSSLint
|
|
32
35
|
|
33
36
|
private
|
34
37
|
|
35
|
-
MESSAGE = 'Properties should be sorted in order, with vendor-prefixed ' \
|
36
|
-
'extensions before the standardized CSS property'
|
37
|
-
|
38
38
|
# Compares two properties which can contain a vendor prefix. It allows for a
|
39
39
|
# sort order like:
|
40
40
|
#
|
@@ -53,8 +53,8 @@ module SCSSLint
|
|
53
53
|
if a[:property] == b[:property]
|
54
54
|
compare_by_vendor(a, b)
|
55
55
|
else
|
56
|
-
if
|
57
|
-
compare_by_order(a, b,
|
56
|
+
if @preferred_order
|
57
|
+
compare_by_order(a, b, @preferred_order)
|
58
58
|
else
|
59
59
|
a[:property] <=> b[:property]
|
60
60
|
end
|
@@ -77,5 +77,57 @@ module SCSSLint
|
|
77
77
|
(order.index(a[:property]) || Float::INFINITY) <=>
|
78
78
|
(order.index(b[:property]) || Float::INFINITY)
|
79
79
|
end
|
80
|
+
|
81
|
+
def extract_preferred_order_from_config
|
82
|
+
case config['order']
|
83
|
+
when nil
|
84
|
+
nil # No custom order specified
|
85
|
+
when Array
|
86
|
+
config['order']
|
87
|
+
when String
|
88
|
+
begin
|
89
|
+
file = File.open(File.join(SCSS_LINT_DATA,
|
90
|
+
'property-sort-orders',
|
91
|
+
"#{config['order']}.txt"))
|
92
|
+
file.read.split("\n").reject { |line| line =~ /^(#|\s*$)/ }
|
93
|
+
rescue Errno::ENOENT
|
94
|
+
raise SCSSLint::LinterError,
|
95
|
+
"Preset property sort order '#{config['order']}' does not exist"
|
96
|
+
end
|
97
|
+
else
|
98
|
+
raise SCSSLint::LinterError,
|
99
|
+
'Invalid property sort order specified -- must be the name of a '\
|
100
|
+
'preset or an array of strings'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Return whether to ignore a property in the sort order.
|
105
|
+
#
|
106
|
+
# This includes:
|
107
|
+
# - properties containing interpolation
|
108
|
+
# - properties not explicitly defined in the sort order (if ignore_unspecified is set)
|
109
|
+
def ignore_property?(prop_node)
|
110
|
+
return true if prop_node.name.any? { |part| !part.is_a?(String) }
|
111
|
+
|
112
|
+
config['ignored_unspecified'] &&
|
113
|
+
@preferred_order &&
|
114
|
+
!@preferred_order.include?(prop_node.name.join)
|
115
|
+
end
|
116
|
+
|
117
|
+
def preset_order?
|
118
|
+
config['order'].is_a?(String)
|
119
|
+
end
|
120
|
+
|
121
|
+
def lint_message
|
122
|
+
if preset_order?
|
123
|
+
"Properties should be sorted according to the #{config['order']} sort order"
|
124
|
+
elsif @preferred_order
|
125
|
+
'Properties should be sorted according to the custom order ' \
|
126
|
+
'specified by the configuration'
|
127
|
+
else
|
128
|
+
'Properties should be sorted in order, with vendor-prefixed ' \
|
129
|
+
'extensions before the standardized CSS property'
|
130
|
+
end
|
131
|
+
end
|
80
132
|
end
|
81
133
|
end
|