haml_lint 0.13.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/default.yml +1 -0
- data/lib/haml_lint/cli.rb +25 -8
- data/lib/haml_lint/configuration.rb +56 -34
- data/lib/haml_lint/configuration_loader.rb +22 -6
- data/lib/haml_lint/constants.rb +1 -1
- data/lib/haml_lint/document.rb +102 -0
- data/lib/haml_lint/file_finder.rb +14 -5
- data/lib/haml_lint/lint.rb +17 -1
- data/lib/haml_lint/linter/alt_text.rb +1 -1
- data/lib/haml_lint/linter/class_attribute_with_static_value.rb +2 -2
- data/lib/haml_lint/linter/classes_before_ids.rb +2 -2
- data/lib/haml_lint/linter/consecutive_comments.rb +3 -5
- data/lib/haml_lint/linter/consecutive_silent_scripts.rb +5 -5
- data/lib/haml_lint/linter/empty_script.rb +1 -1
- data/lib/haml_lint/linter/html_attributes.rb +2 -2
- data/lib/haml_lint/linter/implicit_div.rb +3 -3
- data/lib/haml_lint/linter/leading_comment_space.rb +1 -1
- data/lib/haml_lint/linter/line_length.rb +2 -2
- data/lib/haml_lint/linter/multiline_pipe.rb +3 -3
- data/lib/haml_lint/linter/multiline_script.rb +3 -3
- data/lib/haml_lint/linter/object_reference_attributes.rb +1 -1
- data/lib/haml_lint/linter/rubocop.rb +60 -34
- data/lib/haml_lint/linter/ruby_comments.rb +1 -1
- data/lib/haml_lint/linter/space_before_script.rb +4 -4
- data/lib/haml_lint/linter/space_inside_hash_attributes.rb +2 -2
- data/lib/haml_lint/linter/tag_name.rb +1 -1
- data/lib/haml_lint/linter/trailing_whitespace.rb +2 -2
- data/lib/haml_lint/linter/unnecessary_interpolation.rb +3 -3
- data/lib/haml_lint/linter/unnecessary_string_output.rb +10 -3
- data/lib/haml_lint/linter.rb +30 -10
- data/lib/haml_lint/linter_registry.rb +13 -2
- data/lib/haml_lint/linter_selector.rb +77 -0
- data/lib/haml_lint/node_transformer.rb +5 -5
- data/lib/haml_lint/options.rb +13 -6
- data/lib/haml_lint/rake_task.rb +18 -3
- data/lib/haml_lint/report.rb +7 -0
- data/lib/haml_lint/reporter/default_reporter.rb +2 -2
- data/lib/haml_lint/reporter/json_reporter.rb +15 -10
- data/lib/haml_lint/reporter.rb +17 -11
- data/lib/haml_lint/{script_extractor.rb → ruby_extractor.rb} +26 -17
- data/lib/haml_lint/runner.rb +44 -44
- data/lib/haml_lint/tree/node.rb +7 -8
- data/lib/haml_lint/utils.rb +88 -21
- data/lib/haml_lint/version.rb +1 -1
- data/lib/haml_lint.rb +2 -1
- metadata +5 -4
- data/lib/haml_lint/parser.rb +0 -87
@@ -24,7 +24,7 @@ module HamlLint
|
|
24
24
|
# Plain text nodes are allowed to consist of a single pipe
|
25
25
|
return if line.strip == '|'
|
26
26
|
|
27
|
-
|
27
|
+
record_lint(node, MESSAGE) if line.match(MULTILINE_PIPE_REGEX)
|
28
28
|
end
|
29
29
|
|
30
30
|
private
|
@@ -32,12 +32,12 @@ module HamlLint
|
|
32
32
|
MULTILINE_PIPE_REGEX = /\s+\|\s*$/
|
33
33
|
|
34
34
|
def line_text_for_node(node)
|
35
|
-
|
35
|
+
document.source_lines[node.line - 1]
|
36
36
|
end
|
37
37
|
|
38
38
|
def check(node)
|
39
39
|
line = line_text_for_node(node)
|
40
|
-
|
40
|
+
record_lint(node, MESSAGE) if line.match(MULTILINE_PIPE_REGEX)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -34,9 +34,9 @@ module HamlLint
|
|
34
34
|
def check(node)
|
35
35
|
operator = node.script[/\s+(\S+)\z/, 1]
|
36
36
|
if SPLIT_OPERATORS.include?(operator)
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
record_lint(node,
|
38
|
+
"Script with trailing operator `#{operator}` should be " \
|
39
|
+
'merged with the script on the following line')
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -7,7 +7,7 @@ module HamlLint
|
|
7
7
|
def visit_tag(node)
|
8
8
|
return unless node.object_reference?
|
9
9
|
|
10
|
-
|
10
|
+
record_lint(node, 'Avoid using object reference syntax to assign class/id ' \
|
11
11
|
'attributes for tags')
|
12
12
|
end
|
13
13
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'haml_lint/
|
1
|
+
require 'haml_lint/ruby_extractor'
|
2
2
|
require 'rubocop'
|
3
3
|
require 'tempfile'
|
4
4
|
|
@@ -7,33 +7,35 @@ module HamlLint
|
|
7
7
|
class Linter::RuboCop < Linter
|
8
8
|
include LinterRegistry
|
9
9
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
@ignored_cops = Array(config['ignored_cops']).flatten
|
14
|
-
end
|
10
|
+
def visit_root(_node)
|
11
|
+
extractor = HamlLint::RubyExtractor.new
|
12
|
+
extracted_source = extractor.extract(document)
|
15
13
|
|
16
|
-
|
17
|
-
@parser = parser
|
18
|
-
@extractor = ScriptExtractor.new(parser)
|
19
|
-
extracted_code = @extractor.extract.strip
|
14
|
+
return if extracted_source.source.empty?
|
20
15
|
|
21
|
-
|
22
|
-
find_lints(extracted_code + "\n") unless extracted_code.empty?
|
16
|
+
find_lints(extracted_source.source, extracted_source.source_map)
|
23
17
|
end
|
24
18
|
|
25
19
|
private
|
26
20
|
|
27
|
-
|
28
|
-
|
21
|
+
# Executes RuboCop against the given Ruby code and records the offenses as
|
22
|
+
# lints.
|
23
|
+
#
|
24
|
+
# @param ruby [String] Ruby code
|
25
|
+
# @param source_map [Hash] map of Ruby code line numbers to original line
|
26
|
+
# numbers in the template
|
27
|
+
def find_lints(ruby, source_map)
|
28
|
+
rubocop = ::RuboCop::CLI.new
|
29
|
+
|
30
|
+
original_filename = document.file || 'ruby_script'
|
29
31
|
filename = "#{File.basename(original_filename)}.haml_lint.tmp"
|
30
32
|
directory = File.dirname(original_filename)
|
31
33
|
|
32
34
|
Tempfile.open(filename, directory) do |f|
|
33
35
|
begin
|
34
|
-
f.write(
|
36
|
+
f.write(ruby)
|
35
37
|
f.close
|
36
|
-
|
38
|
+
extract_lints_from_offenses(lint_file(rubocop, f.path), source_map)
|
37
39
|
ensure
|
38
40
|
f.unlink
|
39
41
|
end
|
@@ -41,36 +43,60 @@ module HamlLint
|
|
41
43
|
end
|
42
44
|
|
43
45
|
# Defined so we can stub the results in tests
|
44
|
-
|
45
|
-
|
46
|
-
|
46
|
+
#
|
47
|
+
# @param rubocop [RuboCop::CLI]
|
48
|
+
# @param file [String]
|
49
|
+
# @return [Array<RuboCop::Cop::Offense>]
|
50
|
+
def lint_file(rubocop, file)
|
51
|
+
rubocop.run(rubocop_flags << file)
|
52
|
+
OffenseCollector.offenses
|
47
53
|
end
|
48
54
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
# Aggregates RuboCop offenses and converts them to {HamlLint::Lint}s
|
56
|
+
# suitable for reporting.
|
57
|
+
#
|
58
|
+
# @param offenses [Array<RuboCop::Cop::Offense>]
|
59
|
+
# @param source_map [Hash]
|
60
|
+
def extract_lints_from_offenses(offenses, source_map)
|
61
|
+
dummy_node = Struct.new(:line)
|
62
|
+
|
63
|
+
offenses.select { |offense| !config['ignored_cops'].include?(offense.cop_name) }
|
64
|
+
.each do |offense|
|
65
|
+
record_lint(dummy_node.new(source_map[offense.line]), offense.message)
|
56
66
|
end
|
57
67
|
end
|
58
|
-
end
|
59
68
|
|
60
|
-
|
61
|
-
|
62
|
-
|
69
|
+
# Returns flags that will be passed to RuboCop CLI.
|
70
|
+
#
|
71
|
+
# @return [Array<String>]
|
72
|
+
def rubocop_flags
|
73
|
+
flags = %w[--format HamlLint::OffenseCollector]
|
74
|
+
flags += ['--config', ENV['HAML_LINT_RUBOCOP_CONF']] if ENV['HAML_LINT_RUBOCOP_CONF']
|
75
|
+
flags
|
76
|
+
end
|
77
|
+
end
|
63
78
|
|
79
|
+
# Collects offenses detected by RuboCop.
|
80
|
+
class OffenseCollector < ::RuboCop::Formatter::BaseFormatter
|
64
81
|
class << self
|
65
|
-
|
82
|
+
# List of offenses reported by RuboCop.
|
83
|
+
attr_accessor :offenses
|
66
84
|
end
|
67
85
|
|
86
|
+
# Executed when RuboCop begins linting.
|
87
|
+
#
|
88
|
+
# @param _target_files [Array<String>]
|
68
89
|
def started(_target_files)
|
69
|
-
self.class.
|
90
|
+
self.class.offenses = []
|
70
91
|
end
|
71
92
|
|
72
|
-
|
73
|
-
|
93
|
+
# Executed when a file has been scanned by RuboCop, adding the reported
|
94
|
+
# offenses to our collection.
|
95
|
+
#
|
96
|
+
# @param _file [String]
|
97
|
+
# @param offenses [Array<RuboCop::Cop::Offense>]
|
98
|
+
def file_finished(_file, offenses)
|
99
|
+
self.class.offenses += offenses
|
74
100
|
end
|
75
101
|
end
|
76
102
|
end
|
@@ -28,18 +28,18 @@ module HamlLint
|
|
28
28
|
# (need to do it this way as the parser strips whitespace from node)
|
29
29
|
return unless tag_with_text[index - 1] != ' '
|
30
30
|
|
31
|
-
|
31
|
+
record_lint(node, MESSAGE_FORMAT % '=')
|
32
32
|
end
|
33
33
|
|
34
34
|
def visit_script(node)
|
35
35
|
# Plain text nodes with interpolation are converted to script nodes, so we
|
36
36
|
# need to ignore them here.
|
37
|
-
return unless
|
38
|
-
|
37
|
+
return unless document.source_lines[node.line - 1].lstrip.start_with?('=')
|
38
|
+
record_lint(node, MESSAGE_FORMAT % '=') if missing_space?(node)
|
39
39
|
end
|
40
40
|
|
41
41
|
def visit_silent_script(node)
|
42
|
-
|
42
|
+
record_lint(node, MESSAGE_FORMAT % '-') if missing_space?(node)
|
43
43
|
end
|
44
44
|
|
45
45
|
private
|
@@ -25,8 +25,8 @@ module HamlLint
|
|
25
25
|
style = STYLE[config['style'] == 'no_space' ? 'no_space' : 'space']
|
26
26
|
source = node.hash_attributes_source
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
record_lint(node, style[:start_message]) unless source =~ style[:start_regex]
|
29
|
+
record_lint(node, style[:end_message]) unless source =~ style[:end_regex]
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -6,10 +6,10 @@ module HamlLint
|
|
6
6
|
def visit_root(_node)
|
7
7
|
dummy_node = Struct.new(:line)
|
8
8
|
|
9
|
-
|
9
|
+
document.source_lines.each_with_index do |line, index|
|
10
10
|
next unless line =~ /\s+$/
|
11
11
|
|
12
|
-
|
12
|
+
record_lint dummy_node.new(index + 1), 'Line contains trailing whitespace'
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -14,15 +14,15 @@ module HamlLint
|
|
14
14
|
|
15
15
|
count = 0
|
16
16
|
chars = 2 # Include surrounding quote chars
|
17
|
-
HamlLint::Utils.extract_interpolated_values(node.script) do |interpolated_code|
|
17
|
+
HamlLint::Utils.extract_interpolated_values(node.script) do |interpolated_code, _line|
|
18
18
|
count += 1
|
19
19
|
return if count > 1 # rubocop:disable Lint/NonLocalExitFromIterator
|
20
20
|
chars += interpolated_code.length + 3
|
21
21
|
end
|
22
22
|
|
23
23
|
if chars == node.script.length
|
24
|
-
|
25
|
-
|
24
|
+
record_lint(node, '`%... \#{expression}` can be written without ' \
|
25
|
+
'interpolation as `%...= expression`')
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -13,7 +13,7 @@ module HamlLint
|
|
13
13
|
|
14
14
|
def visit_tag(node)
|
15
15
|
if tag_has_inline_script?(node) && inline_content_is_string?(node)
|
16
|
-
|
16
|
+
record_lint(node, MESSAGE)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -23,7 +23,7 @@ module HamlLint
|
|
23
23
|
return if node.source_code !~ /\s*=/
|
24
24
|
|
25
25
|
if outputs_string_literal?(node)
|
26
|
-
|
26
|
+
record_lint(node, MESSAGE)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -31,9 +31,16 @@ module HamlLint
|
|
31
31
|
|
32
32
|
def outputs_string_literal?(script_node)
|
33
33
|
tree = parse_ruby(script_node.script)
|
34
|
-
[:str, :dstr].include?(tree.type)
|
34
|
+
[:str, :dstr].include?(tree.type) &&
|
35
|
+
starts_with_reserved_character?(tree.children.first)
|
35
36
|
rescue ::Parser::SyntaxError # rubocop:disable Lint/HandleExceptions
|
36
37
|
# Gracefully ignore syntax errors, as that's managed by a different linter
|
37
38
|
end
|
39
|
+
|
40
|
+
# Returns whether a string starts with a character that would otherwise be
|
41
|
+
# given special treatment, thus making enclosing it in a string necessary.
|
42
|
+
def starts_with_reserved_character?(string)
|
43
|
+
string !~ %r{\A\s*[/#-=%~]}
|
44
|
+
end
|
38
45
|
end
|
39
46
|
end
|
data/lib/haml_lint/linter.rb
CHANGED
@@ -1,33 +1,52 @@
|
|
1
1
|
module HamlLint
|
2
2
|
# Base implementation for all lint checks.
|
3
|
+
#
|
4
|
+
# @abstract
|
3
5
|
class Linter
|
4
6
|
include HamlVisitor
|
5
7
|
|
6
|
-
|
8
|
+
# List of lints reported by this linter.
|
9
|
+
#
|
10
|
+
# @todo Remove once spec/support/shared_linter_context returns an array of
|
11
|
+
# lints for the subject instead of the linter itself.
|
12
|
+
attr_reader :lints
|
7
13
|
|
14
|
+
# Initializes a linter with the specified configuration.
|
15
|
+
#
|
8
16
|
# @param config [Hash] configuration for this linter
|
9
17
|
def initialize(config)
|
10
18
|
@config = config
|
11
19
|
@lints = []
|
12
|
-
@ruby_parser = nil
|
13
20
|
end
|
14
21
|
|
15
|
-
|
16
|
-
|
17
|
-
|
22
|
+
# Runs the linter against the given Haml document.
|
23
|
+
#
|
24
|
+
# @param document [HamlLint::Document]
|
25
|
+
def run(document)
|
26
|
+
@document = document
|
27
|
+
@lints = []
|
28
|
+
visit(document.tree)
|
29
|
+
@lints
|
18
30
|
end
|
19
31
|
|
20
32
|
# Returns the simple name for this linter.
|
33
|
+
#
|
34
|
+
# @return [String]
|
21
35
|
def name
|
22
36
|
self.class.name.split('::').last
|
23
37
|
end
|
24
38
|
|
25
39
|
private
|
26
40
|
|
27
|
-
attr_reader :config
|
41
|
+
attr_reader :config, :document
|
28
42
|
|
29
|
-
|
30
|
-
|
43
|
+
# Record a lint for reporting back to the user.
|
44
|
+
#
|
45
|
+
# @param node [#line] node to extract the line number from
|
46
|
+
# @param message [String] error/warning to display to the user
|
47
|
+
def record_lint(node, message)
|
48
|
+
@lints << HamlLint::Lint.new(self, @document.file, node.line, message,
|
49
|
+
config.fetch('severity', :warning).to_sym)
|
31
50
|
end
|
32
51
|
|
33
52
|
# Parse Ruby code into an abstract syntax tree.
|
@@ -136,7 +155,7 @@ module HamlLint
|
|
136
155
|
def following_node_line(node)
|
137
156
|
[
|
138
157
|
[node.children.first, next_node(node)].compact.map(&:line),
|
139
|
-
|
158
|
+
@document.source_lines.count + 1,
|
140
159
|
].flatten.min
|
141
160
|
end
|
142
161
|
|
@@ -148,7 +167,8 @@ module HamlLint
|
|
148
167
|
def tag_with_inline_text(tag_node)
|
149
168
|
# Normalize each of the lines to ignore the multiline bar (|) and
|
150
169
|
# excess whitespace
|
151
|
-
|
170
|
+
@document.source_lines[(tag_node.line - 1)...(following_node_line(tag_node) - 1)]
|
171
|
+
.map do |line|
|
152
172
|
line.strip.gsub(/\|\z/, '').rstrip
|
153
173
|
end.join(' ')
|
154
174
|
end
|
@@ -6,12 +6,23 @@ module HamlLint
|
|
6
6
|
@linters = []
|
7
7
|
|
8
8
|
class << self
|
9
|
+
# List of all registered linters.
|
9
10
|
attr_reader :linters
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
# Executed when a linter includes the {LinterRegistry} module.
|
13
|
+
#
|
14
|
+
# This results in the linter being registered with the registry.
|
15
|
+
#
|
16
|
+
# @param subclass [Class]
|
17
|
+
def included(subclass)
|
18
|
+
@linters << subclass
|
13
19
|
end
|
14
20
|
|
21
|
+
# Return a list of {HamlLint::Linter} {Class}es corresponding to the
|
22
|
+
# specified list of names.
|
23
|
+
#
|
24
|
+
# @param linter_names [Array<String>]
|
25
|
+
# @return [Array<Class>]
|
15
26
|
def extract_linters_from(linter_names)
|
16
27
|
linter_names.map do |linter_name|
|
17
28
|
begin
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module HamlLint
|
2
|
+
# Chooses the appropriate linters to run given the specified configuration.
|
3
|
+
class LinterSelector
|
4
|
+
# Creates a selector using the given configuration and additional options.
|
5
|
+
#
|
6
|
+
# @param config [HamlLint::Configuration]
|
7
|
+
# @param options [Hash]
|
8
|
+
def initialize(config, options)
|
9
|
+
@config = config
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the set of linters to run against the given file.
|
14
|
+
#
|
15
|
+
# @param file [String]
|
16
|
+
# @raise [HamlLint::Exceptions::NoLintersError] when no linters are enabled
|
17
|
+
# @return [Array<HamlLint::Linter>]
|
18
|
+
def linters_for_file(file)
|
19
|
+
@linters ||= extract_enabled_linters(@config, @options)
|
20
|
+
@linters.select { |linter| run_linter_on_file?(@config, linter, file) }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Returns a list of linters that are enabled given the specified
|
26
|
+
# configuration and additional options.
|
27
|
+
#
|
28
|
+
# @param config [HamlLint::Configuration]
|
29
|
+
# @param options [Hash]
|
30
|
+
# @return [Array<HamlLint::Linter>]
|
31
|
+
def extract_enabled_linters(config, options)
|
32
|
+
included_linters = LinterRegistry
|
33
|
+
.extract_linters_from(options.fetch(:included_linters, []))
|
34
|
+
|
35
|
+
included_linters = LinterRegistry.linters if included_linters.empty?
|
36
|
+
|
37
|
+
excluded_linters = LinterRegistry
|
38
|
+
.extract_linters_from(options.fetch(:excluded_linters, []))
|
39
|
+
|
40
|
+
# After filtering out explicitly included/excluded linters, only include
|
41
|
+
# linters which are enabled in the configuration
|
42
|
+
linters = (included_linters - excluded_linters).map do |linter_class|
|
43
|
+
linter_config = config.for_linter(linter_class)
|
44
|
+
linter_class.new(linter_config) if linter_config['enabled']
|
45
|
+
end.compact
|
46
|
+
|
47
|
+
# Highlight condition where all linters were filtered out, as this was
|
48
|
+
# likely a mistake on the user's part
|
49
|
+
if linters.empty?
|
50
|
+
raise HamlLint::Exceptions::NoLintersError, 'No linters specified'
|
51
|
+
end
|
52
|
+
|
53
|
+
linters
|
54
|
+
end
|
55
|
+
|
56
|
+
# Whether to run the given linter against the specified file.
|
57
|
+
#
|
58
|
+
# @param config [HamlLint::Configuration]
|
59
|
+
# @param linter [HamlLint::Linter]
|
60
|
+
# @param file [String]
|
61
|
+
# @return [Boolean]
|
62
|
+
def run_linter_on_file?(config, linter, file)
|
63
|
+
linter_config = config.for_linter(linter)
|
64
|
+
|
65
|
+
if linter_config['include'].any? &&
|
66
|
+
!HamlLint::Utils.any_glob_matches?(linter_config['include'], file)
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
|
70
|
+
if HamlLint::Utils.any_glob_matches?(linter_config['exclude'], file)
|
71
|
+
return false
|
72
|
+
end
|
73
|
+
|
74
|
+
true
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -7,11 +7,11 @@ module HamlLint
|
|
7
7
|
# would expect. This class is intended to isolate and handle these cases so
|
8
8
|
# that linters don't have to deal with them.
|
9
9
|
class NodeTransformer
|
10
|
-
# Creates a node transformer for the given
|
10
|
+
# Creates a node transformer for the given Haml document.
|
11
11
|
#
|
12
|
-
# @param
|
13
|
-
def initialize(
|
14
|
-
@
|
12
|
+
# @param document [HamlLint::Document]
|
13
|
+
def initialize(document)
|
14
|
+
@document = document
|
15
15
|
end
|
16
16
|
|
17
17
|
# Transforms the given {Haml::Parser::ParseNode} into its corresponding
|
@@ -19,7 +19,7 @@ module HamlLint
|
|
19
19
|
def transform(haml_node)
|
20
20
|
node_class = "#{HamlLint::Utils.camel_case(haml_node.type.to_s)}Node"
|
21
21
|
|
22
|
-
HamlLint::Tree.const_get(node_class).new(@
|
22
|
+
HamlLint::Tree.const_get(node_class).new(@document, haml_node)
|
23
23
|
rescue NameError
|
24
24
|
# TODO: Wrap in parser error?
|
25
25
|
raise
|
data/lib/haml_lint/options.rb
CHANGED
@@ -31,11 +31,6 @@ module HamlLint
|
|
31
31
|
private
|
32
32
|
|
33
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
34
|
parser.on('-i', '--include-linter linter,...', Array,
|
40
35
|
'Specify which linters you want to run') do |linters|
|
41
36
|
@options[:included_linters] = linters
|
@@ -48,10 +43,22 @@ module HamlLint
|
|
48
43
|
|
49
44
|
parser.on('-r', '--reporter reporter', String,
|
50
45
|
'Specify which reporter you want to use to generate the output') do |reporter|
|
51
|
-
@options[:reporter] =
|
46
|
+
@options[:reporter] = load_reporter_class(reporter.capitalize)
|
52
47
|
end
|
53
48
|
end
|
54
49
|
|
50
|
+
# Returns the class of the specified Reporter.
|
51
|
+
#
|
52
|
+
# @param reporter_name [String]
|
53
|
+
# @raise [HamlLint::Exceptions::InvalidCLIOption] if reporter doesn't exist
|
54
|
+
# @return [Class]
|
55
|
+
def load_reporter_class(reporter_name)
|
56
|
+
HamlLint::Reporter.const_get("#{reporter_name}Reporter")
|
57
|
+
rescue NameError
|
58
|
+
raise HamlLint::Exceptions::InvalidCLIOption,
|
59
|
+
"#{reporter_name}Reporter does not exist"
|
60
|
+
end
|
61
|
+
|
55
62
|
def add_file_options(parser)
|
56
63
|
parser.on('-c', '--config config-file', String,
|
57
64
|
'Specify which configuration file you want to use') do |conf_file|
|
data/lib/haml_lint/rake_task.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'rake/tasklib'
|
3
|
+
require 'haml_lint/constants'
|
3
4
|
|
4
5
|
module HamlLint
|
5
6
|
# Rake task interface for haml-lint command line interface.
|
@@ -51,6 +52,8 @@ module HamlLint
|
|
51
52
|
attr_accessor :quiet
|
52
53
|
|
53
54
|
# Create the task so it exists in the current namespace.
|
55
|
+
#
|
56
|
+
# @param name [Symbol] task name
|
54
57
|
def initialize(name = :haml_lint)
|
55
58
|
@name = name
|
56
59
|
@files = ['.'] # Search for everything under current directory by default
|
@@ -63,27 +66,34 @@ module HamlLint
|
|
63
66
|
|
64
67
|
private
|
65
68
|
|
69
|
+
# Defines the Rake task.
|
66
70
|
def define
|
67
71
|
desc default_description unless ::Rake.application.last_description
|
68
72
|
|
69
73
|
task(name, [:files]) do |_task, task_args|
|
70
74
|
# Lazy-load so task doesn't affect Rakefile load time
|
71
|
-
require 'haml_lint'
|
72
75
|
require 'haml_lint/cli'
|
73
76
|
|
74
77
|
run_cli(task_args)
|
75
78
|
end
|
76
79
|
end
|
77
80
|
|
81
|
+
# Executes the CLI given the specified task arguments.
|
82
|
+
#
|
83
|
+
# @param task_args [Rake::TaskArguments]
|
78
84
|
def run_cli(task_args)
|
79
85
|
cli_args = ['--config', config] if config
|
80
86
|
|
81
87
|
logger = quiet ? HamlLint::Logger.silent : HamlLint::Logger.new(STDOUT)
|
82
88
|
result = HamlLint::CLI.new(logger).run(Array(cli_args) + files_to_lint(task_args))
|
83
89
|
|
84
|
-
fail "
|
90
|
+
fail "#{HamlLint::APP_NAME} failed with exit code #{result}" unless result == 0
|
85
91
|
end
|
86
92
|
|
93
|
+
# Returns the list of files that should be linted given the specified task
|
94
|
+
# arguments.
|
95
|
+
#
|
96
|
+
# @param task_args [Rake::TaskArguments]
|
87
97
|
def files_to_lint(task_args)
|
88
98
|
# Note: we're abusing Rake's argument handling a bit here. We call the
|
89
99
|
# first argument `files` but it's actually only the first file--we pull
|
@@ -96,8 +106,13 @@ module HamlLint
|
|
96
106
|
end
|
97
107
|
|
98
108
|
# Friendly description that shows the full command that will be executed.
|
109
|
+
#
|
110
|
+
# This allows us to change the information displayed by `rake --tasks` based
|
111
|
+
# on the options passed to the constructor which defined the task.
|
112
|
+
#
|
113
|
+
# @return [String]
|
99
114
|
def default_description
|
100
|
-
description =
|
115
|
+
description = "Run `#{HamlLint::APP_NAME}"
|
101
116
|
description += " --config #{config}" if config
|
102
117
|
description += " #{files.join(' ')}" if files.any?
|
103
118
|
description += ' [files...]`'
|
data/lib/haml_lint/report.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
module HamlLint
|
2
2
|
# Contains information about all lints detected during a scan.
|
3
3
|
class Report
|
4
|
+
# List of lints that were found.
|
4
5
|
attr_accessor :lints
|
6
|
+
|
7
|
+
# List of files that were linted.
|
5
8
|
attr_reader :files
|
6
9
|
|
10
|
+
# Creates a report.
|
11
|
+
#
|
12
|
+
# @param lints [Array<HamlLint::Lint>] lints that were found
|
13
|
+
# @param files [Array<String>] files that were linted
|
7
14
|
def initialize(lints, files)
|
8
15
|
@lints = lints.sort_by { |l| [l.filename, l.line] }
|
9
16
|
@files = files
|
@@ -2,8 +2,8 @@ module HamlLint
|
|
2
2
|
# Outputs lints in a simple format with the filename, line number, and lint
|
3
3
|
# message.
|
4
4
|
class Reporter::DefaultReporter < Reporter
|
5
|
-
def
|
6
|
-
sorted_lints = lints.sort_by { |l| [l.filename, l.line] }
|
5
|
+
def display_report(report)
|
6
|
+
sorted_lints = report.lints.sort_by { |l| [l.filename, l.line] }
|
7
7
|
|
8
8
|
sorted_lints.each do |lint|
|
9
9
|
print_location(lint)
|