scss-lint 0.29.0 → 0.30.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +31 -1
  3. data/data/prefixed-identifiers/base.txt +107 -0
  4. data/data/prefixed-identifiers/bourbon.txt +71 -0
  5. data/lib/scss_lint/cli.rb +34 -7
  6. data/lib/scss_lint/config.rb +12 -1
  7. data/lib/scss_lint/engine.rb +3 -1
  8. data/lib/scss_lint/linter/bang_format.rb +40 -0
  9. data/lib/scss_lint/linter/declaration_order.rb +35 -14
  10. data/lib/scss_lint/linter/import_path.rb +62 -0
  11. data/lib/scss_lint/linter/name_format.rb +1 -1
  12. data/lib/scss_lint/linter/nesting_depth.rb +24 -0
  13. data/lib/scss_lint/linter/property_sort_order.rb +4 -11
  14. data/lib/scss_lint/linter/property_spelling.rb +25 -8
  15. data/lib/scss_lint/linter/qualifying_element.rb +42 -0
  16. data/lib/scss_lint/linter/selector_format.rb +23 -11
  17. data/lib/scss_lint/linter/space_after_property_colon.rb +4 -4
  18. data/lib/scss_lint/linter/space_after_property_name.rb +16 -1
  19. data/lib/scss_lint/linter/space_before_brace.rb +36 -9
  20. data/lib/scss_lint/linter/trailing_semicolon.rb +6 -2
  21. data/lib/scss_lint/linter/vendor_prefixes.rb +64 -0
  22. data/lib/scss_lint/rake_task.rb +1 -0
  23. data/lib/scss_lint/runner.rb +2 -1
  24. data/lib/scss_lint/sass/script.rb +10 -0
  25. data/lib/scss_lint/version.rb +1 -1
  26. data/spec/scss_lint/cli_spec.rb +45 -2
  27. data/spec/scss_lint/linter/bang_format_spec.rb +79 -0
  28. data/spec/scss_lint/linter/declaration_order_spec.rb +466 -0
  29. data/spec/scss_lint/linter/import_path_spec.rb +300 -0
  30. data/spec/scss_lint/linter/nesting_depth_spec.rb +114 -0
  31. data/spec/scss_lint/linter/property_spelling_spec.rb +27 -0
  32. data/spec/scss_lint/linter/qualifying_element_spec.rb +125 -0
  33. data/spec/scss_lint/linter/selector_format_spec.rb +329 -0
  34. data/spec/scss_lint/linter/space_after_property_colon_spec.rb +14 -0
  35. data/spec/scss_lint/linter/space_after_property_name_spec.rb +14 -0
  36. data/spec/scss_lint/linter/space_before_brace_spec.rb +401 -17
  37. data/spec/scss_lint/linter/trailing_semicolon_spec.rb +47 -0
  38. data/spec/scss_lint/linter/vendor_prefixes_spec.rb +350 -0
  39. metadata +19 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fe908b2051342dce6151d04897242d18a3f9bf0b
4
- data.tar.gz: 785752360ba0475386ecc1c8f8d202268293f428
3
+ metadata.gz: 63daef837600174964f65634095dac45b13a1bb2
4
+ data.tar.gz: 8b8e1fdebc6d40d577c02a0092c092212f0d46ee
5
5
  SHA512:
6
- metadata.gz: cd5a9205186a67350c31d52d0cbd8677510fa5ae270ca0b25fc99dd87768c8b02393ebbbf2d068cc66f19ddf89ff7fb19b467d74a99ea3702fe7ff134352ef0a
7
- data.tar.gz: 8063dfc1815c2d3b4a3edb76540635282484e0e56a1ce7b71bf2a60ec493ad5b3948b17b4fae56db6e1df226aa6704802946530aaf6304bd3a5952875b6d1409
6
+ metadata.gz: 6ed4cbb25cacdfdf0645ea476ccc86032ae8032c22fdb1b3cf8fdd6934601bdc075aa4ed71fa1a05d2dec08785ceb591d53eeb2c6541b5567f434ff4ce161d20
7
+ data.tar.gz: 07a85bc0d214819857e179babe4b465193adf01391f7eecadd25611264c28605e0990911e1eb6dd75ed79b65b0cb50b6529187513e6842674e5c5abeafeca5f2
@@ -1,5 +1,13 @@
1
1
  # Default application configuration that all configurations inherit from.
2
+
3
+ scss_files: "**/*.scss"
4
+
2
5
  linters:
6
+ BangFormat:
7
+ enabled: true
8
+ space_before_bang: true
9
+ space_after_bang: false
10
+
3
11
  BorderZero:
4
12
  enabled: true
5
13
 
@@ -47,6 +55,11 @@ linters:
47
55
  IdWithExtraneousSelector:
48
56
  enabled: true
49
57
 
58
+ ImportPath:
59
+ enabled: true
60
+ leading_underscore: false
61
+ filename_extension: false
62
+
50
63
  Indentation:
51
64
  enabled: true
52
65
  character: space # or 'tab'
@@ -64,6 +77,10 @@ linters:
64
77
  enabled: true
65
78
  convention: hyphenated_lowercase # or 'BEM', or a regex pattern
66
79
 
80
+ NestingDepth:
81
+ enabled: true
82
+ max_depth: 3
83
+
67
84
  PlaceholderInExtend:
68
85
  enabled: true
69
86
 
@@ -75,13 +92,19 @@ linters:
75
92
  enabled: true
76
93
  extra_properties: []
77
94
 
95
+ QualifyingElement:
96
+ enabled: true
97
+ allow_with_attribute: false
98
+ allow_with_class: false
99
+ allow_with_id: false
100
+
78
101
  SelectorDepth:
79
102
  enabled: true
80
103
  max_depth: 3
81
104
 
82
105
  SelectorFormat:
83
106
  enabled: true
84
- convention: hyphenated_lowercase # or 'snake_case', or 'camel_case', or a regex pattern
107
+ convention: hyphenated_lowercase # or 'BEM', or 'snake_case', or 'camel_case', or a regex pattern
85
108
 
86
109
  Shorthand:
87
110
  enabled: true
@@ -105,6 +128,7 @@ linters:
105
128
 
106
129
  SpaceBeforeBrace:
107
130
  enabled: true
131
+ style: space
108
132
  allow_single_line_padding: false
109
133
 
110
134
  SpaceBetweenParens:
@@ -133,6 +157,12 @@ linters:
133
157
  UrlQuotes:
134
158
  enabled: true
135
159
 
160
+ VendorPrefixes:
161
+ enabled: true
162
+ identifier_list: base
163
+ include: []
164
+ exclude: []
165
+
136
166
  ZeroUnit:
137
167
  enabled: true
138
168
 
@@ -0,0 +1,107 @@
1
+ # Based on Autoprefixer --info
2
+
3
+ # At-Rules
4
+ keyframes
5
+
6
+ # Selectors
7
+ selection
8
+ placeholder
9
+ fullscreen
10
+
11
+ # Properties
12
+ transition
13
+ transition-property
14
+ border-radius
15
+ border-top-left-radius
16
+ border-top-right-radius
17
+ border-bottom-right-radius
18
+ border-bottom-left-radius
19
+ box-shadow
20
+ animation
21
+ animation-name
22
+ animation-duration
23
+ animation-delay
24
+ animation-direction
25
+ animation-fill-mode
26
+ animation-iteration-count
27
+ animation-play-state
28
+ animation-timing-function
29
+ transition-duration
30
+ transition-delay
31
+ transition-timing-function
32
+ transform
33
+ transform-origin
34
+ perspective
35
+ perspective-origin
36
+ transform-style
37
+ backface-visibility
38
+ border-image
39
+ box-sizing
40
+ filter
41
+ columns
42
+ column-width
43
+ column-gap
44
+ column-rule
45
+ column-rule-color
46
+ column-rule-width
47
+ column-count
48
+ column-rule-style
49
+ column-span
50
+ column-fill
51
+ break-before
52
+ break-after
53
+ break-inside
54
+ user-select
55
+ flex
56
+ flex-grow
57
+ flex-shrink
58
+ flex-basis
59
+ flex-direction
60
+ flex-wrap
61
+ flex-flow
62
+ justify-content
63
+ order
64
+ align-items
65
+ align-self
66
+ align-content
67
+ background-clip
68
+ background-origin
69
+ background-size
70
+ font-feature-settings
71
+ font-variant-ligatures
72
+ font-language-override
73
+ font-kerning
74
+ hyphens
75
+ tab-size
76
+ touch-action
77
+ text-decoration-style
78
+ text-decoration-line
79
+ text-decoration-color
80
+ text-size-adjust
81
+ clip-path
82
+ mask
83
+ mask-clip
84
+ mask-composite
85
+ mask-image
86
+ mask-origin
87
+ mask-position
88
+ mask-repeat
89
+ mask-size
90
+
91
+ # Values
92
+ linear-gradient
93
+ repeating-linear-gradient
94
+ radial-gradient
95
+ repeating-radial-gradient
96
+ flex
97
+ inline-flex
98
+ calc
99
+ max-content
100
+ min-content
101
+ fit-content
102
+ fill-available
103
+ zoom-in
104
+ zoom-out
105
+ grab
106
+ grabbing
107
+ sticky
@@ -0,0 +1,71 @@
1
+ # Identifiers covered by Bourbon mixins
2
+
3
+ # At-Rules
4
+ keyframes
5
+
6
+ # Selectors
7
+ placeholder
8
+
9
+ # Properties
10
+ animation
11
+ animation-delay
12
+ animation-direction
13
+ animation-duration
14
+ animation-fill-mode
15
+ animation-iteration-count
16
+ animation-name
17
+ animation-play-state
18
+ animation-timing-function
19
+ appearance
20
+ backface-visibility
21
+ background
22
+ border-image
23
+ border-top-left-radius
24
+ border-top-right-radius
25
+ border-bottom-right-radius
26
+ border-bottom-left-radius
27
+ box-sizing
28
+ calc
29
+ columns
30
+ column-width
31
+ column-gap
32
+ column-rule
33
+ column-rule-color
34
+ column-rule-width
35
+ column-count
36
+ column-rule-style
37
+ column-span
38
+ column-fill
39
+ filter
40
+ flex
41
+ flex-grow
42
+ flex-shrink
43
+ flex-basis
44
+ flex-direction
45
+ flex-wrap
46
+ flex-flow
47
+ justify-content
48
+ order
49
+ align-items
50
+ align-self
51
+ align-content
52
+ font-feature-settings
53
+ hyphens
54
+ perspective
55
+ perspective-origin
56
+ transform
57
+ transform-origin
58
+ transform-style
59
+ transition
60
+ transition-property
61
+ transition-duration
62
+ transition-delay
63
+ transition-timing-function
64
+ user-select
65
+
66
+
67
+ # Values
68
+ crisp-edges
69
+ optimize-contrast
70
+ linear-gradient
71
+ radial-gradient
@@ -20,10 +20,12 @@ module SCSSLint
20
20
  config: 78, # Configuration error
21
21
  }
22
22
 
23
+ DEFAULT_REPORTER = [SCSSLint::Reporter::DefaultReporter, :stdout]
24
+
23
25
  # @param args [Array]
24
26
  def initialize(args = [])
25
27
  @args = args
26
- @options = {}
28
+ @options = { reporters: [DEFAULT_REPORTER] }
27
29
  @config = Config.default
28
30
  end
29
31
 
@@ -32,7 +34,13 @@ module SCSSLint
32
34
  options_parser.parse!(@args)
33
35
 
34
36
  # Take the rest of the arguments as files/directories
35
- @options[:files] = @args
37
+
38
+ if @args.empty?
39
+ @options[:files] = @config.scss_files
40
+ else
41
+ @options[:files] = @args
42
+ end
43
+
36
44
  rescue OptionParser::InvalidOption => ex
37
45
  print_help options_parser.help, ex
38
46
  end
@@ -66,6 +74,14 @@ module SCSSLint
66
74
  define_output_format(format)
67
75
  end
68
76
 
77
+ opts.on('-o', '--out path', 'Write output to a file instead of STDOUT', String) do |path|
78
+ define_output_path(path)
79
+ end
80
+
81
+ opts.on('-r', '--require path', 'Require Ruby file', String) do |path|
82
+ require path
83
+ end
84
+
69
85
  opts.on_tail('--show-formatters', 'Shows available formatters') do
70
86
  print_formatters
71
87
  end
@@ -192,20 +208,31 @@ module SCSSLint
192
208
  # @param lints [Array<Lint>]
193
209
  def report_lints(lints)
194
210
  sorted_lints = lints.sort_by { |l| [l.filename, l.location] }
195
- reporter = @options.fetch(:reporter, SCSSLint::Reporter::DefaultReporter)
196
- .new(sorted_lints)
197
- output = reporter.report_lints
198
- print output if output
211
+ @options.fetch(:reporters).each do |reporter, output|
212
+ results = reporter.new(sorted_lints).report_lints
213
+ io = (output == :stdout ? $stdout : File.new(output, 'w+'))
214
+ io.print results if results
215
+ end
199
216
  end
200
217
 
201
218
  # @param format [String]
202
219
  def define_output_format(format)
203
- @options[:reporter] = SCSSLint::Reporter.const_get(format + 'Reporter')
220
+ unless @options[:reporters] == [DEFAULT_REPORTER] && format == 'Default'
221
+ @options[:reporters].reject! { |i| i == DEFAULT_REPORTER }
222
+ reporter = SCSSLint::Reporter.const_get(format + 'Reporter')
223
+ @options[:reporters] << [reporter, :stdout]
224
+ end
204
225
  rescue NameError
205
226
  puts "Invalid output format specified: #{format}"
206
227
  halt :config
207
228
  end
208
229
 
230
+ # @param path [String]
231
+ def define_output_path(path)
232
+ last_reporter, _output = @options[:reporters].pop
233
+ @options[:reporters] << [last_reporter, path]
234
+ end
235
+
209
236
  def print_formatters
210
237
  puts 'Installed formatters:'
211
238
 
@@ -175,7 +175,9 @@ module SCSSLint
175
175
  if relative_include_path.start_with?('/')
176
176
  relative_include_path
177
177
  else
178
- File.join(File.dirname(base_config_path), relative_include_path)
178
+ path = File.join(File.dirname(base_config_path), relative_include_path)
179
+ # Remove double backslashes appearing in Windows paths.
180
+ path.gsub(%r{^//}, File::SEPARATOR)
179
181
  end
180
182
  end
181
183
 
@@ -262,6 +264,15 @@ module SCSSLint
262
264
  @options['exclude'] << abs_path
263
265
  end
264
266
 
267
+ # @return Array
268
+ def scss_files
269
+ if path = @options['scss_files']
270
+ Dir[path]
271
+ else
272
+ []
273
+ end
274
+ end
275
+
265
276
  private
266
277
 
267
278
  def validate_linters
@@ -20,7 +20,9 @@ module SCSSLint
20
20
  @contents = scss_or_filename
21
21
  end
22
22
 
23
- @lines = @contents.lines.to_a # Need `to_a` for Ruby 1.9.3
23
+ # Need to force encoding to avoid Windows-related bugs.
24
+ # Need `to_a` for Ruby 1.9.3.
25
+ @lines = @contents.force_encoding('UTF-8').lines.to_a
24
26
  @tree = @engine.to_tree
25
27
  rescue Encoding::UndefinedConversionError, Sass::SyntaxError => error
26
28
  if error.is_a?(Encoding::UndefinedConversionError) ||
@@ -0,0 +1,40 @@
1
+ module SCSSLint
2
+ # Checks spacing of ! declarations, like !important and !default
3
+ class Linter::BangFormat < Linter
4
+ include LinterRegistry
5
+
6
+ def visit_prop(node)
7
+ return unless node.to_sass.include?('!')
8
+ return unless check_spacing(node)
9
+
10
+ before_qualifier = config['space_before_bang'] ? '' : 'not '
11
+ after_qualifier = config['space_after_bang'] ? '' : 'not '
12
+
13
+ add_lint(node, "! should #{before_qualifier}be preceeded by a space, " \
14
+ "and should #{after_qualifier}be followed by a space")
15
+ end
16
+
17
+ private
18
+
19
+ def find_bang_offset(range)
20
+ offset = 0
21
+ offset += 1 while character_at(range.start_pos, offset) != '!'
22
+ offset
23
+ end
24
+
25
+ def check_spacing(node)
26
+ range = node.value_source_range
27
+ offset = find_bang_offset(range)
28
+
29
+ before_expected = config['space_before_bang'] ? / / : /[^ ]/
30
+ before_actual = character_at(range.start_pos, offset - 1)
31
+ before_is_wrong = (before_actual =~ before_expected).nil?
32
+
33
+ after_expected = config['space_after_bang'] ? / / : /[^ ]/
34
+ after_actual = character_at(range.start_pos, offset + 1)
35
+ after_is_wrong = (after_actual =~ after_expected).nil?
36
+
37
+ before_is_wrong || after_is_wrong
38
+ end
39
+ end
40
+ end
@@ -3,35 +3,56 @@ module SCSSLint
3
3
  class Linter::DeclarationOrder < Linter
4
4
  include LinterRegistry
5
5
 
6
+ def check_order(node)
7
+ check_node(node)
8
+ yield # Continue linting children
9
+ end
10
+
11
+ alias_method :visit_rule, :check_order
12
+ alias_method :visit_mixin, :check_order
13
+
14
+ private
15
+
16
+ MESSAGE =
17
+ 'Rule sets should be ordered as follows: '\
18
+ '@extends, @includes without @content, ' \
19
+ 'properties, @includes with @content, ' \
20
+ 'nested rule sets'
21
+
22
+ MIXIN_WITH_CONTENT = 'mixin_with_content'
23
+
6
24
  DECLARATION_ORDER = [
7
25
  Sass::Tree::ExtendNode,
26
+ Sass::Tree::MixinNode,
8
27
  Sass::Tree::PropNode,
28
+ MIXIN_WITH_CONTENT,
9
29
  Sass::Tree::RuleNode,
10
30
  ]
11
31
 
12
- def visit_rule(node)
32
+ def important_node?(node)
33
+ DECLARATION_ORDER.include?(node.class)
34
+ end
35
+
36
+ def check_node(node)
13
37
  children = node.children.select { |n| important_node?(n) }
14
- .map(&:class)
38
+ .map { |n| node_declaration_type(n) }
15
39
 
16
40
  sorted_children = children.sort do |a, b|
17
41
  DECLARATION_ORDER.index(a) <=> DECLARATION_ORDER.index(b)
18
42
  end
19
43
 
20
- if children != sorted_children
21
- add_lint(node.children.first, MESSAGE)
22
- end
23
-
24
- yield # Continue linting children
44
+ return unless children != sorted_children
45
+ add_lint(node.children.first, MESSAGE)
25
46
  end
26
47
 
27
- private
28
-
29
- MESSAGE =
30
- 'Rule sets should start with @extend declarations, followed by ' \
31
- 'properties and nested rule sets, in that order'
48
+ def node_declaration_type(node)
49
+ # If the node has no children, return the class.
50
+ return node.class unless node.has_children
32
51
 
33
- def important_node?(node)
34
- DECLARATION_ORDER.include? node.class
52
+ # If the node is a mixin with children, indicate that;
53
+ # otherwise, just return the class.
54
+ return node.class unless node.is_a?(Sass::Tree::MixinNode)
55
+ MIXIN_WITH_CONTENT
35
56
  end
36
57
  end
37
58
  end