haml_lint 0.45.0 → 0.48.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +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