haml_lint 0.45.0 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/bin/haml-lint +1 -1
  3. data/config/default.yml +6 -28
  4. data/config/forced_rubocop_config.yml +171 -0
  5. data/lib/haml_lint/adapter/haml_4.rb +18 -0
  6. data/lib/haml_lint/adapter/haml_5.rb +11 -0
  7. data/lib/haml_lint/adapter/haml_6.rb +11 -0
  8. data/lib/haml_lint/cli.rb +8 -3
  9. data/lib/haml_lint/configuration_loader.rb +9 -10
  10. data/lib/haml_lint/document.rb +89 -8
  11. data/lib/haml_lint/exceptions.rb +6 -0
  12. data/lib/haml_lint/extensions/haml_util_unescape_interpolation_tracking.rb +35 -0
  13. data/lib/haml_lint/file_finder.rb +2 -2
  14. data/lib/haml_lint/lint.rb +10 -1
  15. data/lib/haml_lint/linter/final_newline.rb +4 -3
  16. data/lib/haml_lint/linter/implicit_div.rb +1 -1
  17. data/lib/haml_lint/linter/indentation.rb +3 -3
  18. data/lib/haml_lint/linter/no_placeholders.rb +18 -0
  19. data/lib/haml_lint/linter/repeated_id.rb +2 -1
  20. data/lib/haml_lint/linter/rubocop.rb +353 -60
  21. data/lib/haml_lint/linter/space_before_script.rb +8 -10
  22. data/lib/haml_lint/linter/unnecessary_string_output.rb +1 -1
  23. data/lib/haml_lint/linter/view_length.rb +1 -1
  24. data/lib/haml_lint/linter.rb +60 -10
  25. data/lib/haml_lint/linter_registry.rb +3 -5
  26. data/lib/haml_lint/logger.rb +2 -2
  27. data/lib/haml_lint/options.rb +26 -2
  28. data/lib/haml_lint/rake_task.rb +2 -2
  29. data/lib/haml_lint/reporter/hash_reporter.rb +1 -3
  30. data/lib/haml_lint/reporter/offense_count_reporter.rb +1 -1
  31. data/lib/haml_lint/reporter/utils.rb +33 -4
  32. data/lib/haml_lint/ruby_extraction/ad_hoc_chunk.rb +24 -0
  33. data/lib/haml_lint/ruby_extraction/base_chunk.rb +114 -0
  34. data/lib/haml_lint/ruby_extraction/chunk_extractor.rb +630 -0
  35. data/lib/haml_lint/ruby_extraction/coordinator.rb +181 -0
  36. data/lib/haml_lint/ruby_extraction/haml_comment_chunk.rb +34 -0
  37. data/lib/haml_lint/ruby_extraction/implicit_end_chunk.rb +17 -0
  38. data/lib/haml_lint/ruby_extraction/interpolation_chunk.rb +26 -0
  39. data/lib/haml_lint/ruby_extraction/non_ruby_filter_chunk.rb +32 -0
  40. data/lib/haml_lint/ruby_extraction/placeholder_marker_chunk.rb +40 -0
  41. data/lib/haml_lint/ruby_extraction/ruby_filter_chunk.rb +33 -0
  42. data/lib/haml_lint/ruby_extraction/ruby_source.rb +5 -0
  43. data/lib/haml_lint/ruby_extraction/script_chunk.rb +244 -0
  44. data/lib/haml_lint/ruby_extraction/tag_attributes_chunk.rb +63 -0
  45. data/lib/haml_lint/ruby_extraction/tag_script_chunk.rb +30 -0
  46. data/lib/haml_lint/runner.rb +35 -3
  47. data/lib/haml_lint/spec/matchers/report_lint.rb +22 -7
  48. data/lib/haml_lint/spec/normalize_indent.rb +2 -2
  49. data/lib/haml_lint/spec/shared_linter_context.rb +9 -1
  50. data/lib/haml_lint/spec/shared_rubocop_autocorrect_context.rb +158 -0
  51. data/lib/haml_lint/spec.rb +1 -0
  52. data/lib/haml_lint/tree/filter_node.rb +10 -0
  53. data/lib/haml_lint/tree/node.rb +13 -4
  54. data/lib/haml_lint/tree/tag_node.rb +5 -9
  55. data/lib/haml_lint/utils.rb +135 -5
  56. data/lib/haml_lint/version.rb +1 -1
  57. data/lib/haml_lint/version_comparer.rb +25 -0
  58. data/lib/haml_lint.rb +12 -0
  59. metadata +24 -6
  60. data/lib/haml_lint/ruby_extractor.rb +0 -223
data/lib/haml_lint.rb CHANGED
@@ -21,8 +21,14 @@ require 'haml_lint/file_finder'
21
21
  require 'haml_lint/runner'
22
22
  require 'haml_lint/utils'
23
23
  require 'haml_lint/version'
24
+ require 'haml_lint/version_comparer'
24
25
  require 'haml_lint/severity'
25
26
 
27
+ # Lead all extensions to external source code
28
+ Dir[File.expand_path('haml_lint/extensions/*.rb', File.dirname(__FILE__))].sort.each do |file|
29
+ require file
30
+ end
31
+
26
32
  # Load all parse tree node classes
27
33
  require 'haml_lint/tree/node'
28
34
  require 'haml_lint/node_transformer'
@@ -39,3 +45,9 @@ end
39
45
  Dir[File.expand_path('haml_lint/reporter/*.rb', File.dirname(__FILE__))].sort.each do |file|
40
46
  require file
41
47
  end
48
+
49
+ # Load all the chunks for RubyExtraction
50
+ require 'haml_lint/ruby_extraction/base_chunk'
51
+ Dir[File.expand_path('haml_lint/ruby_extraction/*.rb', File.dirname(__FILE__))].sort.each do |file|
52
+ require file
53
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haml_lint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.45.0
4
+ version: 0.48.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane da Silva
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-28 00:00:00.000000000 Z
11
+ date: 2023-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: haml
@@ -64,14 +64,14 @@ dependencies:
64
64
  requirements:
65
65
  - - ">="
66
66
  - !ruby/object:Gem::Version
67
- version: 0.50.0
67
+ version: '1.0'
68
68
  type: :runtime
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - ">="
73
73
  - !ruby/object:Gem::Version
74
- version: 0.50.0
74
+ version: '1.0'
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: sysexits
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -96,6 +96,7 @@ extra_rdoc_files: []
96
96
  files:
97
97
  - bin/haml-lint
98
98
  - config/default.yml
99
+ - config/forced_rubocop_config.yml
99
100
  - lib/haml_lint.rb
100
101
  - lib/haml_lint/adapter.rb
101
102
  - lib/haml_lint/adapter/haml_4.rb
@@ -109,6 +110,7 @@ files:
109
110
  - lib/haml_lint/directive.rb
110
111
  - lib/haml_lint/document.rb
111
112
  - lib/haml_lint/exceptions.rb
113
+ - lib/haml_lint/extensions/haml_util_unescape_interpolation_tracking.rb
112
114
  - lib/haml_lint/file_finder.rb
113
115
  - lib/haml_lint/haml_visitor.rb
114
116
  - lib/haml_lint/lint.rb
@@ -132,6 +134,7 @@ files:
132
134
  - lib/haml_lint/linter/line_length.rb
133
135
  - lib/haml_lint/linter/multiline_pipe.rb
134
136
  - lib/haml_lint/linter/multiline_script.rb
137
+ - lib/haml_lint/linter/no_placeholders.rb
135
138
  - lib/haml_lint/linter/object_reference_attributes.rb
136
139
  - lib/haml_lint/linter/repeated_id.rb
137
140
  - lib/haml_lint/linter/rubocop.rb
@@ -162,7 +165,20 @@ files:
162
165
  - lib/haml_lint/reporter/offense_count_reporter.rb
163
166
  - lib/haml_lint/reporter/progress_reporter.rb
164
167
  - lib/haml_lint/reporter/utils.rb
165
- - lib/haml_lint/ruby_extractor.rb
168
+ - lib/haml_lint/ruby_extraction/ad_hoc_chunk.rb
169
+ - lib/haml_lint/ruby_extraction/base_chunk.rb
170
+ - lib/haml_lint/ruby_extraction/chunk_extractor.rb
171
+ - lib/haml_lint/ruby_extraction/coordinator.rb
172
+ - lib/haml_lint/ruby_extraction/haml_comment_chunk.rb
173
+ - lib/haml_lint/ruby_extraction/implicit_end_chunk.rb
174
+ - lib/haml_lint/ruby_extraction/interpolation_chunk.rb
175
+ - lib/haml_lint/ruby_extraction/non_ruby_filter_chunk.rb
176
+ - lib/haml_lint/ruby_extraction/placeholder_marker_chunk.rb
177
+ - lib/haml_lint/ruby_extraction/ruby_filter_chunk.rb
178
+ - lib/haml_lint/ruby_extraction/ruby_source.rb
179
+ - lib/haml_lint/ruby_extraction/script_chunk.rb
180
+ - lib/haml_lint/ruby_extraction/tag_attributes_chunk.rb
181
+ - lib/haml_lint/ruby_extraction/tag_script_chunk.rb
166
182
  - lib/haml_lint/ruby_parser.rb
167
183
  - lib/haml_lint/runner.rb
168
184
  - lib/haml_lint/severity.rb
@@ -170,6 +186,7 @@ files:
170
186
  - lib/haml_lint/spec/matchers/report_lint.rb
171
187
  - lib/haml_lint/spec/normalize_indent.rb
172
188
  - lib/haml_lint/spec/shared_linter_context.rb
189
+ - lib/haml_lint/spec/shared_rubocop_autocorrect_context.rb
173
190
  - lib/haml_lint/tree/comment_node.rb
174
191
  - lib/haml_lint/tree/doctype_node.rb
175
192
  - lib/haml_lint/tree/filter_node.rb
@@ -183,6 +200,7 @@ files:
183
200
  - lib/haml_lint/tree/tag_node.rb
184
201
  - lib/haml_lint/utils.rb
185
202
  - lib/haml_lint/version.rb
203
+ - lib/haml_lint/version_comparer.rb
186
204
  homepage: https://github.com/sds/haml-lint
187
205
  licenses:
188
206
  - MIT
@@ -195,7 +213,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
195
213
  requirements:
196
214
  - - ">="
197
215
  - !ruby/object:Gem::Version
198
- version: 2.4.0
216
+ version: 2.6.0
199
217
  required_rubygems_version: !ruby/object:Gem::Requirement
200
218
  requirements:
201
219
  - - ">="
@@ -1,223 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module HamlLint
4
- # Utility class for extracting Ruby script from a HAML file that can then be
5
- # linted with a Ruby linter (i.e. is "legal" Ruby). The goal is to turn this:
6
- #
7
- # - if signed_in?(viewer)
8
- # %span Stuff
9
- # = link_to 'Sign Out', sign_out_path
10
- # - else
11
- # .some-class{ class: my_method }= my_method
12
- # = link_to 'Sign In', sign_in_path
13
- #
14
- # into this:
15
- #
16
- # if signed_in?(viewer)
17
- # link_to 'Sign Out', sign_out_path
18
- # else
19
- # { class: my_method }
20
- # my_method
21
- # link_to 'Sign In', sign_in_path
22
- # end
23
- #
24
- # The translation won't be perfect, and won't make any real sense, but the
25
- # relationship between variable declarations/uses and the flow control graph
26
- # will remain intact.
27
- class RubyExtractor # rubocop:disable Metrics/ClassLength
28
- include HamlVisitor
29
-
30
- # Stores the extracted source and a map of lines of generated source to the
31
- # original source that created them.
32
- #
33
- # @attr_reader source [String] generated source code
34
- # @attr_reader source_map [Hash] map of line numbers from generated source
35
- # to original source line number
36
- RubySource = Struct.new(:source, :source_map)
37
-
38
- # Extracts Ruby code from Sexp representing a Slim document.
39
- #
40
- # @param document [HamlLint::Document]
41
- # @return [HamlLint::RubyExtractor::RubySource]
42
- def extract(document)
43
- visit(document.tree)
44
- RubySource.new(@source_lines.join("\n"), @source_map)
45
- end
46
-
47
- def visit_root(_node)
48
- @source_lines = []
49
- @source_map = {}
50
- @line_count = 0
51
- @indent_level = 0
52
- @output_count = 0
53
-
54
- yield # Collect lines of code from children
55
- end
56
-
57
- def visit_plain(node)
58
- # Don't output the text, as we don't want to have to deal with any RuboCop
59
- # cops regarding StringQuotes or AsciiComments, and it's not important to
60
- # overall document anyway.
61
- add_dummy_puts(node)
62
- end
63
-
64
- def visit_tag(node)
65
- additional_attributes = node.dynamic_attributes_sources
66
-
67
- # Include dummy references to code executed in attributes list
68
- # (this forces a "use" of a variable to prevent "assigned but unused
69
- # variable" lints)
70
- additional_attributes.each do |attributes_code|
71
- # Normalize by removing excess whitespace to avoid format lints
72
- attributes_code = attributes_code.gsub(/\s*\n\s*/, "\n").strip
73
-
74
- # Attributes can either be a method call or a literal hash, so wrap it
75
- # in a method call itself in order to avoid having to differentiate the
76
- # two.
77
- add_line("{}.merge(#{attributes_code})", node)
78
- end
79
-
80
- check_tag_static_hash_source(node)
81
-
82
- # We add a dummy puts statement to represent the tag name being output.
83
- # This prevents some erroneous RuboCop warnings.
84
- add_dummy_puts(node, node.tag_name)
85
-
86
- code = node.script.strip
87
- add_line(code, node) unless code.empty?
88
- end
89
-
90
- def after_visit_tag(node)
91
- # We add a dummy puts statement for closing tag.
92
- add_dummy_puts(node, "#{node.tag_name}/")
93
- end
94
-
95
- def visit_script(node)
96
- code = node.text
97
-
98
- add_line(code.strip, node)
99
-
100
- start_block = anonymous_block?(code) || start_block_keyword?(code)
101
-
102
- if start_block
103
- @indent_level += 1
104
- end
105
-
106
- yield # Continue extracting code from children
107
-
108
- if start_block
109
- @indent_level -= 1
110
- add_line('end', node)
111
- end
112
- end
113
-
114
- def visit_haml_comment(node)
115
- # We want to preseve leading whitespace if it exists, but include leading
116
- # whitespace if it doesn't exist so that RuboCop's LeadingCommentSpace
117
- # doesn't complain
118
- comment = node.text
119
- .gsub(/\n(\S)/, "\n# \\1")
120
- .gsub(/\n(\s)/, "\n#\\1")
121
- add_line("##{comment}", node)
122
- end
123
-
124
- def visit_silent_script(node, &block)
125
- visit_script(node, &block)
126
- end
127
-
128
- def visit_filter(node)
129
- if node.filter_type == 'ruby'
130
- node.text.split("\n").each_with_index do |line, index|
131
- add_line(line, node.line + index + 1, false)
132
- end
133
- else
134
- add_dummy_puts(node, ":#{node.filter_type}")
135
- HamlLint::Utils.extract_interpolated_values(node.text) do |interpolated_code, line|
136
- add_line(interpolated_code, node.line + line)
137
- end
138
- end
139
- end
140
-
141
- private
142
-
143
- def check_tag_static_hash_source(node)
144
- # Haml::Parser converts hashrocket-style hash attributes of strings and symbols
145
- # to static attributes, and excludes them from the dynamic attribute sources:
146
- # https://github.com/haml/haml/blob/08f97ec4dc8f59fe3d7f6ab8f8807f86f2a15b68/lib/haml/parser.rb#L400-L404
147
- # https://github.com/haml/haml/blob/08f97ec4dc8f59fe3d7f6ab8f8807f86f2a15b68/lib/haml/parser.rb#L540-L554
148
- # Here, we add the hash source back in so it can be inspected by rubocop.
149
- if node.hash_attributes? && node.dynamic_attributes_sources.empty?
150
- normalized_attr_source = node.dynamic_attributes_source[:hash].gsub(/\s*\n\s*/, ' ')
151
-
152
- add_line(normalized_attr_source, node)
153
- end
154
- end
155
-
156
- # Adds a dummy method call with a unique name so we don't get
157
- # Style/IdenticalConditionalBranches RuboCop warnings
158
- def add_dummy_puts(node, annotation = nil)
159
- annotation = " # #{annotation}" if annotation
160
- add_line("_haml_lint_puts_#{@output_count}#{annotation}", node)
161
- @output_count += 1
162
- end
163
-
164
- def add_line(code, node_or_line, discard_blanks = true)
165
- return if code.empty? && discard_blanks
166
-
167
- indent_level = @indent_level
168
-
169
- if node_or_line.respond_to?(:line)
170
- # Since mid-block keywords are children of the corresponding start block
171
- # keyword, we need to reduce their indentation level by 1. However, we
172
- # don't do this unless this is an actual tag node (a raw line number
173
- # means this came from a `:ruby` filter).
174
- indent_level -= 1 if mid_block_keyword?(code)
175
- end
176
-
177
- indent = (' ' * 2 * indent_level)
178
-
179
- @source_lines << indent_code(code, indent)
180
-
181
- original_line =
182
- node_or_line.respond_to?(:line) ? node_or_line.line : node_or_line
183
-
184
- # For interpolated code in filters that spans multiple lines, the
185
- # resulting code will span multiple lines, so we need to create a
186
- # mapping for each line.
187
- (code.count("\n") + 1).times do
188
- @line_count += 1
189
- @source_map[@line_count] = original_line
190
- end
191
- end
192
-
193
- def indent_code(code, indent)
194
- codes = code.split("\n")
195
- codes.map { |c| indent + c }.join("\n")
196
- end
197
-
198
- def anonymous_block?(text)
199
- text =~ /\bdo\s*(\|\s*[^\|]*\s*\|)?(\s*#.*)?\z/
200
- end
201
-
202
- START_BLOCK_KEYWORDS = %w[if unless case begin for until while].freeze
203
- def start_block_keyword?(text)
204
- START_BLOCK_KEYWORDS.include?(block_keyword(text))
205
- end
206
-
207
- MID_BLOCK_KEYWORDS = %w[else elsif when rescue ensure].freeze
208
- def mid_block_keyword?(text)
209
- MID_BLOCK_KEYWORDS.include?(block_keyword(text))
210
- end
211
-
212
- LOOP_KEYWORDS = %w[for until while].freeze
213
- def block_keyword(text)
214
- # Need to handle 'for'/'while' since regex stolen from HAML parser doesn't
215
- if keyword = text[/\A\s*([^\s]+)\s+/, 1]
216
- return keyword if LOOP_KEYWORDS.include?(keyword)
217
- end
218
-
219
- return unless keyword = text.scan(Haml::Parser::BLOCK_KEYWORD_REGEX)[0]
220
- keyword[0] || keyword[1]
221
- end
222
- end
223
- end