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.
- 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
|