immosquare-cleaner 0.1.80 → 0.1.82

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 160d617b3d77622b3c4c3b4978166b13a832eebd125e7e3cec3d27f250befcff
4
- data.tar.gz: 7f4ea0e67420419f1c97b4cc6780aa6f6de11ead46f3e74f9fc6c32ba973325b
3
+ metadata.gz: 8e38b197fb7d113d6719e69a3aaa53cbb4ccd91663ee13f7f987a2651be2d9b1
4
+ data.tar.gz: 68feb71efbcd5ffcc6b71906230e0db69cdc11bd0b67eec47abe618bae0f3323
5
5
  SHA512:
6
- metadata.gz: c6a38cffd458d0174ce2ac361249da090c7037d1d28ddc6dcc3653ad21f1c0091ee3c8056a2030abf8324f74bbe6f713038a064751b218b96b3d40c228d19869
7
- data.tar.gz: 2f3b914ea9df14c7fe03d18cd45d9df078645aa5f37fc6fb27027d4c0a90c52c1e27527661fb12007ff5912f5b0430f1869e954dffa9b3098a640808d352651d
6
+ metadata.gz: fe430816fd49ab2e28ef871af7769567eeaa948e3f662c1ae576590633df4d6785782b0d03060808b4a1c0c2986ec6f17808c25f3ada516325a2f7bd5336dc70
7
+ data.tar.gz: ce6c6e8abdcae48294d6dca00a08f3fe437a0c731c22867ef99af46ace2d66624349a7b3dbb5283a927ea6bf3e7c3165bb6903cf631079675296470a98e7f423
@@ -1,3 +1,3 @@
1
1
  module ImmosquareCleaner
2
- VERSION = "0.1.80".freeze
2
+ VERSION = "0.1.82".freeze
3
3
  end
@@ -9,6 +9,8 @@ linters:
9
9
  enabled: true
10
10
  CustomHtmlToContentTag:
11
11
  enabled: true
12
+ CustomNormalizeAttributeSpaces:
13
+ enabled: true
12
14
  Rubocop:
13
15
  enabled: true
14
16
  rubocop_config:
data/linters/erb-lint.yml CHANGED
@@ -8,6 +8,8 @@ linters:
8
8
  enabled: true
9
9
  CustomHtmlToContentTag:
10
10
  enabled: true
11
+ CustomNormalizeAttributeSpaces:
12
+ enabled: true
11
13
  Rubocop:
12
14
  enabled: true
13
15
  rubocop_config:
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "prism"
4
+
3
5
  module ERBLint
4
6
  module Linters
5
7
  ##============================================================##
@@ -7,14 +9,15 @@ module ERBLint
7
9
  ## output statement and converts them to content_tag helpers.
8
10
  ##
9
11
  ## @example
10
- ## # bad
12
+ ## bad
11
13
  ## <div class="card-title"><%= t("app.title") %></div>
12
14
  ##
13
- ## # good
15
+ ## good
14
16
  ## <%= content_tag(:div, t("app.title"), :class => "card-title") %>
15
17
  ##
16
18
  ##============================================================##
17
19
  class CustomHtmlToContentTag < Linter
20
+
18
21
  include LinterRegistry
19
22
 
20
23
  MSG = "Use content_tag helper instead of HTML tag with ERB output."
@@ -22,9 +25,26 @@ module ERBLint
22
25
  ##============================================================##
23
26
  ## Void elements that cannot have content (self-closing tags)
24
27
  ##============================================================##
25
- VOID_ELEMENTS = %w[
26
- area base br col command embed hr img input keygen
27
- link menuitem meta param source track wbr
28
+ VOID_ELEMENTS = [
29
+ "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "menuitem", "meta", "param", "source", "track", "wbr"
30
+ ].freeze
31
+
32
+ ##============================================================##
33
+ ## Methods that should not be converted to content_tag
34
+ ## - render: partials should stay in HTML for readability
35
+ ## - content_tag: prevents cascading on multiple erb_lint passes
36
+ ##
37
+ ## Note: If content_tag is removed from exclusions, additional
38
+ ## logic is needed to prevent cascading conversions. The approach
39
+ ## would be to check if the content_tag has HTML attributes
40
+ ## (:class, :id) which indicates it was generated by us in a
41
+ ## previous pass. See same_tag_content_tag? method pattern:
42
+ ## - content_tag(:div, x, :class => "y") → skip (generated)
43
+ ## - content_tag(:div, x) → allow (user-written)
44
+ ##============================================================##
45
+ EXCLUDED_METHODS = [
46
+ "render",
47
+ "content_tag"
28
48
  ].freeze
29
49
 
30
50
  def run(processed_source)
@@ -60,11 +80,15 @@ module ERBLint
60
80
  next unless extract_tag_name(closing_child) == tag_name
61
81
 
62
82
  ##============================================================##
63
- ## Build the content_tag replacement
83
+ ## Extract ERB code and check if it should be skipped
64
84
  ##============================================================##
65
- attributes = extract_attributes(child)
66
85
  erb_code = extract_erb_code(erb_node)
86
+ next if excluded_method?(erb_code)
67
87
 
88
+ ##============================================================##
89
+ ## Build the content_tag replacement
90
+ ##============================================================##
91
+ attributes = extract_attributes(child)
68
92
  new_code = build_content_tag(tag_name, erb_code, attributes)
69
93
 
70
94
  ##============================================================##
@@ -140,6 +164,34 @@ module ERBLint
140
164
  code_node&.loc&.source&.strip
141
165
  end
142
166
 
167
+ ##============================================================##
168
+ ## Check if the ERB code calls an excluded method
169
+ ## Uses Prism parser for proper Ruby AST analysis
170
+ ##============================================================##
171
+ def excluded_method?(erb_code)
172
+ return false unless erb_code
173
+
174
+ method_name = extract_method_name(erb_code)
175
+ return false unless method_name
176
+
177
+ EXCLUDED_METHODS.include?(method_name)
178
+ end
179
+
180
+ ##============================================================##
181
+ ## Extract the method name from Ruby code using Prism parser
182
+ ##============================================================##
183
+ def extract_method_name(erb_code)
184
+ result = Prism.parse(erb_code)
185
+ return nil unless result.success?
186
+
187
+ node = result.value.statements.body.first
188
+ return nil unless node.is_a?(Prism::CallNode)
189
+
190
+ node.name.to_s
191
+ rescue StandardError
192
+ nil
193
+ end
194
+
143
195
  ##============================================================##
144
196
  ## Extract attributes from tag node as hash
145
197
  ##============================================================##
@@ -203,10 +255,8 @@ module ERBLint
203
255
  ## Check if value contains interpolation
204
256
  ##============================================================##
205
257
  if value&.include?('#{')
206
- "#{key} => \"#{value}\""
207
- else
208
- "#{key} => \"#{value}\""
209
258
  end
259
+ "#{key} => \"#{value}\""
210
260
  end.join(", ")
211
261
 
212
262
  "<%= content_tag(:#{tag_name}, #{content}, #{attrs_str}) %>"
@@ -224,6 +274,7 @@ module ERBLint
224
274
  ":\"#{name}\""
225
275
  end
226
276
  end
277
+
227
278
  end
228
279
  end
229
280
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ERBLint
4
+ module Linters
5
+ ##============================================================##
6
+ ## This linter detects and removes multiple consecutive spaces
7
+ ## in HTML attribute values.
8
+ ##
9
+ ## @example
10
+ ## bad
11
+ ## <div class="flex grow items-center">
12
+ ##
13
+ ## good
14
+ ## <div class="flex grow items-center">
15
+ ##
16
+ ##============================================================##
17
+ class CustomNormalizeAttributeSpaces < Linter
18
+
19
+ include LinterRegistry
20
+
21
+ MSG = "Remove multiple consecutive spaces in attribute value."
22
+
23
+ def run(processed_source)
24
+ process_node(processed_source, processed_source.ast)
25
+ end
26
+
27
+ def autocorrect(_processed_source, offense)
28
+ lambda do |corrector|
29
+ corrector.replace(offense.source_range, offense.context[:new_value])
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ ##============================================================##
36
+ ## Recursively process all nodes in the AST
37
+ ##============================================================##
38
+ def process_node(processed_source, node)
39
+ return unless node
40
+
41
+ ##============================================================##
42
+ ## Check attribute values for multiple spaces
43
+ ##============================================================##
44
+ if node.respond_to?(:type) && node.type == :attribute_value
45
+ value_source = node.loc.source
46
+ if value_source.match?(/ +/)
47
+ normalized = value_source.gsub(/ +/, " ")
48
+ range = processed_source.to_source_range(node.loc.begin_pos...node.loc.end_pos)
49
+ add_offense(range, MSG, {:new_value => normalized})
50
+ end
51
+ end
52
+
53
+ ##============================================================##
54
+ ## Recurse into children
55
+ ##============================================================##
56
+ return unless node.respond_to?(:children)
57
+
58
+ node.children.each {|child| process_node(processed_source, child) }
59
+ end
60
+
61
+ end
62
+ end
63
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: immosquare-cleaner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.80
4
+ version: 0.1.82
5
5
  platform: ruby
6
6
  authors:
7
7
  - immosquare
@@ -150,6 +150,7 @@ files:
150
150
  - linters/erb-lint-3.4.1.yml
151
151
  - linters/erb-lint.yml
152
152
  - linters/erb_lint/custom_html_to_content_tag.rb
153
+ - linters/erb_lint/custom_normalize_attribute_spaces.rb
153
154
  - linters/erb_lint/custom_single_line_if_modifier.rb
154
155
  - linters/eslint.config.mjs
155
156
  - linters/prettier.yml