scss_lint 0.43.2 → 0.44.0

Sign up to get free protection for your applications and to get access to all the features.
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