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
@@ -14,14 +14,14 @@ module SCSSLint
14
14
  yield
15
15
  end
16
16
 
17
- alias_method :visit_function, :check_node
18
- alias_method :visit_each, :check_node
19
- alias_method :visit_for, :check_node
20
- alias_method :visit_function, :check_node
21
- alias_method :visit_mixindef, :check_node
22
- alias_method :visit_mixin, :check_node
23
- alias_method :visit_rule, :check_node
24
- alias_method :visit_while, :check_node
17
+ alias visit_function check_node
18
+ alias visit_each check_node
19
+ alias visit_for check_node
20
+ alias visit_function check_node
21
+ alias visit_mixindef check_node
22
+ alias visit_mixin check_node
23
+ alias visit_rule check_node
24
+ alias visit_while check_node
25
25
 
26
26
  private
27
27
 
@@ -10,13 +10,13 @@ module SCSSLint
10
10
  yield
11
11
  end
12
12
 
13
- alias_method :visit_atroot, :check_node
14
- alias_method :visit_cssimport, :check_node
15
- alias_method :visit_function, :check_node
16
- alias_method :visit_media, :check_node
17
- alias_method :visit_mixindef, :check_node
18
- alias_method :visit_mixin, :check_node
19
- alias_method :visit_script_funcall, :check_node
13
+ alias visit_atroot check_node
14
+ alias visit_cssimport check_node
15
+ alias visit_function check_node
16
+ alias visit_media check_node
17
+ alias visit_mixindef check_node
18
+ alias visit_mixin check_node
19
+ alias visit_script_funcall check_node
20
20
 
21
21
  def feel_for_parens_and_check_node(node)
22
22
  source = feel_for_enclosing_parens(node)
@@ -24,10 +24,10 @@ module SCSSLint
24
24
  yield
25
25
  end
26
26
 
27
- alias_method :visit_script_listliteral, :feel_for_parens_and_check_node
28
- alias_method :visit_script_mapliteral, :feel_for_parens_and_check_node
29
- alias_method :visit_script_operation, :feel_for_parens_and_check_node
30
- alias_method :visit_script_string, :feel_for_parens_and_check_node
27
+ alias visit_script_listliteral feel_for_parens_and_check_node
28
+ alias visit_script_mapliteral feel_for_parens_and_check_node
29
+ alias visit_script_operation feel_for_parens_and_check_node
30
+ alias visit_script_string feel_for_parens_and_check_node
31
31
 
32
32
  private
33
33
 
@@ -12,8 +12,9 @@ module SCSSLint
12
12
  #
13
13
  # Thus we manually skip the substrings in the string interpolation and
14
14
  # visit the expressions in the interpolation itself.
15
- node.children.reject { |child| child.is_a?(Sass::Script::Tree::Literal) }
16
- .each { |child| visit(child) }
15
+ node.children
16
+ .reject { |child| child.is_a?(Sass::Script::Tree::Literal) }
17
+ .each { |child| visit(child) }
17
18
  end
18
19
 
19
20
  def visit_script_string(node)
@@ -27,7 +28,7 @@ module SCSSLint
27
28
 
28
29
  def visit_charset(node)
29
30
  # `@charset` source range includes entire declaration, so exclude that prefix
30
- source = source_from_range(node.source_range)[(CHARSET_DIRECTIVE_LENGTH)..-1]
31
+ source = source_from_range(node.source_range)[CHARSET_DIRECTIVE_LENGTH..-1]
31
32
 
32
33
  check_quotes(node, source)
33
34
  end
@@ -63,11 +64,9 @@ module SCSSLint
63
64
  def check_double_quotes(node, string)
64
65
  if config['style'] == 'single_quotes'
65
66
  add_lint(node, 'Prefer single quoted strings') if string !~ /'/
66
- else
67
- if string =~ /(?<! \\) \\"/x && string !~ /'/
68
- add_lint(node, 'Use single-quoted strings when writing double ' \
69
- 'quotes to avoid having to escape the double quotes')
70
- end
67
+ elsif string =~ /(?<! \\) \\"/x && string !~ /'/
68
+ add_lint(node, 'Use single-quoted strings when writing double ' \
69
+ 'quotes to avoid having to escape the double quotes')
71
70
  end
72
71
  end
73
72
 
@@ -79,8 +78,8 @@ module SCSSLint
79
78
  elsif string =~ /(?<! \\) \\"/x
80
79
  add_lint(node, "Don't escape double quotes in single-quoted strings")
81
80
  end
82
- else
83
- add_lint(node, 'Prefer double-quoted strings') if string !~ /"/
81
+ elsif string !~ /"/
82
+ add_lint(node, 'Prefer double-quoted strings')
84
83
  end
85
84
  end
86
85
  end
@@ -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)[FRACTIONAL_DIGIT_REGEX, 1]
18
+ source_from_range(node.source_range)[FRACTIONAL_DIGIT_REGEX, 1]
19
19
 
20
20
  check_for_trailing_zeros(node, number)
21
21
  end
@@ -6,7 +6,7 @@ module SCSSLint
6
6
  TRANSITION_PROPERTIES = %w[
7
7
  transition
8
8
  transition-property
9
- ]
9
+ ].freeze
10
10
 
11
11
  def visit_prop(node)
12
12
  property = node.name.first.to_s
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SCSSLint
2
4
  # Checks for the unnecessary inclusion of a zero-value mantissa in numbers.
3
5
  # (e.g. `4.0` could be written as just `4`)
@@ -34,7 +36,7 @@ module SCSSLint
34
36
  )\b
35
37
  /ix
36
38
 
37
- MESSAGE_FORMAT = '`%s` should be written without the mantissa as `%s%s`'
39
+ MESSAGE_FORMAT = '`%s` should be written without the mantissa as `%s%s`'.freeze
38
40
 
39
41
  def unnecessary_mantissa?(mantissa)
40
42
  mantissa !~ /[^0]/
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SCSSLint
2
4
  # Checks for unnecessary uses of the parent reference (&) in nested selectors.
3
5
  class Linter::UnnecessaryParentReference < Linter
4
6
  include LinterRegistry
5
7
 
6
- MESSAGE = 'Unnecessary parent selector (&)'
8
+ MESSAGE = 'Unnecessary parent selector (&)'.freeze
7
9
 
8
10
  def visit_comma_sequence(comma_sequence)
9
11
  @multiple_sequences = comma_sequence.members.size > 1
@@ -22,7 +24,7 @@ module SCSSLint
22
24
  # element {
23
25
  # & + & { ... }
24
26
  # }
25
- return if sequence.members[1..-1].any? { |ss| sequence_starts_with_parent?(ss) }
27
+ return if sequence.members[1..-1].any? { |ss| sequence_contains_parent_reference?(ss) }
26
28
 
27
29
  # Special case: allow an isolated parent to appear if it is part of a
28
30
  # comma sequence of more than one sequence, as this could be used to DRY
@@ -45,5 +47,10 @@ module SCSSLint
45
47
  first.is_a?(Sass::Selector::Parent) &&
46
48
  first.suffix.nil? # Ignore concatenated selectors, like `&-something`
47
49
  end
50
+
51
+ def sequence_contains_parent_reference?(simple_sequence)
52
+ return unless simple_sequence.is_a?(Sass::Selector::SimpleSequence)
53
+ simple_sequence.members.any? { |s| s.is_a?(Sass::Selector::Parent) }
54
+ end
48
55
  end
49
56
  end
@@ -8,8 +8,10 @@ module SCSSLint
8
8
  when Sass::Script::Tree::Literal
9
9
  check(node, node.value.value.to_s)
10
10
  when Sass::Script::Tree::ListLiteral
11
- node.value.children.select { |child| child.is_a?(Sass::Script::Tree::Literal) }
12
- .each { |child| check(node, child.value.to_s) }
11
+ node.value
12
+ .children
13
+ .select { |child| child.is_a?(Sass::Script::Tree::Literal) }
14
+ .each { |child| check(node, child.value.to_s) }
13
15
  end
14
16
 
15
17
  yield
@@ -3,7 +3,7 @@ module SCSSLint
3
3
  class Linter::VariableForProperty < Linter
4
4
  include LinterRegistry
5
5
 
6
- IGNORED_VALUES = %w[currentColor inherit transparent]
6
+ IGNORED_VALUES = %w[currentColor inherit initial transparent].freeze
7
7
 
8
8
  def visit_root(_node)
9
9
  @properties = Set.new(config['properties'])
@@ -20,9 +20,9 @@ module SCSSLint
20
20
  check_identifier(node, source_from_range(node.value.source_range))
21
21
  end
22
22
 
23
- alias_method :visit_prop, :check_node
24
- alias_method :visit_pseudo, :check_node
25
- alias_method :visit_directive, :check_node
23
+ alias visit_prop check_node
24
+ alias visit_pseudo check_node
25
+ alias visit_directive check_node
26
26
 
27
27
  private
28
28
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SCSSLint
2
4
  # Checks for unnecessary units on zero values.
3
5
  class Linter::ZeroUnit < Linter
@@ -28,7 +30,7 @@ module SCSSLint
28
30
  \b
29
31
  /ix
30
32
 
31
- MESSAGE_FORMAT = '`%s` should be written without units as `0`'
33
+ MESSAGE_FORMAT = '`%s` should be written without units as `0`'.freeze
32
34
 
33
35
  LENGTH_UNITS = %w[em ex ch rem vw vh vmin vmax cm mm in pt pc px].to_set
34
36
 
@@ -24,7 +24,7 @@ module SCSSLint
24
24
  end
25
25
  end
26
26
 
27
- alias_method :eql?, :==
27
+ alias eql? ==
28
28
 
29
29
  def <=>(other)
30
30
  [:line, :column, :length].each do |attr|
@@ -0,0 +1,149 @@
1
+ module SCSSLint
2
+ # Encapsulates all communication to an output source.
3
+ class Logger
4
+ # Whether colored output via ANSI escape sequences is enabled.
5
+ # @return [true,false]
6
+ attr_accessor :color_enabled
7
+
8
+ # Creates a logger which outputs nothing.
9
+ # @return [SCSSLint::Logger]
10
+ def self.silent
11
+ new(File.open('/dev/null', 'w'))
12
+ end
13
+
14
+ # Creates a new {SCSSLint::Logger} instance.
15
+ #
16
+ # @param out [IO] the output destination.
17
+ def initialize(out)
18
+ @out = out
19
+ end
20
+
21
+ # Print the specified output.
22
+ #
23
+ # @param output [String] the output to send
24
+ # @param newline [true,false] whether to append a newline
25
+ def log(output, newline = true)
26
+ @out.print(output)
27
+ @out.print("\n") if newline
28
+ end
29
+
30
+ # Print the specified output in a color indicative of error.
31
+ # If output destination is not a TTY, behaves the same as {#log}.
32
+ #
33
+ # @param output [String] the output to send
34
+ # @param newline [true,false] whether to append a newline
35
+ def error(output, newline = true)
36
+ log(red(output), newline)
37
+ end
38
+
39
+ # Print the specified output in a bold face and color indicative of error.
40
+ # If output destination is not a TTY, behaves the same as {#log}.
41
+ #
42
+ # @param output [String] the output to send
43
+ # @param newline [true,false] whether to append a newline
44
+ def bold_error(output, newline = true)
45
+ log(bold_red(output), newline)
46
+ end
47
+
48
+ # Print the specified output in a color indicative of success.
49
+ # If output destination is not a TTY, behaves the same as {#log}.
50
+ #
51
+ # @param output [String] the output to send
52
+ # @param newline [true,false] whether to append a newline
53
+ def success(output, newline = true)
54
+ log(green(output), newline)
55
+ end
56
+
57
+ # Print the specified output in a color indicative of a warning.
58
+ # If output destination is not a TTY, behaves the same as {#log}.
59
+ #
60
+ # @param output [String] the output to send
61
+ # @param newline [true,false] whether to append a newline
62
+ def warning(output, newline = true)
63
+ log(yellow(output), newline)
64
+ end
65
+
66
+ # Print the specified output in a color indicating information.
67
+ # If output destination is not a TTY, behaves the same as {#log}.
68
+ #
69
+ # @param output [String] the output to send
70
+ # @param newline [true,false] whether to append a newline
71
+ def info(output, newline = true)
72
+ log(cyan(output), newline)
73
+ end
74
+
75
+ # Print a blank line.
76
+ def newline
77
+ log('')
78
+ end
79
+
80
+ # Mark the specified output in bold face.
81
+ # If output destination is not a TTY, this is a noop.
82
+ #
83
+ # @param output [String] the output to format
84
+ def bold(output)
85
+ color('1', output)
86
+ end
87
+
88
+ # Mark the specified output in bold red.
89
+ # If output destination is not a TTY, this is a noop.
90
+ #
91
+ # @param output [String] the output to format
92
+ def bold_red(output)
93
+ color('1;31', output)
94
+ end
95
+
96
+ # Mark the specified output in red.
97
+ # If output destination is not a TTY, this is a noop.
98
+ #
99
+ # @param output [String] the output to format
100
+ def red(output)
101
+ color(31, output)
102
+ end
103
+
104
+ # Mark the specified output in green.
105
+ # If output destination is not a TTY, this is a noop.
106
+ #
107
+ # @param output [String] the output to format
108
+ def green(output)
109
+ color(32, output)
110
+ end
111
+
112
+ # Mark the specified output in yellow.
113
+ # If output destination is not a TTY, this is a noop.
114
+ #
115
+ # @param output [String] the output to format
116
+ def yellow(output)
117
+ color(33, output)
118
+ end
119
+
120
+ # Mark the specified output in magenta.
121
+ # If output destination is not a TTY, this is a noop.
122
+ #
123
+ # @param output [String] the output to format
124
+ def magenta(output)
125
+ color(35, output)
126
+ end
127
+
128
+ # Mark the specified output in cyan.
129
+ # If output destination is not a TTY, this is a noop.
130
+ #
131
+ # @param output [String] the output to format
132
+ def cyan(output)
133
+ color(36, output)
134
+ end
135
+
136
+ # Whether this logger is outputting to a TTY.
137
+ #
138
+ # @return [true,false]
139
+ def tty?
140
+ @out.respond_to?(:tty?) && @out.tty?
141
+ end
142
+
143
+ private
144
+
145
+ def color(code, output)
146
+ color_enabled ? "\033[#{code}m#{output}\033[0m" : output
147
+ end
148
+ end
149
+ end
@@ -3,7 +3,7 @@ require 'optparse'
3
3
  module SCSSLint
4
4
  # Handles option parsing for the command line application.
5
5
  class Options
6
- DEFAULT_REPORTER = ['Default', :stdout]
6
+ DEFAULT_REPORTER = ['Default', :stdout].freeze
7
7
 
8
8
  # Parses command line options into an options hash.
9
9
  #
@@ -102,6 +102,10 @@ module SCSSLint
102
102
  @options[:show_linters] = true
103
103
  end
104
104
 
105
+ parser.on('--[no-]color', 'Force output to be colorized') do |color|
106
+ @options[:color] = color
107
+ end
108
+
105
109
  parser.on_tail('-h', '--help', 'Display help documentation') do
106
110
  @options[:help] = parser.help
107
111
  end
@@ -42,6 +42,11 @@ module SCSSLint
42
42
  # @return [Array<String>]
43
43
  attr_accessor :files
44
44
 
45
+ # Whether output from SCSS-lint should not be displayed to the standard out
46
+ # stream.
47
+ # @return [true,false]
48
+ attr_accessor :quiet
49
+
45
50
  # Create the task so it is accessible via +Rake::Task['scss_lint']+.
46
51
  def initialize(name = :scss_lint)
47
52
  @name = name
@@ -68,10 +73,12 @@ module SCSSLint
68
73
  end
69
74
  end
70
75
 
71
- def run_cli(task_args)
76
+ def run_cli(task_args) # rubocop:disable AbcSize
72
77
  cli_args = ['--config', config] if config
73
78
 
74
- result = SCSSLint::CLI.new.run(Array(cli_args) + Array(args) + files_to_lint(task_args))
79
+ logger = quiet ? SCSSLint::Logger.silent : SCSSLint::Logger.new(STDOUT)
80
+ run_args = Array(cli_args) + Array(args) + files_to_lint(task_args)
81
+ result = SCSSLint::CLI.new(logger).run(run_args)
75
82
 
76
83
  message =
77
84
  case result
@@ -83,7 +90,7 @@ module SCSSLint
83
90
  'scss-lint failed with an error'
84
91
  end
85
92
 
86
- puts message
93
+ logger.log message
87
94
  exit result unless result == 0
88
95
  end
89
96
 
@@ -1,7 +1,7 @@
1
1
  module SCSSLint
2
2
  # Responsible for displaying lints to the user in some format.
3
3
  class Reporter
4
- attr_reader :lints, :files
4
+ attr_reader :lints, :files, :log
5
5
 
6
6
  def self.descendants
7
7
  ObjectSpace.each_object(Class).select { |klass| klass < self }
@@ -9,9 +9,11 @@ module SCSSLint
9
9
 
10
10
  # @param lints [List<Lint>] a list of Lints sorted by file and line number
11
11
  # @param files [List<String>] a list of the files that were linted
12
- def initialize(lints, files)
12
+ # @param logger [SCSSLint::Logger]
13
+ def initialize(lints, files, logger)
13
14
  @lints = lints
14
15
  @files = files
16
+ @log = logger
15
17
  end
16
18
 
17
19
  def report_lints