scss_lint 0.38.0 → 0.39.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 +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
|