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