scss-lint 0.28.0 → 0.29.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 42f28b5047a9fc0063a953cfe84bf4d7e61939ae
4
- data.tar.gz: dbf4ea16dadcdff7fde653b7433478085bf80260
3
+ metadata.gz: fe908b2051342dce6151d04897242d18a3f9bf0b
4
+ data.tar.gz: 785752360ba0475386ecc1c8f8d202268293f428
5
5
  SHA512:
6
- metadata.gz: 0354b7af1ee236a8add0483e6adc97751e687b1f19850cc248cc94a9200fa28c5a1f84244b35b01a812a7f48a6c25056fdb07508d9e3d9b55ba928b7daa2a998
7
- data.tar.gz: 321fbeeee64c9d02cd4dddd2176ac1b546f71a4f4b66bb861d089de1490316ffe0ce804395ff02727b78a8c4908fc1cf2c7c30ca198147ec5f3f828af3406efa
6
+ metadata.gz: cd5a9205186a67350c31d52d0cbd8677510fa5ae270ca0b25fc99dd87768c8b02393ebbbf2d068cc66f19ddf89ff7fb19b467d74a99ea3702fe7ff134352ef0a
7
+ data.tar.gz: 8063dfc1815c2d3b4a3edb76540635282484e0e56a1ce7b71bf2a60ec493ad5b3948b17b4fae56db6e1df226aa6704802946530aaf6304bd3a5952875b6d1409
@@ -3,9 +3,6 @@ linters:
3
3
  BorderZero:
4
4
  enabled: true
5
5
 
6
- CapitalizationInSelector:
7
- enabled: true
8
-
9
6
  ColorKeyword:
10
7
  enabled: true
11
8
 
@@ -82,6 +79,10 @@ linters:
82
79
  enabled: true
83
80
  max_depth: 3
84
81
 
82
+ SelectorFormat:
83
+ enabled: true
84
+ convention: hyphenated_lowercase # or 'snake_case', or 'camel_case', or a regex pattern
85
+
85
86
  Shorthand:
86
87
  enabled: true
87
88
 
@@ -117,6 +118,9 @@ linters:
117
118
  TrailingSemicolon:
118
119
  enabled: true
119
120
 
121
+ TrailingZero:
122
+ enabled: false
123
+
120
124
  UnnecessaryMantissa:
121
125
  enabled: true
122
126
 
@@ -1,8 +1,10 @@
1
+ @keyframes
1
2
  align-content
2
3
  align-items
4
+ align-self
3
5
  alignment-adjust
4
6
  alignment-baseline
5
- align-self
7
+ all
6
8
  animation
7
9
  animation-delay
8
10
  animation-direction
@@ -12,8 +14,8 @@ animation-iteration-count
12
14
  animation-name
13
15
  animation-play-state
14
16
  animation-timing-function
15
- appearance
16
17
  app-region
18
+ appearance
17
19
  aspect-ratio
18
20
  backface-visibility
19
21
  background
@@ -123,9 +125,9 @@ column-rule
123
125
  column-rule-color
124
126
  column-rule-style
125
127
  column-rule-width
126
- columns
127
128
  column-span
128
129
  column-width
130
+ columns
129
131
  content
130
132
  counter-increment
131
133
  counter-reset
@@ -176,7 +178,9 @@ font-variant-ligatures
176
178
  font-weight
177
179
  glyph-orientation-horizontal
178
180
  glyph-orientation-vertical
181
+ grid
179
182
  grid-after
183
+ grid-area
180
184
  grid-auto-columns
181
185
  grid-auto-flow
182
186
  grid-auto-rows
@@ -188,6 +192,7 @@ grid-row
188
192
  grid-rows
189
193
  grid-start
190
194
  grid-template
195
+ grid-template-areas
191
196
  hanging-punctuation
192
197
  height
193
198
  highlight
@@ -207,8 +212,8 @@ image-resolution
207
212
  inline-box-align
208
213
  isolation
209
214
  justify-content
215
+ justify-self
210
216
  kerning
211
- @keyframes
212
217
  left
213
218
  letter-spacing
214
219
  lighting-color
@@ -354,6 +359,7 @@ ruby-align
354
359
  ruby-overhang
355
360
  ruby-position
356
361
  ruby-span
362
+ scroll-behavior
357
363
  shape-image-threshold
358
364
  shape-inside
359
365
  shape-margin
@@ -375,8 +381,8 @@ stroke-miterlimit
375
381
  stroke-opacity
376
382
  stroke-width
377
383
  svg-shadow
378
- table-layout
379
384
  tab-size
385
+ table-layout
380
386
  tap-highlight-color
381
387
  target
382
388
  target-name
@@ -389,8 +395,8 @@ text-combine
389
395
  text-decoration
390
396
  text-decoration-color
391
397
  text-decoration-line
392
- text-decorations-in-effect
393
398
  text-decoration-style
399
+ text-decorations-in-effect
394
400
  text-emphasis
395
401
  text-emphasis-color
396
402
  text-emphasis-position
@@ -425,6 +431,7 @@ text-underline-width
425
431
  text-wrap
426
432
  top
427
433
  touch-action
434
+ touch-action-delay
428
435
  transform
429
436
  transform-origin
430
437
  transform-origin-x
@@ -455,6 +462,7 @@ voice-volume
455
462
  white-space
456
463
  widows
457
464
  width
465
+ will-change
458
466
  word-break
459
467
  word-spacing
460
468
  word-wrap
@@ -99,7 +99,7 @@ module SCSSLint
99
99
  runner.run(files_to_lint)
100
100
  report_lints(runner.lints)
101
101
 
102
- if runner.lints.any? { |lint| lint.error? }
102
+ if runner.lints.any?(&:error?)
103
103
  halt :error
104
104
  elsif runner.lints.any?
105
105
  halt :warning
@@ -36,7 +36,7 @@ module SCSSLint
36
36
  @dir_to_config ||= {}
37
37
  @dir_to_config[directory] ||=
38
38
  begin
39
- config_file = possible_config_files(directory).find { |path| path.file? }
39
+ config_file = possible_config_files(directory).find(&:file?)
40
40
  Config.load(config_file.to_s) if config_file
41
41
  end
42
42
  end
@@ -11,7 +11,7 @@ module SCSSLint
11
11
 
12
12
  def visit_rule(node)
13
13
  children = node.children.select { |n| important_node?(n) }
14
- .map { |n| n.class }
14
+ .map(&:class)
15
15
 
16
16
  sorted_children = children.sort do |a, b|
17
17
  DECLARATION_ORDER.index(a) <=> DECLARATION_ORDER.index(b)
@@ -36,8 +36,17 @@ module SCSSLint
36
36
  end
37
37
 
38
38
  def check_following_node(node, type)
39
- return unless (following_node = next_node(node)) && (next_start_line = following_node.line)
40
- return if engine.lines[next_start_line - 2].strip.empty?
39
+ return unless (following_node = next_node(node)) &&
40
+ (next_start_line = following_node.line)
41
+
42
+ # Special case: ignore comments immediately after a closing brace
43
+ line = engine.lines[next_start_line - 1].strip
44
+ return if following_node.is_a?(Sass::Tree::CommentNode) &&
45
+ line =~ %r{\s*\}\s*/(/|\*)}
46
+
47
+ # Otherwise check if line before the next node's starting line is blank
48
+ line = engine.lines[next_start_line - 2].strip
49
+ return if line.empty?
41
50
 
42
51
  add_lint(next_start_line - 1, MESSAGE_FORMAT % [type, 'followed'])
43
52
  end
@@ -13,8 +13,6 @@ module SCSSLint
13
13
  end
14
14
  return unless can_be_simplified
15
15
 
16
- # TODO: Sass::Selector::SimpleSequence#source_range sometimes lies about
17
- # its line, so reference `#line` directly
18
16
  add_lint(seq.line, "Selector `#{seq}` can be simplified to `#{id_sel}`, " \
19
17
  'since IDs should be uniquely identifying')
20
18
  end
@@ -14,10 +14,8 @@ module SCSSLint
14
14
  end
15
15
 
16
16
  def visit_script_number(node)
17
- # TODO: Remove once https://github.com/sass/sass/issues/1343 is fixed
18
- return unless source = source_from_range(node.source_range)
19
-
20
- return unless number = source[FRACTIONAL_DIGIT_REGEX, 1]
17
+ return unless number =
18
+ source_from_range(node.source_range)[FRACTIONAL_DIGIT_REGEX, 1]
21
19
 
22
20
  check_number(node, number)
23
21
  end
@@ -33,8 +33,9 @@ module SCSSLint
33
33
  yield # Continue linting children
34
34
  end
35
35
 
36
- alias_method :visit_rule, :check_sort_order
36
+ alias_method :visit_media, :check_sort_order
37
37
  alias_method :visit_mixin, :check_sort_order
38
+ alias_method :visit_rule, :check_sort_order
38
39
 
39
40
  def visit_if(node, &block)
40
41
  check_sort_order(node, &block)
@@ -0,0 +1,79 @@
1
+ module SCSSLint
2
+ # Checks that selector names use a specified convention
3
+ class Linter::SelectorFormat < Linter
4
+ include LinterRegistry
5
+
6
+ def visit_root(_node)
7
+ @ignored_names = Array(config['ignored_names']).to_set
8
+ @ignored_types = Array(config['ignored_types']).to_set
9
+ yield
10
+ end
11
+
12
+ def visit_attribute(attribute)
13
+ check(attribute) unless @ignored_types.include?('attribute')
14
+ end
15
+
16
+ def visit_class(klass)
17
+ check(klass) unless @ignored_types.include?('class')
18
+ end
19
+
20
+ def visit_element(element)
21
+ check(element) unless @ignored_types.include?('element')
22
+ end
23
+
24
+ def visit_id(id)
25
+ check(id) unless @ignored_types.include?('id')
26
+ end
27
+
28
+ def visit_placeholder(placeholder)
29
+ check(placeholder) unless @ignored_types.include?('placeholder')
30
+ end
31
+
32
+ def visit_pseudo(pseudo)
33
+ check(pseudo) unless @ignored_types.include?('pseudo-selector')
34
+ end
35
+
36
+ private
37
+
38
+ def check(node)
39
+ name = node.name
40
+
41
+ return if @ignored_names.include?(name)
42
+ return unless violation = violated_convention(name)
43
+
44
+ add_lint(node, "Selector `#{name}` should be " \
45
+ "written #{violation[:explanation]}")
46
+ end
47
+
48
+ CONVENTIONS = {
49
+ 'hyphenated_lowercase' => {
50
+ explanation: 'in lowercase with hyphens',
51
+ validator: ->(name) { name !~ /[^\-a-z0-9]/ },
52
+ },
53
+ 'snake_case' => {
54
+ explanation: 'in lowercase with underscores',
55
+ validator: ->(name) { name !~ /[^_a-z0-9]/ },
56
+ },
57
+ 'camel_case' => {
58
+ explanation: 'has no spaces with capitalized words except first',
59
+ validator: ->(name) { name =~ /^[a-z][a-zA-Z0-9]*$/ },
60
+ },
61
+ 'BEM' => {
62
+ explanation: 'in BEM (Block Element Modifier) format',
63
+ validator: ->(name) { name !~ /[A-Z]|-{3}|_{3}|[^_]_[^_]/ },
64
+ },
65
+ }
66
+
67
+ # Checks the given name and returns the violated convention if it failed.
68
+ def violated_convention(name_string)
69
+ convention_name = config['convention'] || 'hyphenated_lowercase'
70
+
71
+ convention = CONVENTIONS[convention_name] || {
72
+ explanation: "must match regex /#{convention_name}/",
73
+ validator: ->(name) { name =~ /#{convention_name}/ }
74
+ }
75
+
76
+ convention unless convention[:validator].call(name_string)
77
+ end
78
+ end
79
+ end
@@ -38,6 +38,9 @@ module SCSSLint
38
38
  add_lint line,
39
39
  'Declaration should not have a space before ' \
40
40
  'the terminating semicolon'
41
+ elsif ends_with_multiple_semicolons?(node)
42
+ line = node.source_range.start_pos.line
43
+ add_lint line, 'Declaration should be terminated by a single semicolon'
41
44
  end
42
45
  end
43
46
 
@@ -46,6 +49,11 @@ module SCSSLint
46
49
  source_from_range(node.source_range) =~ /;$/
47
50
  end
48
51
 
52
+ def ends_with_multiple_semicolons?(node)
53
+ # Look one character past the end to see if there's another semicolon
54
+ character_at(node.source_range.end_pos, 1) == ';'
55
+ end
56
+
49
57
  def has_space_before_semicolon?(node)
50
58
  source_from_range(node.source_range) =~ /\s;$/
51
59
  end
@@ -0,0 +1,41 @@
1
+ module SCSSLint
2
+ # Checks for unnecessary leading zeros in numeric values with decimal points.
3
+ class Linter::TrailingZero < Linter
4
+ include LinterRegistry
5
+
6
+ def visit_script_string(node)
7
+ return unless node.type == :identifier
8
+
9
+ non_string_values = remove_quoted_strings(node.value).split
10
+ non_string_values.each do |value|
11
+ next unless number = value[FRACTIONAL_DIGIT_REGEX, 1]
12
+ check_number(node, number)
13
+ end
14
+ end
15
+
16
+ def visit_script_number(node)
17
+ return unless number =
18
+ source_from_range(node.source_range)[FRACTIONAL_DIGIT_REGEX, 1]
19
+
20
+ check_number(node, number)
21
+ end
22
+
23
+ private
24
+
25
+ FRACTIONAL_DIGIT_REGEX = /^-?(\d*\.\d+)/
26
+
27
+ def check_number(node, original_number)
28
+ return unless match = /^(\d*\.\d*)0+$/.match(original_number)
29
+
30
+ fixed_number = match[1]
31
+
32
+ # Handle special case of 0 being the only trailing digit
33
+ fixed_number = fixed_number[0..-2] if fixed_number.end_with?('.')
34
+ fixed_number = 0 if fixed_number.empty? # Handle ".0" -> "0"
35
+
36
+ add_lint(node,
37
+ "`#{original_number}` should be written without a trailing " \
38
+ "zero as `#{fixed_number}`")
39
+ end
40
+ end
41
+ end
@@ -12,6 +12,12 @@ module SCSSLint
12
12
  def visit_sequence(sequence)
13
13
  return unless sequence_starts_with_parent?(sequence.members.first)
14
14
 
15
+ # Allow concatentation, e.g.
16
+ # element {
17
+ # &.foo {}
18
+ # }
19
+ return if sequence.members.first.members.size > 1
20
+
15
21
  # Allow sequences that contain multiple parent references, e.g.
16
22
  # element {
17
23
  # & + & { ... }
@@ -36,8 +42,7 @@ module SCSSLint
36
42
  def sequence_starts_with_parent?(simple_sequence)
37
43
  return unless simple_sequence.is_a?(Sass::Selector::SimpleSequence)
38
44
  first = simple_sequence.members.first
39
- simple_sequence.members.size == 1 &&
40
- first.is_a?(Sass::Selector::Parent) &&
45
+ first.is_a?(Sass::Selector::Parent) &&
41
46
  first.suffix.nil? # Ignore concatenated selectors, like `&-something`
42
47
  end
43
48
  end
@@ -19,6 +19,8 @@ module SCSSLint
19
19
 
20
20
  def check(node, string)
21
21
  return unless string =~ /^\s*url\(\s*[^"']/
22
+ return if string =~ /^\s*url\(\s*data:/ # Ignore data URIs
23
+
22
24
  add_lint(node, 'URLs should be enclosed in quotes')
23
25
  end
24
26
  end
@@ -0,0 +1,23 @@
1
+ require 'json'
2
+
3
+ module SCSSLint
4
+ # Reports lints in a JSON format.
5
+ class Reporter::JSONReporter < Reporter
6
+ def report_lints
7
+ output = {}
8
+ lints.group_by(&:filename).each do |filename, file_lints|
9
+ output[filename] = file_lints.map do |lint|
10
+ issue = {}
11
+ issue['linter'] = lint.linter.name if lint.linter
12
+ issue['line'] = lint.location.line
13
+ issue['column'] = lint.location.column
14
+ issue['length'] = lint.location.length
15
+ issue['severity'] = lint.severity
16
+ issue['reason'] = lint.description
17
+ issue
18
+ end
19
+ end
20
+ JSON.pretty_generate(output)
21
+ end
22
+ end
23
+ end
@@ -15,7 +15,7 @@ module Sass::Script
15
15
  {
16
16
  'Value' => %w[ArgList Bool Color List Map Null Number String],
17
17
  'Tree' => %w[Funcall Interpolation ListLiteral Literal MapLiteral
18
- Operation StringInterpolation UnaryOperation Variable],
18
+ Operation Selector StringInterpolation UnaryOperation Variable],
19
19
  }.each do |namespace, types|
20
20
  types.each do |type|
21
21
  node_name = type.downcase
@@ -1,4 +1,4 @@
1
1
  # Defines the gem version.
2
2
  module SCSSLint
3
- VERSION = '0.28.0'
3
+ VERSION = '0.29.0'
4
4
  end
@@ -204,6 +204,18 @@ describe SCSSLint::Linter::EmptyLineBetweenBlocks do
204
204
  it { should_not report_lint }
205
205
  end
206
206
 
207
+ context 'when a rule set is immediately followed by a comment' do
208
+ let(:css) { <<-CSS }
209
+ a {
210
+ } // A comment
211
+
212
+ a {
213
+ } /* Another comment */
214
+ CSS
215
+
216
+ it { should_not report_lint }
217
+ end
218
+
207
219
  context 'when there are multiple placeholder rule sets' do
208
220
  context 'with blank lines between them' do
209
221
  let(:css) { <<-CSS }
@@ -185,6 +185,17 @@ describe SCSSLint::Linter::PropertySortOrder do
185
185
  it { should report_lint line: 3 }
186
186
  end
187
187
 
188
+ context 'when @media block contains properties not in sorted order' do
189
+ let(:css) { <<-CSS }
190
+ @media screen and (min-width: 500px) {
191
+ margin: 5px;
192
+ display: none;
193
+ }
194
+ CSS
195
+
196
+ it { should report_lint line: 2 }
197
+ end
198
+
188
199
  context 'when if block contains properties in sorted order' do
189
200
  let(:css) { <<-CSS }
190
201
  @if $var {
@@ -0,0 +1,272 @@
1
+ require 'spec_helper'
2
+
3
+ describe SCSSLint::Linter::SelectorFormat do
4
+ context 'when class has alphanumeric chars and is separated by hyphens' do
5
+ let(:css) { <<-CSS }
6
+ .foo-bar-77 {
7
+ }
8
+ CSS
9
+
10
+ it { should_not report_lint }
11
+ end
12
+
13
+ context 'when id has alphanumeric chars and is separated by hyphens' do
14
+ let(:css) { <<-CSS }
15
+ #foo-bar-77 {
16
+ }
17
+ CSS
18
+
19
+ it { should_not report_lint }
20
+ end
21
+
22
+ context 'when element has alphanumeric chars and is separated by hyphens' do
23
+ let(:css) { <<-CSS }
24
+ foo-bar-77 {
25
+ }
26
+ CSS
27
+
28
+ it { should_not report_lint }
29
+ end
30
+
31
+ context 'when placeholder has alphanumeric chars and is separated by hyphens' do
32
+ let(:css) { <<-CSS }
33
+ %foo-bar-77 {
34
+ }
35
+ CSS
36
+
37
+ it { should_not report_lint }
38
+ end
39
+
40
+ context 'when pseudo-selector has alphanumeric chars and is separated by hyphens' do
41
+ let(:css) { <<-CSS }
42
+ [foo-bar-77=text] {
43
+ }
44
+ CSS
45
+
46
+ it { should_not report_lint }
47
+ end
48
+
49
+ context 'when selector has alphanumeric chars and is separated by underscores' do
50
+ let(:css) { <<-CSS }
51
+ .foo_bar {
52
+ }
53
+ CSS
54
+
55
+ it { should report_lint line: 1 }
56
+ end
57
+
58
+ context 'when selector has is in camelCase' do
59
+ let(:css) { <<-CSS }
60
+ fooBar77 {
61
+ }
62
+ CSS
63
+
64
+ it { should report_lint line: 1 }
65
+ end
66
+
67
+ context 'when placeholder has alphanumeric chars and is separated by underscores' do
68
+ let(:css) { <<-CSS }
69
+ %foo_bar {
70
+ }
71
+ CSS
72
+
73
+ it { should report_lint line: 1 }
74
+ end
75
+
76
+ context 'psuedo-selector has alphanumeric chars and is separated by underscores' do
77
+ let(:css) { <<-CSS }
78
+ :foo_bar {
79
+ }
80
+ CSS
81
+
82
+ it { should report_lint line: 1 }
83
+ end
84
+
85
+ context 'when attribute has alphanumeric chars and is separated by underscores' do
86
+ let(:css) { <<-CSS }
87
+ [data_text] {
88
+ }
89
+ CSS
90
+
91
+ it { should report_lint line: 1 }
92
+ end
93
+
94
+ context 'when attribute selector has alphanumeric chars and is separated by underscores' do
95
+ let(:css) { <<-CSS }
96
+ [data-text=some_text] {
97
+ }
98
+ CSS
99
+
100
+ it { should_not report_lint }
101
+ end
102
+
103
+ context 'when convention is set to snake_case' do
104
+ let(:linter_config) { { 'convention' => 'snake_case' } }
105
+
106
+ context 'when selector has alphanumeric chars and is separated by underscores' do
107
+ let(:css) { <<-CSS }
108
+ .foo_bar_77 {
109
+ }
110
+ CSS
111
+
112
+ it { should_not report_lint }
113
+ end
114
+
115
+ context 'when selector has alphanumeric chars and is separated by hyphens' do
116
+ let(:css) { <<-CSS }
117
+ .foo-bar-77 {
118
+ }
119
+ CSS
120
+
121
+ it { should report_lint line: 1 }
122
+ end
123
+
124
+ context 'when selector has is in camelCase' do
125
+ let(:css) { <<-CSS }
126
+ .fooBar77 {
127
+ }
128
+ CSS
129
+
130
+ it { should report_lint line: 1 }
131
+ end
132
+ end
133
+
134
+ context 'when convention is set to camel_case' do
135
+ let(:linter_config) { { 'convention' => 'camel_case' } }
136
+
137
+ context 'when selector has is in camelCase' do
138
+ let(:css) { <<-CSS }
139
+ .fooBar77 {
140
+ }
141
+ CSS
142
+
143
+ it { should_not report_lint }
144
+ end
145
+
146
+ context 'when selector capitalizes first word' do
147
+ let(:css) { <<-CSS }
148
+ .FooBar77 {
149
+ }
150
+ CSS
151
+
152
+ it { should report_lint line: 1 }
153
+ end
154
+
155
+ context 'when selector has alphanumeric chars and is separated by underscores' do
156
+ let(:css) { <<-CSS }
157
+ .foo_bar_77 {
158
+ }
159
+ CSS
160
+
161
+ it { should report_lint line: 1 }
162
+ end
163
+
164
+ context 'when selector has alphanumeric chars and is separated by hyphens' do
165
+ let(:css) { <<-CSS }
166
+ .foo-bar-77 {
167
+ }
168
+ CSS
169
+
170
+ it { should report_lint line: 1 }
171
+ end
172
+ end
173
+
174
+ context 'when convention is set to use a regex' do
175
+ let(:linter_config) { { 'convention' => /^[0-9]*$/ } }
176
+
177
+ context 'when selector uses regex properly' do
178
+ let(:css) { <<-CSS }
179
+ .1337 {
180
+ }
181
+ CSS
182
+
183
+ it { should_not report_lint }
184
+ end
185
+
186
+ context 'when selector does not use regex properly' do
187
+ let(:css) { <<-CSS }
188
+ .leet {
189
+ }
190
+ CSS
191
+
192
+ it { should report_lint line: 1 }
193
+ end
194
+ end
195
+
196
+ context 'when ignored names are set' do
197
+ let(:linter_config) { { 'ignored_names' => ['fooBar'] } }
198
+
199
+ context 'it ignores exact string matches' do
200
+ let(:css) { <<-CSS }
201
+ fooBar {
202
+ }
203
+ CSS
204
+
205
+ it { should_not report_lint }
206
+ end
207
+ end
208
+
209
+ context 'when ignored types is set to class' do
210
+ let(:linter_config) { { 'ignored_types' => ['class'] } }
211
+
212
+ context 'it ignores all invalid classes' do
213
+ let(:css) { <<-CSS }
214
+ .fooBar {
215
+ }
216
+ CSS
217
+
218
+ it { should_not report_lint }
219
+ end
220
+ end
221
+
222
+ context 'when ignored types is set to id, element, placeholder, pseudo-selector' do
223
+ let(:linter_config) do
224
+ { 'ignored_types' => %w[id attribute element placeholder pseudo-selector] }
225
+ end
226
+
227
+ context 'it ignores all invalid ids' do
228
+ let(:css) { <<-CSS }
229
+ #fooBar {
230
+ }
231
+ CSS
232
+
233
+ it { should_not report_lint }
234
+ end
235
+
236
+ context 'it ignores all invalid elements' do
237
+ let(:css) { <<-CSS }
238
+ fooBar {
239
+ }
240
+ CSS
241
+
242
+ it { should_not report_lint }
243
+ end
244
+
245
+ context 'it ignores all invalid placeholders' do
246
+ let(:css) { <<-CSS }
247
+ %fooBar {
248
+ }
249
+ CSS
250
+
251
+ it { should_not report_lint }
252
+ end
253
+
254
+ context 'it ignores all invalid attributes' do
255
+ let(:css) { <<-CSS }
256
+ [fooBar=fooBar] {
257
+ }
258
+ CSS
259
+
260
+ it { should_not report_lint }
261
+ end
262
+
263
+ context 'it ignores all invalid pseudo-selectors' do
264
+ let(:css) { <<-CSS }
265
+ :fooBar {
266
+ }
267
+ CSS
268
+
269
+ it { should_not report_lint }
270
+ end
271
+ end
272
+ end
@@ -225,4 +225,14 @@ describe SCSSLint::Linter::TrailingSemicolon do
225
225
 
226
226
  it { should_not report_lint }
227
227
  end
228
+
229
+ context 'when variable declaration ends with multiple semicolons' do
230
+ let(:css) { <<-CSS }
231
+ .foo {
232
+ $bar: 1;;
233
+ }
234
+ CSS
235
+
236
+ it { should report_lint line: 2 }
237
+ end
228
238
  end
@@ -0,0 +1,176 @@
1
+ require 'spec_helper'
2
+
3
+ describe SCSSLint::Linter::TrailingZero do
4
+ context 'when no values exist' do
5
+ let(:css) { 'p {}' }
6
+
7
+ it { should_not report_lint }
8
+ end
9
+
10
+ context 'when a zero exists' do
11
+ let(:css) { <<-CSS }
12
+ p {
13
+ margin: 0;
14
+ }
15
+ CSS
16
+
17
+ it { should_not report_lint }
18
+ end
19
+
20
+ context 'when an integer value exists' do
21
+ let(:css) { <<-CSS }
22
+ p {
23
+ line-height: 2;
24
+ }
25
+ CSS
26
+
27
+ it { should_not report_lint }
28
+ end
29
+
30
+ context 'when an integer value with units exists' do
31
+ let(:css) { <<-CSS }
32
+ p {
33
+ margin: 5px;
34
+ }
35
+ CSS
36
+
37
+ it { should_not report_lint }
38
+ end
39
+
40
+ context 'when a unitless fractional value with no trailing zero exists' do
41
+ let(:css) { <<-CSS }
42
+ p {
43
+ line-height: .5;
44
+ }
45
+ CSS
46
+
47
+ it { should_not report_lint }
48
+ end
49
+
50
+ context 'when a negative unitless fractional value with no trailing zero exists' do
51
+ let(:css) { <<-CSS }
52
+ p {
53
+ line-height: -.5;
54
+ }
55
+ CSS
56
+
57
+ it { should_not report_lint }
58
+ end
59
+
60
+ context 'when a fractional value with units and no trailing zero exists' do
61
+ let(:css) { <<-CSS }
62
+ p {
63
+ margin: .5em;
64
+ }
65
+ CSS
66
+
67
+ it { should_not report_lint }
68
+ end
69
+
70
+ context 'when a negative fractional value with units and no trailing zero exists' do
71
+ let(:css) { <<-CSS }
72
+ p {
73
+ margin: -.5em;
74
+ }
75
+ CSS
76
+
77
+ it { should_not report_lint }
78
+ end
79
+
80
+ context 'when a fractional value with a trailing zero exists' do
81
+ let(:css) { <<-CSS }
82
+ p {
83
+ line-height: .50;
84
+ }
85
+ CSS
86
+
87
+ it { should report_lint line: 2 }
88
+ end
89
+
90
+ context 'when a fractional value with units and a trailing zero exists' do
91
+ let(:css) { <<-CSS }
92
+ p {
93
+ margin: .50em;
94
+ }
95
+ CSS
96
+
97
+ it { should report_lint line: 2 }
98
+ end
99
+
100
+ context 'when a negative fractional value with units and a trailing zero exists' do
101
+ let(:css) { <<-CSS }
102
+ p {
103
+ margin: -.50em;
104
+ }
105
+ CSS
106
+
107
+ it { should report_lint line: 2 }
108
+ end
109
+
110
+ context 'when a fractional value with a mantissa ending in zero exists' do
111
+ let(:css) { <<-CSS }
112
+ p {
113
+ line-height: 10.5;
114
+ }
115
+ CSS
116
+
117
+ it { should_not report_lint }
118
+ end
119
+
120
+ context 'when multiple fractional values with trailing zeros exist' do
121
+ let(:css) { <<-CSS }
122
+ p {
123
+ margin: 0.50em .50 0.10px .90pt;
124
+ }
125
+ CSS
126
+
127
+ it { should report_lint count: 4, line: 2 }
128
+ end
129
+
130
+ context 'when trailing zeros appear in function arguments' do
131
+ let(:css) { <<-CSS }
132
+ p {
133
+ margin: some-function(.50em, 0.40 0.30 .2);
134
+ }
135
+ CSS
136
+
137
+ it { should report_lint count: 3, line: 2 }
138
+ end
139
+
140
+ context 'when trailing zeros appear in mixin arguments' do
141
+ let(:css) { <<-CSS }
142
+ p {
143
+ @include some-mixin(0.50em, 0.40 0.30 .2);
144
+ }
145
+ CSS
146
+
147
+ it { should report_lint count: 3, line: 2 }
148
+ end
149
+
150
+ context 'when trailiing zeros appear in variable declarations' do
151
+ let(:css) { '$some-var: .50em;' }
152
+
153
+ it { should report_lint line: 1 }
154
+ end
155
+
156
+ context 'when trailing zeros appear in named arguments' do
157
+ let(:css) { <<-CSS }
158
+ p {
159
+ @include line-clamp($line-height: .90, $line-count: 2);
160
+ }
161
+ CSS
162
+
163
+ it { should report_lint line: 2 }
164
+ end
165
+
166
+ context 'when trailing zeros appear in parameter defaults' do
167
+ let(:css) { <<-CSS }
168
+ @mixin my-mixin($bad-value: .50, $good-value: .9, $string-value: ".90") {
169
+ margin: $some-value;
170
+ padding: $some-other-value;
171
+ }
172
+ CSS
173
+
174
+ it { should report_lint count: 1, line: 1 }
175
+ end
176
+ end
@@ -52,6 +52,16 @@ describe SCSSLint::Linter::UnnecessaryParentReference do
52
52
  it { should_not report_lint }
53
53
  end
54
54
 
55
+ context 'when multiple ampersands exist with one concatenated' do
56
+ let(:css) { <<-CSS }
57
+ p {
58
+ & + &:hover {}
59
+ }
60
+ CSS
61
+
62
+ it { should_not report_lint }
63
+ end
64
+
55
65
  context 'when an amperand is used in a comma sequence to DRY up code' do
56
66
  let(:css) { <<-CSS }
57
67
  p {
@@ -60,4 +60,14 @@ describe SCSSLint::Linter::UrlQuotes do
60
60
 
61
61
  it { should_not report_lint }
62
62
  end
63
+
64
+ context 'when property has a data URI' do
65
+ let(:css) { <<-CSS }
66
+ .tracking-pixel {
67
+ background: url(data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==);
68
+ }
69
+ CSS
70
+
71
+ it { should_not report_lint }
72
+ end
63
73
  end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ describe SCSSLint::Reporter::JSONReporter do
4
+ subject { SCSSLint::Reporter::JSONReporter.new(lints) }
5
+
6
+ describe '#report_lints' do
7
+ let(:json) { JSON.parse(subject.report_lints) }
8
+
9
+ shared_examples_for 'parsed JSON' do
10
+ it 'is a Hash' do
11
+ json.is_a?(Hash)
12
+ end
13
+ end
14
+
15
+ context 'when there are no lints' do
16
+ let(:lints) { [] }
17
+
18
+ it_should_behave_like 'parsed JSON'
19
+ end
20
+
21
+ context 'when there are lints' do
22
+ let(:filenames) { ['f1.scss', 'f2.scss', 'f1.scss'] }
23
+ # Include invalid XML characters in the third description to validate
24
+ # that escaping happens for preventing broken XML output
25
+ let(:descriptions) { ['lint 1', 'lint 2', 'lint 3 " \' < & >'] }
26
+ let(:severities) { [:warning] * 3 }
27
+
28
+ let(:locations) do
29
+ [
30
+ SCSSLint::Location.new(5, 2, 3),
31
+ SCSSLint::Location.new(7, 6, 2),
32
+ SCSSLint::Location.new(9, 10, 1)
33
+ ]
34
+ end
35
+
36
+ let(:lints) do
37
+ filenames.each_with_index.map do |filename, index|
38
+ SCSSLint::Lint.new(nil, filename, locations[index],
39
+ descriptions[index], severities[index])
40
+ end
41
+ end
42
+
43
+ it_should_behave_like 'parsed JSON'
44
+
45
+ it 'contains an <issue> node for each lint' do
46
+ json.values.inject(0) { |sum, issues| sum + issues.size }.should == 3
47
+ end
48
+
49
+ it 'contains a group of issues for each file' do
50
+ json.keys.should == filenames.uniq
51
+ end
52
+
53
+ it 'contains <issue> nodes grouped by <file>' do
54
+ json.values.map(&:size).should == [2, 1]
55
+ end
56
+
57
+ it 'marks each issue with a line number' do
58
+ json.values.flat_map { |issues| issues.map { |issue| issue['line'] } }
59
+ .should =~ locations.map(&:line)
60
+ end
61
+
62
+ it 'marks each issue with a column number' do
63
+ json.values.flat_map { |issues| issues.map { |issue| issue['column'] } }
64
+ .should =~ locations.map(&:column)
65
+ end
66
+
67
+ it 'marks each issue with a length' do
68
+ json.values.flat_map { |issues| issues.map { |issue| issue['length'] } }
69
+ .should =~ locations.map(&:length)
70
+ end
71
+
72
+ it 'marks each issue with a reason containing the lint description' do
73
+ json.values.flat_map { |issues| issues.map { |issue| issue['reason'] } }
74
+ .should =~ descriptions
75
+ end
76
+
77
+ context 'when lints are warnings' do
78
+ it 'marks each issue with a severity of "warning"' do
79
+ json.values.inject(0) do |sum, issues|
80
+ sum + issues.select { |i| i['severity'] == 'warning' }.size
81
+ end.should == 3
82
+ end
83
+ end
84
+
85
+ context 'when lints are errors' do
86
+ let(:severities) { [:error] * 3 }
87
+
88
+ it 'marks each issue with a severity of "error"' do
89
+ json.values.inject(0) do |sum, issues|
90
+ sum + issues.select { |i| i['severity'] == 'error' }.size
91
+ end.should == 3
92
+ end
93
+ end
94
+ end
95
+ end
96
+ 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.28.0
4
+ version: 0.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Causes Engineering
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-09-04 00:00:00.000000000 Z
12
+ date: 2014-10-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rainbow
@@ -73,14 +73,14 @@ dependencies:
73
73
  requirements:
74
74
  - - '='
75
75
  - !ruby/object:Gem::Version
76
- version: 0.25.0
76
+ version: 0.26.0
77
77
  type: :development
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - '='
82
82
  - !ruby/object:Gem::Version
83
- version: 0.25.0
83
+ version: 0.26.0
84
84
  description: Configurable tool for writing clean and consistent SCSS
85
85
  email:
86
86
  - eng@causes.com
@@ -100,6 +100,7 @@ files:
100
100
  - lib/scss_lint/reporter/files_reporter.rb
101
101
  - lib/scss_lint/reporter/xml_reporter.rb
102
102
  - lib/scss_lint/reporter/default_reporter.rb
103
+ - lib/scss_lint/reporter/json_reporter.rb
103
104
  - lib/scss_lint/reporter/config_reporter.rb
104
105
  - lib/scss_lint/runner.rb
105
106
  - lib/scss_lint/reporter.rb
@@ -113,8 +114,8 @@ files:
113
114
  - lib/scss_lint/config.rb
114
115
  - lib/scss_lint/linter/compass.rb
115
116
  - lib/scss_lint/linter/space_after_comma.rb
117
+ - lib/scss_lint/linter/trailing_zero.rb
116
118
  - lib/scss_lint/linter/space_before_brace.rb
117
- - lib/scss_lint/linter/capitalization_in_selector.rb
118
119
  - lib/scss_lint/linter/hex_length.rb
119
120
  - lib/scss_lint/linter/indentation.rb
120
121
  - lib/scss_lint/linter/mergeable_selector.rb
@@ -128,6 +129,7 @@ files:
128
129
  - lib/scss_lint/linter/placeholder_in_extend.rb
129
130
  - lib/scss_lint/linter/selector_depth.rb
130
131
  - lib/scss_lint/linter/else_placement.rb
132
+ - lib/scss_lint/linter/selector_format.rb
131
133
  - lib/scss_lint/linter/hex_validation.rb
132
134
  - lib/scss_lint/linter/compass/property_with_mixin.rb
133
135
  - lib/scss_lint/linter/space_after_property_name.rb
@@ -157,6 +159,7 @@ files:
157
159
  - lib/scss_lint.rb
158
160
  - spec/scss_lint/location_spec.rb
159
161
  - spec/scss_lint/reporter/files_reporter_spec.rb
162
+ - spec/scss_lint/reporter/json_reporter_spec.rb
160
163
  - spec/scss_lint/reporter/xml_reporter_spec.rb
161
164
  - spec/scss_lint/reporter/config_reporter_spec.rb
162
165
  - spec/scss_lint/reporter/default_reporter_spec.rb
@@ -175,6 +178,7 @@ files:
175
178
  - spec/scss_lint/linter/hex_length_spec.rb
176
179
  - spec/scss_lint/linter/url_quotes_spec.rb
177
180
  - spec/scss_lint/linter/space_between_parens_spec.rb
181
+ - spec/scss_lint/linter/selector_format_spec.rb
178
182
  - spec/scss_lint/linter/border_zero_spec.rb
179
183
  - spec/scss_lint/linter/property_spelling_spec.rb
180
184
  - spec/scss_lint/linter/property_sort_order_spec.rb
@@ -183,7 +187,6 @@ files:
183
187
  - spec/scss_lint/linter/mergeable_selector_spec.rb
184
188
  - spec/scss_lint/linter/unnecessary_mantissa_spec.rb
185
189
  - spec/scss_lint/linter/name_format_spec.rb
186
- - spec/scss_lint/linter/capitalization_in_selector_spec.rb
187
190
  - spec/scss_lint/linter/duplicate_property_spec.rb
188
191
  - spec/scss_lint/linter/hex_validation_spec.rb
189
192
  - spec/scss_lint/linter/space_before_brace_spec.rb
@@ -198,6 +201,7 @@ files:
198
201
  - spec/scss_lint/linter/space_after_property_colon_spec.rb
199
202
  - spec/scss_lint/linter/declaration_order_spec.rb
200
203
  - spec/scss_lint/linter/leading_zero_spec.rb
204
+ - spec/scss_lint/linter/trailing_zero_spec.rb
201
205
  - spec/scss_lint/linter/url_format_spec.rb
202
206
  - spec/scss_lint/linter/comment_spec.rb
203
207
  - spec/scss_lint/linter/trailing_semicolon_spec.rb
@@ -237,6 +241,7 @@ summary: SCSS lint tool
237
241
  test_files:
238
242
  - spec/scss_lint/location_spec.rb
239
243
  - spec/scss_lint/reporter/files_reporter_spec.rb
244
+ - spec/scss_lint/reporter/json_reporter_spec.rb
240
245
  - spec/scss_lint/reporter/xml_reporter_spec.rb
241
246
  - spec/scss_lint/reporter/config_reporter_spec.rb
242
247
  - spec/scss_lint/reporter/default_reporter_spec.rb
@@ -255,6 +260,7 @@ test_files:
255
260
  - spec/scss_lint/linter/hex_length_spec.rb
256
261
  - spec/scss_lint/linter/url_quotes_spec.rb
257
262
  - spec/scss_lint/linter/space_between_parens_spec.rb
263
+ - spec/scss_lint/linter/selector_format_spec.rb
258
264
  - spec/scss_lint/linter/border_zero_spec.rb
259
265
  - spec/scss_lint/linter/property_spelling_spec.rb
260
266
  - spec/scss_lint/linter/property_sort_order_spec.rb
@@ -263,7 +269,6 @@ test_files:
263
269
  - spec/scss_lint/linter/mergeable_selector_spec.rb
264
270
  - spec/scss_lint/linter/unnecessary_mantissa_spec.rb
265
271
  - spec/scss_lint/linter/name_format_spec.rb
266
- - spec/scss_lint/linter/capitalization_in_selector_spec.rb
267
272
  - spec/scss_lint/linter/duplicate_property_spec.rb
268
273
  - spec/scss_lint/linter/hex_validation_spec.rb
269
274
  - spec/scss_lint/linter/space_before_brace_spec.rb
@@ -278,6 +283,7 @@ test_files:
278
283
  - spec/scss_lint/linter/space_after_property_colon_spec.rb
279
284
  - spec/scss_lint/linter/declaration_order_spec.rb
280
285
  - spec/scss_lint/linter/leading_zero_spec.rb
286
+ - spec/scss_lint/linter/trailing_zero_spec.rb
281
287
  - spec/scss_lint/linter/url_format_spec.rb
282
288
  - spec/scss_lint/linter/comment_spec.rb
283
289
  - spec/scss_lint/linter/trailing_semicolon_spec.rb
@@ -1,49 +0,0 @@
1
- module SCSSLint
2
- # Checks for capitalized letters in IDs, classes, types, etc. in selectors.
3
- class Linter::CapitalizationInSelector < Linter
4
- include LinterRegistry
5
-
6
- def visit_root(_node)
7
- @ignored_names = Array(config['ignored_names']).to_set
8
- @ignored_types = Array(config['ignored_types']).to_set
9
- yield
10
- end
11
-
12
- def visit_attribute(attribute)
13
- check(attribute) unless @ignored_types.include?('attribute')
14
- end
15
-
16
- def visit_class(klass)
17
- check(klass) unless @ignored_types.include?('class')
18
- end
19
-
20
- def visit_element(element)
21
- check(element) unless @ignored_types.include?('element')
22
- end
23
-
24
- def visit_id(id)
25
- check(id) unless @ignored_types.include?('id')
26
- end
27
-
28
- def visit_placeholder(placeholder)
29
- check(placeholder) unless @ignored_types.include?('placeholder')
30
- end
31
-
32
- def visit_pseudo(pseudo)
33
- check(pseudo, 'Pseudo-selector') unless @ignored_types.include?('pseudo-selector')
34
- end
35
-
36
- private
37
-
38
- def check(node, selector_name = nil)
39
- name = node.name
40
-
41
- return if @ignored_names.include?(name)
42
- return unless name =~ /[A-Z]/
43
-
44
- selector_name ||= node.class.name.split('::').last
45
- add_lint(node, "#{selector_name} `#{name}` in selector should be " \
46
- "written in all lowercase as `#{name.downcase}`")
47
- end
48
- end
49
- end
@@ -1,71 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe SCSSLint::Linter::CapitalizationInSelector do
4
- context 'when selector is all lowercase' do
5
- let(:css) { <<-CSS }
6
- span {
7
- }
8
- CSS
9
-
10
- it { should_not report_lint }
11
- end
12
-
13
- context 'when selector is lowercase with non-alphabetic characters' do
14
- let(:css) { <<-CSS }
15
- .foo-bar {
16
- }
17
- CSS
18
-
19
- it { should_not report_lint }
20
- end
21
-
22
- context 'when selector is camelCase' do
23
- let(:css) { <<-CSS }
24
- .fooBar {
25
- }
26
- CSS
27
-
28
- it { should report_lint line: 1 }
29
- end
30
-
31
- context 'when selector is UPPER CASE' do
32
- let(:css) { <<-CSS }
33
- SPAN {
34
- }
35
- CSS
36
-
37
- it { should report_lint line: 1 }
38
- end
39
-
40
- context 'when attribute selector has attribute containing uppercase letters' do
41
- let(:css) { <<-CSS }
42
- [dataText] {
43
- }
44
- CSS
45
-
46
- it { should report_lint line: 1 }
47
- end
48
-
49
- context 'when attribute selector has value containing uppercase letters' do
50
- let(:css) { <<-CSS }
51
- [data-text=someText] {
52
- }
53
- CSS
54
-
55
- it { should_not report_lint }
56
- end
57
-
58
- context 'when a selector name is whitelisted' do
59
- let(:linter_config) { { 'ignored_names' => %w[Foo] } }
60
- let(:css) { '.Foo {}' }
61
-
62
- it { should_not report_lint }
63
- end
64
-
65
- context 'when a certain type of selector is ignored' do
66
- let(:linter_config) { { 'ignored_types' => %w[class] } }
67
- let(:css) { '.Foo {}' }
68
-
69
- it { should_not report_lint }
70
- end
71
- end