scss_lint 0.38.0 → 0.39.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 +5 -2
- data/lib/scss_lint/cli.rb +27 -11
- data/lib/scss_lint/config.rb +8 -0
- data/lib/scss_lint/linter/bem_depth.rb +32 -0
- data/lib/scss_lint/linter/color_keyword.rb +6 -0
- data/lib/scss_lint/linter/empty_line_between_blocks.rb +16 -5
- data/lib/scss_lint/linter/indentation.rb +29 -7
- data/lib/scss_lint/sass/tree.rb +12 -1
- data/lib/scss_lint/utils.rb +9 -0
- data/lib/scss_lint/version.rb +1 -1
- data/spec/scss_lint/cli_spec.rb +44 -1
- data/spec/scss_lint/linter/bem_depth_spec.rb +117 -0
- data/spec/scss_lint/linter/color_keyword_spec.rb +19 -0
- data/spec/scss_lint/linter/empty_line_between_blocks_spec.rb +14 -0
- data/spec/scss_lint/linter/indentation_spec.rb +23 -0
- data/spec/scss_lint/reporter/json_reporter_spec.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- metadata +5 -19
- data/lib/scss_lint/reporter/xml_reporter.rb +0 -33
- data/spec/scss_lint/reporter/xml_reporter_spec.rb +0 -103
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 246cc4b3c9ca9fe6b52f8ef4edd262694b701809
|
4
|
+
data.tar.gz: 18a0b888b01b4ddc391a17719b3853a77075ff92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49174f90f307f91898c0c409b4e8e711f9b0d99088005f5e1e979a1663b9be89c953a2a2431497c06b224a48d3e9dd7113392ac891c6bba6e08f9b0c67a2263b
|
7
|
+
data.tar.gz: 14098a3c8478fc0a65c222052054f3190a623a0bfc85fc4b107bb15b4cc5d802a1f95f341413a0dafe9f06d8fef74f6a9e845e1587c0ef1e317034c39dac2d15
|
data/config/default.yml
CHANGED
@@ -8,6 +8,10 @@ linters:
|
|
8
8
|
space_before_bang: true
|
9
9
|
space_after_bang: false
|
10
10
|
|
11
|
+
BemDepth:
|
12
|
+
enabled: false
|
13
|
+
max_elements: 1
|
14
|
+
|
11
15
|
BorderZero:
|
12
16
|
enabled: true
|
13
17
|
convention: zero # or `none`
|
@@ -108,8 +112,7 @@ linters:
|
|
108
112
|
'ms', 's', # Duration
|
109
113
|
'Hz', 'kHz', # Frequency
|
110
114
|
'dpi', 'dpcm', 'dppx', # Resolution
|
111
|
-
'%'
|
112
|
-
]
|
115
|
+
'%'] # Other
|
113
116
|
properties: {}
|
114
117
|
|
115
118
|
PropertySortOrder:
|
data/lib/scss_lint/cli.rb
CHANGED
@@ -26,7 +26,7 @@ module SCSSLint
|
|
26
26
|
options = SCSSLint::Options.new.parse(args)
|
27
27
|
act_on_options(options)
|
28
28
|
rescue => ex
|
29
|
-
handle_runtime_exception(ex)
|
29
|
+
handle_runtime_exception(ex, options)
|
30
30
|
end
|
31
31
|
|
32
32
|
private
|
@@ -63,7 +63,7 @@ module SCSSLint
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
def handle_runtime_exception(exception) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/LineLength, Metrics/MethodLength
|
66
|
+
def handle_runtime_exception(exception, options) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/LineLength, Metrics/MethodLength
|
67
67
|
case exception
|
68
68
|
when SCSSLint::Exceptions::InvalidCLIOption
|
69
69
|
puts exception.message
|
@@ -88,26 +88,42 @@ module SCSSLint
|
|
88
88
|
puts exception.message
|
89
89
|
halt :usage
|
90
90
|
else
|
91
|
+
config_file = relevant_configuration_file(options) if options
|
92
|
+
|
91
93
|
puts exception.message
|
92
94
|
puts exception.backtrace
|
93
95
|
puts 'Report this bug at '.color(:yellow) + BUG_REPORT_URL.color(:cyan)
|
96
|
+
puts
|
97
|
+
puts 'To help fix this issue, please include:'.color(:green)
|
98
|
+
puts '- The above stack trace'
|
99
|
+
puts "- SCSS-Lint version: #{SCSSLint::VERSION.color(:cyan)}"
|
100
|
+
puts "- Sass version: #{Gem.loaded_specs['sass'].version.to_s.color(:cyan)}"
|
101
|
+
puts "- Ruby version: #{RUBY_VERSION.color(:cyan)}"
|
102
|
+
puts "- Contents of #{File.expand_path(config_file).color(:cyan)}" if config_file
|
94
103
|
halt :software
|
95
104
|
end
|
96
105
|
end
|
97
106
|
|
98
107
|
def setup_configuration(options)
|
99
|
-
|
100
|
-
|
101
|
-
Config.load(options[:config_file])
|
102
|
-
elsif File.exist?(Config::FILE_NAME)
|
103
|
-
Config.load(Config::FILE_NAME)
|
104
|
-
else
|
105
|
-
Config.default
|
106
|
-
end
|
107
|
-
|
108
|
+
config_file = relevant_configuration_file(options)
|
109
|
+
config = config_file ? Config.load(config_file) : Config.default
|
108
110
|
merge_options_with_config(options, config)
|
109
111
|
end
|
110
112
|
|
113
|
+
# Return the path of the configuration file that should be loaded.
|
114
|
+
#
|
115
|
+
# @param options [Hash]
|
116
|
+
# @return [String]
|
117
|
+
def relevant_configuration_file(options)
|
118
|
+
if options[:config_file]
|
119
|
+
options[:config_file]
|
120
|
+
elsif File.exist?(Config::FILE_NAME)
|
121
|
+
Config::FILE_NAME
|
122
|
+
elsif File.exist?(Config.user_file)
|
123
|
+
Config.user_file
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
111
127
|
# @param options [Hash]
|
112
128
|
# @param config [Config]
|
113
129
|
# @return [Config]
|
data/lib/scss_lint/config.rb
CHANGED
@@ -25,6 +25,14 @@ module SCSSLint
|
|
25
25
|
Config.new(config_options)
|
26
26
|
end
|
27
27
|
|
28
|
+
# Returns the location of the user-wide scss-lint configuration.
|
29
|
+
#
|
30
|
+
# This needs to be a method instead of a constant so that we can change
|
31
|
+
# the user's home directory in tests.
|
32
|
+
def user_file
|
33
|
+
File.join(Dir.home, FILE_NAME)
|
34
|
+
end
|
35
|
+
|
28
36
|
def linter_name(linter)
|
29
37
|
linter = linter.is_a?(Class) ? linter : linter.class
|
30
38
|
linter.name.split('::')[2..-1].join('::')
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Checks for BEM selectors with more elements than a specified maximum number.
|
3
|
+
class Linter::BemDepth < Linter
|
4
|
+
include LinterRegistry
|
5
|
+
|
6
|
+
def visit_root(_node)
|
7
|
+
@max_elements = config['max_elements']
|
8
|
+
yield # Continue linting children
|
9
|
+
end
|
10
|
+
|
11
|
+
def visit_class(klass)
|
12
|
+
check_depth(klass, 'selectors')
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_placeholder(placeholder)
|
16
|
+
check_depth(placeholder, 'placeholders')
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def check_depth(node, plural_type)
|
22
|
+
selector = node.name
|
23
|
+
parts = selector.split('__')
|
24
|
+
num_elements = (parts[1..-1] || []).length
|
25
|
+
return if num_elements <= @max_elements
|
26
|
+
|
27
|
+
found_elements = pluralize(@max_elements, 'element')
|
28
|
+
add_lint(node, "BEM #{plural_type} should have no more than #{found_elements}, " \
|
29
|
+
"but `#{selector}` has #{num_elements}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -20,6 +20,8 @@ module SCSSLint
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def add_color_lint(node, original)
|
23
|
+
return if in_map?(node)
|
24
|
+
|
23
25
|
hex_form = Sass::Script::Value::Color.new(color_keyword_to_code(original)).tap do |color|
|
24
26
|
color.options = {} # `inspect` requires options to be set
|
25
27
|
end.inspect
|
@@ -28,5 +30,9 @@ module SCSSLint
|
|
28
30
|
"Color `#{original}` should be written in hexadecimal form " \
|
29
31
|
"as `#{hex_form}`")
|
30
32
|
end
|
33
|
+
|
34
|
+
def in_map?(node)
|
35
|
+
node_ancestor(node, 2).is_a?(Sass::Script::Tree::MapLiteral)
|
36
|
+
end
|
31
37
|
end
|
32
38
|
end
|
@@ -40,17 +40,28 @@ module SCSSLint
|
|
40
40
|
(next_start_line = following_node.line)
|
41
41
|
|
42
42
|
# Special case: ignore comments immediately after a closing brace
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
return if comment_after_closing_brace?(following_node, next_start_line)
|
44
|
+
|
45
|
+
# Special case: ignore `@else` nodes which are children of the parent `@if`
|
46
|
+
return if else_node?(following_node)
|
46
47
|
|
47
48
|
# Otherwise check if line before the next node's starting line is blank
|
48
|
-
|
49
|
-
return if line.empty?
|
49
|
+
return if next_line_blank?(next_start_line)
|
50
50
|
|
51
51
|
add_lint(next_start_line - 1, MESSAGE_FORMAT % [type, 'followed'])
|
52
52
|
end
|
53
53
|
|
54
|
+
def comment_after_closing_brace?(node, next_start_line)
|
55
|
+
line = engine.lines[next_start_line - 1].strip
|
56
|
+
|
57
|
+
node.is_a?(Sass::Tree::CommentNode) &&
|
58
|
+
line =~ %r{\s*\}?\s*/(/|\*)}
|
59
|
+
end
|
60
|
+
|
61
|
+
def next_line_blank?(next_start_line)
|
62
|
+
engine.lines[next_start_line - 2].strip.empty?
|
63
|
+
end
|
64
|
+
|
54
65
|
# In cases where the previous node is not a block declaration, we won't
|
55
66
|
# have run any checks against it, so we need to check here if the previous
|
56
67
|
# line is an empty line
|
@@ -56,10 +56,16 @@ module SCSSLint
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
-
# Deal with `else` statements
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
# Deal with `else` statements, which require special care since they are
|
60
|
+
# considered children of `if` statements.
|
61
|
+
def visit_if(node)
|
62
|
+
check_indentation(node)
|
63
|
+
|
64
|
+
if config['allow_non_nested_indentation']
|
65
|
+
yield # Continue linting else statement
|
66
|
+
else
|
67
|
+
visit(node.else) if node.else
|
68
|
+
end
|
63
69
|
end
|
64
70
|
|
65
71
|
# Need to define this explicitly since @at-root directives can contain
|
@@ -89,6 +95,12 @@ module SCSSLint
|
|
89
95
|
end
|
90
96
|
end
|
91
97
|
|
98
|
+
def visit_import(node)
|
99
|
+
prev = previous_node(node)
|
100
|
+
return if prev.is_a?(Sass::Tree::ImportNode) && source_from_range(prev.source_range) =~ /,$/
|
101
|
+
check_indentation(node)
|
102
|
+
end
|
103
|
+
|
92
104
|
# Define node types that increase indentation level
|
93
105
|
alias_method :visit_directive, :check_and_visit_children
|
94
106
|
alias_method :visit_each, :check_and_visit_children
|
@@ -107,7 +119,6 @@ module SCSSLint
|
|
107
119
|
alias_method :visit_content, :check_indentation
|
108
120
|
alias_method :visit_cssimport, :check_indentation
|
109
121
|
alias_method :visit_extend, :check_indentation
|
110
|
-
alias_method :visit_import, :check_indentation
|
111
122
|
alias_method :visit_return, :check_indentation
|
112
123
|
alias_method :visit_variable, :check_indentation
|
113
124
|
alias_method :visit_warn, :check_indentation
|
@@ -157,7 +168,7 @@ module SCSSLint
|
|
157
168
|
return true
|
158
169
|
end
|
159
170
|
elsif !one_shift_greater_than_parent?(node, actual_indent)
|
160
|
-
parent_indent = node_indent(node
|
171
|
+
parent_indent = node_indent(node_indent_parent(node)).length
|
161
172
|
expected_indent = parent_indent + @indent_width
|
162
173
|
|
163
174
|
add_lint(node.line,
|
@@ -181,7 +192,7 @@ module SCSSLint
|
|
181
192
|
# @param node [Sass::Tree::Node]
|
182
193
|
# @return [true,false]
|
183
194
|
def one_shift_greater_than_parent?(node, actual_indent)
|
184
|
-
parent_indent = node_indent(node
|
195
|
+
parent_indent = node_indent(node_indent_parent(node)).length
|
185
196
|
expected_indent = parent_indent + @indent_width
|
186
197
|
expected_indent == actual_indent
|
187
198
|
end
|
@@ -193,5 +204,16 @@ module SCSSLint
|
|
193
204
|
def node_indent(node)
|
194
205
|
engine.lines[node.line - 1][/^(\s*)/, 1]
|
195
206
|
end
|
207
|
+
|
208
|
+
def node_indent_parent(node)
|
209
|
+
if else_node?(node)
|
210
|
+
while node.node_parent.is_a?(Sass::Tree::IfNode) &&
|
211
|
+
node.node_parent.else == node
|
212
|
+
node = node.node_parent
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
node.node_parent
|
217
|
+
end
|
196
218
|
end
|
197
219
|
end
|
data/lib/scss_lint/sass/tree.rb
CHANGED
@@ -104,7 +104,7 @@ module Sass::Tree
|
|
104
104
|
|
105
105
|
class IfNode
|
106
106
|
def children
|
107
|
-
concat_expr_lists super, expr
|
107
|
+
concat_expr_lists super, expr, self.else
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
@@ -165,4 +165,15 @@ module Sass::Tree
|
|
165
165
|
concat_expr_lists super, expr
|
166
166
|
end
|
167
167
|
end
|
168
|
+
|
169
|
+
class ImportNode
|
170
|
+
# Compares the names and values of two imports.
|
171
|
+
#
|
172
|
+
# @param other [Object] The object to compare with
|
173
|
+
# @return [Boolean] Whether or not this node and the other object
|
174
|
+
# are the same
|
175
|
+
def ==(other)
|
176
|
+
self.class == other.class && imported_filename == other.imported_filename && super
|
177
|
+
end
|
178
|
+
end
|
168
179
|
end
|
data/lib/scss_lint/utils.rb
CHANGED
@@ -35,6 +35,15 @@ module SCSSLint
|
|
35
35
|
Sass::Script::Value::Color::COLOR_NAMES[string]
|
36
36
|
end
|
37
37
|
|
38
|
+
# Returns whether a node is an IfNode corresponding to an @else/@else if
|
39
|
+
# statement.
|
40
|
+
#
|
41
|
+
# @param node [Sass::Tree::Node]
|
42
|
+
# @return [true,false]
|
43
|
+
def else_node?(node)
|
44
|
+
source_from_range(node.source_range).strip.start_with?('@else')
|
45
|
+
end
|
46
|
+
|
38
47
|
# Given a selector array which is a list of strings with Sass::Script::Nodes
|
39
48
|
# interspersed within them, return an array of strings representing those
|
40
49
|
# selectors with the Sass::Script::Nodes removed (i.e., ignoring
|
data/lib/scss_lint/version.rb
CHANGED
data/spec/scss_lint/cli_spec.rb
CHANGED
@@ -2,6 +2,8 @@ require 'spec_helper'
|
|
2
2
|
require 'scss_lint/cli'
|
3
3
|
|
4
4
|
describe SCSSLint::CLI do
|
5
|
+
include_context 'isolated environment'
|
6
|
+
|
5
7
|
let(:config_options) do
|
6
8
|
{
|
7
9
|
'linters' => {
|
@@ -21,7 +23,9 @@ describe SCSSLint::CLI do
|
|
21
23
|
@output = ''
|
22
24
|
STDOUT.stub(:write) { |*args| @output.<<(*args) }
|
23
25
|
|
24
|
-
SCSSLint::Config.stub(:load)
|
26
|
+
SCSSLint::Config.stub(:load)
|
27
|
+
.with(SCSSLint::Config::DEFAULT_FILE, merge_with_default: false)
|
28
|
+
.and_return(config)
|
25
29
|
SCSSLint::LinterRegistry.stub(:linters)
|
26
30
|
.and_return([SCSSLint::Linter::FakeTestLinter1,
|
27
31
|
SCSSLint::Linter::FakeTestLinter2])
|
@@ -173,5 +177,44 @@ describe SCSSLint::CLI do
|
|
173
177
|
safe_run
|
174
178
|
end
|
175
179
|
end
|
180
|
+
|
181
|
+
context 'when a config file is specified' do
|
182
|
+
let(:flags) { ['--config', 'custom_config.yml'] }
|
183
|
+
|
184
|
+
before do
|
185
|
+
File.stub(:exist?).with('.scss-lint.yml').and_return(true)
|
186
|
+
File.stub(:exist?).with(SCSSLint::Config.user_file).and_return(true)
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'loads config from the specified file' do
|
190
|
+
SCSSLint::Config.should_receive(:load).with('custom_config.yml').and_return(config)
|
191
|
+
safe_run
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'when a config file exists in the current directory and home directory' do
|
196
|
+
before do
|
197
|
+
File.stub(:exist?).with('.scss-lint.yml').and_return(true)
|
198
|
+
File.stub(:exist?).with(SCSSLint::Config.user_file).and_return(true)
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'loads config from the current directory' do
|
202
|
+
SCSSLint::Config.should_receive(:load).with('.scss-lint.yml').and_return(config)
|
203
|
+
safe_run
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context 'when a config file exists only in the user home directory' do
|
208
|
+
before do
|
209
|
+
File.stub(:exist?).with('.scss-lint.yml').and_return(false)
|
210
|
+
File.stub(:exist?).with(SCSSLint::Config.user_file).and_return(true)
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'loads config from the home directory' do
|
214
|
+
config_path = File.expand_path('.scss-lint.yml', Dir.home)
|
215
|
+
SCSSLint::Config.should_receive(:load).with(config_path).and_return(config)
|
216
|
+
safe_run
|
217
|
+
end
|
218
|
+
end
|
176
219
|
end
|
177
220
|
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SCSSLint::Linter::BemDepth do
|
4
|
+
context 'with the default maximum number of elements' do
|
5
|
+
context 'when a selector lacks elements' do
|
6
|
+
let(:scss) { <<-SCSS }
|
7
|
+
.block {
|
8
|
+
background: #f00;
|
9
|
+
}
|
10
|
+
%block {
|
11
|
+
background: #0f0;
|
12
|
+
}
|
13
|
+
SCSS
|
14
|
+
|
15
|
+
it { should_not report_lint }
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when a selector contains one element' do
|
19
|
+
let(:scss) { <<-SCSS }
|
20
|
+
.block__element {
|
21
|
+
background: #f00;
|
22
|
+
}
|
23
|
+
%block__element {
|
24
|
+
background: #f00;
|
25
|
+
}
|
26
|
+
SCSS
|
27
|
+
|
28
|
+
it { should_not report_lint }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when a selector contains one element with a modifier' do
|
32
|
+
let(:scss) { <<-SCSS }
|
33
|
+
.block__element--modifier {
|
34
|
+
background: #f00;
|
35
|
+
}
|
36
|
+
%block__element--modifier {
|
37
|
+
background: #f00;
|
38
|
+
}
|
39
|
+
SCSS
|
40
|
+
|
41
|
+
it { should_not report_lint }
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when a selector contains more than one element' do
|
45
|
+
let(:scss) { <<-SCSS }
|
46
|
+
.block__element__subelement {
|
47
|
+
background: #f00;
|
48
|
+
}
|
49
|
+
%block__element__subelement {
|
50
|
+
background: #f00;
|
51
|
+
}
|
52
|
+
SCSS
|
53
|
+
|
54
|
+
it { should report_lint line: 1 }
|
55
|
+
it { should report_lint line: 4 }
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when a selector contains more than one element with a modifier' do
|
59
|
+
let(:scss) { <<-SCSS }
|
60
|
+
.block__element__subelement--modifier {
|
61
|
+
background: #f00;
|
62
|
+
}
|
63
|
+
%block__element__subelement--modifier {
|
64
|
+
background: #f00;
|
65
|
+
}
|
66
|
+
SCSS
|
67
|
+
|
68
|
+
it { should report_lint line: 1 }
|
69
|
+
it { should report_lint line: 4 }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'with a custom maximum number of elements' do
|
74
|
+
let(:linter_config) { { 'max_elements' => 2 } }
|
75
|
+
|
76
|
+
context 'when selectors have up to the custom number of elements' do
|
77
|
+
let(:scss) { <<-SCSS }
|
78
|
+
.block__element__subelement {
|
79
|
+
background: #f00;
|
80
|
+
}
|
81
|
+
%block__element__subelement {
|
82
|
+
background: #f00;
|
83
|
+
}
|
84
|
+
.block__element__subelement--modifier {
|
85
|
+
background: #0f0;
|
86
|
+
}
|
87
|
+
%block__element__subelement--modifier {
|
88
|
+
background: #0f0;
|
89
|
+
}
|
90
|
+
SCSS
|
91
|
+
|
92
|
+
it { should_not report_lint }
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'when selectors have more than the custom number of elements' do
|
96
|
+
let(:scss) { <<-SCSS }
|
97
|
+
.block__element__subelement__other {
|
98
|
+
background: #f00;
|
99
|
+
}
|
100
|
+
%block__element__subelement__other {
|
101
|
+
background: #f00;
|
102
|
+
}
|
103
|
+
.block__element__subelement__other--modifier {
|
104
|
+
background: #0f0;
|
105
|
+
}
|
106
|
+
%block__element__subelement__other--modifier {
|
107
|
+
background: #0f0;
|
108
|
+
}
|
109
|
+
SCSS
|
110
|
+
|
111
|
+
it { should report_lint line: 1 }
|
112
|
+
it { should report_lint line: 4 }
|
113
|
+
it { should report_lint line: 7 }
|
114
|
+
it { should report_lint line: 10 }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -80,4 +80,23 @@ describe SCSSLint::Linter::ColorKeyword do
|
|
80
80
|
|
81
81
|
it { should_not report_lint }
|
82
82
|
end
|
83
|
+
|
84
|
+
context 'when a color keyword is used in a map declaration as keys' do
|
85
|
+
let(:scss) { <<-SCSS }
|
86
|
+
$palette: (
|
87
|
+
white: (
|
88
|
+
first: #fff,
|
89
|
+
second: #ccc,
|
90
|
+
third: #000
|
91
|
+
),
|
92
|
+
'black': (
|
93
|
+
first: #000,
|
94
|
+
second: #ccc,
|
95
|
+
third: #fff
|
96
|
+
)
|
97
|
+
);
|
98
|
+
SCSS
|
99
|
+
|
100
|
+
it { should_not report_lint }
|
101
|
+
end
|
83
102
|
end
|
@@ -88,6 +88,20 @@ describe SCSSLint::Linter::EmptyLineBetweenBlocks do
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
+
context 'when a rule set is in an @if statement with an @else following it' do
|
92
|
+
let(:scss) { <<-SCSS }
|
93
|
+
@if $condition {
|
94
|
+
p {
|
95
|
+
}
|
96
|
+
} @else {
|
97
|
+
p {
|
98
|
+
}
|
99
|
+
}
|
100
|
+
SCSS
|
101
|
+
|
102
|
+
it { should_not report_lint }
|
103
|
+
end
|
104
|
+
|
91
105
|
context 'when mixins are defined' do
|
92
106
|
context 'and there is no blank line between them' do
|
93
107
|
let(:scss) { <<-SCSS }
|
@@ -217,6 +217,15 @@ describe SCSSLint::Linter::Indentation do
|
|
217
217
|
it { should report_lint line: 2 }
|
218
218
|
end
|
219
219
|
|
220
|
+
context 'when an @import spans multiple lines' do
|
221
|
+
let(:scss) { <<-SCSS }
|
222
|
+
@import 'foo',
|
223
|
+
'bar';
|
224
|
+
SCSS
|
225
|
+
|
226
|
+
it { should_not report_lint }
|
227
|
+
end
|
228
|
+
|
220
229
|
context 'when tabs are preferred' do
|
221
230
|
let(:linter_config) { { 'character' => 'tab', 'width' => 1 } }
|
222
231
|
|
@@ -259,6 +268,20 @@ describe SCSSLint::Linter::Indentation do
|
|
259
268
|
}
|
260
269
|
end
|
261
270
|
|
271
|
+
context 'and an if statement is accompanied by a correctly indented else statement' do
|
272
|
+
let(:scss) { <<-SCSS }
|
273
|
+
@mixin my-func() {
|
274
|
+
@if $condition {
|
275
|
+
padding: 0;
|
276
|
+
} @else {
|
277
|
+
margin: 0;
|
278
|
+
}
|
279
|
+
}
|
280
|
+
SCSS
|
281
|
+
|
282
|
+
it { should_not report_lint }
|
283
|
+
end
|
284
|
+
|
262
285
|
context 'and non-nested code is indented' do
|
263
286
|
let(:scss) { <<-SCSS }
|
264
287
|
.component {}
|
@@ -77,7 +77,7 @@ describe SCSSLint::Reporter::JSONReporter do
|
|
77
77
|
context 'when lints are warnings' do
|
78
78
|
it 'marks each issue with a severity of "warning"' do
|
79
79
|
json.values.inject(0) do |sum, issues|
|
80
|
-
sum + issues.
|
80
|
+
sum + issues.count { |i| i['severity'] == 'warning' }
|
81
81
|
end.should == 3
|
82
82
|
end
|
83
83
|
end
|
@@ -87,7 +87,7 @@ describe SCSSLint::Reporter::JSONReporter do
|
|
87
87
|
|
88
88
|
it 'marks each issue with a severity of "error"' do
|
89
89
|
json.values.inject(0) do |sum, issues|
|
90
|
-
sum + issues.
|
90
|
+
sum + issues.count { |i| i['severity'] == 'error' }
|
91
91
|
end.should == 3
|
92
92
|
end
|
93
93
|
end
|
data/spec/spec_helper.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: 0.
|
4
|
+
version: 0.39.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brigade Engineering
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-05-
|
12
|
+
date: 2015-05-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rainbow
|
@@ -39,20 +39,6 @@ dependencies:
|
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: 3.4.1
|
42
|
-
- !ruby/object:Gem::Dependency
|
43
|
-
name: nokogiri
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - "~>"
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: 1.6.0
|
49
|
-
type: :development
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - "~>"
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: 1.6.0
|
56
42
|
- !ruby/object:Gem::Dependency
|
57
43
|
name: rspec
|
58
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,6 +81,7 @@ files:
|
|
95
81
|
- lib/scss_lint/lint.rb
|
96
82
|
- lib/scss_lint/linter.rb
|
97
83
|
- lib/scss_lint/linter/bang_format.rb
|
84
|
+
- lib/scss_lint/linter/bem_depth.rb
|
98
85
|
- lib/scss_lint/linter/border_zero.rb
|
99
86
|
- lib/scss_lint/linter/color_keyword.rb
|
100
87
|
- lib/scss_lint/linter/color_variable.rb
|
@@ -154,7 +141,6 @@ files:
|
|
154
141
|
- lib/scss_lint/reporter/default_reporter.rb
|
155
142
|
- lib/scss_lint/reporter/files_reporter.rb
|
156
143
|
- lib/scss_lint/reporter/json_reporter.rb
|
157
|
-
- lib/scss_lint/reporter/xml_reporter.rb
|
158
144
|
- lib/scss_lint/runner.rb
|
159
145
|
- lib/scss_lint/sass/script.rb
|
160
146
|
- lib/scss_lint/sass/tree.rb
|
@@ -166,6 +152,7 @@ files:
|
|
166
152
|
- spec/scss_lint/engine_spec.rb
|
167
153
|
- spec/scss_lint/file_finder_spec.rb
|
168
154
|
- spec/scss_lint/linter/bang_format_spec.rb
|
155
|
+
- spec/scss_lint/linter/bem_depth_spec.rb
|
169
156
|
- spec/scss_lint/linter/border_zero_spec.rb
|
170
157
|
- spec/scss_lint/linter/color_keyword_spec.rb
|
171
158
|
- spec/scss_lint/linter/color_variable_spec.rb
|
@@ -224,7 +211,6 @@ files:
|
|
224
211
|
- spec/scss_lint/reporter/default_reporter_spec.rb
|
225
212
|
- spec/scss_lint/reporter/files_reporter_spec.rb
|
226
213
|
- spec/scss_lint/reporter/json_reporter_spec.rb
|
227
|
-
- spec/scss_lint/reporter/xml_reporter_spec.rb
|
228
214
|
- spec/scss_lint/reporter_spec.rb
|
229
215
|
- spec/scss_lint/runner_spec.rb
|
230
216
|
- spec/scss_lint/selector_visitor_spec.rb
|
@@ -261,6 +247,7 @@ test_files:
|
|
261
247
|
- spec/scss_lint/engine_spec.rb
|
262
248
|
- spec/scss_lint/file_finder_spec.rb
|
263
249
|
- spec/scss_lint/linter/bang_format_spec.rb
|
250
|
+
- spec/scss_lint/linter/bem_depth_spec.rb
|
264
251
|
- spec/scss_lint/linter/border_zero_spec.rb
|
265
252
|
- spec/scss_lint/linter/color_keyword_spec.rb
|
266
253
|
- spec/scss_lint/linter/color_variable_spec.rb
|
@@ -319,7 +306,6 @@ test_files:
|
|
319
306
|
- spec/scss_lint/reporter/default_reporter_spec.rb
|
320
307
|
- spec/scss_lint/reporter/files_reporter_spec.rb
|
321
308
|
- spec/scss_lint/reporter/json_reporter_spec.rb
|
322
|
-
- spec/scss_lint/reporter/xml_reporter_spec.rb
|
323
309
|
- spec/scss_lint/reporter_spec.rb
|
324
310
|
- spec/scss_lint/runner_spec.rb
|
325
311
|
- spec/scss_lint/selector_visitor_spec.rb
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module SCSSLint
|
2
|
-
# Reports lints in an XML format.
|
3
|
-
class Reporter::XMLReporter < Reporter
|
4
|
-
def report_lints
|
5
|
-
output = '<?xml version="1.0" encoding="utf-8"?>'
|
6
|
-
|
7
|
-
output << '<lint>'
|
8
|
-
lints.group_by(&:filename).each do |filename, file_lints|
|
9
|
-
output << "<file name=#{filename.encode(xml: :attr)}>"
|
10
|
-
|
11
|
-
file_lints.each do |lint|
|
12
|
-
output << issue_tag(lint)
|
13
|
-
end
|
14
|
-
|
15
|
-
output << '</file>'
|
16
|
-
end
|
17
|
-
output << '</lint>'
|
18
|
-
|
19
|
-
output
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def issue_tag(lint)
|
25
|
-
"<issue linter=\"#{lint.linter.name if lint.linter}\" " \
|
26
|
-
"line=\"#{lint.location.line}\" " \
|
27
|
-
"column=\"#{lint.location.column}\" " \
|
28
|
-
"length=\"#{lint.location.length}\" " \
|
29
|
-
"severity=\"#{lint.severity}\" " \
|
30
|
-
"reason=#{lint.description.encode(xml: :attr)} />"
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,103 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe SCSSLint::Reporter::XMLReporter do
|
4
|
-
subject { SCSSLint::Reporter::XMLReporter.new(lints) }
|
5
|
-
|
6
|
-
describe '#report_lints' do
|
7
|
-
let(:xml) { Nokogiri::XML(subject.report_lints) }
|
8
|
-
|
9
|
-
shared_examples_for 'XML document' do
|
10
|
-
it 'has an encoding of UTF-8' do
|
11
|
-
xml.encoding.should == 'utf-8'
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'has an XML version of 1.0' do
|
15
|
-
xml.version.should == '1.0'
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'contains a <lint> root element' do
|
19
|
-
xml.root.name.should == 'lint'
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
context 'when there are no lints' do
|
24
|
-
let(:lints) { [] }
|
25
|
-
|
26
|
-
it_should_behave_like 'XML document'
|
27
|
-
end
|
28
|
-
|
29
|
-
context 'when there are lints' do
|
30
|
-
let(:filenames) { ['f1.scss', 'f2.scss', 'f1.scss'] }
|
31
|
-
# Include invalid XML characters in the third description to validate
|
32
|
-
# that escaping happens for preventing broken XML output
|
33
|
-
let(:descriptions) { ['lint 1', 'lint 2', 'lint 3 " \' < & >'] }
|
34
|
-
let(:severities) { [:warning] * 3 }
|
35
|
-
|
36
|
-
let(:locations) do
|
37
|
-
[
|
38
|
-
SCSSLint::Location.new(5, 2, 3),
|
39
|
-
SCSSLint::Location.new(7, 6, 2),
|
40
|
-
SCSSLint::Location.new(9, 10, 1)
|
41
|
-
]
|
42
|
-
end
|
43
|
-
|
44
|
-
let(:lints) do
|
45
|
-
filenames.each_with_index.map do |filename, index|
|
46
|
-
SCSSLint::Lint.new(nil, filename, locations[index],
|
47
|
-
descriptions[index], severities[index])
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
it_should_behave_like 'XML document'
|
52
|
-
|
53
|
-
it 'contains an <issue> node for each lint' do
|
54
|
-
xml.xpath('//issue').count.should == 3
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'contains a <file> node for each file' do
|
58
|
-
xml.xpath('//file').map { |node| node[:name] }
|
59
|
-
.should =~ filenames.uniq
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'contains <issue> nodes grouped by <file>' do
|
63
|
-
xml.xpath('//file').map do |file_node|
|
64
|
-
file_node.xpath('./issue').count
|
65
|
-
end.should =~ [1, 2]
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'marks each issue with a line number' do
|
69
|
-
xml.xpath('//issue[@line]').map { |node| node[:line] }
|
70
|
-
.should =~ locations.map { |location| location.line.to_s }
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'marks each issue with a column number' do
|
74
|
-
xml.xpath('//issue[@column]').map { |node| node[:column] }
|
75
|
-
.should =~ locations.map { |location| location.column.to_s }
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'marks each issue with a length' do
|
79
|
-
xml.xpath('//issue[@length]').map { |node| node[:length] }
|
80
|
-
.should =~ locations.map { |location| location.length.to_s }
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'marks each issue with a reason containing the lint description' do
|
84
|
-
xml.xpath('//issue[@reason]').map { |node| node[:reason] }
|
85
|
-
.should =~ descriptions
|
86
|
-
end
|
87
|
-
|
88
|
-
context 'when lints are warnings' do
|
89
|
-
it 'marks each issue with a severity of "warning"' do
|
90
|
-
xml.xpath("//issue[@severity='warning']").count == 3
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
context 'when lints are errors' do
|
95
|
-
let(:severities) { [:error] * 3 }
|
96
|
-
|
97
|
-
it 'marks each issue with a severity of "error"' do
|
98
|
-
xml.xpath("//issue[@severity='error']").count == 3
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|