scss_lint 0.38.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/bin/scss-lint +6 -0
  3. data/config/default.yml +205 -0
  4. data/data/prefixed-identifiers/base.txt +107 -0
  5. data/data/prefixed-identifiers/bourbon.txt +71 -0
  6. data/data/properties.txt +477 -0
  7. data/data/property-sort-orders/concentric.txt +134 -0
  8. data/data/property-sort-orders/recess.txt +149 -0
  9. data/data/property-sort-orders/smacss.txt +137 -0
  10. data/lib/scss_lint.rb +31 -0
  11. data/lib/scss_lint/cli.rb +215 -0
  12. data/lib/scss_lint/config.rb +251 -0
  13. data/lib/scss_lint/constants.rb +8 -0
  14. data/lib/scss_lint/control_comment_processor.rb +126 -0
  15. data/lib/scss_lint/engine.rb +56 -0
  16. data/lib/scss_lint/exceptions.rb +21 -0
  17. data/lib/scss_lint/file_finder.rb +68 -0
  18. data/lib/scss_lint/lint.rb +24 -0
  19. data/lib/scss_lint/linter.rb +161 -0
  20. data/lib/scss_lint/linter/bang_format.rb +52 -0
  21. data/lib/scss_lint/linter/border_zero.rb +39 -0
  22. data/lib/scss_lint/linter/color_keyword.rb +32 -0
  23. data/lib/scss_lint/linter/color_variable.rb +60 -0
  24. data/lib/scss_lint/linter/comment.rb +21 -0
  25. data/lib/scss_lint/linter/compass.rb +7 -0
  26. data/lib/scss_lint/linter/compass/property_with_mixin.rb +47 -0
  27. data/lib/scss_lint/linter/debug_statement.rb +10 -0
  28. data/lib/scss_lint/linter/declaration_order.rb +71 -0
  29. data/lib/scss_lint/linter/duplicate_property.rb +58 -0
  30. data/lib/scss_lint/linter/else_placement.rb +48 -0
  31. data/lib/scss_lint/linter/empty_line_between_blocks.rb +85 -0
  32. data/lib/scss_lint/linter/empty_rule.rb +11 -0
  33. data/lib/scss_lint/linter/final_newline.rb +20 -0
  34. data/lib/scss_lint/linter/hex_length.rb +56 -0
  35. data/lib/scss_lint/linter/hex_notation.rb +38 -0
  36. data/lib/scss_lint/linter/hex_validation.rb +23 -0
  37. data/lib/scss_lint/linter/id_selector.rb +10 -0
  38. data/lib/scss_lint/linter/import_path.rb +62 -0
  39. data/lib/scss_lint/linter/important_rule.rb +12 -0
  40. data/lib/scss_lint/linter/indentation.rb +197 -0
  41. data/lib/scss_lint/linter/leading_zero.rb +49 -0
  42. data/lib/scss_lint/linter/mergeable_selector.rb +60 -0
  43. data/lib/scss_lint/linter/name_format.rb +117 -0
  44. data/lib/scss_lint/linter/nesting_depth.rb +24 -0
  45. data/lib/scss_lint/linter/placeholder_in_extend.rb +22 -0
  46. data/lib/scss_lint/linter/property_count.rb +44 -0
  47. data/lib/scss_lint/linter/property_sort_order.rb +198 -0
  48. data/lib/scss_lint/linter/property_spelling.rb +49 -0
  49. data/lib/scss_lint/linter/property_units.rb +59 -0
  50. data/lib/scss_lint/linter/qualifying_element.rb +42 -0
  51. data/lib/scss_lint/linter/selector_depth.rb +64 -0
  52. data/lib/scss_lint/linter/selector_format.rb +102 -0
  53. data/lib/scss_lint/linter/shorthand.rb +139 -0
  54. data/lib/scss_lint/linter/single_line_per_property.rb +59 -0
  55. data/lib/scss_lint/linter/single_line_per_selector.rb +35 -0
  56. data/lib/scss_lint/linter/space_after_comma.rb +110 -0
  57. data/lib/scss_lint/linter/space_after_property_colon.rb +92 -0
  58. data/lib/scss_lint/linter/space_after_property_name.rb +27 -0
  59. data/lib/scss_lint/linter/space_before_brace.rb +72 -0
  60. data/lib/scss_lint/linter/space_between_parens.rb +35 -0
  61. data/lib/scss_lint/linter/string_quotes.rb +94 -0
  62. data/lib/scss_lint/linter/trailing_semicolon.rb +67 -0
  63. data/lib/scss_lint/linter/trailing_zero.rb +41 -0
  64. data/lib/scss_lint/linter/unnecessary_mantissa.rb +42 -0
  65. data/lib/scss_lint/linter/unnecessary_parent_reference.rb +49 -0
  66. data/lib/scss_lint/linter/url_format.rb +56 -0
  67. data/lib/scss_lint/linter/url_quotes.rb +27 -0
  68. data/lib/scss_lint/linter/variable_for_property.rb +30 -0
  69. data/lib/scss_lint/linter/vendor_prefix.rb +64 -0
  70. data/lib/scss_lint/linter/zero_unit.rb +39 -0
  71. data/lib/scss_lint/linter_registry.rb +26 -0
  72. data/lib/scss_lint/location.rb +38 -0
  73. data/lib/scss_lint/options.rb +109 -0
  74. data/lib/scss_lint/rake_task.rb +106 -0
  75. data/lib/scss_lint/reporter.rb +18 -0
  76. data/lib/scss_lint/reporter/config_reporter.rb +26 -0
  77. data/lib/scss_lint/reporter/default_reporter.rb +27 -0
  78. data/lib/scss_lint/reporter/files_reporter.rb +8 -0
  79. data/lib/scss_lint/reporter/json_reporter.rb +30 -0
  80. data/lib/scss_lint/reporter/xml_reporter.rb +33 -0
  81. data/lib/scss_lint/runner.rb +51 -0
  82. data/lib/scss_lint/sass/script.rb +78 -0
  83. data/lib/scss_lint/sass/tree.rb +168 -0
  84. data/lib/scss_lint/selector_visitor.rb +34 -0
  85. data/lib/scss_lint/utils.rb +112 -0
  86. data/lib/scss_lint/version.rb +4 -0
  87. data/spec/scss_lint/cli_spec.rb +177 -0
  88. data/spec/scss_lint/config_spec.rb +253 -0
  89. data/spec/scss_lint/engine_spec.rb +24 -0
  90. data/spec/scss_lint/file_finder_spec.rb +134 -0
  91. data/spec/scss_lint/linter/bang_format_spec.rb +121 -0
  92. data/spec/scss_lint/linter/border_zero_spec.rb +118 -0
  93. data/spec/scss_lint/linter/color_keyword_spec.rb +83 -0
  94. data/spec/scss_lint/linter/color_variable_spec.rb +155 -0
  95. data/spec/scss_lint/linter/comment_spec.rb +79 -0
  96. data/spec/scss_lint/linter/compass/property_with_mixin_spec.rb +55 -0
  97. data/spec/scss_lint/linter/debug_statement_spec.rb +21 -0
  98. data/spec/scss_lint/linter/declaration_order_spec.rb +575 -0
  99. data/spec/scss_lint/linter/duplicate_property_spec.rb +189 -0
  100. data/spec/scss_lint/linter/else_placement_spec.rb +106 -0
  101. data/spec/scss_lint/linter/empty_line_between_blocks_spec.rb +276 -0
  102. data/spec/scss_lint/linter/empty_rule_spec.rb +27 -0
  103. data/spec/scss_lint/linter/final_newline_spec.rb +49 -0
  104. data/spec/scss_lint/linter/hex_length_spec.rb +104 -0
  105. data/spec/scss_lint/linter/hex_notation_spec.rb +104 -0
  106. data/spec/scss_lint/linter/hex_validation_spec.rb +40 -0
  107. data/spec/scss_lint/linter/id_selector_spec.rb +62 -0
  108. data/spec/scss_lint/linter/import_path_spec.rb +300 -0
  109. data/spec/scss_lint/linter/important_rule_spec.rb +43 -0
  110. data/spec/scss_lint/linter/indentation_spec.rb +347 -0
  111. data/spec/scss_lint/linter/leading_zero_spec.rb +233 -0
  112. data/spec/scss_lint/linter/mergeable_selector_spec.rb +283 -0
  113. data/spec/scss_lint/linter/name_format_spec.rb +282 -0
  114. data/spec/scss_lint/linter/nesting_depth_spec.rb +114 -0
  115. data/spec/scss_lint/linter/placeholder_in_extend_spec.rb +63 -0
  116. data/spec/scss_lint/linter/property_count_spec.rb +104 -0
  117. data/spec/scss_lint/linter/property_sort_order_spec.rb +482 -0
  118. data/spec/scss_lint/linter/property_spelling_spec.rb +84 -0
  119. data/spec/scss_lint/linter/property_units_spec.rb +229 -0
  120. data/spec/scss_lint/linter/qualifying_element_spec.rb +125 -0
  121. data/spec/scss_lint/linter/selector_depth_spec.rb +159 -0
  122. data/spec/scss_lint/linter/selector_format_spec.rb +632 -0
  123. data/spec/scss_lint/linter/shorthand_spec.rb +198 -0
  124. data/spec/scss_lint/linter/single_line_per_property_spec.rb +73 -0
  125. data/spec/scss_lint/linter/single_line_per_selector_spec.rb +130 -0
  126. data/spec/scss_lint/linter/space_after_comma_spec.rb +332 -0
  127. data/spec/scss_lint/linter/space_after_property_colon_spec.rb +373 -0
  128. data/spec/scss_lint/linter/space_after_property_name_spec.rb +37 -0
  129. data/spec/scss_lint/linter/space_before_brace_spec.rb +829 -0
  130. data/spec/scss_lint/linter/space_between_parens_spec.rb +263 -0
  131. data/spec/scss_lint/linter/string_quotes_spec.rb +335 -0
  132. data/spec/scss_lint/linter/trailing_semicolon_spec.rb +304 -0
  133. data/spec/scss_lint/linter/trailing_zero_spec.rb +176 -0
  134. data/spec/scss_lint/linter/unnecessary_mantissa_spec.rb +67 -0
  135. data/spec/scss_lint/linter/unnecessary_parent_reference_spec.rb +98 -0
  136. data/spec/scss_lint/linter/url_format_spec.rb +55 -0
  137. data/spec/scss_lint/linter/url_quotes_spec.rb +73 -0
  138. data/spec/scss_lint/linter/variable_for_property_spec.rb +145 -0
  139. data/spec/scss_lint/linter/vendor_prefix_spec.rb +371 -0
  140. data/spec/scss_lint/linter/zero_unit_spec.rb +113 -0
  141. data/spec/scss_lint/linter_registry_spec.rb +50 -0
  142. data/spec/scss_lint/linter_spec.rb +292 -0
  143. data/spec/scss_lint/location_spec.rb +42 -0
  144. data/spec/scss_lint/options_spec.rb +34 -0
  145. data/spec/scss_lint/rake_task_spec.rb +43 -0
  146. data/spec/scss_lint/reporter/config_reporter_spec.rb +42 -0
  147. data/spec/scss_lint/reporter/default_reporter_spec.rb +73 -0
  148. data/spec/scss_lint/reporter/files_reporter_spec.rb +38 -0
  149. data/spec/scss_lint/reporter/json_reporter_spec.rb +96 -0
  150. data/spec/scss_lint/reporter/xml_reporter_spec.rb +103 -0
  151. data/spec/scss_lint/reporter_spec.rb +11 -0
  152. data/spec/scss_lint/runner_spec.rb +123 -0
  153. data/spec/scss_lint/selector_visitor_spec.rb +264 -0
  154. data/spec/spec_helper.rb +34 -0
  155. data/spec/support/isolated_environment.rb +25 -0
  156. data/spec/support/matchers/report_lint.rb +48 -0
  157. 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