haml_lint 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/bin/haml-lint +7 -0
  3. data/config/default.yml +91 -0
  4. data/lib/haml_lint/cli.rb +122 -0
  5. data/lib/haml_lint/configuration.rb +97 -0
  6. data/lib/haml_lint/configuration_loader.rb +68 -0
  7. data/lib/haml_lint/constants.rb +8 -0
  8. data/lib/haml_lint/exceptions.rb +15 -0
  9. data/lib/haml_lint/file_finder.rb +69 -0
  10. data/lib/haml_lint/haml_visitor.rb +36 -0
  11. data/lib/haml_lint/lint.rb +25 -0
  12. data/lib/haml_lint/linter/alt_text.rb +12 -0
  13. data/lib/haml_lint/linter/class_attribute_with_static_value.rb +51 -0
  14. data/lib/haml_lint/linter/classes_before_ids.rb +26 -0
  15. data/lib/haml_lint/linter/consecutive_comments.rb +20 -0
  16. data/lib/haml_lint/linter/consecutive_silent_scripts.rb +23 -0
  17. data/lib/haml_lint/linter/empty_script.rb +12 -0
  18. data/lib/haml_lint/linter/html_attributes.rb +14 -0
  19. data/lib/haml_lint/linter/implicit_div.rb +20 -0
  20. data/lib/haml_lint/linter/leading_comment_space.rb +14 -0
  21. data/lib/haml_lint/linter/line_length.rb +19 -0
  22. data/lib/haml_lint/linter/multiline_pipe.rb +43 -0
  23. data/lib/haml_lint/linter/multiline_script.rb +43 -0
  24. data/lib/haml_lint/linter/object_reference_attributes.rb +14 -0
  25. data/lib/haml_lint/linter/rubocop.rb +76 -0
  26. data/lib/haml_lint/linter/ruby_comments.rb +18 -0
  27. data/lib/haml_lint/linter/space_before_script.rb +52 -0
  28. data/lib/haml_lint/linter/space_inside_hash_attributes.rb +32 -0
  29. data/lib/haml_lint/linter/tag_name.rb +13 -0
  30. data/lib/haml_lint/linter/trailing_whitespace.rb +16 -0
  31. data/lib/haml_lint/linter/unnecessary_interpolation.rb +29 -0
  32. data/lib/haml_lint/linter/unnecessary_string_output.rb +39 -0
  33. data/lib/haml_lint/linter.rb +156 -0
  34. data/lib/haml_lint/linter_registry.rb +26 -0
  35. data/lib/haml_lint/logger.rb +107 -0
  36. data/lib/haml_lint/node_transformer.rb +28 -0
  37. data/lib/haml_lint/options.rb +89 -0
  38. data/lib/haml_lint/parser.rb +87 -0
  39. data/lib/haml_lint/rake_task.rb +107 -0
  40. data/lib/haml_lint/report.rb +16 -0
  41. data/lib/haml_lint/reporter/default_reporter.rb +39 -0
  42. data/lib/haml_lint/reporter/json_reporter.rb +44 -0
  43. data/lib/haml_lint/reporter.rb +36 -0
  44. data/lib/haml_lint/ruby_parser.rb +29 -0
  45. data/lib/haml_lint/runner.rb +76 -0
  46. data/lib/haml_lint/script_extractor.rb +181 -0
  47. data/lib/haml_lint/tree/comment_node.rb +5 -0
  48. data/lib/haml_lint/tree/doctype_node.rb +5 -0
  49. data/lib/haml_lint/tree/filter_node.rb +9 -0
  50. data/lib/haml_lint/tree/haml_comment_node.rb +18 -0
  51. data/lib/haml_lint/tree/node.rb +98 -0
  52. data/lib/haml_lint/tree/plain_node.rb +5 -0
  53. data/lib/haml_lint/tree/root_node.rb +5 -0
  54. data/lib/haml_lint/tree/script_node.rb +11 -0
  55. data/lib/haml_lint/tree/silent_script_node.rb +12 -0
  56. data/lib/haml_lint/tree/tag_node.rb +221 -0
  57. data/lib/haml_lint/utils.rb +58 -0
  58. data/lib/haml_lint/version.rb +4 -0
  59. data/lib/haml_lint.rb +36 -0
  60. metadata +175 -0
@@ -0,0 +1,107 @@
1
+ module HamlLint
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 [HamlLint::Logger]
10
+ def self.silent
11
+ new(File.open('/dev/null', 'w'))
12
+ end
13
+
14
+ # Creates a new {HamlLint::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
+ # @return [nil]
26
+ def log(output, newline = true)
27
+ @out.print(output)
28
+ @out.print("\n") if newline
29
+ end
30
+
31
+ # Print the specified output in bold face.
32
+ # If output destination is not a TTY, behaves the same as {#log}.
33
+ #
34
+ # @param args [Array<String>]
35
+ # @return [nil]
36
+ def bold(*args)
37
+ color('1', *args)
38
+ end
39
+
40
+ # Print the specified output in a color indicative of error.
41
+ # If output destination is not a TTY, behaves the same as {#log}.
42
+ #
43
+ # @param args [Array<String>]
44
+ # @return [nil]
45
+ def error(*args)
46
+ color(31, *args)
47
+ end
48
+
49
+ # Print the specified output in a bold face and color indicative of error.
50
+ # If output destination is not a TTY, behaves the same as {#log}.
51
+ #
52
+ # @param args [Array<String>]
53
+ # @return [nil]
54
+ def bold_error(*args)
55
+ color('1;31', *args)
56
+ end
57
+
58
+ # Print the specified output in a color indicative of success.
59
+ # If output destination is not a TTY, behaves the same as {#log}.
60
+ #
61
+ # @param args [Array<String>]
62
+ # @return [nil]
63
+ def success(*args)
64
+ color(32, *args)
65
+ end
66
+
67
+ # Print the specified output in a color indicative of a warning.
68
+ # If output destination is not a TTY, behaves the same as {#log}.
69
+ #
70
+ # @param args [Array<String>]
71
+ # @return [nil]
72
+ def warning(*args)
73
+ color(33, *args)
74
+ end
75
+
76
+ # Print specified output in bold face in a color indicative of a warning.
77
+ # If output destination is not a TTY, behaves the same as {#log}.
78
+ #
79
+ # @param args [Array<String>]
80
+ # @return [nil]
81
+ def bold_warning(*args)
82
+ color('1;33', *args)
83
+ end
84
+
85
+ # Print the specified output in a color indicating information.
86
+ # If output destination is not a TTY, behaves the same as {#log}.
87
+ #
88
+ # @param args [Array<String>]
89
+ # @return [nil]
90
+ def info(*args)
91
+ color(36, *args)
92
+ end
93
+
94
+ # Whether this logger is outputting to a TTY.
95
+ #
96
+ # @return [true,false]
97
+ def tty?
98
+ @out.respond_to?(:tty?) && @out.tty?
99
+ end
100
+
101
+ private
102
+
103
+ def color(code, output, newline = true)
104
+ log(color_enabled ? "\033[#{code}m#{output}\033[0m" : output, newline)
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,28 @@
1
+ module HamlLint
2
+ # Responsible for transforming {Haml::Parser::ParseNode} objects into
3
+ # corresponding {HamlLint::Tree::Node} objects.
4
+ #
5
+ # The parse tree generated by HAML has a number of strange cases where certain
6
+ # types of nodes are created that don't necessarily correspond to what one
7
+ # would expect. This class is intended to isolate and handle these cases so
8
+ # that linters don't have to deal with them.
9
+ class NodeTransformer
10
+ # Creates a node transformer for the given parser context.
11
+ #
12
+ # @param parser [HamlLint::Parser]
13
+ def initialize(parser)
14
+ @parser = parser
15
+ end
16
+
17
+ # Transforms the given {Haml::Parser::ParseNode} into its corresponding
18
+ # {HamlLint::Tree::Node}.
19
+ def transform(haml_node)
20
+ node_class = "#{HamlLint::Utils.camel_case(haml_node.type.to_s)}Node"
21
+
22
+ HamlLint::Tree.const_get(node_class).new(@parser, haml_node)
23
+ rescue NameError
24
+ # TODO: Wrap in parser error?
25
+ raise
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,89 @@
1
+ require 'optparse'
2
+
3
+ module HamlLint
4
+ # Handles option parsing for the command line application.
5
+ class Options
6
+ # Parses command line options into an options hash.
7
+ #
8
+ # @param args [Array<String>] arguments passed via the command line
9
+ # @return [Hash] parsed options
10
+ def parse(args)
11
+ @options = {}
12
+
13
+ OptionParser.new do |parser|
14
+ parser.banner = "Usage: #{APP_NAME} [options] [file1, file2, ...]"
15
+
16
+ add_linter_options parser
17
+ add_file_options parser
18
+ add_info_options parser
19
+ end.parse!(args)
20
+
21
+ # Any remaining arguments are assumed to be files
22
+ @options[:files] = args
23
+
24
+ @options
25
+ rescue OptionParser::InvalidOption => ex
26
+ raise Exceptions::InvalidCLIOption,
27
+ ex.message,
28
+ ex.backtrace
29
+ end
30
+
31
+ private
32
+
33
+ def add_linter_options(parser)
34
+ parser.on('-e', '--exclude file,...', Array,
35
+ 'List of file names to exclude') do |files|
36
+ @options[:excluded_files] = files
37
+ end
38
+
39
+ parser.on('-i', '--include-linter linter,...', Array,
40
+ 'Specify which linters you want to run') do |linters|
41
+ @options[:included_linters] = linters
42
+ end
43
+
44
+ parser.on('-x', '--exclude-linter linter,...', Array,
45
+ "Specify which linters you don't want to run") do |linters|
46
+ @options[:excluded_linters] = linters
47
+ end
48
+
49
+ parser.on('-r', '--reporter reporter', String,
50
+ 'Specify which reporter you want to use to generate the output') do |reporter|
51
+ @options[:reporter] = HamlLint::Reporter.const_get("#{reporter.capitalize}Reporter")
52
+ end
53
+ end
54
+
55
+ def add_file_options(parser)
56
+ parser.on('-c', '--config config-file', String,
57
+ 'Specify which configuration file you want to use') do |conf_file|
58
+ @options[:config_file] = conf_file
59
+ end
60
+
61
+ parser.on('-e', '--exclude file,...', Array,
62
+ 'List of file names to exclude') do |files|
63
+ @options[:excluded_files] = files
64
+ end
65
+ end
66
+
67
+ def add_info_options(parser)
68
+ parser.on('--show-linters', 'Display available linters') do
69
+ @options[:show_linters] = true
70
+ end
71
+
72
+ parser.on('--show-reporters', 'Display available reporters') do
73
+ @options[:show_reporters] = true
74
+ end
75
+
76
+ parser.on('--[no-]color', 'Force output to be colorized') do |color|
77
+ @options[:color] = color
78
+ end
79
+
80
+ parser.on_tail('-h', '--help', 'Display help documentation') do
81
+ @options[:help] = parser.help
82
+ end
83
+
84
+ parser.on_tail('-v', '--version', 'Display version') do
85
+ @options[:version] = true
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,87 @@
1
+ require 'haml'
2
+
3
+ module HamlLint
4
+ # Parses a HAML document for inspection by linters.
5
+ class Parser
6
+ attr_reader :contents, :filename, :lines, :tree
7
+
8
+ # Creates a parser containing the parse tree of a HAML document.
9
+ #
10
+ # @param haml_or_filename [String]
11
+ # @param options [Hash]
12
+ # @option options [true,false] 'skip_frontmatter' Whether to skip
13
+ # frontmatter included by frameworks such as Middleman or Jekyll
14
+ def initialize(haml_or_filename, options = {})
15
+ if File.exist?(haml_or_filename)
16
+ build_from_file(haml_or_filename)
17
+ else
18
+ build_from_string(haml_or_filename)
19
+ end
20
+
21
+ process_options(options)
22
+
23
+ build_parse_tree
24
+ end
25
+
26
+ private
27
+
28
+ # @param path [String]
29
+ def build_from_file(path)
30
+ @filename = path
31
+ @contents = File.read(path)
32
+ end
33
+
34
+ # @param haml [String]
35
+ def build_from_string(haml)
36
+ @contents = haml
37
+ end
38
+
39
+ def build_parse_tree
40
+ original_tree = Haml::Parser.new(@contents, Haml::Options.new).parse
41
+
42
+ # Remove the trailing empty HAML comment that the parser creates to signal
43
+ # the end of the HAML document
44
+ if Gem::Requirement.new('~> 4.0.0').satisfied_by?(Gem.loaded_specs['haml'].version)
45
+ original_tree.children.pop
46
+ end
47
+
48
+ @node_transformer = HamlLint::NodeTransformer.new(self)
49
+ @tree = convert_tree(original_tree)
50
+ end
51
+
52
+ def process_options(options)
53
+ if options['skip_frontmatter'] &&
54
+ @contents =~ /
55
+ # From the start of the string
56
+ \A
57
+ # First-capture match --- followed by optional whitespace up
58
+ # to a newline then 0 or more chars followed by an optional newline.
59
+ # This matches the --- and the contents of the frontmatter
60
+ (---\s*\n.*?\n?)
61
+ # From the start of the line
62
+ ^
63
+ # Second capture match --- or ... followed by optional whitespace
64
+ # and newline. This matches the closing --- for the frontmatter.
65
+ (---|\.\.\.)\s*$\n?/mx
66
+ @contents = $POSTMATCH
67
+ end
68
+
69
+ @lines = @contents.split("\n")
70
+ end
71
+
72
+ # Converts a HAML parse tree to a tree of {HamlLint::Tree::Node} objects.
73
+ #
74
+ # This provides a cleaner interface with which the linters can interact with
75
+ # the parse tree.
76
+ def convert_tree(haml_node, parent = nil)
77
+ new_node = @node_transformer.transform(haml_node)
78
+ new_node.parent = parent
79
+
80
+ new_node.children = haml_node.children.map do |child|
81
+ convert_tree(child, new_node)
82
+ end
83
+
84
+ new_node
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,107 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+
4
+ module HamlLint
5
+ # Rake task interface for haml-lint command line interface.
6
+ #
7
+ # @example
8
+ # # Add the following to your Rakefile...
9
+ # require 'haml_lint/rake_task'
10
+ #
11
+ # HamlLint::RakeTask.new do |t|
12
+ # t.config = 'path/to/custom/haml-lint.yml'
13
+ # t.files = %w[app/views/**/*.haml custom/*.haml]
14
+ # t.quiet = true # Don't display output from haml-lint
15
+ # end
16
+ #
17
+ # # ...and then execute from the command line:
18
+ # rake haml_lint
19
+ #
20
+ # You can also specify the list of files as explicit task arguments:
21
+ #
22
+ # @example
23
+ # # Add the following to your Rakefile...
24
+ # require 'haml_lint/rake_task'
25
+ #
26
+ # HamlLint::RakeTask.new
27
+ #
28
+ # # ...and then execute from the command line (single quotes prevent shell
29
+ # # glob expansion and allow us to have a space after commas):
30
+ # rake 'haml_lint[app/views/**/*.haml, other_files/**/*.haml]'
31
+ #
32
+ class RakeTask < Rake::TaskLib
33
+ # Name of the task.
34
+ # @return [String]
35
+ attr_accessor :name
36
+
37
+ # Configuration file to use.
38
+ # @return [String]
39
+ attr_accessor :config
40
+
41
+ # List of files to lint (can contain shell globs).
42
+ #
43
+ # Note that this will be ignored if you explicitly pass a list of files as
44
+ # task arguments via the command line or a task definition.
45
+ # @return [Array<String>]
46
+ attr_accessor :files
47
+
48
+ # Whether output from haml-lint should not be displayed to the standard out
49
+ # stream.
50
+ # @return [true,false]
51
+ attr_accessor :quiet
52
+
53
+ # Create the task so it exists in the current namespace.
54
+ def initialize(name = :haml_lint)
55
+ @name = name
56
+ @files = ['.'] # Search for everything under current directory by default
57
+ @quiet = false
58
+
59
+ yield self if block_given?
60
+
61
+ define
62
+ end
63
+
64
+ private
65
+
66
+ def define
67
+ desc default_description unless ::Rake.application.last_description
68
+
69
+ task(name, [:files]) do |_task, task_args|
70
+ # Lazy-load so task doesn't affect Rakefile load time
71
+ require 'haml_lint'
72
+ require 'haml_lint/cli'
73
+
74
+ run_cli(task_args)
75
+ end
76
+ end
77
+
78
+ def run_cli(task_args)
79
+ cli_args = ['--config', config] if config
80
+
81
+ logger = quiet ? HamlLint::Logger.silent : HamlLint::Logger.new(STDOUT)
82
+ result = HamlLint::CLI.new(logger).run(Array(cli_args) + files_to_lint(task_args))
83
+
84
+ fail "haml-lint failed with exit code #{result}" unless result == 0
85
+ end
86
+
87
+ def files_to_lint(task_args)
88
+ # Note: we're abusing Rake's argument handling a bit here. We call the
89
+ # first argument `files` but it's actually only the first file--we pull
90
+ # the rest out of the `extras` from the task arguments. This is so we
91
+ # can specify an arbitrary list of files separated by commas on the
92
+ # command line or in a custom task definition.
93
+ explicit_files = Array(task_args[:files]) + Array(task_args.extras)
94
+
95
+ explicit_files.any? ? explicit_files : files
96
+ end
97
+
98
+ # Friendly description that shows the full command that will be executed.
99
+ def default_description
100
+ description = 'Run `haml-lint'
101
+ description += " --config #{config}" if config
102
+ description += " #{files.join(' ')}" if files.any?
103
+ description += ' [files...]`'
104
+ description
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,16 @@
1
+ module HamlLint
2
+ # Contains information about all lints detected during a scan.
3
+ class Report
4
+ attr_accessor :lints
5
+ attr_reader :files
6
+
7
+ def initialize(lints, files)
8
+ @lints = lints.sort_by { |l| [l.filename, l.line] }
9
+ @files = files
10
+ end
11
+
12
+ def failed?
13
+ @lints.any?
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ module HamlLint
2
+ # Outputs lints in a simple format with the filename, line number, and lint
3
+ # message.
4
+ class Reporter::DefaultReporter < Reporter
5
+ def report_lints
6
+ sorted_lints = lints.sort_by { |l| [l.filename, l.line] }
7
+
8
+ sorted_lints.each do |lint|
9
+ print_location(lint)
10
+ print_type(lint)
11
+ print_message(lint)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def print_location(lint)
18
+ log.info lint.filename, false
19
+ log.log ':', false
20
+ log.bold lint.line, false
21
+ end
22
+
23
+ def print_type(lint)
24
+ if lint.error?
25
+ log.error ' [E] ', false
26
+ else
27
+ log.warning ' [W] ', false
28
+ end
29
+ end
30
+
31
+ def print_message(lint)
32
+ if lint.linter
33
+ log.success("#{lint.linter.name}: ", false)
34
+ end
35
+
36
+ log.log lint.message
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,44 @@
1
+ module HamlLint
2
+ # Outputs report as a JSON document.
3
+ class Reporter::JsonReporter < Reporter
4
+ def report_lints
5
+ grouped = lints.group_by(&:filename)
6
+
7
+ report = {
8
+ metadata: {
9
+ hamllint_version: VERSION,
10
+ ruby_engine: RUBY_ENGINE,
11
+ ruby_patchlevel: RUBY_PATCHLEVEL.to_s,
12
+ ruby_platform: RUBY_PLATFORM,
13
+ },
14
+ files: grouped.map { |l| map_file(l) },
15
+ summary: {
16
+ offense_count: lints.length,
17
+ target_file_count: grouped.length,
18
+ inspected_file_count: files.length,
19
+ },
20
+ }
21
+
22
+ log.log report.to_json
23
+ end
24
+
25
+ private
26
+
27
+ def map_file(file)
28
+ {
29
+ path: file.first,
30
+ offenses: file.last.map { |o| map_offense(o) },
31
+ }
32
+ end
33
+
34
+ def map_offense(offense)
35
+ {
36
+ severity: offense.severity,
37
+ message: offense.message,
38
+ location: {
39
+ line: offense.line,
40
+ },
41
+ }
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,36 @@
1
+ module HamlLint
2
+ # Abstract lint reporter. Subclass and override {#report_lints} to
3
+ # implement a custom lint reporter.
4
+ #
5
+ # @abstract
6
+ class Reporter
7
+ attr_reader :lints
8
+ attr_reader :files
9
+
10
+ # @param logger [HamlLint::Logger]
11
+ # @param report [HamlLint::Report]
12
+ def initialize(logger, report)
13
+ @log = logger
14
+ @lints = report.lints
15
+ @files = report.files
16
+ end
17
+
18
+ # Implemented by subclasses to display lints from a {HamlLint::Report}.
19
+ def report_lints
20
+ raise NotImplementedError
21
+ end
22
+
23
+ # Keep tracking all the descendants of this class for the list of available reporters
24
+ def self.descendants
25
+ @descendants ||= []
26
+ end
27
+
28
+ def self.inherited(descendant)
29
+ descendants << descendant
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :log
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ require 'astrolabe/builder'
2
+ require 'parser/current'
3
+
4
+ module HamlLint
5
+ # Parser for the Ruby language.
6
+ #
7
+ # This provides a convenient wrapper around the `parser` gem and the
8
+ # `astrolabe` integration to go with it. It is intended to be used for linter
9
+ # checks that require deep inspection of Ruby code.
10
+ class RubyParser
11
+ # Creates a reusable parser.
12
+ def initialize
13
+ @builder = ::Astrolabe::Builder.new
14
+ @parser = ::Parser::CurrentRuby.new(@builder)
15
+ end
16
+
17
+ # Parse the given Ruby source into an abstract syntax tree.
18
+ #
19
+ # @param source [String] Ruby source code
20
+ # @return [Array] syntax tree in the form returned by Parser gem
21
+ def parse(source)
22
+ buffer = ::Parser::Source::Buffer.new('(string)')
23
+ buffer.source = source
24
+
25
+ @parser.reset
26
+ @parser.parse(buffer)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,76 @@
1
+ module HamlLint
2
+ # Responsible for running the applicable linters against the desired files.
3
+ class Runner
4
+ # Make the list of applicable files available
5
+ attr_reader :files
6
+
7
+ # Runs the appropriate linters against the desired files given the specified
8
+ # options.
9
+ #
10
+ # @param options [Hash]
11
+ # @raise [HamlLint::Exceptions::NoLintersError] when no linters are enabled
12
+ # @return [HamlLint::Report] a summary of all lints found
13
+ def run(options = {})
14
+ config = load_applicable_config(options)
15
+ files = extract_applicable_files(options, config)
16
+ linters = extract_enabled_linters(config, options)
17
+
18
+ raise HamlLint::Exceptions::NoLintersError, 'No linters specified' if linters.empty?
19
+
20
+ @lints = []
21
+ files.each do |file|
22
+ find_lints(file, linters, config)
23
+ end
24
+
25
+ linters.each do |linter|
26
+ @lints += linter.lints
27
+ end
28
+
29
+ HamlLint::Report.new(@lints, files)
30
+ end
31
+
32
+ private
33
+
34
+ def load_applicable_config(options)
35
+ if options[:config_file]
36
+ HamlLint::ConfigurationLoader.load_file(options[:config_file])
37
+ else
38
+ HamlLint::ConfigurationLoader.load_applicable_config
39
+ end
40
+ end
41
+
42
+ def extract_enabled_linters(config, options)
43
+ included_linters = LinterRegistry
44
+ .extract_linters_from(options.fetch(:included_linters, []))
45
+
46
+ included_linters = LinterRegistry.linters if included_linters.empty?
47
+
48
+ excluded_linters = LinterRegistry
49
+ .extract_linters_from(options.fetch(:excluded_linters, []))
50
+
51
+ # After filtering out explicitly included/excluded linters, only include
52
+ # linters which are enabled in the configuration
53
+ (included_linters - excluded_linters).map do |linter_class|
54
+ linter_config = config.for_linter(linter_class)
55
+ linter_class.new(linter_config) if linter_config['enabled']
56
+ end.compact
57
+ end
58
+
59
+ def find_lints(file, linters, config)
60
+ parser = Parser.new(file, config.hash)
61
+
62
+ linters.each do |linter|
63
+ linter.run(parser)
64
+ end
65
+ rescue Haml::Error => ex
66
+ @lints << Lint.new(nil, file, ex.line, ex.to_s, :error)
67
+ end
68
+
69
+ def extract_applicable_files(options, config)
70
+ included_patterns = options[:files]
71
+ excluded_files = options.fetch(:excluded_files, [])
72
+
73
+ HamlLint::FileFinder.new(config).find(included_patterns, excluded_files)
74
+ end
75
+ end
76
+ end