scss_lint 0.38.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 +7 -0
- data/bin/scss-lint +6 -0
- data/config/default.yml +205 -0
- data/data/prefixed-identifiers/base.txt +107 -0
- data/data/prefixed-identifiers/bourbon.txt +71 -0
- data/data/properties.txt +477 -0
- data/data/property-sort-orders/concentric.txt +134 -0
- data/data/property-sort-orders/recess.txt +149 -0
- data/data/property-sort-orders/smacss.txt +137 -0
- data/lib/scss_lint.rb +31 -0
- data/lib/scss_lint/cli.rb +215 -0
- data/lib/scss_lint/config.rb +251 -0
- data/lib/scss_lint/constants.rb +8 -0
- data/lib/scss_lint/control_comment_processor.rb +126 -0
- data/lib/scss_lint/engine.rb +56 -0
- data/lib/scss_lint/exceptions.rb +21 -0
- data/lib/scss_lint/file_finder.rb +68 -0
- data/lib/scss_lint/lint.rb +24 -0
- data/lib/scss_lint/linter.rb +161 -0
- data/lib/scss_lint/linter/bang_format.rb +52 -0
- data/lib/scss_lint/linter/border_zero.rb +39 -0
- data/lib/scss_lint/linter/color_keyword.rb +32 -0
- data/lib/scss_lint/linter/color_variable.rb +60 -0
- data/lib/scss_lint/linter/comment.rb +21 -0
- data/lib/scss_lint/linter/compass.rb +7 -0
- data/lib/scss_lint/linter/compass/property_with_mixin.rb +47 -0
- data/lib/scss_lint/linter/debug_statement.rb +10 -0
- data/lib/scss_lint/linter/declaration_order.rb +71 -0
- data/lib/scss_lint/linter/duplicate_property.rb +58 -0
- data/lib/scss_lint/linter/else_placement.rb +48 -0
- data/lib/scss_lint/linter/empty_line_between_blocks.rb +85 -0
- data/lib/scss_lint/linter/empty_rule.rb +11 -0
- data/lib/scss_lint/linter/final_newline.rb +20 -0
- data/lib/scss_lint/linter/hex_length.rb +56 -0
- data/lib/scss_lint/linter/hex_notation.rb +38 -0
- data/lib/scss_lint/linter/hex_validation.rb +23 -0
- data/lib/scss_lint/linter/id_selector.rb +10 -0
- data/lib/scss_lint/linter/import_path.rb +62 -0
- data/lib/scss_lint/linter/important_rule.rb +12 -0
- data/lib/scss_lint/linter/indentation.rb +197 -0
- data/lib/scss_lint/linter/leading_zero.rb +49 -0
- data/lib/scss_lint/linter/mergeable_selector.rb +60 -0
- data/lib/scss_lint/linter/name_format.rb +117 -0
- data/lib/scss_lint/linter/nesting_depth.rb +24 -0
- data/lib/scss_lint/linter/placeholder_in_extend.rb +22 -0
- data/lib/scss_lint/linter/property_count.rb +44 -0
- data/lib/scss_lint/linter/property_sort_order.rb +198 -0
- data/lib/scss_lint/linter/property_spelling.rb +49 -0
- data/lib/scss_lint/linter/property_units.rb +59 -0
- data/lib/scss_lint/linter/qualifying_element.rb +42 -0
- data/lib/scss_lint/linter/selector_depth.rb +64 -0
- data/lib/scss_lint/linter/selector_format.rb +102 -0
- data/lib/scss_lint/linter/shorthand.rb +139 -0
- data/lib/scss_lint/linter/single_line_per_property.rb +59 -0
- data/lib/scss_lint/linter/single_line_per_selector.rb +35 -0
- data/lib/scss_lint/linter/space_after_comma.rb +110 -0
- data/lib/scss_lint/linter/space_after_property_colon.rb +92 -0
- data/lib/scss_lint/linter/space_after_property_name.rb +27 -0
- data/lib/scss_lint/linter/space_before_brace.rb +72 -0
- data/lib/scss_lint/linter/space_between_parens.rb +35 -0
- data/lib/scss_lint/linter/string_quotes.rb +94 -0
- data/lib/scss_lint/linter/trailing_semicolon.rb +67 -0
- data/lib/scss_lint/linter/trailing_zero.rb +41 -0
- data/lib/scss_lint/linter/unnecessary_mantissa.rb +42 -0
- data/lib/scss_lint/linter/unnecessary_parent_reference.rb +49 -0
- data/lib/scss_lint/linter/url_format.rb +56 -0
- data/lib/scss_lint/linter/url_quotes.rb +27 -0
- data/lib/scss_lint/linter/variable_for_property.rb +30 -0
- data/lib/scss_lint/linter/vendor_prefix.rb +64 -0
- data/lib/scss_lint/linter/zero_unit.rb +39 -0
- data/lib/scss_lint/linter_registry.rb +26 -0
- data/lib/scss_lint/location.rb +38 -0
- data/lib/scss_lint/options.rb +109 -0
- data/lib/scss_lint/rake_task.rb +106 -0
- data/lib/scss_lint/reporter.rb +18 -0
- data/lib/scss_lint/reporter/config_reporter.rb +26 -0
- data/lib/scss_lint/reporter/default_reporter.rb +27 -0
- data/lib/scss_lint/reporter/files_reporter.rb +8 -0
- data/lib/scss_lint/reporter/json_reporter.rb +30 -0
- data/lib/scss_lint/reporter/xml_reporter.rb +33 -0
- data/lib/scss_lint/runner.rb +51 -0
- data/lib/scss_lint/sass/script.rb +78 -0
- data/lib/scss_lint/sass/tree.rb +168 -0
- data/lib/scss_lint/selector_visitor.rb +34 -0
- data/lib/scss_lint/utils.rb +112 -0
- data/lib/scss_lint/version.rb +4 -0
- data/spec/scss_lint/cli_spec.rb +177 -0
- data/spec/scss_lint/config_spec.rb +253 -0
- data/spec/scss_lint/engine_spec.rb +24 -0
- data/spec/scss_lint/file_finder_spec.rb +134 -0
- data/spec/scss_lint/linter/bang_format_spec.rb +121 -0
- data/spec/scss_lint/linter/border_zero_spec.rb +118 -0
- data/spec/scss_lint/linter/color_keyword_spec.rb +83 -0
- data/spec/scss_lint/linter/color_variable_spec.rb +155 -0
- data/spec/scss_lint/linter/comment_spec.rb +79 -0
- data/spec/scss_lint/linter/compass/property_with_mixin_spec.rb +55 -0
- data/spec/scss_lint/linter/debug_statement_spec.rb +21 -0
- data/spec/scss_lint/linter/declaration_order_spec.rb +575 -0
- data/spec/scss_lint/linter/duplicate_property_spec.rb +189 -0
- data/spec/scss_lint/linter/else_placement_spec.rb +106 -0
- data/spec/scss_lint/linter/empty_line_between_blocks_spec.rb +276 -0
- data/spec/scss_lint/linter/empty_rule_spec.rb +27 -0
- data/spec/scss_lint/linter/final_newline_spec.rb +49 -0
- data/spec/scss_lint/linter/hex_length_spec.rb +104 -0
- data/spec/scss_lint/linter/hex_notation_spec.rb +104 -0
- data/spec/scss_lint/linter/hex_validation_spec.rb +40 -0
- data/spec/scss_lint/linter/id_selector_spec.rb +62 -0
- data/spec/scss_lint/linter/import_path_spec.rb +300 -0
- data/spec/scss_lint/linter/important_rule_spec.rb +43 -0
- data/spec/scss_lint/linter/indentation_spec.rb +347 -0
- data/spec/scss_lint/linter/leading_zero_spec.rb +233 -0
- data/spec/scss_lint/linter/mergeable_selector_spec.rb +283 -0
- data/spec/scss_lint/linter/name_format_spec.rb +282 -0
- data/spec/scss_lint/linter/nesting_depth_spec.rb +114 -0
- data/spec/scss_lint/linter/placeholder_in_extend_spec.rb +63 -0
- data/spec/scss_lint/linter/property_count_spec.rb +104 -0
- data/spec/scss_lint/linter/property_sort_order_spec.rb +482 -0
- data/spec/scss_lint/linter/property_spelling_spec.rb +84 -0
- data/spec/scss_lint/linter/property_units_spec.rb +229 -0
- data/spec/scss_lint/linter/qualifying_element_spec.rb +125 -0
- data/spec/scss_lint/linter/selector_depth_spec.rb +159 -0
- data/spec/scss_lint/linter/selector_format_spec.rb +632 -0
- data/spec/scss_lint/linter/shorthand_spec.rb +198 -0
- data/spec/scss_lint/linter/single_line_per_property_spec.rb +73 -0
- data/spec/scss_lint/linter/single_line_per_selector_spec.rb +130 -0
- data/spec/scss_lint/linter/space_after_comma_spec.rb +332 -0
- data/spec/scss_lint/linter/space_after_property_colon_spec.rb +373 -0
- data/spec/scss_lint/linter/space_after_property_name_spec.rb +37 -0
- data/spec/scss_lint/linter/space_before_brace_spec.rb +829 -0
- data/spec/scss_lint/linter/space_between_parens_spec.rb +263 -0
- data/spec/scss_lint/linter/string_quotes_spec.rb +335 -0
- data/spec/scss_lint/linter/trailing_semicolon_spec.rb +304 -0
- data/spec/scss_lint/linter/trailing_zero_spec.rb +176 -0
- data/spec/scss_lint/linter/unnecessary_mantissa_spec.rb +67 -0
- data/spec/scss_lint/linter/unnecessary_parent_reference_spec.rb +98 -0
- data/spec/scss_lint/linter/url_format_spec.rb +55 -0
- data/spec/scss_lint/linter/url_quotes_spec.rb +73 -0
- data/spec/scss_lint/linter/variable_for_property_spec.rb +145 -0
- data/spec/scss_lint/linter/vendor_prefix_spec.rb +371 -0
- data/spec/scss_lint/linter/zero_unit_spec.rb +113 -0
- data/spec/scss_lint/linter_registry_spec.rb +50 -0
- data/spec/scss_lint/linter_spec.rb +292 -0
- data/spec/scss_lint/location_spec.rb +42 -0
- data/spec/scss_lint/options_spec.rb +34 -0
- data/spec/scss_lint/rake_task_spec.rb +43 -0
- data/spec/scss_lint/reporter/config_reporter_spec.rb +42 -0
- data/spec/scss_lint/reporter/default_reporter_spec.rb +73 -0
- data/spec/scss_lint/reporter/files_reporter_spec.rb +38 -0
- data/spec/scss_lint/reporter/json_reporter_spec.rb +96 -0
- data/spec/scss_lint/reporter/xml_reporter_spec.rb +103 -0
- data/spec/scss_lint/reporter_spec.rb +11 -0
- data/spec/scss_lint/runner_spec.rb +123 -0
- data/spec/scss_lint/selector_visitor_spec.rb +264 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/isolated_environment.rb +25 -0
- data/spec/support/matchers/report_lint.rb +48 -0
- metadata +328 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
require 'rainbow'
|
|
2
|
+
require 'rainbow/ext/string'
|
|
3
|
+
require 'scss_lint/options'
|
|
4
|
+
|
|
5
|
+
module SCSSLint
|
|
6
|
+
# Responsible for parsing command-line options and executing the appropriate
|
|
7
|
+
# application logic based on the options specified.
|
|
8
|
+
class CLI
|
|
9
|
+
attr_reader :config, :options
|
|
10
|
+
|
|
11
|
+
# Subset of semantic exit codes conforming to `sysexits` documentation.
|
|
12
|
+
EXIT_CODES = {
|
|
13
|
+
ok: 0,
|
|
14
|
+
warning: 1, # One or more warnings (but no errors) were reported
|
|
15
|
+
error: 2, # One or more errors were reported
|
|
16
|
+
usage: 64, # Command line usage error
|
|
17
|
+
no_input: 66, # Input file did not exist or was not readable
|
|
18
|
+
unavailable: 69, # Required library is unavailable
|
|
19
|
+
software: 70, # Internal software error
|
|
20
|
+
config: 78, # Configuration error
|
|
21
|
+
no_files: 80, # No files matched by specified glob patterns
|
|
22
|
+
files_filtered: 81, # All matched files were filtered by exclusions
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
def run(args)
|
|
26
|
+
options = SCSSLint::Options.new.parse(args)
|
|
27
|
+
act_on_options(options)
|
|
28
|
+
rescue => ex
|
|
29
|
+
handle_runtime_exception(ex)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def act_on_options(options)
|
|
35
|
+
load_required_paths(options)
|
|
36
|
+
load_reporters(options)
|
|
37
|
+
|
|
38
|
+
if options[:help]
|
|
39
|
+
print_help(options)
|
|
40
|
+
elsif options[:version]
|
|
41
|
+
print_version
|
|
42
|
+
elsif options[:show_linters]
|
|
43
|
+
print_linters
|
|
44
|
+
elsif options[:show_formatters]
|
|
45
|
+
print_formatters
|
|
46
|
+
else
|
|
47
|
+
config = setup_configuration(options)
|
|
48
|
+
scan_for_lints(options, config)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def scan_for_lints(options, config)
|
|
53
|
+
runner = Runner.new(config)
|
|
54
|
+
runner.run(FileFinder.new(config).find(options[:files]))
|
|
55
|
+
report_lints(options, runner.lints)
|
|
56
|
+
|
|
57
|
+
if runner.lints.any?(&:error?)
|
|
58
|
+
halt :error
|
|
59
|
+
elsif runner.lints.any?
|
|
60
|
+
halt :warning
|
|
61
|
+
else
|
|
62
|
+
halt :ok
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def handle_runtime_exception(exception) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/LineLength, Metrics/MethodLength
|
|
67
|
+
case exception
|
|
68
|
+
when SCSSLint::Exceptions::InvalidCLIOption
|
|
69
|
+
puts exception.message
|
|
70
|
+
puts 'Run `scss-lint --help` for usage documentation'
|
|
71
|
+
halt :usage
|
|
72
|
+
when SCSSLint::Exceptions::InvalidConfiguration
|
|
73
|
+
puts exception.message
|
|
74
|
+
halt :config
|
|
75
|
+
when SCSSLint::Exceptions::RequiredLibraryMissingError
|
|
76
|
+
puts exception.message
|
|
77
|
+
halt :unavailable
|
|
78
|
+
when SCSSLint::Exceptions::AllFilesFilteredError
|
|
79
|
+
puts exception.message
|
|
80
|
+
halt :files_filtered
|
|
81
|
+
when SCSSLint::Exceptions::NoFilesError
|
|
82
|
+
puts exception.message
|
|
83
|
+
halt :no_files
|
|
84
|
+
when Errno::ENOENT
|
|
85
|
+
puts exception.message
|
|
86
|
+
halt :no_input
|
|
87
|
+
when NoSuchLinter
|
|
88
|
+
puts exception.message
|
|
89
|
+
halt :usage
|
|
90
|
+
else
|
|
91
|
+
puts exception.message
|
|
92
|
+
puts exception.backtrace
|
|
93
|
+
puts 'Report this bug at '.color(:yellow) + BUG_REPORT_URL.color(:cyan)
|
|
94
|
+
halt :software
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def setup_configuration(options)
|
|
99
|
+
config =
|
|
100
|
+
if options[:config_file]
|
|
101
|
+
Config.load(options[:config_file])
|
|
102
|
+
elsif File.exist?(Config::FILE_NAME)
|
|
103
|
+
Config.load(Config::FILE_NAME)
|
|
104
|
+
else
|
|
105
|
+
Config.default
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
merge_options_with_config(options, config)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# @param options [Hash]
|
|
112
|
+
# @param config [Config]
|
|
113
|
+
# @return [Config]
|
|
114
|
+
def merge_options_with_config(options, config)
|
|
115
|
+
if options[:excluded_files]
|
|
116
|
+
options[:excluded_files].each do |file|
|
|
117
|
+
config.exclude_file(file)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
if options[:included_linters]
|
|
122
|
+
config.disable_all_linters
|
|
123
|
+
LinterRegistry.extract_linters_from(options[:included_linters]).each do |linter|
|
|
124
|
+
config.enable_linter(linter)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
if options[:excluded_linters]
|
|
129
|
+
LinterRegistry.extract_linters_from(options[:excluded_linters]).each do |linter|
|
|
130
|
+
config.disable_linter(linter)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
config
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @param options [Hash]
|
|
138
|
+
# @param lints [Array<Lint>]
|
|
139
|
+
def report_lints(options, lints)
|
|
140
|
+
sorted_lints = lints.sort_by { |l| [l.filename, l.location] }
|
|
141
|
+
options.fetch(:reporters).each do |reporter, output|
|
|
142
|
+
results = reporter.new(sorted_lints).report_lints
|
|
143
|
+
io = (output == :stdout ? $stdout : File.new(output, 'w+'))
|
|
144
|
+
io.print results if results
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def load_required_paths(options)
|
|
149
|
+
Array(options[:required_paths]).each do |path|
|
|
150
|
+
require path
|
|
151
|
+
end
|
|
152
|
+
rescue LoadError => ex
|
|
153
|
+
raise SCSSLint::Exceptions::RequiredLibraryMissingError,
|
|
154
|
+
"Required library not found: #{ex.message}"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def load_reporters(options)
|
|
158
|
+
options[:reporters].map! do |reporter_name, output_file|
|
|
159
|
+
begin
|
|
160
|
+
reporter = SCSSLint::Reporter.const_get(reporter_name + 'Reporter')
|
|
161
|
+
rescue NameError
|
|
162
|
+
raise SCSSLint::Exceptions::InvalidCLIOption,
|
|
163
|
+
"Invalid output format specified: #{reporter_name}"
|
|
164
|
+
end
|
|
165
|
+
[reporter, output_file]
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def print_formatters
|
|
170
|
+
puts 'Installed formatters:'
|
|
171
|
+
|
|
172
|
+
reporter_names = SCSSLint::Reporter.descendants.map do |reporter|
|
|
173
|
+
reporter.name.split('::').last.split('Reporter').first
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
reporter_names.sort.each do |reporter_name|
|
|
177
|
+
puts " - #{reporter_name}"
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
halt
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def print_linters
|
|
184
|
+
puts 'Installed linters:'
|
|
185
|
+
|
|
186
|
+
linter_names = LinterRegistry.linters.map do |linter|
|
|
187
|
+
linter.name.split('::').last
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
linter_names.sort.each do |linter_name|
|
|
191
|
+
puts " - #{linter_name}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
halt
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# @param options [Hash]
|
|
198
|
+
def print_help(options)
|
|
199
|
+
puts options[:help]
|
|
200
|
+
halt :ok
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# @param options [Hash]
|
|
204
|
+
def print_version
|
|
205
|
+
puts "scss-lint #{SCSSLint::VERSION}"
|
|
206
|
+
halt :ok
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Used for ease-of testing
|
|
210
|
+
# @param exit_status [Symbol]
|
|
211
|
+
def halt(exit_status = :ok)
|
|
212
|
+
EXIT_CODES[exit_status]
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
|
|
3
|
+
module SCSSLint
|
|
4
|
+
# Loads and manages application configuration.
|
|
5
|
+
class Config
|
|
6
|
+
FILE_NAME = '.scss-lint.yml'
|
|
7
|
+
DEFAULT_FILE = File.join(SCSS_LINT_HOME, 'config', 'default.yml')
|
|
8
|
+
|
|
9
|
+
attr_reader :options, :warnings
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def default
|
|
13
|
+
load(DEFAULT_FILE, merge_with_default: false)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Loads a configuration from a file, merging it with the default
|
|
17
|
+
# configuration.
|
|
18
|
+
def load(file, options = {})
|
|
19
|
+
config_options = load_options_hash_from_file(file)
|
|
20
|
+
|
|
21
|
+
if options.fetch(:merge_with_default, true)
|
|
22
|
+
config_options = smart_merge(default_options_hash, config_options)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Config.new(config_options)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def linter_name(linter)
|
|
29
|
+
linter = linter.is_a?(Class) ? linter : linter.class
|
|
30
|
+
linter.name.split('::')[2..-1].join('::')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def default_options_hash
|
|
36
|
+
@default_options_hash ||= load_options_hash_from_file(DEFAULT_FILE)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Recursively load config files, fetching files specified by `include`
|
|
40
|
+
# directives and merging the file's config with the files specified.
|
|
41
|
+
def load_options_hash_from_file(file)
|
|
42
|
+
file_contents = load_file_contents(file)
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
options =
|
|
46
|
+
if yaml = YAML.load(file_contents)
|
|
47
|
+
yaml.to_hash
|
|
48
|
+
else
|
|
49
|
+
{}
|
|
50
|
+
end
|
|
51
|
+
rescue => ex
|
|
52
|
+
raise SCSSLint::Exceptions::InvalidConfiguration,
|
|
53
|
+
"Invalid configuration: #{ex.message}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
options = convert_single_options_to_arrays(options)
|
|
57
|
+
options = merge_wildcard_linter_options(options)
|
|
58
|
+
options = ensure_exclude_paths_are_absolute(options, file)
|
|
59
|
+
options = ensure_linter_exclude_paths_are_absolute(options, file)
|
|
60
|
+
options
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Convert any config options that accept a single value or an array to an
|
|
64
|
+
# array form so that merging works.
|
|
65
|
+
def convert_single_options_to_arrays(options)
|
|
66
|
+
options = options.dup
|
|
67
|
+
|
|
68
|
+
if options['exclude']
|
|
69
|
+
# Ensure exclude is an array, since we allow user to specify a single
|
|
70
|
+
# string.
|
|
71
|
+
options['exclude'] = [options['exclude']].flatten
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
options
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Merge options from wildcard linters into individual linter configs
|
|
78
|
+
def merge_wildcard_linter_options(options)
|
|
79
|
+
options = options.dup
|
|
80
|
+
|
|
81
|
+
options.fetch('linters', {}).keys.each do |class_name|
|
|
82
|
+
next unless class_name.include?('*')
|
|
83
|
+
|
|
84
|
+
wildcard_options = options['linters'].delete(class_name)
|
|
85
|
+
apply_options_to_matching_linters(class_name, options, wildcard_options)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
options
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def apply_options_to_matching_linters(class_name_glob, current_options, linter_options)
|
|
92
|
+
linter_names_matching_glob(class_name_glob).each do |linter_name|
|
|
93
|
+
old_options = current_options['linters'].fetch(linter_name, {})
|
|
94
|
+
current_options['linters'][linter_name] = smart_merge(old_options, linter_options)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def linter_names_matching_glob(class_name_glob)
|
|
99
|
+
class_name_regex = /#{class_name_glob.gsub('*', '[^:]+')}/
|
|
100
|
+
|
|
101
|
+
LinterRegistry.linters.map { |linter_class| linter_name(linter_class) }
|
|
102
|
+
.select { |linter_name| linter_name.match(class_name_regex) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def ensure_linter_exclude_paths_are_absolute(options, original_file)
|
|
106
|
+
options = options.dup
|
|
107
|
+
|
|
108
|
+
options['linters'] ||= {}
|
|
109
|
+
|
|
110
|
+
options['linters'].keys.each do |linter_name|
|
|
111
|
+
options['linters'][linter_name] =
|
|
112
|
+
ensure_exclude_paths_are_absolute(options['linters'][linter_name], original_file)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
options
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Ensure all excludes are absolute paths
|
|
119
|
+
def ensure_exclude_paths_are_absolute(options, original_file)
|
|
120
|
+
options = options.dup
|
|
121
|
+
|
|
122
|
+
if options['exclude']
|
|
123
|
+
excludes = [options['exclude']].flatten
|
|
124
|
+
|
|
125
|
+
options['exclude'] = excludes.map do |exclusion_glob|
|
|
126
|
+
if exclusion_glob.start_with?('/')
|
|
127
|
+
exclusion_glob
|
|
128
|
+
else
|
|
129
|
+
# Expand the path assuming it is relative to the config file itself
|
|
130
|
+
File.expand_path(exclusion_glob, File.expand_path(File.dirname(original_file)))
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
options
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def path_relative_to_config(relative_include_path, base_config_path)
|
|
139
|
+
if relative_include_path.start_with?('/')
|
|
140
|
+
relative_include_path
|
|
141
|
+
else
|
|
142
|
+
path = File.join(File.dirname(base_config_path), relative_include_path)
|
|
143
|
+
# Remove double backslashes appearing in Windows paths.
|
|
144
|
+
path.gsub(%r{^//}, File::SEPARATOR)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# For easy stubbing in tests
|
|
149
|
+
def load_file_contents(file)
|
|
150
|
+
File.open(file, 'r').read
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Merge two hashes, concatenating lists and further merging nested hashes.
|
|
154
|
+
def smart_merge(parent, child)
|
|
155
|
+
parent.merge(child) do |_key, old, new|
|
|
156
|
+
case old
|
|
157
|
+
when Hash
|
|
158
|
+
smart_merge(old, new)
|
|
159
|
+
else
|
|
160
|
+
new
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def initialize(options)
|
|
167
|
+
@options = options
|
|
168
|
+
@warnings = []
|
|
169
|
+
|
|
170
|
+
validate_linters
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def enabled_linters
|
|
174
|
+
LinterRegistry.extract_linters_from(@options['linters'].keys).select do |linter|
|
|
175
|
+
linter_options(linter)['enabled']
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def linter_enabled?(linter)
|
|
180
|
+
linter_options(linter)['enabled']
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def enable_linter(linter)
|
|
184
|
+
linter_options(linter)['enabled'] = true
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def disable_linter(linter)
|
|
188
|
+
linter_options(linter)['enabled'] = false
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def disable_all_linters
|
|
192
|
+
@options['linters'].values.each do |linter_config|
|
|
193
|
+
linter_config['enabled'] = false
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def linter_options(linter)
|
|
198
|
+
@options['linters'][self.class.linter_name(linter)]
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def excluded_file?(file_path)
|
|
202
|
+
abs_path = File.expand_path(file_path)
|
|
203
|
+
|
|
204
|
+
@options.fetch('exclude', []).any? do |exclusion_glob|
|
|
205
|
+
File.fnmatch(exclusion_glob, abs_path)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def exclude_patterns
|
|
210
|
+
@options.fetch('exclude', [])
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def excluded_file_for_linter?(file_path, linter)
|
|
214
|
+
abs_path = File.expand_path(file_path)
|
|
215
|
+
|
|
216
|
+
linter_options(linter).fetch('exclude', []).any? do |exclusion_glob|
|
|
217
|
+
File.fnmatch(exclusion_glob, abs_path)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def exclude_file(file_path)
|
|
222
|
+
abs_path = File.expand_path(file_path)
|
|
223
|
+
|
|
224
|
+
@options['exclude'] ||= []
|
|
225
|
+
@options['exclude'] << abs_path
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# @return Array
|
|
229
|
+
def scss_files
|
|
230
|
+
if path = @options['scss_files']
|
|
231
|
+
Dir[path]
|
|
232
|
+
else
|
|
233
|
+
[]
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
private
|
|
238
|
+
|
|
239
|
+
def validate_linters
|
|
240
|
+
return unless linters = @options['linters']
|
|
241
|
+
|
|
242
|
+
linters.keys.each do |name|
|
|
243
|
+
begin
|
|
244
|
+
Linter.const_get(name)
|
|
245
|
+
rescue NameError
|
|
246
|
+
@warnings << "Linter #{name} does not exist; ignoring"
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|