scss-lint 0.29.0 → 0.30.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 +31 -1
- data/data/prefixed-identifiers/base.txt +107 -0
- data/data/prefixed-identifiers/bourbon.txt +71 -0
- data/lib/scss_lint/cli.rb +34 -7
- data/lib/scss_lint/config.rb +12 -1
- data/lib/scss_lint/engine.rb +3 -1
- data/lib/scss_lint/linter/bang_format.rb +40 -0
- data/lib/scss_lint/linter/declaration_order.rb +35 -14
- data/lib/scss_lint/linter/import_path.rb +62 -0
- data/lib/scss_lint/linter/name_format.rb +1 -1
- data/lib/scss_lint/linter/nesting_depth.rb +24 -0
- data/lib/scss_lint/linter/property_sort_order.rb +4 -11
- data/lib/scss_lint/linter/property_spelling.rb +25 -8
- data/lib/scss_lint/linter/qualifying_element.rb +42 -0
- data/lib/scss_lint/linter/selector_format.rb +23 -11
- data/lib/scss_lint/linter/space_after_property_colon.rb +4 -4
- data/lib/scss_lint/linter/space_after_property_name.rb +16 -1
- data/lib/scss_lint/linter/space_before_brace.rb +36 -9
- data/lib/scss_lint/linter/trailing_semicolon.rb +6 -2
- data/lib/scss_lint/linter/vendor_prefixes.rb +64 -0
- data/lib/scss_lint/rake_task.rb +1 -0
- data/lib/scss_lint/runner.rb +2 -1
- data/lib/scss_lint/sass/script.rb +10 -0
- data/lib/scss_lint/version.rb +1 -1
- data/spec/scss_lint/cli_spec.rb +45 -2
- data/spec/scss_lint/linter/bang_format_spec.rb +79 -0
- data/spec/scss_lint/linter/declaration_order_spec.rb +466 -0
- data/spec/scss_lint/linter/import_path_spec.rb +300 -0
- data/spec/scss_lint/linter/nesting_depth_spec.rb +114 -0
- data/spec/scss_lint/linter/property_spelling_spec.rb +27 -0
- data/spec/scss_lint/linter/qualifying_element_spec.rb +125 -0
- data/spec/scss_lint/linter/selector_format_spec.rb +329 -0
- data/spec/scss_lint/linter/space_after_property_colon_spec.rb +14 -0
- data/spec/scss_lint/linter/space_after_property_name_spec.rb +14 -0
- data/spec/scss_lint/linter/space_before_brace_spec.rb +401 -17
- data/spec/scss_lint/linter/trailing_semicolon_spec.rb +47 -0
- data/spec/scss_lint/linter/vendor_prefixes_spec.rb +350 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 63daef837600174964f65634095dac45b13a1bb2
|
4
|
+
data.tar.gz: 8b8e1fdebc6d40d577c02a0092c092212f0d46ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ed4cbb25cacdfdf0645ea476ccc86032ae8032c22fdb1b3cf8fdd6934601bdc075aa4ed71fa1a05d2dec08785ceb591d53eeb2c6541b5567f434ff4ce161d20
|
7
|
+
data.tar.gz: 07a85bc0d214819857e179babe4b465193adf01391f7eecadd25611264c28605e0990911e1eb6dd75ed79b65b0cb50b6529187513e6842674e5c5abeafeca5f2
|
data/config/default.yml
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Default application configuration that all configurations inherit from.
|
2
|
+
|
3
|
+
scss_files: "**/*.scss"
|
4
|
+
|
2
5
|
linters:
|
6
|
+
BangFormat:
|
7
|
+
enabled: true
|
8
|
+
space_before_bang: true
|
9
|
+
space_after_bang: false
|
10
|
+
|
3
11
|
BorderZero:
|
4
12
|
enabled: true
|
5
13
|
|
@@ -47,6 +55,11 @@ linters:
|
|
47
55
|
IdWithExtraneousSelector:
|
48
56
|
enabled: true
|
49
57
|
|
58
|
+
ImportPath:
|
59
|
+
enabled: true
|
60
|
+
leading_underscore: false
|
61
|
+
filename_extension: false
|
62
|
+
|
50
63
|
Indentation:
|
51
64
|
enabled: true
|
52
65
|
character: space # or 'tab'
|
@@ -64,6 +77,10 @@ linters:
|
|
64
77
|
enabled: true
|
65
78
|
convention: hyphenated_lowercase # or 'BEM', or a regex pattern
|
66
79
|
|
80
|
+
NestingDepth:
|
81
|
+
enabled: true
|
82
|
+
max_depth: 3
|
83
|
+
|
67
84
|
PlaceholderInExtend:
|
68
85
|
enabled: true
|
69
86
|
|
@@ -75,13 +92,19 @@ linters:
|
|
75
92
|
enabled: true
|
76
93
|
extra_properties: []
|
77
94
|
|
95
|
+
QualifyingElement:
|
96
|
+
enabled: true
|
97
|
+
allow_with_attribute: false
|
98
|
+
allow_with_class: false
|
99
|
+
allow_with_id: false
|
100
|
+
|
78
101
|
SelectorDepth:
|
79
102
|
enabled: true
|
80
103
|
max_depth: 3
|
81
104
|
|
82
105
|
SelectorFormat:
|
83
106
|
enabled: true
|
84
|
-
convention: hyphenated_lowercase # or 'snake_case', or 'camel_case', or a regex pattern
|
107
|
+
convention: hyphenated_lowercase # or 'BEM', or 'snake_case', or 'camel_case', or a regex pattern
|
85
108
|
|
86
109
|
Shorthand:
|
87
110
|
enabled: true
|
@@ -105,6 +128,7 @@ linters:
|
|
105
128
|
|
106
129
|
SpaceBeforeBrace:
|
107
130
|
enabled: true
|
131
|
+
style: space
|
108
132
|
allow_single_line_padding: false
|
109
133
|
|
110
134
|
SpaceBetweenParens:
|
@@ -133,6 +157,12 @@ linters:
|
|
133
157
|
UrlQuotes:
|
134
158
|
enabled: true
|
135
159
|
|
160
|
+
VendorPrefixes:
|
161
|
+
enabled: true
|
162
|
+
identifier_list: base
|
163
|
+
include: []
|
164
|
+
exclude: []
|
165
|
+
|
136
166
|
ZeroUnit:
|
137
167
|
enabled: true
|
138
168
|
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# Based on Autoprefixer --info
|
2
|
+
|
3
|
+
# At-Rules
|
4
|
+
keyframes
|
5
|
+
|
6
|
+
# Selectors
|
7
|
+
selection
|
8
|
+
placeholder
|
9
|
+
fullscreen
|
10
|
+
|
11
|
+
# Properties
|
12
|
+
transition
|
13
|
+
transition-property
|
14
|
+
border-radius
|
15
|
+
border-top-left-radius
|
16
|
+
border-top-right-radius
|
17
|
+
border-bottom-right-radius
|
18
|
+
border-bottom-left-radius
|
19
|
+
box-shadow
|
20
|
+
animation
|
21
|
+
animation-name
|
22
|
+
animation-duration
|
23
|
+
animation-delay
|
24
|
+
animation-direction
|
25
|
+
animation-fill-mode
|
26
|
+
animation-iteration-count
|
27
|
+
animation-play-state
|
28
|
+
animation-timing-function
|
29
|
+
transition-duration
|
30
|
+
transition-delay
|
31
|
+
transition-timing-function
|
32
|
+
transform
|
33
|
+
transform-origin
|
34
|
+
perspective
|
35
|
+
perspective-origin
|
36
|
+
transform-style
|
37
|
+
backface-visibility
|
38
|
+
border-image
|
39
|
+
box-sizing
|
40
|
+
filter
|
41
|
+
columns
|
42
|
+
column-width
|
43
|
+
column-gap
|
44
|
+
column-rule
|
45
|
+
column-rule-color
|
46
|
+
column-rule-width
|
47
|
+
column-count
|
48
|
+
column-rule-style
|
49
|
+
column-span
|
50
|
+
column-fill
|
51
|
+
break-before
|
52
|
+
break-after
|
53
|
+
break-inside
|
54
|
+
user-select
|
55
|
+
flex
|
56
|
+
flex-grow
|
57
|
+
flex-shrink
|
58
|
+
flex-basis
|
59
|
+
flex-direction
|
60
|
+
flex-wrap
|
61
|
+
flex-flow
|
62
|
+
justify-content
|
63
|
+
order
|
64
|
+
align-items
|
65
|
+
align-self
|
66
|
+
align-content
|
67
|
+
background-clip
|
68
|
+
background-origin
|
69
|
+
background-size
|
70
|
+
font-feature-settings
|
71
|
+
font-variant-ligatures
|
72
|
+
font-language-override
|
73
|
+
font-kerning
|
74
|
+
hyphens
|
75
|
+
tab-size
|
76
|
+
touch-action
|
77
|
+
text-decoration-style
|
78
|
+
text-decoration-line
|
79
|
+
text-decoration-color
|
80
|
+
text-size-adjust
|
81
|
+
clip-path
|
82
|
+
mask
|
83
|
+
mask-clip
|
84
|
+
mask-composite
|
85
|
+
mask-image
|
86
|
+
mask-origin
|
87
|
+
mask-position
|
88
|
+
mask-repeat
|
89
|
+
mask-size
|
90
|
+
|
91
|
+
# Values
|
92
|
+
linear-gradient
|
93
|
+
repeating-linear-gradient
|
94
|
+
radial-gradient
|
95
|
+
repeating-radial-gradient
|
96
|
+
flex
|
97
|
+
inline-flex
|
98
|
+
calc
|
99
|
+
max-content
|
100
|
+
min-content
|
101
|
+
fit-content
|
102
|
+
fill-available
|
103
|
+
zoom-in
|
104
|
+
zoom-out
|
105
|
+
grab
|
106
|
+
grabbing
|
107
|
+
sticky
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Identifiers covered by Bourbon mixins
|
2
|
+
|
3
|
+
# At-Rules
|
4
|
+
keyframes
|
5
|
+
|
6
|
+
# Selectors
|
7
|
+
placeholder
|
8
|
+
|
9
|
+
# Properties
|
10
|
+
animation
|
11
|
+
animation-delay
|
12
|
+
animation-direction
|
13
|
+
animation-duration
|
14
|
+
animation-fill-mode
|
15
|
+
animation-iteration-count
|
16
|
+
animation-name
|
17
|
+
animation-play-state
|
18
|
+
animation-timing-function
|
19
|
+
appearance
|
20
|
+
backface-visibility
|
21
|
+
background
|
22
|
+
border-image
|
23
|
+
border-top-left-radius
|
24
|
+
border-top-right-radius
|
25
|
+
border-bottom-right-radius
|
26
|
+
border-bottom-left-radius
|
27
|
+
box-sizing
|
28
|
+
calc
|
29
|
+
columns
|
30
|
+
column-width
|
31
|
+
column-gap
|
32
|
+
column-rule
|
33
|
+
column-rule-color
|
34
|
+
column-rule-width
|
35
|
+
column-count
|
36
|
+
column-rule-style
|
37
|
+
column-span
|
38
|
+
column-fill
|
39
|
+
filter
|
40
|
+
flex
|
41
|
+
flex-grow
|
42
|
+
flex-shrink
|
43
|
+
flex-basis
|
44
|
+
flex-direction
|
45
|
+
flex-wrap
|
46
|
+
flex-flow
|
47
|
+
justify-content
|
48
|
+
order
|
49
|
+
align-items
|
50
|
+
align-self
|
51
|
+
align-content
|
52
|
+
font-feature-settings
|
53
|
+
hyphens
|
54
|
+
perspective
|
55
|
+
perspective-origin
|
56
|
+
transform
|
57
|
+
transform-origin
|
58
|
+
transform-style
|
59
|
+
transition
|
60
|
+
transition-property
|
61
|
+
transition-duration
|
62
|
+
transition-delay
|
63
|
+
transition-timing-function
|
64
|
+
user-select
|
65
|
+
|
66
|
+
|
67
|
+
# Values
|
68
|
+
crisp-edges
|
69
|
+
optimize-contrast
|
70
|
+
linear-gradient
|
71
|
+
radial-gradient
|
data/lib/scss_lint/cli.rb
CHANGED
@@ -20,10 +20,12 @@ module SCSSLint
|
|
20
20
|
config: 78, # Configuration error
|
21
21
|
}
|
22
22
|
|
23
|
+
DEFAULT_REPORTER = [SCSSLint::Reporter::DefaultReporter, :stdout]
|
24
|
+
|
23
25
|
# @param args [Array]
|
24
26
|
def initialize(args = [])
|
25
27
|
@args = args
|
26
|
-
@options = {}
|
28
|
+
@options = { reporters: [DEFAULT_REPORTER] }
|
27
29
|
@config = Config.default
|
28
30
|
end
|
29
31
|
|
@@ -32,7 +34,13 @@ module SCSSLint
|
|
32
34
|
options_parser.parse!(@args)
|
33
35
|
|
34
36
|
# Take the rest of the arguments as files/directories
|
35
|
-
|
37
|
+
|
38
|
+
if @args.empty?
|
39
|
+
@options[:files] = @config.scss_files
|
40
|
+
else
|
41
|
+
@options[:files] = @args
|
42
|
+
end
|
43
|
+
|
36
44
|
rescue OptionParser::InvalidOption => ex
|
37
45
|
print_help options_parser.help, ex
|
38
46
|
end
|
@@ -66,6 +74,14 @@ module SCSSLint
|
|
66
74
|
define_output_format(format)
|
67
75
|
end
|
68
76
|
|
77
|
+
opts.on('-o', '--out path', 'Write output to a file instead of STDOUT', String) do |path|
|
78
|
+
define_output_path(path)
|
79
|
+
end
|
80
|
+
|
81
|
+
opts.on('-r', '--require path', 'Require Ruby file', String) do |path|
|
82
|
+
require path
|
83
|
+
end
|
84
|
+
|
69
85
|
opts.on_tail('--show-formatters', 'Shows available formatters') do
|
70
86
|
print_formatters
|
71
87
|
end
|
@@ -192,20 +208,31 @@ module SCSSLint
|
|
192
208
|
# @param lints [Array<Lint>]
|
193
209
|
def report_lints(lints)
|
194
210
|
sorted_lints = lints.sort_by { |l| [l.filename, l.location] }
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
211
|
+
@options.fetch(:reporters).each do |reporter, output|
|
212
|
+
results = reporter.new(sorted_lints).report_lints
|
213
|
+
io = (output == :stdout ? $stdout : File.new(output, 'w+'))
|
214
|
+
io.print results if results
|
215
|
+
end
|
199
216
|
end
|
200
217
|
|
201
218
|
# @param format [String]
|
202
219
|
def define_output_format(format)
|
203
|
-
@options[:
|
220
|
+
unless @options[:reporters] == [DEFAULT_REPORTER] && format == 'Default'
|
221
|
+
@options[:reporters].reject! { |i| i == DEFAULT_REPORTER }
|
222
|
+
reporter = SCSSLint::Reporter.const_get(format + 'Reporter')
|
223
|
+
@options[:reporters] << [reporter, :stdout]
|
224
|
+
end
|
204
225
|
rescue NameError
|
205
226
|
puts "Invalid output format specified: #{format}"
|
206
227
|
halt :config
|
207
228
|
end
|
208
229
|
|
230
|
+
# @param path [String]
|
231
|
+
def define_output_path(path)
|
232
|
+
last_reporter, _output = @options[:reporters].pop
|
233
|
+
@options[:reporters] << [last_reporter, path]
|
234
|
+
end
|
235
|
+
|
209
236
|
def print_formatters
|
210
237
|
puts 'Installed formatters:'
|
211
238
|
|
data/lib/scss_lint/config.rb
CHANGED
@@ -175,7 +175,9 @@ module SCSSLint
|
|
175
175
|
if relative_include_path.start_with?('/')
|
176
176
|
relative_include_path
|
177
177
|
else
|
178
|
-
File.join(File.dirname(base_config_path), relative_include_path)
|
178
|
+
path = File.join(File.dirname(base_config_path), relative_include_path)
|
179
|
+
# Remove double backslashes appearing in Windows paths.
|
180
|
+
path.gsub(%r{^//}, File::SEPARATOR)
|
179
181
|
end
|
180
182
|
end
|
181
183
|
|
@@ -262,6 +264,15 @@ module SCSSLint
|
|
262
264
|
@options['exclude'] << abs_path
|
263
265
|
end
|
264
266
|
|
267
|
+
# @return Array
|
268
|
+
def scss_files
|
269
|
+
if path = @options['scss_files']
|
270
|
+
Dir[path]
|
271
|
+
else
|
272
|
+
[]
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
265
276
|
private
|
266
277
|
|
267
278
|
def validate_linters
|
data/lib/scss_lint/engine.rb
CHANGED
@@ -20,7 +20,9 @@ module SCSSLint
|
|
20
20
|
@contents = scss_or_filename
|
21
21
|
end
|
22
22
|
|
23
|
-
|
23
|
+
# Need to force encoding to avoid Windows-related bugs.
|
24
|
+
# Need `to_a` for Ruby 1.9.3.
|
25
|
+
@lines = @contents.force_encoding('UTF-8').lines.to_a
|
24
26
|
@tree = @engine.to_tree
|
25
27
|
rescue Encoding::UndefinedConversionError, Sass::SyntaxError => error
|
26
28
|
if error.is_a?(Encoding::UndefinedConversionError) ||
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks spacing of ! declarations, like !important and !default
|
3
|
+
class Linter::BangFormat < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_prop(node)
|
7
|
+
return unless node.to_sass.include?('!')
|
8
|
+
return unless check_spacing(node)
|
9
|
+
|
10
|
+
before_qualifier = config['space_before_bang'] ? '' : 'not '
|
11
|
+
after_qualifier = config['space_after_bang'] ? '' : 'not '
|
12
|
+
|
13
|
+
add_lint(node, "! should #{before_qualifier}be preceeded by a space, " \
|
14
|
+
"and should #{after_qualifier}be followed by a space")
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def find_bang_offset(range)
|
20
|
+
offset = 0
|
21
|
+
offset += 1 while character_at(range.start_pos, offset) != '!'
|
22
|
+
offset
|
23
|
+
end
|
24
|
+
|
25
|
+
def check_spacing(node)
|
26
|
+
range = node.value_source_range
|
27
|
+
offset = find_bang_offset(range)
|
28
|
+
|
29
|
+
before_expected = config['space_before_bang'] ? / / : /[^ ]/
|
30
|
+
before_actual = character_at(range.start_pos, offset - 1)
|
31
|
+
before_is_wrong = (before_actual =~ before_expected).nil?
|
32
|
+
|
33
|
+
after_expected = config['space_after_bang'] ? / / : /[^ ]/
|
34
|
+
after_actual = character_at(range.start_pos, offset + 1)
|
35
|
+
after_is_wrong = (after_actual =~ after_expected).nil?
|
36
|
+
|
37
|
+
before_is_wrong || after_is_wrong
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -3,35 +3,56 @@ module SCSSLint
|
|
3
3
|
class Linter::DeclarationOrder < Linter
|
4
4
|
include LinterRegistry
|
5
5
|
|
6
|
+
def check_order(node)
|
7
|
+
check_node(node)
|
8
|
+
yield # Continue linting children
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method :visit_rule, :check_order
|
12
|
+
alias_method :visit_mixin, :check_order
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
MESSAGE =
|
17
|
+
'Rule sets should be ordered as follows: '\
|
18
|
+
'@extends, @includes without @content, ' \
|
19
|
+
'properties, @includes with @content, ' \
|
20
|
+
'nested rule sets'
|
21
|
+
|
22
|
+
MIXIN_WITH_CONTENT = 'mixin_with_content'
|
23
|
+
|
6
24
|
DECLARATION_ORDER = [
|
7
25
|
Sass::Tree::ExtendNode,
|
26
|
+
Sass::Tree::MixinNode,
|
8
27
|
Sass::Tree::PropNode,
|
28
|
+
MIXIN_WITH_CONTENT,
|
9
29
|
Sass::Tree::RuleNode,
|
10
30
|
]
|
11
31
|
|
12
|
-
def
|
32
|
+
def important_node?(node)
|
33
|
+
DECLARATION_ORDER.include?(node.class)
|
34
|
+
end
|
35
|
+
|
36
|
+
def check_node(node)
|
13
37
|
children = node.children.select { |n| important_node?(n) }
|
14
|
-
.map(
|
38
|
+
.map { |n| node_declaration_type(n) }
|
15
39
|
|
16
40
|
sorted_children = children.sort do |a, b|
|
17
41
|
DECLARATION_ORDER.index(a) <=> DECLARATION_ORDER.index(b)
|
18
42
|
end
|
19
43
|
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
yield # Continue linting children
|
44
|
+
return unless children != sorted_children
|
45
|
+
add_lint(node.children.first, MESSAGE)
|
25
46
|
end
|
26
47
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
'Rule sets should start with @extend declarations, followed by ' \
|
31
|
-
'properties and nested rule sets, in that order'
|
48
|
+
def node_declaration_type(node)
|
49
|
+
# If the node has no children, return the class.
|
50
|
+
return node.class unless node.has_children
|
32
51
|
|
33
|
-
|
34
|
-
|
52
|
+
# If the node is a mixin with children, indicate that;
|
53
|
+
# otherwise, just return the class.
|
54
|
+
return node.class unless node.is_a?(Sass::Tree::MixinNode)
|
55
|
+
MIXIN_WITH_CONTENT
|
35
56
|
end
|
36
57
|
end
|
37
58
|
end
|