scss-lint 0.33.0 → 0.34.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +19 -1
  3. data/data/properties.txt +4 -0
  4. data/lib/scss_lint.rb +1 -0
  5. data/lib/scss_lint/cli.rb +4 -42
  6. data/lib/scss_lint/config.rb +1 -45
  7. data/lib/scss_lint/control_comment_processor.rb +47 -15
  8. data/lib/scss_lint/file_finder.rb +57 -0
  9. data/lib/scss_lint/linter/bang_format.rb +1 -1
  10. data/lib/scss_lint/linter/border_zero.rb +25 -9
  11. data/lib/scss_lint/linter/color_keyword.rb +3 -13
  12. data/lib/scss_lint/linter/color_variable.rb +36 -0
  13. data/lib/scss_lint/linter/declaration_order.rb +2 -2
  14. data/lib/scss_lint/linter/important_rule.rb +12 -0
  15. data/lib/scss_lint/linter/indentation.rb +7 -1
  16. data/lib/scss_lint/linter/property_count.rb +44 -0
  17. data/lib/scss_lint/linter/property_sort_order.rb +73 -19
  18. data/lib/scss_lint/linter/string_quotes.rb +9 -0
  19. data/lib/scss_lint/linter/variable_for_property.rb +20 -0
  20. data/lib/scss_lint/linter/vendor_prefixes.rb +3 -3
  21. data/lib/scss_lint/runner.rb +5 -7
  22. data/lib/scss_lint/utils.rb +34 -0
  23. data/lib/scss_lint/version.rb +1 -1
  24. data/spec/scss_lint/cli_spec.rb +1 -1
  25. data/spec/scss_lint/config_spec.rb +4 -203
  26. data/spec/scss_lint/engine_spec.rb +4 -4
  27. data/spec/scss_lint/file_finder_spec.rb +110 -0
  28. data/spec/scss_lint/linter/bang_format_spec.rb +28 -18
  29. data/spec/scss_lint/linter/border_zero_spec.rb +50 -16
  30. data/spec/scss_lint/linter/color_keyword_spec.rb +16 -16
  31. data/spec/scss_lint/linter/color_variable_spec.rb +102 -0
  32. data/spec/scss_lint/linter/comment_spec.rb +9 -9
  33. data/spec/scss_lint/linter/compass/property_with_mixin_spec.rb +10 -10
  34. data/spec/scss_lint/linter/debug_statement_spec.rb +4 -4
  35. data/spec/scss_lint/linter/declaration_order_spec.rb +80 -80
  36. data/spec/scss_lint/linter/duplicate_property_spec.rb +30 -30
  37. data/spec/scss_lint/linter/else_placement_spec.rb +16 -16
  38. data/spec/scss_lint/linter/empty_line_between_blocks_spec.rb +38 -38
  39. data/spec/scss_lint/linter/empty_rule_spec.rb +4 -4
  40. data/spec/scss_lint/linter/final_newline_spec.rb +6 -6
  41. data/spec/scss_lint/linter/hex_length_spec.rb +16 -16
  42. data/spec/scss_lint/linter/hex_notation_spec.rb +16 -16
  43. data/spec/scss_lint/linter/hex_validation_spec.rb +6 -6
  44. data/spec/scss_lint/linter/id_selector_spec.rb +10 -10
  45. data/spec/scss_lint/linter/import_path_spec.rb +45 -45
  46. data/spec/scss_lint/linter/important_rule_spec.rb +43 -0
  47. data/spec/scss_lint/linter/indentation_spec.rb +103 -43
  48. data/spec/scss_lint/linter/leading_zero_spec.rb +45 -45
  49. data/spec/scss_lint/linter/mergeable_selector_spec.rb +32 -32
  50. data/spec/scss_lint/linter/name_format_spec.rb +75 -41
  51. data/spec/scss_lint/linter/nesting_depth_spec.rb +14 -14
  52. data/spec/scss_lint/linter/placeholder_in_extend_spec.rb +12 -12
  53. data/spec/scss_lint/linter/property_count_spec.rb +104 -0
  54. data/spec/scss_lint/linter/property_sort_order_spec.rb +138 -48
  55. data/spec/scss_lint/linter/property_spelling_spec.rb +14 -14
  56. data/spec/scss_lint/linter/qualifying_element_spec.rb +26 -26
  57. data/spec/scss_lint/linter/selector_depth_spec.rb +26 -26
  58. data/spec/scss_lint/linter/selector_format_spec.rb +114 -114
  59. data/spec/scss_lint/linter/shorthand_spec.rb +32 -32
  60. data/spec/scss_lint/linter/single_line_per_property_spec.rb +10 -10
  61. data/spec/scss_lint/linter/single_line_per_selector_spec.rb +24 -24
  62. data/spec/scss_lint/linter/space_after_comma_spec.rb +60 -60
  63. data/spec/scss_lint/linter/space_after_property_colon_spec.rb +44 -44
  64. data/spec/scss_lint/linter/space_after_property_name_spec.rb +6 -6
  65. data/spec/scss_lint/linter/space_before_brace_spec.rb +119 -119
  66. data/spec/scss_lint/linter/space_between_parens_spec.rb +48 -48
  67. data/spec/scss_lint/linter/string_quotes_spec.rb +74 -62
  68. data/spec/scss_lint/linter/trailing_semicolon_spec.rb +53 -54
  69. data/spec/scss_lint/linter/trailing_zero_spec.rb +34 -34
  70. data/spec/scss_lint/linter/unnecessary_mantissa_spec.rb +10 -10
  71. data/spec/scss_lint/linter/unnecessary_parent_reference_spec.rb +18 -18
  72. data/spec/scss_lint/linter/url_format_spec.rb +4 -4
  73. data/spec/scss_lint/linter/url_quotes_spec.rb +14 -14
  74. data/spec/scss_lint/linter/variable_for_property_spec.rb +115 -0
  75. data/spec/scss_lint/linter/vendor_prefixes_spec.rb +66 -56
  76. data/spec/scss_lint/linter/zero_unit_spec.rb +22 -22
  77. data/spec/scss_lint/linter_spec.rb +72 -28
  78. data/spec/scss_lint/runner_spec.rb +0 -1
  79. data/spec/scss_lint/selector_visitor_spec.rb +23 -23
  80. data/spec/spec_helper.rb +2 -2
  81. metadata +27 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e1109af5e1f6cd4016185ea4d079a4188774f2d3
4
- data.tar.gz: 41389f3cfc6b63b002101e8608b4b2f5eda48ec6
3
+ metadata.gz: d9d896111a17640c23d31de062d34329fd52a56a
4
+ data.tar.gz: 6f34a07866cd4b7c77a18137f2bec2570e9f31e5
5
5
  SHA512:
6
- metadata.gz: 9120548dc3302ae1c7922e4b46166d6fa9238f4bf755824abad0176dec920d4722c0b4b67042258c7f73ed99903785a4f8bde6e6471809093c74cf0a7f0fbfbb
7
- data.tar.gz: 046761b2017cbb1f333e446cae0fda6fb52a5fd8055764e91797b3eccb9eddcf862a18597c7a24ebe5dcd2e6f76d40397b6144298bbcd78d5a4574c88fe774d3
6
+ metadata.gz: d100dfddcde8591cbff342d88bc37f685ac337eb32b9193b87f09c46cf50dc61c04717a4e67fe843f6135fb186f8c5e43b7c6711680d67e766e56e71bb04ff94
7
+ data.tar.gz: 65e67618d2e00e428a38adb5243236dea6464f17df75914937da60afbf3eded4f3b0e7be4d526b4d05c264c9a0ef5f5ed1cc13cbab3dac4a5cde3673f4e991d0
@@ -10,10 +10,14 @@ linters:
10
10
 
11
11
  BorderZero:
12
12
  enabled: true
13
+ convention: zero # or `none`
13
14
 
14
15
  ColorKeyword:
15
16
  enabled: true
16
17
 
18
+ ColorVariable:
19
+ enabled: true
20
+
17
21
  Comment:
18
22
  enabled: true
19
23
 
@@ -55,6 +59,9 @@ linters:
55
59
  IdSelector:
56
60
  enabled: true
57
61
 
62
+ ImportantRule:
63
+ enabled: true
64
+
58
65
  ImportPath:
59
66
  enabled: true
60
67
  leading_underscore: false
@@ -62,6 +69,7 @@ linters:
62
69
 
63
70
  Indentation:
64
71
  enabled: true
72
+ allow_non_nested_indentation: false
65
73
  character: space # or 'tab'
66
74
  width: 2
67
75
 
@@ -85,9 +93,15 @@ linters:
85
93
  PlaceholderInExtend:
86
94
  enabled: true
87
95
 
96
+ PropertyCount:
97
+ enabled: false
98
+ include_nested: false
99
+ max_properties: 10
100
+
88
101
  PropertySortOrder:
89
102
  enabled: true
90
103
  ignore_unspecified: false
104
+ separate_groups: false
91
105
 
92
106
  PropertySpelling:
93
107
  enabled: true
@@ -129,7 +143,7 @@ linters:
129
143
 
130
144
  SpaceBeforeBrace:
131
145
  enabled: true
132
- style: space
146
+ style: space # or 'new_line'
133
147
  allow_single_line_padding: false
134
148
 
135
149
  SpaceBetweenParens:
@@ -158,6 +172,10 @@ linters:
158
172
  UrlQuotes:
159
173
  enabled: true
160
174
 
175
+ VariableForProperty:
176
+ enabled: false
177
+ properties: []
178
+
161
179
  VendorPrefixes:
162
180
  enabled: true
163
181
  identifier_list: base
@@ -102,6 +102,9 @@ box-pack
102
102
  box-reflect
103
103
  box-shadow
104
104
  box-sizing
105
+ break-after
106
+ break-before
107
+ break-inside
105
108
  buffered-rendering
106
109
  caption-side
107
110
  clear
@@ -418,6 +421,7 @@ text-overline-style
418
421
  text-overline-width
419
422
  text-rendering
420
423
  text-security
424
+ text-size-adjust
421
425
  text-shadow
422
426
  text-stroke
423
427
  text-stroke-color
@@ -5,6 +5,7 @@ require 'scss_lint/engine'
5
5
  require 'scss_lint/location'
6
6
  require 'scss_lint/lint'
7
7
  require 'scss_lint/linter_registry'
8
+ require 'scss_lint/file_finder'
8
9
  require 'scss_lint/runner'
9
10
  require 'scss_lint/selector_visitor'
10
11
  require 'scss_lint/control_comment_processor'
@@ -1,4 +1,3 @@
1
- require 'find'
2
1
  require 'rainbow'
3
2
  require 'rainbow/ext/string'
4
3
  require 'scss_lint/options'
@@ -49,7 +48,7 @@ module SCSSLint
49
48
 
50
49
  def scan_for_lints(options, config)
51
50
  runner = Runner.new(config)
52
- runner.run(files_to_lint(options, config))
51
+ runner.run(FileFinder.new(config).find(options[:files]))
53
52
  report_lints(options, runner.lints)
54
53
 
55
54
  if runner.lints.any?(&:error?)
@@ -87,9 +86,9 @@ module SCSSLint
87
86
  def setup_configuration(options)
88
87
  config =
89
88
  if options[:config_file]
90
- Config.load(options[:config_file]).tap do |conf|
91
- conf.preferred = true
92
- end
89
+ Config.load(options[:config_file])
90
+ elsif File.exist?(Config::FILE_NAME)
91
+ Config.load(Config::FILE_NAME)
93
92
  else
94
93
  Config.default
95
94
  end
@@ -123,43 +122,6 @@ module SCSSLint
123
122
  config
124
123
  end
125
124
 
126
- def files_to_lint(options, config)
127
- if options[:files].empty?
128
- options[:files] = config.scss_files
129
- end
130
-
131
- extract_files_from(options[:files]).reject do |file|
132
- actual_config =
133
- if !config.preferred && (config_for_file = Config.for_file(file))
134
- merge_options_with_config(options, config_for_file.dup)
135
- else
136
- config
137
- end
138
-
139
- actual_config.excluded_file?(file)
140
- end
141
- end
142
-
143
- # @param list [Array]
144
- def extract_files_from(list)
145
- files = []
146
- list.each do |file|
147
- Find.find(file) do |f|
148
- files << f if scssish_file?(f)
149
- end
150
- end
151
- files.uniq
152
- end
153
-
154
- VALID_EXTENSIONS = %w[.css .scss]
155
- # @param file [String]
156
- # @return [Boolean]
157
- def scssish_file?(file)
158
- return false unless FileTest.file?(file)
159
-
160
- VALID_EXTENSIONS.include?(File.extname(file))
161
- end
162
-
163
125
  # @param options [Hash]
164
126
  # @param lints [Array<Lint>]
165
127
  def report_lints(options, lints)
@@ -1,4 +1,3 @@
1
- require 'pathname'
2
1
  require 'yaml'
3
2
 
4
3
  module SCSSLint
@@ -7,7 +6,6 @@ module SCSSLint
7
6
  FILE_NAME = '.scss-lint.yml'
8
7
  DEFAULT_FILE = File.join(SCSS_LINT_HOME, 'config', 'default.yml')
9
8
 
10
- attr_accessor :preferred # If this config should be preferred over others
11
9
  attr_reader :options, :warnings
12
10
 
13
11
  class << self
@@ -27,17 +25,6 @@ module SCSSLint
27
25
  Config.new(config_options)
28
26
  end
29
27
 
30
- # Loads the configuration for a given file.
31
- def for_file(file_path)
32
- directory = File.dirname(File.expand_path(file_path))
33
- @dir_to_config ||= {}
34
- @dir_to_config[directory] ||=
35
- begin
36
- config_file = possible_config_files(directory).find(&:file?)
37
- Config.load(config_file.to_s) if config_file
38
- end
39
- end
40
-
41
28
  def linter_name(linter)
42
29
  linter = linter.is_a?(Class) ? linter : linter.class
43
30
  linter.name.split('::')[2..-1].join('::')
@@ -45,13 +32,6 @@ module SCSSLint
45
32
 
46
33
  private
47
34
 
48
- def possible_config_files(directory)
49
- files = Pathname.new(directory)
50
- .enum_for(:ascend)
51
- .map { |path| path + FILE_NAME }
52
- files << Pathname.new(FILE_NAME)
53
- end
54
-
55
35
  def default_options_hash
56
36
  @default_options_hash ||= load_options_hash_from_file(DEFAULT_FILE)
57
37
  end
@@ -74,7 +54,6 @@ module SCSSLint
74
54
  end
75
55
 
76
56
  options = convert_single_options_to_arrays(options)
77
- options = extend_inherited_configs(options, file)
78
57
  options = merge_wildcard_linter_options(options)
79
58
  options = ensure_exclude_paths_are_absolute(options, file)
80
59
  options = ensure_linter_exclude_paths_are_absolute(options, file)
@@ -88,31 +67,13 @@ module SCSSLint
88
67
 
89
68
  if options['exclude']
90
69
  # Ensure exclude is an array, since we allow user to specify a single
91
- # string. We do this before merging with the config loaded via
92
- # inherit_from since this allows us to merge the excludes from that,
93
- # rather than overwriting them.
70
+ # string.
94
71
  options['exclude'] = [options['exclude']].flatten
95
72
  end
96
73
 
97
74
  options
98
75
  end
99
76
 
100
- # Loads and extends a list of inherited options with the given options.
101
- def extend_inherited_configs(options, original_file)
102
- return options unless options['inherit_from']
103
- options = options.dup
104
-
105
- includes = [options.delete('inherit_from')].flatten.map do |include_file|
106
- load_options_hash_from_file(path_relative_to_config(include_file, original_file))
107
- end
108
-
109
- merged_includes = includes[1..-1].inject(includes.first) do |merged, include_file|
110
- smart_merge(merged, include_file)
111
- end
112
-
113
- smart_merge(merged_includes, options)
114
- end
115
-
116
77
  # Merge options from wildcard linters into individual linter configs
117
78
  def merge_wildcard_linter_options(options)
118
79
  options = options.dup
@@ -211,11 +172,6 @@ module SCSSLint
211
172
  validate_linters
212
173
  end
213
174
 
214
- def ==(other)
215
- super || @options == other.options
216
- end
217
- alias_method :eql?, :==
218
-
219
175
  def enabled_linters
220
176
  LinterRegistry.extract_linters_from(@options['linters'].keys).select do |linter|
221
177
  linter_options(linter)['enabled']
@@ -20,25 +20,18 @@ module SCSSLint
20
20
  #
21
21
  # @param node [Sass::Tree::Node]
22
22
  def before_node_visit(node)
23
- return unless node.is_a?(Sass::Tree::CommentNode)
23
+ return unless command = extract_command(node)
24
24
 
25
- return unless match = /(?x)
26
- \*\s* # Comment line start marker
27
- scss-lint:
28
- (?<command>disable|enable)\s+
29
- (?<linters>.*?)
30
- \s*(?:\*\/|\n) # Comment end marker or end of line
31
- /.match(node.value.first)
32
-
33
- linters = match[:linters].split(/\s*,\s*|\s+/)
25
+ linters = command[:linters]
34
26
  return unless linters.include?('all') || linters.include?(@linter.name)
35
27
 
36
- process_command(match[:command], node)
28
+ process_command(command[:action], node)
37
29
 
38
30
  # Is the control comment the only thing on this line?
39
- return if %r{^\s*(//|/\*)}.match(@linter.engine.lines[node.line - 1])
31
+ return if node.is_a?(Sass::Tree::RuleNode) ||
32
+ %r{^\s*(//|/\*)}.match(@linter.engine.lines[node.line - 1])
40
33
 
41
- # If so, pop since we only want the comment to apply to the single line
34
+ # Otherwise, pop since we only want comment to apply to the single line
42
35
  pop_control_comment_stack(node)
43
36
  end
44
37
 
@@ -53,6 +46,29 @@ module SCSSLint
53
46
 
54
47
  private
55
48
 
49
+ def extract_command(node)
50
+ comment =
51
+ case node
52
+ when Sass::Tree::CommentNode
53
+ node.value.first
54
+ when Sass::Tree::RuleNode
55
+ node.rule.select { |chunk| chunk.is_a?(String) }.join
56
+ end
57
+
58
+ return unless match = %r{
59
+ (/|\*)\s* # Comment start marker
60
+ scss-lint:
61
+ (?<action>disable|enable)\s+
62
+ (?<linters>.*?)
63
+ \s*(?:\*/|\n) # Comment end marker or end of line
64
+ }x.match(comment)
65
+
66
+ {
67
+ action: match[:action],
68
+ linters: match[:linters].split(/\s*,\s*|\s+/),
69
+ }
70
+ end
71
+
56
72
  def process_command(command, node)
57
73
  case command
58
74
  when 'disable'
@@ -71,13 +87,29 @@ module SCSSLint
71
87
  # apply (if it is a control comment enable node, it will be the line of
72
88
  # the comment itself).
73
89
  child = node
74
- while child.children.last.is_a?(Sass::Tree::Node)
75
- child = child.children.last
90
+ prev_child = node
91
+ while (child = last_child(child)) != prev_child
92
+ prev_child = child
76
93
  end
77
94
 
78
95
  end_line = child.line
79
96
 
80
97
  @disabled_lines.merge(start_line..end_line)
81
98
  end
99
+
100
+ # Gets the child of the node that resides on the lowest line in the file.
101
+ #
102
+ # This is necessary due to the fact that our monkey patching of the parse
103
+ # tree's {#children} method does not return nodes sorted by their line
104
+ # number.
105
+ #
106
+ # @param node [Sass::Tree::Node, Sass::Script::Tree::Node]
107
+ # @return [Sass::Tree::Node, Sass::Script::Tree::Node]
108
+ def last_child(node)
109
+ node.children.inject(node) do |lowest, child|
110
+ return lowest unless child.respond_to?(:line)
111
+ lowest.line < child.line ? child : lowest
112
+ end
113
+ end
82
114
  end
83
115
  end
@@ -0,0 +1,57 @@
1
+ require 'find'
2
+
3
+ module SCSSLint
4
+ # Finds all SCSS files that should be linted given a set of paths, globs, and
5
+ # configuration.
6
+ class FileFinder
7
+ # List of extensions of files to include when only a directory is specified
8
+ # as a path.
9
+ VALID_EXTENSIONS = %w[.css .scss]
10
+
11
+ # Create a {FileFinder}.
12
+ #
13
+ # @param config [SCSSLint::Config]
14
+ def initialize(config)
15
+ @config = config
16
+ end
17
+
18
+ # Find all files that match given the specified options.
19
+ #
20
+ # @param patterns [Array<String>] a list of file paths and glob patterns
21
+ def find(patterns)
22
+ # If no explicit patterns given, use patterns listed in config
23
+ patterns = @config.scss_files if patterns.empty?
24
+
25
+ extract_files_from(patterns).reject do |file|
26
+ @config.excluded_file?(file)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ # @param list [Array]
33
+ def extract_files_from(list)
34
+ files = []
35
+
36
+ list.each do |file|
37
+ if File.directory?(file)
38
+ Find.find(file) do |f|
39
+ files << f if scssish_file?(f)
40
+ end
41
+ else
42
+ files << file # Otherwise include file as-is
43
+ end
44
+ end
45
+
46
+ files.uniq
47
+ end
48
+
49
+ # @param file [String]
50
+ # @return [true,false]
51
+ def scssish_file?(file)
52
+ return false unless FileTest.file?(file)
53
+
54
+ VALID_EXTENSIONS.include?(File.extname(file))
55
+ end
56
+ end
57
+ end
@@ -6,7 +6,7 @@ module SCSSLint
6
6
  STOPPING_CHARACTERS = ['!', "'", '"', nil]
7
7
 
8
8
  def visit_prop(node)
9
- return unless node.to_sass.include?('!')
9
+ return unless source_from_range(node.source_range).include?('!')
10
10
  return unless check_spacing(node)
11
11
 
12
12
  before_qualifier = config['space_before_bang'] ? '' : 'not '
@@ -1,16 +1,12 @@
1
1
  module SCSSLint
2
- # Reports when `border: 0` can be used instead of `border: none`.
2
+ # Enforce a particular value for empty borders.
3
3
  class Linter::BorderZero < Linter
4
4
  include LinterRegistry
5
5
 
6
- def visit_prop(node)
7
- return unless BORDER_PROPERTIES.include?(node.name.first.to_s)
8
- return unless node.value.to_sass.strip == 'none'
9
-
10
- add_lint(node, '`border: 0;` is preferred over `border: none;`')
11
- end
12
-
13
- private
6
+ CONVENTION_TO_PREFERENCE = {
7
+ 'zero' => %w[0 none],
8
+ 'none' => %w[none 0],
9
+ }
14
10
 
15
11
  BORDER_PROPERTIES = %w[
16
12
  border
@@ -19,5 +15,25 @@ module SCSSLint
19
15
  border-bottom
20
16
  border-left
21
17
  ]
18
+
19
+ def visit_root(_node)
20
+ @preference = CONVENTION_TO_PREFERENCE[config['convention']]
21
+ yield # Continue linting children
22
+ end
23
+
24
+ def visit_prop(node)
25
+ return unless BORDER_PROPERTIES.include?(node.name.first.to_s)
26
+ check_border(node, node.value.to_sass.strip)
27
+ end
28
+
29
+ private
30
+
31
+ def check_border(node, border)
32
+ return unless %w[0 none].include?(border)
33
+ return if @preference[0] == border
34
+
35
+ add_lint(node, "`border: #{@preference[0]} is preferred over " \
36
+ "`border: #{@preference[1]}`")
37
+ end
22
38
  end
23
39
  end