scss-lint 0.28.0 → 0.29.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 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