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.
- checksums.yaml +4 -4
- data/bin/scss-lint +2 -1
- data/config/default.yml +3 -0
- data/data/properties.txt +35 -0
- data/data/property-sort-orders/smacss.txt +9 -0
- data/data/pseudo-elements.txt +5 -0
- data/lib/scss_lint.rb +1 -0
- data/lib/scss_lint/cli.rb +50 -30
- data/lib/scss_lint/config.rb +28 -3
- data/lib/scss_lint/constants.rb +6 -4
- data/lib/scss_lint/control_comment_processor.rb +8 -7
- data/lib/scss_lint/engine.rb +1 -1
- data/lib/scss_lint/file_finder.rb +1 -1
- data/lib/scss_lint/linter.rb +8 -7
- data/lib/scss_lint/linter/bang_format.rb +2 -2
- data/lib/scss_lint/linter/border_zero.rb +2 -2
- data/lib/scss_lint/linter/color_variable.rb +1 -1
- data/lib/scss_lint/linter/declaration_order.rb +10 -8
- data/lib/scss_lint/linter/duplicate_property.rb +2 -2
- data/lib/scss_lint/linter/empty_line_between_blocks.rb +3 -1
- data/lib/scss_lint/linter/final_newline.rb +3 -3
- data/lib/scss_lint/linter/indentation.rb +20 -20
- data/lib/scss_lint/linter/leading_zero.rb +2 -2
- data/lib/scss_lint/linter/mergeable_selector.rb +3 -4
- data/lib/scss_lint/linter/name_format.rb +2 -1
- data/lib/scss_lint/linter/nesting_depth.rb +1 -1
- data/lib/scss_lint/linter/property_sort_order.rb +11 -13
- data/lib/scss_lint/linter/selector_depth.rb +9 -8
- data/lib/scss_lint/linter/selector_format.rb +1 -1
- data/lib/scss_lint/linter/shorthand.rb +1 -1
- data/lib/scss_lint/linter/single_line_per_selector.rb +3 -1
- data/lib/scss_lint/linter/space_around_operator.rb +4 -2
- data/lib/scss_lint/linter/space_before_brace.rb +8 -8
- data/lib/scss_lint/linter/space_between_parens.rb +11 -11
- data/lib/scss_lint/linter/string_quotes.rb +9 -10
- data/lib/scss_lint/linter/trailing_zero.rb +1 -1
- data/lib/scss_lint/linter/transition_all.rb +1 -1
- data/lib/scss_lint/linter/unnecessary_mantissa.rb +3 -1
- data/lib/scss_lint/linter/unnecessary_parent_reference.rb +9 -2
- data/lib/scss_lint/linter/url_quotes.rb +4 -2
- data/lib/scss_lint/linter/variable_for_property.rb +1 -1
- data/lib/scss_lint/linter/vendor_prefix.rb +3 -3
- data/lib/scss_lint/linter/zero_unit.rb +3 -1
- data/lib/scss_lint/location.rb +1 -1
- data/lib/scss_lint/logger.rb +149 -0
- data/lib/scss_lint/options.rb +5 -1
- data/lib/scss_lint/rake_task.rb +10 -3
- data/lib/scss_lint/reporter.rb +4 -2
- data/lib/scss_lint/reporter/default_reporter.rb +3 -3
- data/lib/scss_lint/version.rb +3 -1
- data/spec/scss_lint/cli_spec.rb +66 -14
- data/spec/scss_lint/config_spec.rb +25 -5
- data/spec/scss_lint/linter/name_format_spec.rb +10 -0
- data/spec/scss_lint/linter/property_sort_order_spec.rb +28 -0
- data/spec/scss_lint/linter/unnecessary_parent_reference_spec.rb +10 -0
- data/spec/scss_lint/linter/variable_for_property_spec.rb +10 -0
- data/spec/scss_lint/logger_spec.rb +27 -0
- data/spec/scss_lint/options_spec.rb +18 -0
- data/spec/scss_lint/plugins/linter_dir_spec.rb +1 -1
- data/spec/scss_lint/reporter/clean_files_reporter_spec.rb +1 -1
- data/spec/scss_lint/reporter/config_reporter_spec.rb +1 -1
- data/spec/scss_lint/reporter/default_reporter_spec.rb +2 -1
- data/spec/scss_lint/reporter/files_reporter_spec.rb +1 -1
- data/spec/scss_lint/reporter/json_reporter_spec.rb +5 -5
- metadata +10 -7
@@ -14,14 +14,14 @@ module SCSSLint
|
|
14
14
|
yield
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
16
|
-
|
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)[
|
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
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
83
|
-
add_lint(node, 'Prefer double-quoted strings')
|
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
|
-
|
18
|
+
source_from_range(node.source_range)[FRACTIONAL_DIGIT_REGEX, 1]
|
19
19
|
|
20
20
|
check_for_trailing_zeros(node, number)
|
21
21
|
end
|
@@ -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|
|
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
|
12
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
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
|
|
data/lib/scss_lint/location.rb
CHANGED
@@ -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
|
data/lib/scss_lint/options.rb
CHANGED
@@ -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
|
data/lib/scss_lint/rake_task.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
93
|
+
logger.log message
|
87
94
|
exit result unless result == 0
|
88
95
|
end
|
89
96
|
|
data/lib/scss_lint/reporter.rb
CHANGED
@@ -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
|
-
|
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
|