scss_lint 0.45.0 → 0.46.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 +4 -0
- data/lib/scss_lint/linter/duplicate_property.rb +27 -2
- data/lib/scss_lint/linter/private_naming_convention.rb +157 -0
- data/lib/scss_lint/reporter/tap_reporter.rb +123 -0
- data/lib/scss_lint/version.rb +1 -1
- data/spec/scss_lint/linter/duplicate_property_spec.rb +55 -0
- data/spec/scss_lint/linter/private_naming_convention_spec.rb +445 -0
- data/spec/scss_lint/reporter/tap_reporter_spec.rb +97 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c199f69c02fbbb36ce36e564adaa78528fd5b49
|
4
|
+
data.tar.gz: 7372af4e048f1935dc730dfffacf89bcc4a1faed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dad57b1b932447052533abef8b58f2bd0288badf398b2eb946ed7bcfa8dc225e2615e7b90fce017092d5647c1057600b1e7edde12f5fd50781c358530b84e88e
|
7
|
+
data.tar.gz: 3581b2b0fb3d039346241eec26c889083fca38b0d3f1021052e94107bc0b766e636dd370af2c0e9c92fad70a9df7a5e3494c6f50f4d00005427ceb3b22fab54d
|
data/config/default.yml
CHANGED
@@ -3,13 +3,22 @@ module SCSSLint
|
|
3
3
|
class Linter::DuplicateProperty < Linter
|
4
4
|
include LinterRegistry
|
5
5
|
|
6
|
+
def visit_root(_node)
|
7
|
+
@ignore_consecutive = config['ignore_consecutive']
|
8
|
+
yield
|
9
|
+
end
|
10
|
+
|
6
11
|
def check_properties(node)
|
7
12
|
static_properties(node).each_with_object({}) do |prop, prop_names|
|
8
13
|
prop_key = property_key(prop)
|
9
14
|
|
10
15
|
if existing_prop = prop_names[prop_key]
|
11
|
-
|
12
|
-
|
16
|
+
if existing_prop.line < prop.line - 1 || !ignore_consecutive_of?(prop)
|
17
|
+
add_lint(prop, "Property `#{existing_prop.name.join}` already "\
|
18
|
+
"defined on line #{existing_prop.line}")
|
19
|
+
else
|
20
|
+
prop_names[prop_key] = prop
|
21
|
+
end
|
13
22
|
else
|
14
23
|
prop_names[prop_key] = prop
|
15
24
|
end
|
@@ -54,5 +63,21 @@ module SCSSLint
|
|
54
63
|
prop.value.to_s
|
55
64
|
end
|
56
65
|
end
|
66
|
+
|
67
|
+
def ignore_consecutive_of?(prop)
|
68
|
+
case @ignore_consecutive
|
69
|
+
when true
|
70
|
+
return true
|
71
|
+
when false
|
72
|
+
return false
|
73
|
+
when nil
|
74
|
+
return false
|
75
|
+
when Array
|
76
|
+
return @ignore_consecutive.include?(prop.name.join)
|
77
|
+
else
|
78
|
+
raise SCSSLint::Exceptions::LinterError,
|
79
|
+
"#{@ignore_consecutive.inspect} is not a valid value for ignore_consecutive."
|
80
|
+
end
|
81
|
+
end
|
57
82
|
end
|
58
83
|
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Verifies that variables, functions, and mixins that follow the private
|
3
|
+
# naming convention are defined and used within the same file.
|
4
|
+
class Linter::PrivateNamingConvention < Linter # rubocop:disable ClassLength
|
5
|
+
include LinterRegistry
|
6
|
+
|
7
|
+
DEFINITIONS = {
|
8
|
+
Sass::Tree::FunctionNode => {
|
9
|
+
defines: Sass::Script::Tree::Funcall,
|
10
|
+
human_name: 'function',
|
11
|
+
},
|
12
|
+
Sass::Tree::MixinDefNode => {
|
13
|
+
defines: Sass::Tree::MixinNode,
|
14
|
+
human_name: 'mixin',
|
15
|
+
},
|
16
|
+
Sass::Tree::VariableNode => {
|
17
|
+
defines: Sass::Script::Tree::Variable,
|
18
|
+
human_name: 'variable',
|
19
|
+
},
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
HUMAN_NODE_NAMES = Hash[DEFINITIONS.map { |k, v| [k, v[:human_name]] }].freeze
|
23
|
+
DEFINED_BYS = Hash[DEFINITIONS.map { |k, v| [v[:defines], k] }].freeze
|
24
|
+
|
25
|
+
def visit_root(node)
|
26
|
+
# Register all top-level function, mixin, and variable definitions.
|
27
|
+
node.children.each_with_object([]) do |child_node|
|
28
|
+
if DEFINITIONS.key?(child_node.class)
|
29
|
+
register_node child_node
|
30
|
+
else
|
31
|
+
yield
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# After we have visited everything, we want to see if any private things
|
36
|
+
# were defined but not used.
|
37
|
+
after_visit_all
|
38
|
+
end
|
39
|
+
|
40
|
+
def visit_script_funcall(node)
|
41
|
+
check_privacy(node)
|
42
|
+
yield # Continue linting any arguments of this function call
|
43
|
+
end
|
44
|
+
|
45
|
+
def visit_mixin(node)
|
46
|
+
check_privacy(node)
|
47
|
+
yield # Continue into content block of this mixin's block
|
48
|
+
end
|
49
|
+
|
50
|
+
def visit_script_variable(node)
|
51
|
+
check_privacy(node)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def register_node(node, node_text = node.name)
|
57
|
+
return unless private?(node)
|
58
|
+
|
59
|
+
@private_definitions ||= {}
|
60
|
+
@private_definitions[node.class] ||= {}
|
61
|
+
|
62
|
+
@private_definitions[node.class][node_text] = {
|
63
|
+
node: node,
|
64
|
+
times_used: 0,
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def check_privacy(node, node_text = node.name)
|
69
|
+
return unless private?(node)
|
70
|
+
|
71
|
+
defined_by_class = defined_by(node)
|
72
|
+
|
73
|
+
# Look at top-level private definitions
|
74
|
+
if @private_definitions &&
|
75
|
+
@private_definitions[defined_by_class] &&
|
76
|
+
@private_definitions[defined_by_class][node_text]
|
77
|
+
@private_definitions[defined_by_class][node_text][:times_used] += 1
|
78
|
+
return
|
79
|
+
end
|
80
|
+
|
81
|
+
# We did not find a top-level private definition, so let's traverse up the
|
82
|
+
# tree, looking for private definitions of this node that are scoped.
|
83
|
+
looking_for = {
|
84
|
+
node: node,
|
85
|
+
defined_by: defined_by_class,
|
86
|
+
location: location_from_range(node.source_range),
|
87
|
+
}
|
88
|
+
return if node_defined_earlier_in_branch?(node.node_parent, looking_for)
|
89
|
+
|
90
|
+
node_type = humanize_node_class(node)
|
91
|
+
add_lint(
|
92
|
+
node,
|
93
|
+
"Private #{node_type} #{node_text} must be defined in the same file it is used"
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
def node_defined_earlier_in_branch?(node_to_look_in, looking_for)
|
98
|
+
# Look at all of the children of this node and return true if we find a
|
99
|
+
# defining node that matches in name and type.
|
100
|
+
node_to_look_in.children.each_with_object([]) do |child_node|
|
101
|
+
break unless before?(child_node, looking_for[:location])
|
102
|
+
next unless child_node.class == looking_for[:defined_by]
|
103
|
+
next unless child_node.name == looking_for[:node].name
|
104
|
+
|
105
|
+
return true # We found a match, so we are done
|
106
|
+
end
|
107
|
+
|
108
|
+
# We are at the top of the branch and don't want to check the root branch,
|
109
|
+
# since that is handled elsewhere, which means that we did not find a
|
110
|
+
# match.
|
111
|
+
return false unless node_to_look_in.node_parent.node_parent
|
112
|
+
|
113
|
+
# We did not find a match yet, and haven't reached the top of the branch,
|
114
|
+
# so recurse.
|
115
|
+
if node_to_look_in.node_parent
|
116
|
+
node_defined_earlier_in_branch?(node_to_look_in.node_parent, looking_for)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def private?(node)
|
121
|
+
node.name.start_with?(config['prefix'])
|
122
|
+
end
|
123
|
+
|
124
|
+
def before?(node, before_location)
|
125
|
+
location = location_from_range(node.source_range)
|
126
|
+
return true if location.line < before_location.line
|
127
|
+
if location.line == before_location.line &&
|
128
|
+
location.column < before_location.column
|
129
|
+
return true
|
130
|
+
end
|
131
|
+
false
|
132
|
+
end
|
133
|
+
|
134
|
+
def after_visit_all
|
135
|
+
return unless @private_definitions
|
136
|
+
|
137
|
+
@private_definitions.each do |_, nodes|
|
138
|
+
nodes.each do |node_text, node_info|
|
139
|
+
next if node_info[:times_used] > 0
|
140
|
+
node_type = humanize_node_class(node_info[:node])
|
141
|
+
add_lint(
|
142
|
+
node_info[:node],
|
143
|
+
"Private #{node_type} #{node_text} must be used in the same file it is defined"
|
144
|
+
)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def humanize_node_class(node)
|
150
|
+
HUMAN_NODE_NAMES[node.class]
|
151
|
+
end
|
152
|
+
|
153
|
+
def defined_by(node)
|
154
|
+
DEFINED_BYS[node.class]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module SCSSLint
|
2
|
+
# Reports in TAP format.
|
3
|
+
# http://testanything.org/
|
4
|
+
class Reporter::TAPReporter < Reporter
|
5
|
+
TAP_VERSION = 'TAP version 13'.freeze
|
6
|
+
|
7
|
+
def report_lints
|
8
|
+
output = [TAP_VERSION, format_plan(files, lints)]
|
9
|
+
return format_output(output) unless files.any?
|
10
|
+
|
11
|
+
output.concat(format_files(files, lints))
|
12
|
+
format_output(output)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# @param files [Array<String>]
|
18
|
+
# @param lints [Array<SCSSLint::Lint>]
|
19
|
+
# @return [String]
|
20
|
+
def format_plan(files, lints)
|
21
|
+
files_with_lints = lints.map(&:filename).uniq
|
22
|
+
extra_lines = lints.count - files_with_lints.count
|
23
|
+
comment = files.count == 0 ? ' # No files to lint' : ''
|
24
|
+
"1..#{files.count + extra_lines}#{comment}"
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param files [Array<String>]
|
28
|
+
# @param lints [Array<SCSSLint::Lint>]
|
29
|
+
# @return [Array<String>] one item per ok file or not ok lint
|
30
|
+
def format_files(files, lints)
|
31
|
+
unless lints.any?
|
32
|
+
# There are no lints, so we can take a shortcut and just output an ok
|
33
|
+
# test line for every file.
|
34
|
+
return files.map.with_index do |filename, index|
|
35
|
+
format_ok(filename, index + 1)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# There are lints, so we need to go through each file, find the lints
|
40
|
+
# for the file, and add them to the output.
|
41
|
+
|
42
|
+
# Since we'll be looking up lints by filename for each filename, we want
|
43
|
+
# to make a first pass to group all of the lints by filename to make
|
44
|
+
# lookup fast.
|
45
|
+
grouped_lints = group_lints_by_filename(lints)
|
46
|
+
|
47
|
+
test_number = 1
|
48
|
+
files.map do |filename|
|
49
|
+
if grouped_lints.key?(filename)
|
50
|
+
# This file has lints, so we want to generate a "not ok" test line for
|
51
|
+
# each failing lint.
|
52
|
+
grouped_lints[filename].map do |lint|
|
53
|
+
formatted = format_not_ok(lint, test_number)
|
54
|
+
test_number += 1
|
55
|
+
formatted
|
56
|
+
end
|
57
|
+
else
|
58
|
+
formatted = format_ok(filename, test_number)
|
59
|
+
test_number += 1
|
60
|
+
[formatted]
|
61
|
+
end
|
62
|
+
end.flatten
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param lints [Array<SCSSLint::Lint>]
|
66
|
+
# @return [Hash] keyed by filename, values are arrays of lints
|
67
|
+
def group_lints_by_filename(lints)
|
68
|
+
grouped_lints = {}
|
69
|
+
lints.each do |lint|
|
70
|
+
grouped_lints[lint.filename] ||= []
|
71
|
+
grouped_lints[lint.filename] << lint
|
72
|
+
end
|
73
|
+
grouped_lints
|
74
|
+
end
|
75
|
+
|
76
|
+
# @param filename [String]
|
77
|
+
# @param test_number [Number]
|
78
|
+
# @return [String]
|
79
|
+
def format_ok(filename, test_number)
|
80
|
+
"ok #{test_number} - #{filename}"
|
81
|
+
end
|
82
|
+
|
83
|
+
# @param lint [SCSSLint::Lint]
|
84
|
+
# @param test_number [Number]
|
85
|
+
# @return [String]
|
86
|
+
def format_not_ok(lint, test_number)
|
87
|
+
location = lint.location
|
88
|
+
test_line_description = "#{lint.filename}:#{location.line}:#{location.column}"
|
89
|
+
test_line_description += " #{lint.linter.name}" if lint.linter
|
90
|
+
|
91
|
+
<<-EOS.strip
|
92
|
+
not ok #{test_number} - #{test_line_description}
|
93
|
+
---
|
94
|
+
message: #{lint.description}
|
95
|
+
severity: #{lint.severity}
|
96
|
+
data:
|
97
|
+
file: #{lint.filename}
|
98
|
+
line: #{lint.location.line}
|
99
|
+
column: #{lint.location.column}
|
100
|
+
---
|
101
|
+
EOS
|
102
|
+
end
|
103
|
+
|
104
|
+
# @param output [Array<String>]
|
105
|
+
# @return [String]
|
106
|
+
def format_output(output)
|
107
|
+
output.join("\n") + "\n"
|
108
|
+
end
|
109
|
+
|
110
|
+
def location(lint)
|
111
|
+
"#{log.cyan(lint.filename)}:#{log.magenta(lint.location.line.to_s)}"
|
112
|
+
end
|
113
|
+
|
114
|
+
def type(lint)
|
115
|
+
lint.error? ? log.red('[E]') : log.yellow('[W]')
|
116
|
+
end
|
117
|
+
|
118
|
+
def message(lint)
|
119
|
+
linter_name = log.green("#{lint.linter.name}: ") if lint.linter
|
120
|
+
"#{linter_name}#{lint.description}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/scss_lint/version.rb
CHANGED
@@ -186,4 +186,59 @@ describe SCSSLint::Linter::DuplicateProperty do
|
|
186
186
|
|
187
187
|
it { should report_lint line: 4 }
|
188
188
|
end
|
189
|
+
|
190
|
+
context 'when consecutive duplicate properties are allowed' do
|
191
|
+
let(:linter_config) { { 'ignore_consecutive' => true } }
|
192
|
+
|
193
|
+
context 'when rule set contains consecutive duplicates' do
|
194
|
+
let(:scss) { <<-SCSS }
|
195
|
+
p {
|
196
|
+
background-color: #fff;
|
197
|
+
background-color: rgba(255, 255, 255, 0.7);
|
198
|
+
background-color: a-third-thing;
|
199
|
+
}
|
200
|
+
SCSS
|
201
|
+
|
202
|
+
it { should_not report_lint }
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'when rule set contains non-consecutive duplicates' do
|
206
|
+
let(:scss) { <<-SCSS }
|
207
|
+
p {
|
208
|
+
margin: 0;
|
209
|
+
padding: 0;
|
210
|
+
margin: 1em;
|
211
|
+
}
|
212
|
+
SCSS
|
213
|
+
|
214
|
+
it { should report_lint line: 4 }
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context 'when specific consecutive duplicate properties are allowed' do
|
219
|
+
let(:linter_config) { { 'ignore_consecutive' => ['background-color', 'transition'] } }
|
220
|
+
|
221
|
+
context 'when rule set contains consecutive duplicates in whitelist' do
|
222
|
+
let(:scss) { <<-SCSS }
|
223
|
+
p {
|
224
|
+
background-color: #fff;
|
225
|
+
background-color: rgba(255, 255, 255, 0.7);
|
226
|
+
background-color: a-third-thing;
|
227
|
+
}
|
228
|
+
SCSS
|
229
|
+
|
230
|
+
it { should_not report_lint }
|
231
|
+
end
|
232
|
+
|
233
|
+
context 'when rule set contains consecutive duplicates not in whitelist' do
|
234
|
+
let(:scss) { <<-SCSS }
|
235
|
+
p {
|
236
|
+
margin: 0;
|
237
|
+
margin: 5px;
|
238
|
+
}
|
239
|
+
SCSS
|
240
|
+
|
241
|
+
it { should report_lint line: 3 }
|
242
|
+
end
|
243
|
+
end
|
189
244
|
end
|
@@ -0,0 +1,445 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SCSSLint::Linter::PrivateNamingConvention do
|
4
|
+
context 'when a private variable' do
|
5
|
+
context 'is not used in the same file it is defined' do
|
6
|
+
let(:scss) { <<-SCSS }
|
7
|
+
$_foo: red;
|
8
|
+
SCSS
|
9
|
+
|
10
|
+
it { should report_lint line: 1 }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'is used but not defined in the same file' do
|
14
|
+
let(:scss) { <<-SCSS }
|
15
|
+
p {
|
16
|
+
color: $_foo;
|
17
|
+
background: rgba($_foo, 0);
|
18
|
+
}
|
19
|
+
SCSS
|
20
|
+
|
21
|
+
it { should report_lint line: 2 }
|
22
|
+
it { should report_lint line: 3 }
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'is used but has not been defined quite yet' do
|
26
|
+
let(:scss) { <<-SCSS }
|
27
|
+
p {
|
28
|
+
color: $_foo;
|
29
|
+
background: rgba($_foo, 0);
|
30
|
+
}
|
31
|
+
|
32
|
+
$_foo: red;
|
33
|
+
SCSS
|
34
|
+
|
35
|
+
it { should report_lint line: 2 }
|
36
|
+
it { should report_lint line: 3 }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'is defined and used in the same file' do
|
40
|
+
let(:scss) { <<-SCSS }
|
41
|
+
$_foo: red;
|
42
|
+
|
43
|
+
p {
|
44
|
+
color: $_foo;
|
45
|
+
}
|
46
|
+
SCSS
|
47
|
+
|
48
|
+
it { should_not report_lint }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'is defined and used in the same file in a function' do
|
52
|
+
let(:scss) { <<-SCSS }
|
53
|
+
$_foo: red;
|
54
|
+
|
55
|
+
p {
|
56
|
+
color: rgba($_foo, 0);
|
57
|
+
}
|
58
|
+
SCSS
|
59
|
+
|
60
|
+
it { should_not report_lint }
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'is defined within a selector and not used' do
|
64
|
+
let(:scss) { <<-SCSS }
|
65
|
+
p {
|
66
|
+
$_foo: red;
|
67
|
+
}
|
68
|
+
SCSS
|
69
|
+
|
70
|
+
it { should_not report_lint }
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'is defined within a selector and used' do
|
74
|
+
let(:scss) { <<-SCSS }
|
75
|
+
p {
|
76
|
+
$_foo: red;
|
77
|
+
color: $_foo;
|
78
|
+
}
|
79
|
+
SCSS
|
80
|
+
|
81
|
+
it { should_not report_lint }
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'is defined within a selector and used in a nested selector' do
|
85
|
+
let(:scss) { <<-SCSS }
|
86
|
+
p {
|
87
|
+
$_foo: red;
|
88
|
+
|
89
|
+
a {
|
90
|
+
color: $_foo;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
SCSS
|
94
|
+
|
95
|
+
it { should_not report_lint }
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'is defined within a selector but too late' do
|
99
|
+
let(:scss) { <<-SCSS }
|
100
|
+
p {
|
101
|
+
color: $_foo;
|
102
|
+
$_foo: red;
|
103
|
+
}
|
104
|
+
SCSS
|
105
|
+
|
106
|
+
it { should report_lint line: 2 }
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'is defined within a selector but after the nested selector it is used in' do
|
110
|
+
let(:scss) { <<-SCSS }
|
111
|
+
p {
|
112
|
+
a {
|
113
|
+
color: $_foo;
|
114
|
+
}
|
115
|
+
|
116
|
+
$_foo: red;
|
117
|
+
}
|
118
|
+
SCSS
|
119
|
+
|
120
|
+
it { should report_lint line: 3 }
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'is defined in a different selector than it is used in' do
|
124
|
+
let(:scss) { <<-SCSS }
|
125
|
+
p {
|
126
|
+
$_foo: red;
|
127
|
+
}
|
128
|
+
|
129
|
+
a {
|
130
|
+
color: $_foo;
|
131
|
+
}
|
132
|
+
SCSS
|
133
|
+
|
134
|
+
it { should report_lint line: 6 }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'when a public variable' do
|
139
|
+
context 'is used but not defined' do
|
140
|
+
let(:scss) { <<-SCSS }
|
141
|
+
p {
|
142
|
+
color: $foo;
|
143
|
+
}
|
144
|
+
SCSS
|
145
|
+
|
146
|
+
it { should_not report_lint }
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'is defined but not used' do
|
150
|
+
let(:scss) { <<-SCSS }
|
151
|
+
$foo: red;
|
152
|
+
SCSS
|
153
|
+
|
154
|
+
it { should_not report_lint }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'when a private mixin' do
|
159
|
+
context 'is not used in the same file it is defined' do
|
160
|
+
let(:scss) { <<-SCSS }
|
161
|
+
@mixin _foo {
|
162
|
+
color: red;
|
163
|
+
}
|
164
|
+
SCSS
|
165
|
+
|
166
|
+
it { should report_lint line: 1 }
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'is used but not defined in the same file' do
|
170
|
+
let(:scss) { <<-SCSS }
|
171
|
+
p {
|
172
|
+
@include _foo;
|
173
|
+
}
|
174
|
+
SCSS
|
175
|
+
|
176
|
+
it { should report_lint line: 2 }
|
177
|
+
end
|
178
|
+
|
179
|
+
context 'is used but has not been defined quite yet' do
|
180
|
+
let(:scss) { <<-SCSS }
|
181
|
+
p {
|
182
|
+
@include _foo;
|
183
|
+
}
|
184
|
+
|
185
|
+
@mixin _foo {
|
186
|
+
color: red;
|
187
|
+
}
|
188
|
+
SCSS
|
189
|
+
|
190
|
+
it { should report_lint line: 2 }
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'is defined within a selector and not used' do
|
194
|
+
let(:scss) { <<-SCSS }
|
195
|
+
p {
|
196
|
+
@mixin _foo {
|
197
|
+
color: red;
|
198
|
+
}
|
199
|
+
}
|
200
|
+
SCSS
|
201
|
+
|
202
|
+
it { should_not report_lint }
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'is defined within a selector and used within that selector' do
|
206
|
+
let(:scss) { <<-SCSS }
|
207
|
+
p {
|
208
|
+
@mixin _foo {
|
209
|
+
color: red;
|
210
|
+
}
|
211
|
+
|
212
|
+
@include _foo;
|
213
|
+
}
|
214
|
+
SCSS
|
215
|
+
|
216
|
+
it { should_not report_lint }
|
217
|
+
end
|
218
|
+
|
219
|
+
context 'is defined within a selector and used within a nested selector' do
|
220
|
+
let(:scss) { <<-SCSS }
|
221
|
+
p {
|
222
|
+
@mixin _foo {
|
223
|
+
color: red;
|
224
|
+
}
|
225
|
+
|
226
|
+
a {
|
227
|
+
@include _foo;
|
228
|
+
}
|
229
|
+
}
|
230
|
+
SCSS
|
231
|
+
|
232
|
+
it { should_not report_lint }
|
233
|
+
end
|
234
|
+
|
235
|
+
context 'is defined within a selector and used within that selector too early' do
|
236
|
+
let(:scss) { <<-SCSS }
|
237
|
+
p {
|
238
|
+
@include _foo;
|
239
|
+
|
240
|
+
@mixin _foo {
|
241
|
+
color: red;
|
242
|
+
}
|
243
|
+
}
|
244
|
+
SCSS
|
245
|
+
|
246
|
+
it { should report_lint line: 2 }
|
247
|
+
end
|
248
|
+
|
249
|
+
context 'is defined within a selector and used within a nested selector too early' do
|
250
|
+
let(:scss) { <<-SCSS }
|
251
|
+
p {
|
252
|
+
a {
|
253
|
+
@include _foo;
|
254
|
+
}
|
255
|
+
|
256
|
+
@mixin _foo {
|
257
|
+
color: red;
|
258
|
+
}
|
259
|
+
}
|
260
|
+
SCSS
|
261
|
+
|
262
|
+
it { should report_lint line: 3 }
|
263
|
+
end
|
264
|
+
|
265
|
+
context 'is defined and used in the same file' do
|
266
|
+
let(:scss) { <<-SCSS }
|
267
|
+
@mixin _foo {
|
268
|
+
color: red;
|
269
|
+
}
|
270
|
+
|
271
|
+
p {
|
272
|
+
@include _foo;
|
273
|
+
}
|
274
|
+
SCSS
|
275
|
+
|
276
|
+
it { should_not report_lint }
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context 'when a public mixin' do
|
281
|
+
context 'is used but not defined' do
|
282
|
+
let(:scss) { <<-SCSS }
|
283
|
+
p {
|
284
|
+
@include foo;
|
285
|
+
}
|
286
|
+
SCSS
|
287
|
+
|
288
|
+
it { should_not report_lint }
|
289
|
+
end
|
290
|
+
|
291
|
+
context 'is defined but not used' do
|
292
|
+
let(:scss) { <<-SCSS }
|
293
|
+
@mixin foo {
|
294
|
+
color: red;
|
295
|
+
}
|
296
|
+
SCSS
|
297
|
+
|
298
|
+
it { should_not report_lint }
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
describe 'when a private function' do
|
303
|
+
context 'is not used in the same file it is defined' do
|
304
|
+
let(:scss) { <<-SCSS }
|
305
|
+
@function _foo() {
|
306
|
+
@return red;
|
307
|
+
}
|
308
|
+
SCSS
|
309
|
+
|
310
|
+
it { should report_lint line: 1 }
|
311
|
+
end
|
312
|
+
|
313
|
+
context 'is used but not defined in the same file' do
|
314
|
+
let(:scss) { <<-SCSS }
|
315
|
+
p {
|
316
|
+
color: _foo();
|
317
|
+
}
|
318
|
+
SCSS
|
319
|
+
|
320
|
+
it { should report_lint line: 2 }
|
321
|
+
end
|
322
|
+
|
323
|
+
context 'is used but has not been defined quite yet' do
|
324
|
+
let(:scss) { <<-SCSS }
|
325
|
+
p {
|
326
|
+
color: _foo();
|
327
|
+
}
|
328
|
+
|
329
|
+
@function _foo() {
|
330
|
+
@return red;
|
331
|
+
}
|
332
|
+
SCSS
|
333
|
+
|
334
|
+
it { should report_lint line: 2 }
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'is defined within a selector and not used' do
|
338
|
+
let(:scss) { <<-SCSS }
|
339
|
+
p {
|
340
|
+
@function _foo() {
|
341
|
+
@return red;
|
342
|
+
}
|
343
|
+
}
|
344
|
+
SCSS
|
345
|
+
|
346
|
+
it { should_not report_lint }
|
347
|
+
end
|
348
|
+
|
349
|
+
context 'is defined within a selector and used within the same selector' do
|
350
|
+
let(:scss) { <<-SCSS }
|
351
|
+
p {
|
352
|
+
@function _foo() {
|
353
|
+
@return red;
|
354
|
+
}
|
355
|
+
|
356
|
+
color: _foo();
|
357
|
+
}
|
358
|
+
SCSS
|
359
|
+
|
360
|
+
it { should_not report_lint }
|
361
|
+
end
|
362
|
+
|
363
|
+
context 'is defined within a selector and used within a nested selector' do
|
364
|
+
let(:scss) { <<-SCSS }
|
365
|
+
p {
|
366
|
+
@function _foo() {
|
367
|
+
@return red;
|
368
|
+
}
|
369
|
+
|
370
|
+
a {
|
371
|
+
color: _foo();
|
372
|
+
}
|
373
|
+
}
|
374
|
+
SCSS
|
375
|
+
|
376
|
+
it { should_not report_lint }
|
377
|
+
end
|
378
|
+
|
379
|
+
context 'is defined within a selector and used within the same selector too early' do
|
380
|
+
let(:scss) { <<-SCSS }
|
381
|
+
p {
|
382
|
+
color: _foo();
|
383
|
+
|
384
|
+
@function _foo() {
|
385
|
+
@return red;
|
386
|
+
}
|
387
|
+
}
|
388
|
+
SCSS
|
389
|
+
|
390
|
+
it { should report_lint line: 2 }
|
391
|
+
end
|
392
|
+
|
393
|
+
context 'is defined within a selector and used within a nested too early' do
|
394
|
+
let(:scss) { <<-SCSS }
|
395
|
+
p {
|
396
|
+
a {
|
397
|
+
color: _foo();
|
398
|
+
}
|
399
|
+
|
400
|
+
@function _foo() {
|
401
|
+
@return red;
|
402
|
+
}
|
403
|
+
}
|
404
|
+
SCSS
|
405
|
+
|
406
|
+
it { should report_lint line: 3 }
|
407
|
+
end
|
408
|
+
|
409
|
+
context 'is defined and used in the same file' do
|
410
|
+
let(:scss) { <<-SCSS }
|
411
|
+
@function _foo() {
|
412
|
+
@return red;
|
413
|
+
}
|
414
|
+
|
415
|
+
p {
|
416
|
+
color: _foo();
|
417
|
+
}
|
418
|
+
SCSS
|
419
|
+
|
420
|
+
it { should_not report_lint }
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
context 'when a public function' do
|
425
|
+
context 'is used but not defined' do
|
426
|
+
let(:scss) { <<-SCSS }
|
427
|
+
p {
|
428
|
+
color: foo();
|
429
|
+
}
|
430
|
+
SCSS
|
431
|
+
|
432
|
+
it { should_not report_lint }
|
433
|
+
end
|
434
|
+
|
435
|
+
context 'is defined but not used' do
|
436
|
+
let(:scss) { <<-SCSS }
|
437
|
+
@function foo() {
|
438
|
+
@return red;
|
439
|
+
}
|
440
|
+
SCSS
|
441
|
+
|
442
|
+
it { should_not report_lint }
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe SCSSLint::Reporter::TAPReporter do
|
4
|
+
let(:logger) { SCSSLint::Logger.new($stdout) }
|
5
|
+
subject { described_class.new(lints, filenames, logger) }
|
6
|
+
|
7
|
+
describe '#report_lints' do
|
8
|
+
context 'when there are no files' do
|
9
|
+
let(:filenames) { [] }
|
10
|
+
let(:lints) { [] }
|
11
|
+
|
12
|
+
it 'returns the TAP version, plan, and explanation' do
|
13
|
+
subject.report_lints.should == "TAP version 13\n1..0 # No files to lint\n"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when there are files but no lints' do
|
18
|
+
let(:filenames) { ['file.scss', 'another-file.scss'] }
|
19
|
+
let(:lints) { [] }
|
20
|
+
|
21
|
+
it 'returns the TAP version, plan, and ok test lines' do
|
22
|
+
subject.report_lints.should eq(<<-EOS)
|
23
|
+
TAP version 13
|
24
|
+
1..2
|
25
|
+
ok 1 - file.scss
|
26
|
+
ok 2 - another-file.scss
|
27
|
+
EOS
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when there are some lints' do
|
32
|
+
let(:filenames) { %w[ok1.scss not-ok1.scss not-ok2.scss ok2.scss] }
|
33
|
+
|
34
|
+
let(:lints) do
|
35
|
+
[
|
36
|
+
SCSSLint::Lint.new(
|
37
|
+
SCSSLint::Linter::PrivateNamingConvention,
|
38
|
+
filenames[1],
|
39
|
+
SCSSLint::Location.new(123, 10, 8),
|
40
|
+
'Description of lint 1',
|
41
|
+
:warning
|
42
|
+
),
|
43
|
+
SCSSLint::Lint.new(
|
44
|
+
SCSSLint::Linter::PrivateNamingConvention,
|
45
|
+
filenames[2],
|
46
|
+
SCSSLint::Location.new(20, 2, 6),
|
47
|
+
'Description of lint 2',
|
48
|
+
:error
|
49
|
+
),
|
50
|
+
SCSSLint::Lint.new(
|
51
|
+
SCSSLint::Linter::PrivateNamingConvention,
|
52
|
+
filenames[2],
|
53
|
+
SCSSLint::Location.new(21, 3, 4),
|
54
|
+
'Description of lint 3',
|
55
|
+
:warning
|
56
|
+
),
|
57
|
+
]
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'returns the TAP version, plan, and correct test lines' do
|
61
|
+
subject.report_lints.should eq(<<-EOS)
|
62
|
+
TAP version 13
|
63
|
+
1..5
|
64
|
+
ok 1 - ok1.scss
|
65
|
+
not ok 2 - not-ok1.scss:123:10 SCSSLint::Linter::PrivateNamingConvention
|
66
|
+
---
|
67
|
+
message: Description of lint 1
|
68
|
+
severity: warning
|
69
|
+
data:
|
70
|
+
file: not-ok1.scss
|
71
|
+
line: 123
|
72
|
+
column: 10
|
73
|
+
---
|
74
|
+
not ok 3 - not-ok2.scss:20:2 SCSSLint::Linter::PrivateNamingConvention
|
75
|
+
---
|
76
|
+
message: Description of lint 2
|
77
|
+
severity: error
|
78
|
+
data:
|
79
|
+
file: not-ok2.scss
|
80
|
+
line: 20
|
81
|
+
column: 2
|
82
|
+
---
|
83
|
+
not ok 4 - not-ok2.scss:21:3 SCSSLint::Linter::PrivateNamingConvention
|
84
|
+
---
|
85
|
+
message: Description of lint 3
|
86
|
+
severity: warning
|
87
|
+
data:
|
88
|
+
file: not-ok2.scss
|
89
|
+
line: 21
|
90
|
+
column: 3
|
91
|
+
---
|
92
|
+
ok 5 - ok2.scss
|
93
|
+
EOS
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
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.46.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: 2016-02-
|
12
|
+
date: 2016-02-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -118,6 +118,7 @@ files:
|
|
118
118
|
- lib/scss_lint/linter/name_format.rb
|
119
119
|
- lib/scss_lint/linter/nesting_depth.rb
|
120
120
|
- lib/scss_lint/linter/placeholder_in_extend.rb
|
121
|
+
- lib/scss_lint/linter/private_naming_convention.rb
|
121
122
|
- lib/scss_lint/linter/property_count.rb
|
122
123
|
- lib/scss_lint/linter/property_sort_order.rb
|
123
124
|
- lib/scss_lint/linter/property_spelling.rb
|
@@ -163,6 +164,7 @@ files:
|
|
163
164
|
- lib/scss_lint/reporter/default_reporter.rb
|
164
165
|
- lib/scss_lint/reporter/files_reporter.rb
|
165
166
|
- lib/scss_lint/reporter/json_reporter.rb
|
167
|
+
- lib/scss_lint/reporter/tap_reporter.rb
|
166
168
|
- lib/scss_lint/runner.rb
|
167
169
|
- lib/scss_lint/sass/script.rb
|
168
170
|
- lib/scss_lint/sass/tree.rb
|
@@ -203,6 +205,7 @@ files:
|
|
203
205
|
- spec/scss_lint/linter/name_format_spec.rb
|
204
206
|
- spec/scss_lint/linter/nesting_depth_spec.rb
|
205
207
|
- spec/scss_lint/linter/placeholder_in_extend_spec.rb
|
208
|
+
- spec/scss_lint/linter/private_naming_convention_spec.rb
|
206
209
|
- spec/scss_lint/linter/property_count_spec.rb
|
207
210
|
- spec/scss_lint/linter/property_sort_order_spec.rb
|
208
211
|
- spec/scss_lint/linter/property_spelling_spec.rb
|
@@ -249,6 +252,7 @@ files:
|
|
249
252
|
- spec/scss_lint/reporter/default_reporter_spec.rb
|
250
253
|
- spec/scss_lint/reporter/files_reporter_spec.rb
|
251
254
|
- spec/scss_lint/reporter/json_reporter_spec.rb
|
255
|
+
- spec/scss_lint/reporter/tap_reporter_spec.rb
|
252
256
|
- spec/scss_lint/reporter_spec.rb
|
253
257
|
- spec/scss_lint/runner_spec.rb
|
254
258
|
- spec/scss_lint/selector_visitor_spec.rb
|
@@ -314,6 +318,7 @@ test_files:
|
|
314
318
|
- spec/scss_lint/linter/name_format_spec.rb
|
315
319
|
- spec/scss_lint/linter/nesting_depth_spec.rb
|
316
320
|
- spec/scss_lint/linter/placeholder_in_extend_spec.rb
|
321
|
+
- spec/scss_lint/linter/private_naming_convention_spec.rb
|
317
322
|
- spec/scss_lint/linter/property_count_spec.rb
|
318
323
|
- spec/scss_lint/linter/property_sort_order_spec.rb
|
319
324
|
- spec/scss_lint/linter/property_spelling_spec.rb
|
@@ -360,6 +365,7 @@ test_files:
|
|
360
365
|
- spec/scss_lint/reporter/default_reporter_spec.rb
|
361
366
|
- spec/scss_lint/reporter/files_reporter_spec.rb
|
362
367
|
- spec/scss_lint/reporter/json_reporter_spec.rb
|
368
|
+
- spec/scss_lint/reporter/tap_reporter_spec.rb
|
363
369
|
- spec/scss_lint/reporter_spec.rb
|
364
370
|
- spec/scss_lint/runner_spec.rb
|
365
371
|
- spec/scss_lint/selector_visitor_spec.rb
|