scss_lint 0.43.2 → 0.44.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/bin/scss-lint +2 -1
  3. data/config/default.yml +3 -0
  4. data/data/properties.txt +35 -0
  5. data/data/property-sort-orders/smacss.txt +9 -0
  6. data/data/pseudo-elements.txt +5 -0
  7. data/lib/scss_lint.rb +1 -0
  8. data/lib/scss_lint/cli.rb +50 -30
  9. data/lib/scss_lint/config.rb +28 -3
  10. data/lib/scss_lint/constants.rb +6 -4
  11. data/lib/scss_lint/control_comment_processor.rb +8 -7
  12. data/lib/scss_lint/engine.rb +1 -1
  13. data/lib/scss_lint/file_finder.rb +1 -1
  14. data/lib/scss_lint/linter.rb +8 -7
  15. data/lib/scss_lint/linter/bang_format.rb +2 -2
  16. data/lib/scss_lint/linter/border_zero.rb +2 -2
  17. data/lib/scss_lint/linter/color_variable.rb +1 -1
  18. data/lib/scss_lint/linter/declaration_order.rb +10 -8
  19. data/lib/scss_lint/linter/duplicate_property.rb +2 -2
  20. data/lib/scss_lint/linter/empty_line_between_blocks.rb +3 -1
  21. data/lib/scss_lint/linter/final_newline.rb +3 -3
  22. data/lib/scss_lint/linter/indentation.rb +20 -20
  23. data/lib/scss_lint/linter/leading_zero.rb +2 -2
  24. data/lib/scss_lint/linter/mergeable_selector.rb +3 -4
  25. data/lib/scss_lint/linter/name_format.rb +2 -1
  26. data/lib/scss_lint/linter/nesting_depth.rb +1 -1
  27. data/lib/scss_lint/linter/property_sort_order.rb +11 -13
  28. data/lib/scss_lint/linter/selector_depth.rb +9 -8
  29. data/lib/scss_lint/linter/selector_format.rb +1 -1
  30. data/lib/scss_lint/linter/shorthand.rb +1 -1
  31. data/lib/scss_lint/linter/single_line_per_selector.rb +3 -1
  32. data/lib/scss_lint/linter/space_around_operator.rb +4 -2
  33. data/lib/scss_lint/linter/space_before_brace.rb +8 -8
  34. data/lib/scss_lint/linter/space_between_parens.rb +11 -11
  35. data/lib/scss_lint/linter/string_quotes.rb +9 -10
  36. data/lib/scss_lint/linter/trailing_zero.rb +1 -1
  37. data/lib/scss_lint/linter/transition_all.rb +1 -1
  38. data/lib/scss_lint/linter/unnecessary_mantissa.rb +3 -1
  39. data/lib/scss_lint/linter/unnecessary_parent_reference.rb +9 -2
  40. data/lib/scss_lint/linter/url_quotes.rb +4 -2
  41. data/lib/scss_lint/linter/variable_for_property.rb +1 -1
  42. data/lib/scss_lint/linter/vendor_prefix.rb +3 -3
  43. data/lib/scss_lint/linter/zero_unit.rb +3 -1
  44. data/lib/scss_lint/location.rb +1 -1
  45. data/lib/scss_lint/logger.rb +149 -0
  46. data/lib/scss_lint/options.rb +5 -1
  47. data/lib/scss_lint/rake_task.rb +10 -3
  48. data/lib/scss_lint/reporter.rb +4 -2
  49. data/lib/scss_lint/reporter/default_reporter.rb +3 -3
  50. data/lib/scss_lint/version.rb +3 -1
  51. data/spec/scss_lint/cli_spec.rb +66 -14
  52. data/spec/scss_lint/config_spec.rb +25 -5
  53. data/spec/scss_lint/linter/name_format_spec.rb +10 -0
  54. data/spec/scss_lint/linter/property_sort_order_spec.rb +28 -0
  55. data/spec/scss_lint/linter/unnecessary_parent_reference_spec.rb +10 -0
  56. data/spec/scss_lint/linter/variable_for_property_spec.rb +10 -0
  57. data/spec/scss_lint/logger_spec.rb +27 -0
  58. data/spec/scss_lint/options_spec.rb +18 -0
  59. data/spec/scss_lint/plugins/linter_dir_spec.rb +1 -1
  60. data/spec/scss_lint/reporter/clean_files_reporter_spec.rb +1 -1
  61. data/spec/scss_lint/reporter/config_reporter_spec.rb +1 -1
  62. data/spec/scss_lint/reporter/default_reporter_spec.rb +2 -1
  63. data/spec/scss_lint/reporter/files_reporter_spec.rb +1 -1
  64. data/spec/scss_lint/reporter/json_reporter_spec.rb +5 -5
  65. metadata +10 -7
@@ -6,7 +6,7 @@ module SCSSLint
6
6
  # Contains all information for a parsed SCSS file, including its name,
7
7
  # contents, and parse tree.
8
8
  class Engine
9
- ENGINE_OPTIONS = { cache: false, syntax: :scss }
9
+ ENGINE_OPTIONS = { cache: false, syntax: :scss }.freeze
10
10
 
11
11
  attr_reader :contents, :filename, :lines, :tree, :any_control_commands
12
12
 
@@ -6,7 +6,7 @@ module SCSSLint
6
6
  class FileFinder
7
7
  # List of extensions of files to include when only a directory is specified
8
8
  # as a path.
9
- VALID_EXTENSIONS = %w[.css .scss]
9
+ VALID_EXTENSIONS = %w[.css .scss].freeze
10
10
 
11
11
  # Create a {FileFinder}.
12
12
  #
@@ -86,20 +86,21 @@ module SCSSLint
86
86
  last_line = source_range.end_pos.line - 1
87
87
  start_pos = source_range.start_pos.offset - 1
88
88
 
89
- if current_line == last_line
90
- source = engine.lines[current_line][start_pos..(source_range.end_pos.offset - 1)]
91
- else
92
- source = engine.lines[current_line][start_pos..-1]
93
- end
89
+ source =
90
+ if current_line == last_line
91
+ engine.lines[current_line][start_pos..(source_range.end_pos.offset - 1)]
92
+ else
93
+ engine.lines[current_line][start_pos..-1]
94
+ end
94
95
 
95
96
  current_line += 1
96
97
  while current_line < last_line
97
- source += "#{engine.lines[current_line]}"
98
+ source += engine.lines[current_line].to_s
98
99
  current_line += 1
99
100
  end
100
101
 
101
102
  if source_range.start_pos.line != source_range.end_pos.line
102
- source += "#{(engine.lines[current_line] || '')[0...source_range.end_pos.offset]}"
103
+ source += ((engine.lines[current_line] || '')[0...source_range.end_pos.offset]).to_s
103
104
  end
104
105
 
105
106
  source
@@ -3,7 +3,7 @@ module SCSSLint
3
3
  class Linter::BangFormat < Linter
4
4
  include LinterRegistry
5
5
 
6
- STOPPING_CHARACTERS = ['!', "'", '"', nil]
6
+ STOPPING_CHARACTERS = ['!', "'", '"', nil].freeze
7
7
 
8
8
  def visit_extend(node)
9
9
  check_bang(node)
@@ -31,7 +31,7 @@ module SCSSLint
31
31
  before_qualifier = config['space_before_bang'] ? '' : 'not '
32
32
  after_qualifier = config['space_after_bang'] ? '' : 'not '
33
33
 
34
- add_lint(node, "! should #{before_qualifier}be preceeded by a space, " \
34
+ add_lint(node, "! should #{before_qualifier}be preceded by a space, " \
35
35
  "and should #{after_qualifier}be followed by a space")
36
36
  end
37
37
 
@@ -6,7 +6,7 @@ module SCSSLint
6
6
  CONVENTION_TO_PREFERENCE = {
7
7
  'zero' => %w[0 none],
8
8
  'none' => %w[none 0],
9
- }
9
+ }.freeze
10
10
 
11
11
  BORDER_PROPERTIES = %w[
12
12
  border
@@ -14,7 +14,7 @@ module SCSSLint
14
14
  border-right
15
15
  border-bottom
16
16
  border-left
17
- ]
17
+ ].freeze
18
18
 
19
19
  def visit_root(_node)
20
20
  @preference = CONVENTION_TO_PREFERENCE[config['convention']]
@@ -3,7 +3,7 @@ module SCSSLint
3
3
  class Linter::ColorVariable < Linter
4
4
  include LinterRegistry
5
5
 
6
- COLOR_FUNCTIONS = %w[rgb rgba hsl hsla]
6
+ COLOR_FUNCTIONS = %w[rgb rgba hsl hsla].freeze
7
7
 
8
8
  def visit_script_color(node)
9
9
  return if in_variable_declaration?(node) ||
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SCSSLint
2
4
  # Checks the order of nested items within a rule set.
3
5
  class Linter::DeclarationOrder < Linter
@@ -8,9 +10,9 @@ module SCSSLint
8
10
  yield # Continue linting children
9
11
  end
10
12
 
11
- alias_method :visit_rule, :check_order
12
- alias_method :visit_mixin, :check_order
13
- alias_method :visit_media, :check_order
13
+ alias visit_rule check_order
14
+ alias visit_mixin check_order
15
+ alias visit_media check_order
14
16
 
15
17
  private
16
18
 
@@ -18,9 +20,9 @@ module SCSSLint
18
20
  'Rule sets should be ordered as follows: '\
19
21
  '`@extends`, `@includes` without `@content`, ' \
20
22
  'properties, `@includes` with `@content`, ' \
21
- 'nested rule sets'
23
+ 'nested rule sets'.freeze
22
24
 
23
- MIXIN_WITH_CONTENT = 'mixin_with_content'
25
+ MIXIN_WITH_CONTENT = 'mixin_with_content'.freeze
24
26
 
25
27
  DECLARATION_ORDER = [
26
28
  Sass::Tree::ExtendNode,
@@ -28,7 +30,7 @@ module SCSSLint
28
30
  Sass::Tree::PropNode,
29
31
  MIXIN_WITH_CONTENT,
30
32
  Sass::Tree::RuleNode,
31
- ]
33
+ ].freeze
32
34
 
33
35
  def important_node?(node)
34
36
  DECLARATION_ORDER.include?(node.class)
@@ -36,8 +38,8 @@ module SCSSLint
36
38
 
37
39
  def check_node(node)
38
40
  children = node.children.each_with_index
39
- .select { |n, _| important_node?(n) }
40
- .map { |n, i| [n, node_declaration_type(n), i] }
41
+ .select { |n, _| important_node?(n) }
42
+ .map { |n, i| [n, node_declaration_type(n), i] }
41
43
 
42
44
  sorted_children = children.sort do |(_, a_type, i), (_, b_type, j)|
43
45
  [DECLARATION_ORDER.index(a_type), i] <=> [DECLARATION_ORDER.index(b_type), j]
@@ -18,8 +18,8 @@ module SCSSLint
18
18
  yield # Continue linting children
19
19
  end
20
20
 
21
- alias_method :visit_rule, :check_properties
22
- alias_method :visit_mixindef, :check_properties
21
+ alias visit_rule check_properties
22
+ alias visit_mixindef check_properties
23
23
 
24
24
  private
25
25
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SCSSLint
2
4
  # Reports the lack of empty lines between block defintions.
3
5
  class Linter::EmptyLineBetweenBlocks < Linter
@@ -37,7 +39,7 @@ module SCSSLint
37
39
 
38
40
  private
39
41
 
40
- MESSAGE_FORMAT = '%s declaration should be %s by an empty line'
42
+ MESSAGE_FORMAT = '%s declaration should be %s by an empty line'.freeze
41
43
 
42
44
  def check(node, type)
43
45
  return if config['ignore_single_line_blocks'] && node_on_single_line?(node)
@@ -11,10 +11,10 @@ module SCSSLint
11
11
  if config['present']
12
12
  add_lint(engine.lines.count,
13
13
  'Files should end with a trailing newline') unless ends_with_newline
14
- else
15
- add_lint(engine.lines.count,
16
- 'Files should not end with a trailing newline') if ends_with_newline
14
+ elsif ends_with_newline
15
+ add_lint(engine.lines.count, 'Files should not end with a trailing newline')
17
16
  end
17
+
18
18
  yield
19
19
  end
20
20
  end
@@ -64,8 +64,8 @@ module SCSSLint
64
64
 
65
65
  if @allow_non_nested_indentation
66
66
  yield # Continue linting else statement
67
- else
68
- visit(node.else) if node.else
67
+ elsif node.else
68
+ visit(node.else)
69
69
  end
70
70
  end
71
71
 
@@ -103,26 +103,26 @@ module SCSSLint
103
103
  end
104
104
 
105
105
  # Define node types that increase indentation level
106
- alias_method :visit_directive, :check_and_visit_children
107
- alias_method :visit_each, :check_and_visit_children
108
- alias_method :visit_for, :check_and_visit_children
109
- alias_method :visit_function, :check_and_visit_children
110
- alias_method :visit_media, :check_and_visit_children
111
- alias_method :visit_mixin, :check_and_visit_children
112
- alias_method :visit_mixindef, :check_and_visit_children
113
- alias_method :visit_prop, :check_and_visit_children
114
- alias_method :visit_rule, :check_and_visit_children
115
- alias_method :visit_supports, :check_and_visit_children
116
- alias_method :visit_while, :check_and_visit_children
106
+ alias visit_directive check_and_visit_children
107
+ alias visit_each check_and_visit_children
108
+ alias visit_for check_and_visit_children
109
+ alias visit_function check_and_visit_children
110
+ alias visit_media check_and_visit_children
111
+ alias visit_mixin check_and_visit_children
112
+ alias visit_mixindef check_and_visit_children
113
+ alias visit_prop check_and_visit_children
114
+ alias visit_rule check_and_visit_children
115
+ alias visit_supports check_and_visit_children
116
+ alias visit_while check_and_visit_children
117
117
 
118
118
  # Define node types to check indentation of (notice comments are left out)
119
- alias_method :visit_charset, :check_indentation
120
- alias_method :visit_content, :check_indentation
121
- alias_method :visit_cssimport, :check_indentation
122
- alias_method :visit_extend, :check_indentation
123
- alias_method :visit_return, :check_indentation
124
- alias_method :visit_variable, :check_indentation
125
- alias_method :visit_warn, :check_indentation
119
+ alias visit_charset check_indentation
120
+ alias visit_content check_indentation
121
+ alias visit_cssimport check_indentation
122
+ alias visit_extend check_indentation
123
+ alias visit_return check_indentation
124
+ alias visit_variable check_indentation
125
+ alias visit_warn check_indentation
126
126
 
127
127
  private
128
128
 
@@ -15,7 +15,7 @@ module SCSSLint
15
15
 
16
16
  def visit_script_number(node)
17
17
  return unless number =
18
- source_from_range(node.source_range)[NUMBER_WITH_LEADING_ZERO_REGEX, 1]
18
+ source_from_range(node.source_range)[NUMBER_WITH_LEADING_ZERO_REGEX, 1]
19
19
 
20
20
  check_for_leading_zeros(node, number)
21
21
  end
@@ -35,7 +35,7 @@ module SCSSLint
35
35
  validator: ->(original) { original =~ /^0\.\d+$/ },
36
36
  converter: ->(original) { "0#{original}" }
37
37
  },
38
- }
38
+ }.freeze
39
39
 
40
40
  def check_for_leading_zeros(node, original_number)
41
41
  style = config.fetch('style', 'exclude_zero')
@@ -23,8 +23,8 @@ module SCSSLint
23
23
  yield # Continue linting children
24
24
  end
25
25
 
26
- alias_method :visit_root, :check_node
27
- alias_method :visit_rule, :check_node
26
+ alias visit_root check_node
27
+ alias visit_rule check_node
28
28
 
29
29
  private
30
30
 
@@ -75,8 +75,7 @@ module SCSSLint
75
75
  end
76
76
 
77
77
  def subrule?(rule1, rule2)
78
- "#{rule1}".start_with?("#{rule2} ") ||
79
- "#{rule1}".start_with?("#{rule2}.")
78
+ rule1.to_s.start_with?("#{rule2} ", "#{rule2}.")
80
79
  end
81
80
 
82
81
  def whitelist_contains(node)
@@ -20,6 +20,7 @@ module SCSSLint
20
20
 
21
21
  def visit_script_funcall(node)
22
22
  check_name(node, 'function') unless FUNCTION_WHITELIST.include?(node.name)
23
+ yield # Continue linting any arguments of this function call
23
24
  end
24
25
 
25
26
  def visit_script_variable(node)
@@ -72,7 +73,7 @@ module SCSSLint
72
73
  'instead of underscores',
73
74
  validator: ->(name) { name !~ /[_A-Z]/ },
74
75
  },
75
- }
76
+ }.freeze
76
77
 
77
78
  def violated_convention(name_string, type)
78
79
  convention_name = convention_name(type)
@@ -3,7 +3,7 @@ module SCSSLint
3
3
  class Linter::NestingDepth < Linter
4
4
  include LinterRegistry
5
5
 
6
- IGNORED_SELECTORS = [Sass::Selector::Parent, Sass::Selector::Pseudo]
6
+ IGNORED_SELECTORS = [Sass::Selector::Parent, Sass::Selector::Pseudo].freeze
7
7
 
8
8
  def visit_root(_node)
9
9
  @max_depth = config['max_depth']
@@ -19,8 +19,8 @@ module SCSSLint
19
19
  end
20
20
 
21
21
  if sortable_props.count >= config.fetch('min_properties', 2)
22
- sortable_prop_info = sortable_props
23
- .map do |child|
22
+ sortable_prop_info =
23
+ sortable_props.map do |child|
24
24
  name = child.name.join
25
25
  /^(?<vendor>-\w+(-osx)?-)?(?<property>.+)/ =~ name
26
26
  { name: name, vendor: vendor, property: "#{@nested_under}#{property}", node: child }
@@ -33,10 +33,10 @@ module SCSSLint
33
33
  yield # Continue linting children
34
34
  end
35
35
 
36
- alias_method :visit_media, :check_order
37
- alias_method :visit_mixin, :check_order
38
- alias_method :visit_rule, :check_order
39
- alias_method :visit_prop, :check_order
36
+ alias visit_media check_order
37
+ alias visit_mixin check_order
38
+ alias visit_rule check_order
39
+ alias visit_prop check_order
40
40
 
41
41
  def visit_prop(node, &block)
42
42
  # Handle nested properties by appending the parent property they are
@@ -76,8 +76,8 @@ module SCSSLint
76
76
  end
77
77
 
78
78
  def check_sort_order(sortable_prop_info)
79
- sorted_props = sortable_prop_info
80
- .sort { |a, b| compare_properties(a, b) }
79
+ sortable_prop_info = sortable_prop_info.uniq { |item| item[:name] }
80
+ sorted_props = sortable_prop_info.sort { |a, b| compare_properties(a, b) }
81
81
 
82
82
  sorted_props.each_with_index do |prop, index|
83
83
  # Once we reach the portion of the list with unspecified properties, we
@@ -128,12 +128,10 @@ module SCSSLint
128
128
  def compare_properties(a, b)
129
129
  if a[:property] == b[:property]
130
130
  compare_by_vendor(a, b)
131
+ elsif @preferred_order
132
+ compare_by_order(a, b, @preferred_order)
131
133
  else
132
- if @preferred_order
133
- compare_by_order(a, b, @preferred_order)
134
- else
135
- a[:property] <=> b[:property]
136
- end
134
+ a[:property] <=> b[:property]
137
135
  end
138
136
  end
139
137
 
@@ -49,14 +49,15 @@ module SCSSLint
49
49
  depth = simple_sequences.size -
50
50
  separators.count { |item| item == '~' || item == '+' }
51
51
 
52
- if parent_selectors > 0
53
- # If parent selectors are present, add the current depth for each
54
- # additional parent selector.
55
- depth += parent_selectors * (current_depth - 1)
56
- else
57
- # Otherwise this just descends from the containing selector
58
- depth += current_depth
59
- end
52
+ depth +=
53
+ if parent_selectors > 0
54
+ # If parent selectors are present, add the current depth for each
55
+ # additional parent selector.
56
+ parent_selectors * (current_depth - 1)
57
+ else
58
+ # Otherwise this just descends from the containing selector
59
+ current_depth
60
+ end
60
61
 
61
62
  depth
62
63
  end
@@ -67,7 +67,7 @@ module SCSSLint
67
67
  /x
68
68
  end,
69
69
  },
70
- }
70
+ }.freeze
71
71
 
72
72
  # Checks the given name and returns the violated convention if it failed.
73
73
  def violated_convention(name_string, type)
@@ -27,7 +27,7 @@ module SCSSLint
27
27
  border-width
28
28
  margin
29
29
  padding
30
- ]
30
+ ].freeze
31
31
 
32
32
  # @param prop [String]
33
33
  # @param list [Sass::Script::Tree::ListLiteral]
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SCSSLint
2
4
  # Checks that selector sequences are split over multiple lines by comma.
3
5
  class Linter::SingleLinePerSelector < Linter
4
6
  include LinterRegistry
5
7
 
6
- MESSAGE = 'Each selector in a comma sequence should be on its own single line'
8
+ MESSAGE = 'Each selector in a comma sequence should be on its own single line'.freeze
7
9
 
8
10
  def visit_comma_sequence(node)
9
11
  return unless node.members.count > 1
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SCSSLint
2
4
  # Checks for space around operators on values.
3
5
  class Linter::SpaceAroundOperator < Linter
@@ -109,10 +111,10 @@ module SCSSLint
109
111
  private
110
112
 
111
113
  SPACE_MSG = '`%s` should be written with a single space on each side of ' \
112
- 'the operator: `%s %s %s`'
114
+ 'the operator: `%s %s %s`'.freeze
113
115
 
114
116
  NO_SPACE_MSG = '`%s` should be written without spaces around the ' \
115
- 'operator: `%s%s%s`'
117
+ 'operator: `%s%s%s`'.freeze
116
118
 
117
119
  def calculate_operator_source
118
120
  # We don't want to add 1 to range1.end_pos.offset for the same reason as