haml_lint 0.40.0 → 0.51.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/bin/haml-lint +1 -1
- data/config/default.yml +9 -27
- data/config/forced_rubocop_config.yml +180 -0
- data/lib/haml_lint/adapter/haml_4.rb +20 -0
- data/lib/haml_lint/adapter/haml_5.rb +11 -0
- data/lib/haml_lint/adapter/haml_6.rb +59 -0
- data/lib/haml_lint/adapter.rb +2 -0
- data/lib/haml_lint/cli.rb +8 -3
- data/lib/haml_lint/configuration_loader.rb +49 -13
- data/lib/haml_lint/document.rb +89 -8
- data/lib/haml_lint/exceptions.rb +6 -0
- data/lib/haml_lint/extensions/haml_util_unescape_interpolation_tracking.rb +35 -0
- data/lib/haml_lint/file_finder.rb +2 -2
- data/lib/haml_lint/lint.rb +10 -1
- data/lib/haml_lint/linter/final_newline.rb +4 -3
- data/lib/haml_lint/linter/implicit_div.rb +1 -1
- data/lib/haml_lint/linter/indentation.rb +3 -3
- data/lib/haml_lint/linter/no_placeholders.rb +18 -0
- data/lib/haml_lint/linter/repeated_id.rb +2 -1
- data/lib/haml_lint/linter/rubocop.rb +353 -59
- data/lib/haml_lint/linter/space_before_script.rb +8 -10
- data/lib/haml_lint/linter/trailing_empty_lines.rb +22 -0
- data/lib/haml_lint/linter/unnecessary_string_output.rb +1 -1
- data/lib/haml_lint/linter/view_length.rb +1 -1
- data/lib/haml_lint/linter.rb +60 -10
- data/lib/haml_lint/linter_registry.rb +3 -5
- data/lib/haml_lint/logger.rb +2 -2
- data/lib/haml_lint/options.rb +26 -2
- data/lib/haml_lint/rake_task.rb +2 -2
- data/lib/haml_lint/reporter/hash_reporter.rb +1 -3
- data/lib/haml_lint/reporter/offense_count_reporter.rb +1 -1
- data/lib/haml_lint/reporter/utils.rb +33 -4
- data/lib/haml_lint/ruby_extraction/ad_hoc_chunk.rb +24 -0
- data/lib/haml_lint/ruby_extraction/base_chunk.rb +114 -0
- data/lib/haml_lint/ruby_extraction/chunk_extractor.rb +672 -0
- data/lib/haml_lint/ruby_extraction/coordinator.rb +181 -0
- data/lib/haml_lint/ruby_extraction/haml_comment_chunk.rb +34 -0
- data/lib/haml_lint/ruby_extraction/implicit_end_chunk.rb +17 -0
- data/lib/haml_lint/ruby_extraction/interpolation_chunk.rb +26 -0
- data/lib/haml_lint/ruby_extraction/non_ruby_filter_chunk.rb +32 -0
- data/lib/haml_lint/ruby_extraction/placeholder_marker_chunk.rb +40 -0
- data/lib/haml_lint/ruby_extraction/ruby_filter_chunk.rb +33 -0
- data/lib/haml_lint/ruby_extraction/ruby_source.rb +5 -0
- data/lib/haml_lint/ruby_extraction/script_chunk.rb +251 -0
- data/lib/haml_lint/ruby_extraction/tag_attributes_chunk.rb +63 -0
- data/lib/haml_lint/ruby_extraction/tag_script_chunk.rb +30 -0
- data/lib/haml_lint/ruby_parser.rb +11 -1
- data/lib/haml_lint/runner.rb +35 -3
- data/lib/haml_lint/spec/matchers/report_lint.rb +22 -7
- data/lib/haml_lint/spec/normalize_indent.rb +2 -2
- data/lib/haml_lint/spec/shared_linter_context.rb +9 -1
- data/lib/haml_lint/spec/shared_rubocop_autocorrect_context.rb +158 -0
- data/lib/haml_lint/spec.rb +1 -0
- data/lib/haml_lint/tree/filter_node.rb +10 -0
- data/lib/haml_lint/tree/node.rb +13 -4
- data/lib/haml_lint/tree/script_node.rb +7 -1
- data/lib/haml_lint/tree/silent_script_node.rb +16 -1
- data/lib/haml_lint/tree/tag_node.rb +5 -9
- data/lib/haml_lint/utils.rb +135 -5
- data/lib/haml_lint/version.rb +1 -1
- data/lib/haml_lint/version_comparer.rb +25 -0
- data/lib/haml_lint.rb +12 -0
- metadata +29 -15
- data/lib/haml_lint/ruby_extractor.rb +0 -222
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 71dc6acf56f28f4f6878ff300a653d1fb6c5cc6fa9da23c71813463ea9e1b67a
|
4
|
+
data.tar.gz: af2fa134ec9718c24d019e20c359c9a965d6d09490e6f57fe202654f5f7f95a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 73c8a4c7fbd68cfbeacd29e5d313dcf9ae120a624a0359fecf5ca25396a8e68a0cfbad68b0ac9854af82a9233ffb26a9adf7db156f8dd3b5eb8b7a83773db035
|
7
|
+
data.tar.gz: fcb59570ff3b999f81f12208993ab75118013b0262586478145d7d0eaa9bab78002d9808757e10025324082f895bb0962743133898dd31dbb6c3db73d4c7f68c
|
data/bin/haml-lint
CHANGED
data/config/default.yml
CHANGED
@@ -76,6 +76,9 @@ linters:
|
|
76
76
|
MultilineScript:
|
77
77
|
enabled: true
|
78
78
|
|
79
|
+
NoPlaceholders:
|
80
|
+
enabled: false
|
81
|
+
|
79
82
|
ObjectReferenceAttributes:
|
80
83
|
enabled: true
|
81
84
|
|
@@ -85,33 +88,9 @@ linters:
|
|
85
88
|
|
86
89
|
RuboCop:
|
87
90
|
enabled: true
|
88
|
-
#
|
89
|
-
#
|
90
|
-
ignored_cops:
|
91
|
-
- Lint/BlockAlignment
|
92
|
-
- Lint/EndAlignment
|
93
|
-
- Lint/Void
|
94
|
-
- Layout/AlignHash # renamed to Layout/HashAlignment in rubocop 0.77
|
95
|
-
- Layout/AlignParameters # renamed to Layout/ParameterAlignment in rubocop 0.77
|
96
|
-
- Layout/ArgumentAlignment
|
97
|
-
- Layout/CaseIndentation
|
98
|
-
- Layout/ElseAlignment
|
99
|
-
- Layout/EndOfLine
|
100
|
-
- Layout/HashAlignment
|
101
|
-
- Layout/IndentationWidth
|
102
|
-
- Layout/LineLength # renamed from Metrics/LineLength in rubocop 0.79.0
|
103
|
-
- Layout/ParameterAlignment
|
104
|
-
- Layout/TrailingBlankLines # renamed to Layout/TrailingEmptyLines in rubocop 0.77
|
105
|
-
- Layout/TrailingEmptyLines
|
106
|
-
- Layout/TrailingWhitespace
|
107
|
-
- Metrics/BlockLength
|
108
|
-
- Metrics/BlockNesting
|
109
|
-
- Metrics/LineLength
|
110
|
-
- Naming/FileName
|
111
|
-
- Style/FrozenStringLiteralComment
|
112
|
-
- Style/IfUnlessModifier
|
113
|
-
- Style/Next
|
114
|
-
- Style/WhileUntilModifier
|
91
|
+
# Users can ignore cops using this configuration instead of editing their rubocop configuration.
|
92
|
+
# Mostly there for backward compatibility.
|
93
|
+
ignored_cops: []
|
115
94
|
|
116
95
|
RubyComments:
|
117
96
|
enabled: true
|
@@ -126,6 +105,9 @@ linters:
|
|
126
105
|
TagName:
|
127
106
|
enabled: true
|
128
107
|
|
108
|
+
TrailingEmptyLines:
|
109
|
+
enabled: true
|
110
|
+
|
129
111
|
TrailingWhitespace:
|
130
112
|
enabled: true
|
131
113
|
|
@@ -0,0 +1,180 @@
|
|
1
|
+
# These are some configurations that are required for RuboCop because:
|
2
|
+
# * HAML-Lint compiles ruby code with a particular format. If the rules mis-match that format,
|
3
|
+
# HAML-Lint would generate lints that the user cannot fix.
|
4
|
+
# * HAML-Lint can autocorrect code only if the result matches some specific format
|
5
|
+
#
|
6
|
+
# So these configuration should not be overwritable by users.
|
7
|
+
|
8
|
+
Layout/ArgumentAlignment:
|
9
|
+
# The alternative, with_fixed_indentation, breaks because we sometimes remove indentation when
|
10
|
+
# dealing with multi-line scripts. (Because a line starting with "=" adds a "HL.out = " to the
|
11
|
+
# intermediary Ruby source, which requires indentation, and the removal of the indentation)
|
12
|
+
EnforcedStyle: with_first_argument
|
13
|
+
|
14
|
+
Layout/ArrayAlignment:
|
15
|
+
# The alternative, with_fixed_indentation, breaks because we sometimes remove indentation when
|
16
|
+
# dealing with multi-line scripts. (Because a line starting with "=" adds a "HL.out = " to the
|
17
|
+
# intermediary Ruby source, which requires indentation, and the removal of the indentation)
|
18
|
+
EnforcedStyle: with_first_element
|
19
|
+
|
20
|
+
# In Haml, there are edge cases when `when` is indented, such as this one:
|
21
|
+
# - case 1
|
22
|
+
# - when 1
|
23
|
+
# - when 2
|
24
|
+
# foo
|
25
|
+
# This generates 2 `end` when building Ruby.
|
26
|
+
# So it's safer to make the `when` be not indented, to avoid auto-correct chains that could turn
|
27
|
+
# a valid `if` into an invalid `case` such as above.
|
28
|
+
Layout/CaseIndentation:
|
29
|
+
Enabled: true
|
30
|
+
EnforcedStyle: end
|
31
|
+
IndentOneStep: false # Need to force the `false`
|
32
|
+
|
33
|
+
# Need this cop so that code gets formatted similarly to Haml's indentation,
|
34
|
+
# since HAML-Lint relies on Ruby's indentation being the same as Haml's.
|
35
|
+
Layout/ElseAlignment:
|
36
|
+
Enabled: true
|
37
|
+
|
38
|
+
# Need this cop so that code gets formatted similarly to Haml's indentation,
|
39
|
+
# since HAML-Lint relies on Ruby's indentation being the same as Haml's.
|
40
|
+
Layout/EndAlignment:
|
41
|
+
EnforcedStyleAlignWith: start_of_line
|
42
|
+
Enabled: true
|
43
|
+
|
44
|
+
# We generate the ruby content, this is basically useless and should be a lint in HAML-Lint
|
45
|
+
Layout/EndOfLine:
|
46
|
+
Enabled: false
|
47
|
+
|
48
|
+
# Turning this cop on can turn
|
49
|
+
# = content_tag(:span) do
|
50
|
+
# - foo
|
51
|
+
# - bar
|
52
|
+
#
|
53
|
+
# Into
|
54
|
+
# - HL.out =
|
55
|
+
# - content_tag(:span) do
|
56
|
+
# - foo
|
57
|
+
# - bar
|
58
|
+
#
|
59
|
+
# Which is wrong... It would take too much analysis to detect and fix that situation.
|
60
|
+
Layout/MultilineAssignmentLayout:
|
61
|
+
Enabled: false
|
62
|
+
|
63
|
+
Layout/ParameterAlignment:
|
64
|
+
# The alternative, with_fixed_indentation, breaks because we sometimes remove indentation when
|
65
|
+
# dealing with multi-line scripts. (Because a line starting with "=" adds a "HL.out = " to the
|
66
|
+
# intermediary Ruby source, which requires indentation, and the removal of the indentation)
|
67
|
+
EnforcedStyle: with_first_parameter
|
68
|
+
|
69
|
+
# HamlLint generate lots of extra code which would make blocks much longer
|
70
|
+
Metrics/BlockLength:
|
71
|
+
Enabled: false
|
72
|
+
|
73
|
+
# The nesting may be due to the html's nesting nature... These lints are probably not helpful
|
74
|
+
Metrics/BlockNesting:
|
75
|
+
Enabled: false
|
76
|
+
|
77
|
+
# The file names are generated by HamlLint, so any related lint would be unfixable by the user
|
78
|
+
Naming/FileName:
|
79
|
+
Enabled: false
|
80
|
+
|
81
|
+
# HAML doesn't properly support multiline blocks using { }, only using do/end.
|
82
|
+
# If you don't consider the { } block for indentation, things "works", but the indentation is misleading.
|
83
|
+
# For example, this works:
|
84
|
+
# - a = lambda {
|
85
|
+
# - if abc
|
86
|
+
# - something
|
87
|
+
# - }
|
88
|
+
# But if you indented the 2 lines within { }, then HAML would add an extra `end` and the generated
|
89
|
+
# ruby would be invalid.
|
90
|
+
Style/BlockDelimiters:
|
91
|
+
# So we need this cop to cleanup those cases and turn them to `end`.
|
92
|
+
Enabled: true
|
93
|
+
EnforcedStyle: line_count_based
|
94
|
+
# We don't allow the default "Can be anything" exception for lambda/proc
|
95
|
+
<%= rubocop_version < '1.33' ? 'IgnoredMethods' : 'AllowedMethods' %>: []
|
96
|
+
|
97
|
+
# We don't support correcting HAML comments
|
98
|
+
Style/CommentAnnotation:
|
99
|
+
AutoCorrect: false
|
100
|
+
|
101
|
+
# If this was enabled, the equal sign would bubble up in a if like this:
|
102
|
+
# - if a
|
103
|
+
# = abc
|
104
|
+
# - else
|
105
|
+
# = bcd
|
106
|
+
# Into:
|
107
|
+
# = if a
|
108
|
+
# - abc
|
109
|
+
# - else
|
110
|
+
# - bcd
|
111
|
+
# Feels like this might be annoying or less visibly intuitive.
|
112
|
+
Style/ConditionalAssignment:
|
113
|
+
Enabled: false
|
114
|
+
|
115
|
+
# If this gets added, it wont do anything anyways.
|
116
|
+
Style/FrozenStringLiteralComment:
|
117
|
+
Enabled: false
|
118
|
+
|
119
|
+
# Looking at the changelog, this cop has quite a few bugfixes over time.
|
120
|
+
# It still has problematic behaviors for us, such as breaking a line into multiple
|
121
|
+
# ones with high indentation, which doesn't work for haml
|
122
|
+
Style/IfUnlessModifier:
|
123
|
+
AutoCorrect: false
|
124
|
+
|
125
|
+
<% if rubocop_version >= '1.37.0' %>
|
126
|
+
# This new cop can trigger on the here-doc we use for filters that contain interpolation.
|
127
|
+
# Ex:
|
128
|
+
# :javascript
|
129
|
+
# hello #{world} \. bad escape
|
130
|
+
Style/RedundantStringEscape:
|
131
|
+
Enabled: false
|
132
|
+
<% end %>
|
133
|
+
|
134
|
+
# In some case, this cop can cause a change in the spacing.
|
135
|
+
# In HAML 5.2, going from (absurd example for clarity):
|
136
|
+
# = 'abc' rescue nil
|
137
|
+
# = 'def'
|
138
|
+
# to:
|
139
|
+
# = begin
|
140
|
+
# - 'abc'
|
141
|
+
# - rescue StandardError
|
142
|
+
# - nil
|
143
|
+
# = 'def'
|
144
|
+
# Will remove the only whitespace (a \n) that is between the abc and the def.
|
145
|
+
# This could affect spacing, ex: seeing "abc def" become "abcdef" when rendering
|
146
|
+
# after running this auto-correct
|
147
|
+
Style/RescueModifier:
|
148
|
+
AutoCorrect: false
|
149
|
+
|
150
|
+
# Cops that remove commas can be a problem when lines are split on multiple ones.
|
151
|
+
# If we have a big array on more than one line, the removal of the comma generates
|
152
|
+
# invalid HAML
|
153
|
+
Style/SymbolArray:
|
154
|
+
Enabled: false
|
155
|
+
|
156
|
+
# This can easily change the order of the markers in the document, which result in un-transferable corrections
|
157
|
+
Style/UnlessElse:
|
158
|
+
AutoCorrect: false
|
159
|
+
|
160
|
+
# If an array of strings was on multiple lines, this cop will make a %w(...) on multiple lines.
|
161
|
+
# Without the comma at the end of the first line, there the resulting HAML will be invalid, since the only
|
162
|
+
# case where a script can change line is after a comma.
|
163
|
+
Style/WordArray:
|
164
|
+
AutoCorrect: false
|
165
|
+
|
166
|
+
# Not RuboCop's job
|
167
|
+
Layout/TrailingEmptyLines:
|
168
|
+
Enabled: false
|
169
|
+
|
170
|
+
<% if rubocop_version < '1.8.1' %>
|
171
|
+
# There were a few bugs with this cop that got fixed in this version.
|
172
|
+
# Before, those bugs would generate invalid Ruby code and that would make it look like HAML-lint is
|
173
|
+
# responsible, at least from the user's point of view.
|
174
|
+
Style/StringConcatenation:
|
175
|
+
Enabled: false
|
176
|
+
<% end %>
|
177
|
+
|
178
|
+
# There is already a linter dedicated to this in haml-lint
|
179
|
+
Layout/LineLength:
|
180
|
+
Enabled: false
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'forwardable'
|
4
|
+
|
3
5
|
module HamlLint
|
4
6
|
class Adapter
|
5
7
|
# Adapts the Haml::Parser from Haml 4 for use in HamlLint
|
@@ -16,9 +18,21 @@ module HamlLint
|
|
16
18
|
# @param source [String] Haml code to parse
|
17
19
|
# @param options [Haml::Options]
|
18
20
|
def initialize(source, options = Haml::Options.new)
|
21
|
+
@source = source
|
19
22
|
@parser = Haml::Parser.new(source, options)
|
20
23
|
end
|
21
24
|
|
25
|
+
def precompile
|
26
|
+
# Haml uses the filters as part of precompilation... we don't care about those,
|
27
|
+
# but without this tweak, it would fail on filters that are not loaded.
|
28
|
+
real_defined = Haml::Filters.defined
|
29
|
+
Haml::Filters.instance_variable_set(:@defined, Hash.new { real_defined['plain'] })
|
30
|
+
|
31
|
+
::Haml::Engine.new(source).precompiled
|
32
|
+
ensure
|
33
|
+
Haml::Filters.instance_variable_set(:@defined, real_defined)
|
34
|
+
end
|
35
|
+
|
22
36
|
# @!method
|
23
37
|
# Parses the source code into an abstract syntax tree
|
24
38
|
#
|
@@ -37,6 +51,12 @@ module HamlLint
|
|
37
51
|
# @api private
|
38
52
|
# @return [Haml::Parser] the Haml 4 parser
|
39
53
|
attr_reader :parser
|
54
|
+
|
55
|
+
# The Haml code to parse
|
56
|
+
#
|
57
|
+
# @api private
|
58
|
+
# @return [String] Haml code to parse
|
59
|
+
attr_reader :source
|
40
60
|
end
|
41
61
|
end
|
42
62
|
end
|
@@ -30,6 +30,17 @@ module HamlLint
|
|
30
30
|
parser.call(source)
|
31
31
|
end
|
32
32
|
|
33
|
+
def precompile
|
34
|
+
# Haml uses the filters as part of precompilation... we don't care about those,
|
35
|
+
# but without this tweak, it would fail on filters that are not loaded.
|
36
|
+
real_defined = Haml::Filters.defined
|
37
|
+
Haml::Filters.instance_variable_set(:@defined, Hash.new { real_defined['plain'] })
|
38
|
+
|
39
|
+
::Haml::Engine.new(source).precompiled
|
40
|
+
ensure
|
41
|
+
Haml::Filters.instance_variable_set(:@defined, real_defined)
|
42
|
+
end
|
43
|
+
|
33
44
|
private
|
34
45
|
|
35
46
|
# The Haml parser to adapt for HamlLint
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HamlLint
|
4
|
+
class Adapter
|
5
|
+
# Adapts the Haml::Parser from Haml 5 for use in HamlLint
|
6
|
+
# :reek:UncommunicativeModuleName
|
7
|
+
class Haml6 < Adapter
|
8
|
+
# Parses the specified Haml code into an abstract syntax tree
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# HamlLint::Adapter::Haml6.new('%div')
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
# @param source [String] Haml code to parse
|
15
|
+
# @param options [private Haml::Parser::ParserOptions]
|
16
|
+
def initialize(source, options = {})
|
17
|
+
@source = source
|
18
|
+
@parser = Haml::Parser.new(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Parses the source code into an abstract syntax tree
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# HamlLint::Adapter::Haml6.new('%div').parse
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
# @return [Haml::Parser::ParseNode]
|
28
|
+
# @raise [Haml::Error]
|
29
|
+
def parse
|
30
|
+
parser.call(source)
|
31
|
+
end
|
32
|
+
|
33
|
+
def precompile
|
34
|
+
# Haml uses the filters as part of precompilation... we don't care about those,
|
35
|
+
# but without this tweak, it would fail on filters that are not loaded.
|
36
|
+
real_defined = Haml::Filters.registered
|
37
|
+
Haml::Filters.instance_variable_set(:@registered, Hash.new { real_defined['plain'] })
|
38
|
+
|
39
|
+
::Haml::Engine.new.call(source)
|
40
|
+
ensure
|
41
|
+
Haml::Filters.instance_variable_set(:@registered, real_defined)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# The Haml parser to adapt for HamlLint
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
# @return [Haml::Parser] the Haml 4 parser
|
50
|
+
attr_reader :parser
|
51
|
+
|
52
|
+
# The Haml code to parse
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
# @return [String] Haml code to parse
|
56
|
+
attr_reader :source
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/haml_lint/adapter.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'haml_lint/adapter/haml_4'
|
4
4
|
require 'haml_lint/adapter/haml_5'
|
5
|
+
require 'haml_lint/adapter/haml_6'
|
5
6
|
require 'haml_lint/exceptions'
|
6
7
|
|
7
8
|
module HamlLint
|
@@ -20,6 +21,7 @@ module HamlLint
|
|
20
21
|
case version
|
21
22
|
when '~> 4.0' then HamlLint::Adapter::Haml4
|
22
23
|
when '~> 5.0', '~> 5.1', '~> 5.2' then HamlLint::Adapter::Haml5
|
24
|
+
when '~> 6.0', '~> 6.0.a', '~> 6.1', '~> 6.2' then HamlLint::Adapter::Haml6
|
23
25
|
else fail HamlLint::Exceptions::UnknownHamlVersion, "Cannot handle Haml version: #{version}"
|
24
26
|
end
|
25
27
|
end
|
data/lib/haml_lint/cli.rb
CHANGED
@@ -7,7 +7,7 @@ require 'sysexits'
|
|
7
7
|
|
8
8
|
module HamlLint
|
9
9
|
# Command line application interface.
|
10
|
-
class CLI
|
10
|
+
class CLI
|
11
11
|
# Create a CLI that outputs to the specified logger.
|
12
12
|
#
|
13
13
|
# @param logger [HamlLint::Logger]
|
@@ -34,9 +34,14 @@ module HamlLint
|
|
34
34
|
# Given the provided options, execute the appropriate command.
|
35
35
|
#
|
36
36
|
# @return [Integer] exit status code
|
37
|
-
def act_on_options(options)
|
37
|
+
def act_on_options(options) # rubocop:disable Metrics
|
38
38
|
configure_logger(options)
|
39
|
-
|
39
|
+
if options[:debug]
|
40
|
+
ENV['HAML_LINT_DEBUG'] = 'true'
|
41
|
+
end
|
42
|
+
if options[:internal_debug]
|
43
|
+
ENV['HAML_LINT_INTERNAL_DEBUG'] = 'true'
|
44
|
+
end
|
40
45
|
if options[:help]
|
41
46
|
print_help(options)
|
42
47
|
Sysexits::EX_OK
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'yaml'
|
5
|
+
require 'erb'
|
5
6
|
|
6
7
|
module HamlLint
|
7
8
|
# Manages configuration file loading.
|
@@ -31,7 +32,7 @@ module HamlLint
|
|
31
32
|
def default_path_to_config
|
32
33
|
directory = File.expand_path(Dir.pwd)
|
33
34
|
config_file = possible_config_files(directory).find(&:file?)
|
34
|
-
config_file
|
35
|
+
config_file&.to_path
|
35
36
|
end
|
36
37
|
|
37
38
|
# Loads the built-in default configuration.
|
@@ -48,13 +49,20 @@ module HamlLint
|
|
48
49
|
# @option context :exclude_files [Array<String>] files that should not
|
49
50
|
# be loaded even if they're requested via inherits_from
|
50
51
|
# @return [HamlLint::Configuration]
|
51
|
-
def load_file(file, context = {})
|
52
|
+
def load_file(file, context = {}) # rubocop:disable Metrics
|
52
53
|
context[:loaded_files] ||= []
|
54
|
+
context[:loaded_files].map! { |config_file| File.expand_path(config_file) }
|
53
55
|
context[:exclude_files] ||= []
|
54
|
-
|
56
|
+
context[:exclude_files].map! { |config_file| File.expand_path(config_file) }
|
57
|
+
config = load_from_file(File.expand_path(file))
|
55
58
|
|
56
|
-
|
57
|
-
|
59
|
+
configs = if context[:loaded_files].any?
|
60
|
+
[resolve_inheritance(config, context), config]
|
61
|
+
else
|
62
|
+
[default_configuration, resolve_inheritance(config, context), config]
|
63
|
+
end
|
64
|
+
|
65
|
+
configs.reduce { |acc, elem| acc.merge(elem) }
|
58
66
|
rescue Psych::SyntaxError, Errno::ENOENT => e
|
59
67
|
raise HamlLint::Exceptions::ConfigurationError,
|
60
68
|
"Unable to load configuration from '#{file}': #{e}",
|
@@ -78,19 +86,29 @@ module HamlLint
|
|
78
86
|
#
|
79
87
|
# @param file [String]
|
80
88
|
# @return [HamlLint::Configuration]
|
81
|
-
def load_from_file(file)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
{}
|
87
|
-
end
|
89
|
+
def load_from_file(file) # rubocop:disable Metrics
|
90
|
+
content = File.read(file)
|
91
|
+
|
92
|
+
processed_content = HamlLint::Utils.process_erb(content)
|
93
|
+
hash = (YAML.safe_load(processed_content) || {}).to_hash
|
88
94
|
|
89
95
|
if hash.key?('inherit_from')
|
90
96
|
hash['inherits_from'] ||= []
|
91
97
|
hash['inherits_from'].concat(Array(hash.delete('inherit_from')))
|
92
98
|
end
|
93
99
|
|
100
|
+
if hash.key?('inherit_gem')
|
101
|
+
hash['inherits_from'] ||= []
|
102
|
+
|
103
|
+
gems = hash.delete('inherit_gem')
|
104
|
+
(gems || {}).each_pair.reverse_each do |gem_name, config_path|
|
105
|
+
Array(config_path).reverse_each do |path|
|
106
|
+
# Put gem configuration first so local configuration overrides it.
|
107
|
+
hash['inherits_from'].unshift gem_config_path(gem_name, path)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
94
112
|
HamlLint::Configuration.new(hash, file)
|
95
113
|
end
|
96
114
|
|
@@ -129,10 +147,28 @@ module HamlLint
|
|
129
147
|
# @return [HamlLint::Configuration]
|
130
148
|
def resolve_inheritance(config, context)
|
131
149
|
Array(config['inherits_from'])
|
132
|
-
.map { |config_file| resolve(config_file, context) }
|
150
|
+
.map { |config_file| resolve(File.expand_path(config_file), context) }
|
133
151
|
.compact
|
134
152
|
.reduce { |acc, elem| acc.merge(elem) } || config
|
135
153
|
end
|
154
|
+
|
155
|
+
# Resolves the config file path relative to a gem
|
156
|
+
#
|
157
|
+
# @param gem_name [String] name of the gem
|
158
|
+
# @param relative_config_path [String] path of the file to resolve, relative to the gem root
|
159
|
+
# @return [String]
|
160
|
+
def gem_config_path(gem_name, relative_config_path)
|
161
|
+
if defined?(Bundler)
|
162
|
+
gem = Bundler.load.specs[gem_name].first
|
163
|
+
gem_path = gem.full_gem_path if gem
|
164
|
+
end
|
165
|
+
|
166
|
+
gem_path ||= Gem::Specification.find_by_name(gem_name).gem_dir
|
167
|
+
|
168
|
+
File.join(gem_path, relative_config_path)
|
169
|
+
rescue Gem::LoadError => e
|
170
|
+
raise Gem::LoadError, "Unable to find gem #{gem_name}; is the gem installed? #{e}"
|
171
|
+
end
|
136
172
|
end
|
137
173
|
end
|
138
174
|
end
|
data/lib/haml_lint/document.rb
CHANGED
@@ -23,6 +23,14 @@ module HamlLint
|
|
23
23
|
# @return [Array<String>] original source code as an array of lines
|
24
24
|
attr_reader :source_lines
|
25
25
|
|
26
|
+
# @return [Boolean] true if the source was changed (by autocorrect)
|
27
|
+
attr_reader :source_was_changed
|
28
|
+
|
29
|
+
# @return [String] the indentation used in the file
|
30
|
+
attr_reader :indentation
|
31
|
+
|
32
|
+
attr_reader :unescape_interpolation_to_original_cache
|
33
|
+
|
26
34
|
# Parses the specified Haml code into a {Document}.
|
27
35
|
#
|
28
36
|
# @param source [String] Haml code to parse
|
@@ -32,22 +40,80 @@ module HamlLint
|
|
32
40
|
def initialize(source, options)
|
33
41
|
@config = options[:config]
|
34
42
|
@file = options.fetch(:file, STRING_SOURCE)
|
35
|
-
|
43
|
+
@source_was_changed = false
|
36
44
|
process_source(source)
|
37
45
|
end
|
38
46
|
|
47
|
+
# Returns the last non empty line of the document or 1 if all lines are empty
|
48
|
+
#
|
49
|
+
# @return [Integer] last non empty line of the document or 1 if all lines are empty
|
50
|
+
def last_non_empty_line
|
51
|
+
index = source_lines.rindex { |l| !l.empty? }
|
52
|
+
(index || 0) + 1
|
53
|
+
end
|
54
|
+
|
55
|
+
# Reparses the new source and remember that the document was changed
|
56
|
+
# Used when auto-correct does changes to the file. If the source hasn't changed,
|
57
|
+
# then the document will not be marked as changed.
|
58
|
+
#
|
59
|
+
# If the new_source fails to parse, automatically reparses the previous source
|
60
|
+
# to bring the document back to how it should be before re-raising the parse exception
|
61
|
+
#
|
62
|
+
# @param source [String] Haml code to parse
|
63
|
+
def change_source(new_source)
|
64
|
+
return if new_source == @source
|
65
|
+
check_new_source_compatible(new_source)
|
66
|
+
|
67
|
+
old_source = @source
|
68
|
+
begin
|
69
|
+
process_source(new_source)
|
70
|
+
@source_was_changed = true
|
71
|
+
rescue HamlLint::Exceptions::ParseError
|
72
|
+
# Reprocess the previous_source so that other linters can work on this document
|
73
|
+
# object from a clean slate
|
74
|
+
process_source(old_source)
|
75
|
+
raise
|
76
|
+
end
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def write_to_disk!
|
81
|
+
return unless @source_was_changed
|
82
|
+
if file == STRING_SOURCE
|
83
|
+
raise HamlLint::Exceptions::InvalidFilePath, 'Cannot write without :file option'
|
84
|
+
end
|
85
|
+
File.write(file, unstrip_frontmatter(source))
|
86
|
+
@source_was_changed = false
|
87
|
+
end
|
88
|
+
|
39
89
|
private
|
40
90
|
|
41
91
|
# @param source [String] Haml code to parse
|
42
92
|
# @raise [HamlLint::Exceptions::ParseError] if there was a problem parsing
|
43
|
-
def process_source(source)
|
93
|
+
def process_source(source) # rubocop:disable Metrics/MethodLength
|
44
94
|
@source = process_encoding(source)
|
45
95
|
@source = strip_frontmatter(source)
|
46
|
-
|
47
|
-
|
48
|
-
@
|
96
|
+
# the -1 is to keep the empty strings at the end of the array when the source
|
97
|
+
# ended with multiple new-lines
|
98
|
+
@source_lines = @source.split(/\r\n|\r|\n/, -1)
|
99
|
+
adapter = HamlLint::Adapter.detect_class.new(@source)
|
100
|
+
parsed_tree = adapter.parse
|
101
|
+
@indentation = adapter.send(:parser).instance_variable_get(:@indentation)
|
102
|
+
@tree = process_tree(parsed_tree)
|
103
|
+
@unescape_interpolation_to_original_cache =
|
104
|
+
Haml::Util.unescape_interpolation_to_original_cache_take_and_wipe
|
49
105
|
rescue Haml::Error => e
|
50
|
-
|
106
|
+
location = if e.line
|
107
|
+
"#{@file}:#{e.line}"
|
108
|
+
else
|
109
|
+
@file
|
110
|
+
end
|
111
|
+
msg = if ENV['HAML_LINT_DEBUG'] == 'true'
|
112
|
+
"#{location} (DEBUG: source follows) - #{e.message}\n#{source}\n------"
|
113
|
+
else
|
114
|
+
"#{location} - #{e.message}"
|
115
|
+
end
|
116
|
+
error = HamlLint::Exceptions::ParseError.new(msg, e.line)
|
51
117
|
raise error
|
52
118
|
end
|
53
119
|
|
@@ -114,11 +180,26 @@ module HamlLint
|
|
114
180
|
(---|\.\.\.)\s*$\n?/mx
|
115
181
|
|
116
182
|
if config['skip_frontmatter'] && match = source.match(frontmatter)
|
117
|
-
|
118
|
-
|
183
|
+
@stripped_frontmatter = match[0]
|
184
|
+
@nb_newlines_for_frontmatter = match[0].count("\n")
|
185
|
+
source.sub!(frontmatter, "\n" * @nb_newlines_for_frontmatter)
|
119
186
|
end
|
120
187
|
|
121
188
|
source
|
122
189
|
end
|
190
|
+
|
191
|
+
def check_new_source_compatible(new_source)
|
192
|
+
if @stripped_frontmatter && !new_source.start_with?("\n" * @nb_newlines_for_frontmatter)
|
193
|
+
raise HamlLint::Exceptions::IncompatibleNewSource,
|
194
|
+
"Internal error: new_source doesn't start with enough newlines for the Front Matter that was stripped"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def unstrip_frontmatter(source)
|
199
|
+
return source unless @stripped_frontmatter
|
200
|
+
check_new_source_compatible(source)
|
201
|
+
|
202
|
+
source.sub("\n" * @nb_newlines_for_frontmatter, @stripped_frontmatter)
|
203
|
+
end
|
123
204
|
end
|
124
205
|
end
|
data/lib/haml_lint/exceptions.rb
CHANGED
@@ -5,6 +5,12 @@ module HamlLint::Exceptions
|
|
5
5
|
# Raised when a {Configuration} could not be loaded from a file.
|
6
6
|
class ConfigurationError < StandardError; end
|
7
7
|
|
8
|
+
# Raised trying to change source with incompatible one (ex: due to frontmatter)
|
9
|
+
class IncompatibleNewSource < StandardError; end
|
10
|
+
|
11
|
+
# Raised when linter's autocorrection cause an infinite loop
|
12
|
+
class InfiniteLoopError < StandardError; end
|
13
|
+
|
8
14
|
# Raised when invalid/incompatible command line options are provided.
|
9
15
|
class InvalidCLIOption < StandardError; end
|
10
16
|
|