gl_rubocop 0.2.19 → 0.2.20
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e5cfeeb9c783874509854ab8f98e6c8f1d17284f81e16193666d99e49b2a7d0b
|
|
4
|
+
data.tar.gz: 295a40eae88db48e09a7a03026e89744f511d57748e0591565f0767e3ab80257
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8344a4c809b323cdcc4c4a7d15441aa08346a8a6964237d4efc1e9fc9895293e098960d60268e74274720cc5f56c0472aea24b30c75abc8bbcad93b0b48e79f7
|
|
7
|
+
data.tar.gz: 496e86ca9704c2329455f7f001dbd8fe89405dda81b23a9b6a0067b6d0760f9b49d202e4db532d97626673508ab8814a53f0d0dce5b0a6dd2fe4ece5ca85d930
|
data/default.yml
CHANGED
|
@@ -15,6 +15,7 @@ require:
|
|
|
15
15
|
- ./lib/gl_rubocop/gl_cops/rails_cache.rb
|
|
16
16
|
- ./lib/gl_rubocop/gl_cops/sidekiq_inherits_from_sidekiq_job.rb
|
|
17
17
|
- ./lib/gl_rubocop/gl_cops/unique_identifier.rb
|
|
18
|
+
- ./lib/gl_rubocop/gl_cops/tailwind_no_contradicting_class_name.rb
|
|
18
19
|
|
|
19
20
|
AllCops:
|
|
20
21
|
SuggestExtensions: false
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../helpers/haml_content_helper'
|
|
4
|
+
require_relative '../helpers/erb_content_helper'
|
|
5
|
+
|
|
6
|
+
module GLRubocop
|
|
7
|
+
module GLCops
|
|
8
|
+
# Cop to detect contradicting Tailwind CSS class names
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad
|
|
12
|
+
# %div{ class: "tw:w-1 tw:w-2" }
|
|
13
|
+
# .tw:h-5.tw:h-6
|
|
14
|
+
# %button.tw:w-1.tw:w-2
|
|
15
|
+
#
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# %div{ class: "tw:w-1 tw:h-2" }
|
|
19
|
+
# tw:h-5
|
|
20
|
+
# %button.tw:m-4.tw:p-8
|
|
21
|
+
|
|
22
|
+
# rubocop:disable Metrics/ClassLength
|
|
23
|
+
class TailwindNoContradictingClassName < RuboCop::Cop::Cop
|
|
24
|
+
include GLRubocop::HamlContentHelper
|
|
25
|
+
include GLRubocop::ErbContentHelper
|
|
26
|
+
|
|
27
|
+
MSG =
|
|
28
|
+
'Contradicting Tailwind CSS classes found: %<classes>s both affect the same CSS property'
|
|
29
|
+
GIVELIVELY_TAILWIND_CLASS_PREFIX = 'tw:'
|
|
30
|
+
|
|
31
|
+
# Tailwind CSS property groups that should not contradict
|
|
32
|
+
CONTRADICTION_GROUPS = {
|
|
33
|
+
width: %w[w],
|
|
34
|
+
height: %w[h],
|
|
35
|
+
max_width: %w[max-w],
|
|
36
|
+
max_height: %w[max-h],
|
|
37
|
+
min_width: %w[min-w],
|
|
38
|
+
min_height: %w[min-h],
|
|
39
|
+
margin_top: %w[m my mt],
|
|
40
|
+
margin_right: %w[m mx mr],
|
|
41
|
+
margin_bottom: %w[m my mb],
|
|
42
|
+
margin_left: %w[m mx ml],
|
|
43
|
+
padding_top: %w[p py pt],
|
|
44
|
+
padding_right: %w[p px pr],
|
|
45
|
+
padding_bottom: %w[p py pb],
|
|
46
|
+
padding_left: %w[p px pl],
|
|
47
|
+
display: %w[block hidden flex inline inline-block inline-flex grid inline-grid table],
|
|
48
|
+
position: %w[static relative absolute fixed sticky],
|
|
49
|
+
text_align: %w[text-left text-center text-right text-justify],
|
|
50
|
+
flex_direction: %w[flex-row flex-row-reverse flex-col flex-col-reverse],
|
|
51
|
+
flex_wrap: %w[flex-nowrap flex-wrap flex-wrap-reverse],
|
|
52
|
+
justify_content: %w[
|
|
53
|
+
justify-start justify-end justify-center justify-between justify-around justify-evenly
|
|
54
|
+
],
|
|
55
|
+
align_items: %w[items-start items-end items-center items-baseline items-stretch],
|
|
56
|
+
place_content: %w[
|
|
57
|
+
place-content-center place-content-start place-content-end place-content-between
|
|
58
|
+
place-content-around place-content-evenly
|
|
59
|
+
],
|
|
60
|
+
place_items: %w[
|
|
61
|
+
place-items-start place-items-end place-items-center place-items-baseline
|
|
62
|
+
place-items-stretch
|
|
63
|
+
],
|
|
64
|
+
place_self: %w[
|
|
65
|
+
place-self-auto place-self-start place-self-end place-self-center place-self-stretch
|
|
66
|
+
],
|
|
67
|
+
align_content: %w[
|
|
68
|
+
content-center content-start content-end content-between content-around content-evenly
|
|
69
|
+
],
|
|
70
|
+
align_self: %w[
|
|
71
|
+
self-auto self-start self-end self-center self-stretch self-baseline
|
|
72
|
+
],
|
|
73
|
+
justify_items: %w[
|
|
74
|
+
justify-items-start justify-items-end justify-items-center justify-items-stretch
|
|
75
|
+
],
|
|
76
|
+
justify_self: %w[
|
|
77
|
+
justify-self-auto justify-self-start justify-self-end justify-self-center
|
|
78
|
+
justify-self-stretch
|
|
79
|
+
],
|
|
80
|
+
font_size: %w[
|
|
81
|
+
text-xs text-sm text-base text-lg text-xl text-2xl text-3xl text-4xl text-5xl text-6xl
|
|
82
|
+
],
|
|
83
|
+
font_weight: %w[
|
|
84
|
+
font-thin font-extralight font-light font-normal font-medium font-semibold font-bold
|
|
85
|
+
font-extrabold font-black
|
|
86
|
+
],
|
|
87
|
+
font_style: %w[italic not-italic],
|
|
88
|
+
letter_spacing: %w[
|
|
89
|
+
tracking-tighter tracking-tight tracking-normal tracking-wide tracking-wider
|
|
90
|
+
tracking-widest
|
|
91
|
+
],
|
|
92
|
+
line_height: %w[
|
|
93
|
+
leading-none leading-tight leading-snug leading-normal leading-relaxed leading-loose
|
|
94
|
+
],
|
|
95
|
+
text_decoration_line: %w[underline line-through no-underline],
|
|
96
|
+
text_transform: %w[uppercase lowercase capitalize normal-case],
|
|
97
|
+
text_decoration_style: %w[
|
|
98
|
+
decoration-solid decoration-dashed decoration-dotted decoration-double decoration-wavy
|
|
99
|
+
],
|
|
100
|
+
text_wrap: %w[break-normal break-words break-all],
|
|
101
|
+
vertical_align: %w[
|
|
102
|
+
align-baseline align-top align-middle align-bottom align-text-top align-text-bottom
|
|
103
|
+
],
|
|
104
|
+
text_overflow: %w[truncate overflow-ellipsis overflow-clip],
|
|
105
|
+
overflow: %w[
|
|
106
|
+
overflow-auto overflow-hidden overflow-visible overflow-scroll
|
|
107
|
+
],
|
|
108
|
+
visibility: %w[visible invisible collapse],
|
|
109
|
+
border_style: %w[
|
|
110
|
+
border-solid border-dashed border-dotted border-double border-none
|
|
111
|
+
],
|
|
112
|
+
box_shadow: %w[
|
|
113
|
+
shadow-sm shadow shadow-md shadow-lg shadow-xl shadow-2xl shadow-inner shadow-none
|
|
114
|
+
]
|
|
115
|
+
# Add more property groups as needed, currently we have chosen to omit color-related properties and border-radius classes
|
|
116
|
+
# to reduce false positives in common use cases.
|
|
117
|
+
}.freeze
|
|
118
|
+
|
|
119
|
+
BREAKPOINT_ORDER = %w[sm md lg xl 2xl].freeze
|
|
120
|
+
|
|
121
|
+
def on_send(node)
|
|
122
|
+
return unless render_method?(node)
|
|
123
|
+
|
|
124
|
+
if haml_file?
|
|
125
|
+
haml_content = read_haml_file
|
|
126
|
+
return unless haml_content
|
|
127
|
+
|
|
128
|
+
check_haml_content(haml_content, node)
|
|
129
|
+
elsif erb_file?
|
|
130
|
+
erb_content = read_erb_file
|
|
131
|
+
return unless erb_content
|
|
132
|
+
|
|
133
|
+
check_erb_content(erb_content, node)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def on_str(node)
|
|
138
|
+
# Check string literals for Tailwind classes
|
|
139
|
+
check_string_for_tailwind_classes(node)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
def render_method?(node)
|
|
145
|
+
node.method_name == :render && node.arguments.any?
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def check_erb_content(content, node)
|
|
149
|
+
classes = extract_all_erb_classes(content)
|
|
150
|
+
contradicting_classes = find_contradicting_classes(classes)
|
|
151
|
+
|
|
152
|
+
return if contradicting_classes.empty?
|
|
153
|
+
|
|
154
|
+
contradicting_classes.each do |group|
|
|
155
|
+
add_offense(
|
|
156
|
+
node,
|
|
157
|
+
message: format(MSG, classes: group.join(', '))
|
|
158
|
+
)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def extract_all_erb_classes(content)
|
|
163
|
+
classes = []
|
|
164
|
+
classes.concat(extract_classes_from_html_attributes(content))
|
|
165
|
+
classes.concat(extract_classes_from_rails_hash(content))
|
|
166
|
+
classes.concat(extract_classes_from_rails_symbol_hash(content))
|
|
167
|
+
classes.select { |class_name| tailwind_class?(class_name) }
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def extract_classes_from_html_attributes(content)
|
|
171
|
+
# Example: <div class="tw:w-1 tw:w-2"></div>
|
|
172
|
+
content.scan(/class\s*=\s*['"]([^'"]+)['"]/).flat_map { |match| match.first.split(/\s+/) }
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def extract_classes_from_rails_hash(content)
|
|
176
|
+
# Example: <%= radio_button_tag { class: 'tw:w-1 tw:w-2' } %>
|
|
177
|
+
content.scan(/class:\s*['"]([^'"]+)['"]/).flat_map { |match| match.first.split(/\s+/) }
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def extract_classes_from_rails_symbol_hash(content)
|
|
181
|
+
# Example: <%= text_field_tag( ..., :class => 'tw:w-1 tw:w-2' ) %>
|
|
182
|
+
content.scan(/:class\s*=>\s*['"]([^'"]+)['"]/).flat_map { |match| match.first.split(/\s+/) }
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def check_haml_content(content, node)
|
|
186
|
+
classes = extract_all_haml_classes(content)
|
|
187
|
+
contradicting_classes = find_contradicting_classes(classes)
|
|
188
|
+
|
|
189
|
+
return if contradicting_classes.empty?
|
|
190
|
+
|
|
191
|
+
contradicting_classes.each do |group|
|
|
192
|
+
add_offense(
|
|
193
|
+
node,
|
|
194
|
+
message: format(MSG, classes: group.join(', '))
|
|
195
|
+
)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def check_string_for_tailwind_classes(node)
|
|
200
|
+
return unless node.str_type?
|
|
201
|
+
|
|
202
|
+
content = node.value
|
|
203
|
+
classes = extract_classes_from_string(content)
|
|
204
|
+
contradicting_classes = find_contradicting_classes(classes)
|
|
205
|
+
|
|
206
|
+
return if contradicting_classes.empty?
|
|
207
|
+
|
|
208
|
+
contradicting_classes.each do |group|
|
|
209
|
+
add_offense(
|
|
210
|
+
node,
|
|
211
|
+
message: format(MSG, classes: group.join(', '))
|
|
212
|
+
)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def extract_classes_from_string(content)
|
|
217
|
+
# Split by whitespace and filter for Tailwind classes
|
|
218
|
+
content.split(/\s+/).select { |cls| tailwind_class?(cls) }
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def extract_all_haml_classes(content)
|
|
222
|
+
classes = []
|
|
223
|
+
|
|
224
|
+
# Extract from HAML class shortcuts (e.g., %div.tw:w-1.tw:w-2)
|
|
225
|
+
content.scan(/^[^#]*%\w+(?:\.[^{\s#]+)+/m) do |match|
|
|
226
|
+
class_shortcuts = match.scan(/\.([^.{\s#]+)/).flatten
|
|
227
|
+
classes.concat(class_shortcuts)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Extract from HAML hash syntax (e.g., %div{ class: 'tw:m-4 tw:m-8' })
|
|
231
|
+
content.scan(/class:\s*['"]([^'"]+)['"]/) do |match|
|
|
232
|
+
class_list = match.first.split(/\s+/)
|
|
233
|
+
classes.concat(class_list)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
classes.select { |class_name| tailwind_class?(class_name) }
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def tailwind_class?(class_name)
|
|
240
|
+
class_name.start_with?(GIVELIVELY_TAILWIND_CLASS_PREFIX)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
244
|
+
def find_contradicting_classes(classes)
|
|
245
|
+
# Remove the 'tw:' prefix for property matching
|
|
246
|
+
normalized_classes = classes.map { |class_name| class_name.sub(matcher, '') }
|
|
247
|
+
|
|
248
|
+
contradictions = []
|
|
249
|
+
|
|
250
|
+
normalized_classes.each_with_index do |first_class, index|
|
|
251
|
+
first_class_data = extract_class_data(first_class)
|
|
252
|
+
next unless valid_property?(first_class_data[:css_property])
|
|
253
|
+
|
|
254
|
+
classes_to_compare = normalized_classes[(index + 1)..]
|
|
255
|
+
|
|
256
|
+
classes_to_compare.each_with_index do |second_class, j|
|
|
257
|
+
second_class_data = extract_class_data(second_class)
|
|
258
|
+
next unless valid_property?(second_class_data[:css_property])
|
|
259
|
+
|
|
260
|
+
next unless contradiction_found?(first_class_data, second_class_data)
|
|
261
|
+
|
|
262
|
+
original_class = classes[index]
|
|
263
|
+
contradicting_class = classes[index + j + 1]
|
|
264
|
+
contradictions << [original_class, contradicting_class]
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
contradictions
|
|
269
|
+
end
|
|
270
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
271
|
+
|
|
272
|
+
def contradiction_found?(first_class_data, second_class_data)
|
|
273
|
+
breakpoint_ranges_overlap?(first_class_data[:breakpoint_range],
|
|
274
|
+
second_class_data[:breakpoint_range]) &&
|
|
275
|
+
container_queries_overlap?(first_class_data[:container_query],
|
|
276
|
+
second_class_data[:container_query]) &&
|
|
277
|
+
properties_contradict?(first_class_data[:css_property], second_class_data[:css_property])
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def extract_class_data(class_name)
|
|
281
|
+
{
|
|
282
|
+
css_property: extract_css_property(class_name),
|
|
283
|
+
breakpoint_range: extract_breakpoint_range(class_name),
|
|
284
|
+
container_query: extract_container_query(class_name)
|
|
285
|
+
}
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Extracts container query (e.g., @md:) from class name
|
|
289
|
+
def extract_container_query(class_name)
|
|
290
|
+
match = class_name.match(/^@([a-zA-Z0-9_-]+):/)
|
|
291
|
+
match ? match[1] : nil
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def container_queries_overlap?(first_container, second_container)
|
|
295
|
+
# If both have a container query, they overlap
|
|
296
|
+
return true if first_container && second_container
|
|
297
|
+
|
|
298
|
+
# If neither has a container query, they overlap (global)
|
|
299
|
+
return true if first_container.nil? && second_container.nil?
|
|
300
|
+
|
|
301
|
+
# If only one has a container query, treat as non-overlapping
|
|
302
|
+
false
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def matcher
|
|
306
|
+
/^#{GIVELIVELY_TAILWIND_CLASS_PREFIX}/o
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def valid_property?(property)
|
|
310
|
+
property && CONTRADICTION_GROUPS.any? { |_, group| group.include?(property) }
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# rubocop:disable Metrics/MethodLength
|
|
314
|
+
def extract_css_property(class_name)
|
|
315
|
+
# Remove container query prefix (e.g., @md:)
|
|
316
|
+
class_without_container = class_name.sub(/^@([a-zA-Z0-9_-]+):/, '')
|
|
317
|
+
# Remove breakpoint prefixes (including v4 range syntax and max-only syntax)
|
|
318
|
+
class_without_breakpoint = class_without_container.sub(
|
|
319
|
+
/^(?:(?:sm|md|lg|xl|2xl)(?::max-(?:sm|md|lg|xl|2xl))?:|max-(?:sm|md|lg|xl|2xl):)+/,
|
|
320
|
+
''
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
patterns = [
|
|
324
|
+
/^(w|h)-/,
|
|
325
|
+
/^(max-w|max-h)-/,
|
|
326
|
+
/^(min-w|min-h)-/,
|
|
327
|
+
/^(m[trblxy]?)-/,
|
|
328
|
+
/^(p[trblxy]?)-/,
|
|
329
|
+
/^(block|hidden|flex|inline|inline-block|inline-flex|grid|inline-grid|table)$/,
|
|
330
|
+
/^(static|relative|absolute|fixed|sticky)$/,
|
|
331
|
+
/^(text-(?:left|center|right|justify))$/,
|
|
332
|
+
/^(flex-(?:row|row-reverse|col|col-reverse))$/,
|
|
333
|
+
/^(justify-(?:start|end|center|between|around|evenly))$/,
|
|
334
|
+
/^(items-(?:start|end|center|baseline|stretch))$/,
|
|
335
|
+
/^(place-content-(?:center|start|end|between|around|evenly))$/,
|
|
336
|
+
/^(place-items-(?:start|end|center|baseline|stretch))$/,
|
|
337
|
+
/^(place-self-(?:auto|start|end|center|stretch))$/,
|
|
338
|
+
/^(content-(?:center|start|end|between|around|evenly))$/,
|
|
339
|
+
/^(self-(?:auto|start|end|center|stretch|baseline))$/,
|
|
340
|
+
/^(justify-items-(?:start|end|center|stretch))$/,
|
|
341
|
+
/^(justify-self-(?:auto|start|end|center|stretch))$/,
|
|
342
|
+
/^(text-(?:xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl))$/,
|
|
343
|
+
/^(font-(?:thin|extralight|light|normal|medium|semibold|bold|extrabold|black))$/,
|
|
344
|
+
/^(italic|not-italic)$/,
|
|
345
|
+
/^(tracking-(?:tighter|tight|normal|wide|wider|widest))$/,
|
|
346
|
+
/^(leading-(?:none|tight|snug|normal|relaxed|loose))$/,
|
|
347
|
+
/^(underline|line-through|no-underline)$/,
|
|
348
|
+
/^(uppercase|lowercase|capitalize|normal-case)$/,
|
|
349
|
+
/^(decoration-(?:solid|dashed|dotted|double|wavy))$/,
|
|
350
|
+
/^(break-(?:normal|words|all))$/,
|
|
351
|
+
/^(align-(?:baseline|top|middle|bottom|text-top|text-bottom))$/,
|
|
352
|
+
/^(truncate|overflow-(?:ellipsis|clip))$/,
|
|
353
|
+
/^(overflow-(?:auto|hidden|visible|scroll))$/,
|
|
354
|
+
/^(visible|invisible|collapse)$/,
|
|
355
|
+
/^(border-(?:solid|dashed|dotted|double|none))$/,
|
|
356
|
+
/^(shadow(?:-(?:sm|md|lg|xl|2xl|inner|none))?)$/
|
|
357
|
+
]
|
|
358
|
+
|
|
359
|
+
patterns.each do |pattern|
|
|
360
|
+
match = class_without_breakpoint.match(pattern)
|
|
361
|
+
return match[1] if match
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
nil
|
|
365
|
+
end
|
|
366
|
+
# rubocop:enable Metrics/MethodLength
|
|
367
|
+
|
|
368
|
+
def extract_breakpoint_range(class_name)
|
|
369
|
+
# Extract breakpoint range (e.g., 'md', 'lg:max-xl', 'sm:max-md')
|
|
370
|
+
# Returns a hash with :min and :max keys, or nil if no breakpoint
|
|
371
|
+
|
|
372
|
+
# Match Tailwind v4 range syntax: breakpoint:max-breakpoint: or just breakpoint:
|
|
373
|
+
range_match = class_name.match(/^(sm|md|lg|xl|2xl)(?::max-(sm|md|lg|xl|2xl))?:/)
|
|
374
|
+
return nil unless range_match
|
|
375
|
+
|
|
376
|
+
min_breakpoint = range_match[1]
|
|
377
|
+
max_breakpoint = range_match[2]
|
|
378
|
+
|
|
379
|
+
{
|
|
380
|
+
min: min_breakpoint,
|
|
381
|
+
max: max_breakpoint
|
|
382
|
+
}
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def breakpoint_ranges_overlap?(first_range, second_range)
|
|
386
|
+
# If either range is nil (no breakpoint), they're considered the same (base styles)
|
|
387
|
+
return true if first_range.nil? && second_range.nil?
|
|
388
|
+
return false if first_range.nil? || second_range.nil?
|
|
389
|
+
|
|
390
|
+
# Get numeric indices for comparison
|
|
391
|
+
first_min_index = BREAKPOINT_ORDER.index(first_range[:min])
|
|
392
|
+
first_max_index = max_index(first_range)
|
|
393
|
+
|
|
394
|
+
second_min_index = BREAKPOINT_ORDER.index(second_range[:min])
|
|
395
|
+
second_max_index = max_index(second_range)
|
|
396
|
+
|
|
397
|
+
# Check for overlap: ranges overlap if one starts before the other ends
|
|
398
|
+
!(first_max_index < second_min_index || second_max_index < first_min_index)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def max_index(range)
|
|
402
|
+
return BREAKPOINT_ORDER.index(range[:max]) if range[:max]
|
|
403
|
+
|
|
404
|
+
BREAKPOINT_ORDER.length - 1
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def properties_contradict?(first_prop, second_prop)
|
|
408
|
+
first_prop_group = CONTRADICTION_GROUPS.select do |_, group|
|
|
409
|
+
group.include?(first_prop)
|
|
410
|
+
end.keys
|
|
411
|
+
second_prop_group = CONTRADICTION_GROUPS.select do |_, group|
|
|
412
|
+
group.include?(second_prop)
|
|
413
|
+
end.keys
|
|
414
|
+
|
|
415
|
+
return false unless first_prop_group && second_prop_group
|
|
416
|
+
|
|
417
|
+
# Check if both properties belong to the same contradicting group
|
|
418
|
+
first_prop_group.intersect?(second_prop_group)
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
# rubocop:enable Metrics/ClassLength
|
|
422
|
+
end
|
|
423
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module GLRubocop
|
|
2
|
+
module ErbContentHelper
|
|
3
|
+
def erb_file?
|
|
4
|
+
processed_source.file_path&.end_with?('.erb', '.html.erb')
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def read_erb_file
|
|
8
|
+
return unless processed_source.file_path
|
|
9
|
+
|
|
10
|
+
File.read(processed_source.file_path)
|
|
11
|
+
rescue StandardError
|
|
12
|
+
nil
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module GLRubocop
|
|
2
|
+
module HamlContentHelper
|
|
3
|
+
def haml_file?
|
|
4
|
+
file_path = processed_source.file_path
|
|
5
|
+
file_path&.end_with?('.html.haml') && File.exist?(file_path)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def read_haml_file
|
|
9
|
+
File.read(processed_source.file_path)
|
|
10
|
+
rescue StandardError
|
|
11
|
+
nil
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/gl_rubocop/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gl_rubocop
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.20
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Give Lively
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-10-
|
|
11
|
+
date: 2025-10-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rubocop
|
|
@@ -155,7 +155,10 @@ files:
|
|
|
155
155
|
- lib/gl_rubocop/gl_cops/prevent_erb_files.rb
|
|
156
156
|
- lib/gl_rubocop/gl_cops/rails_cache.rb
|
|
157
157
|
- lib/gl_rubocop/gl_cops/sidekiq_inherits_from_sidekiq_job.rb
|
|
158
|
+
- lib/gl_rubocop/gl_cops/tailwind_no_contradicting_class_name.rb
|
|
158
159
|
- lib/gl_rubocop/gl_cops/unique_identifier.rb
|
|
160
|
+
- lib/gl_rubocop/helpers/erb_content_helper.rb
|
|
161
|
+
- lib/gl_rubocop/helpers/haml_content_helper.rb
|
|
159
162
|
- lib/gl_rubocop/version.rb
|
|
160
163
|
homepage: https://github.com/givelively/gl_rubocop
|
|
161
164
|
licenses:
|
|
@@ -177,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
177
180
|
- !ruby/object:Gem::Version
|
|
178
181
|
version: '0'
|
|
179
182
|
requirements: []
|
|
180
|
-
rubygems_version: 3.
|
|
183
|
+
rubygems_version: 3.3.7
|
|
181
184
|
signing_key:
|
|
182
185
|
specification_version: 4
|
|
183
186
|
summary: A shareable configuration of Give Lively's rubocop rules.
|