haml_lint 0.13.0 → 0.14.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 +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)
|