scss-lint 0.25.1 → 0.26.0
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.
- 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
|