This repository has been archived by the owner on Mar 4, 2022. It is now read-only.
forked from Shopify/erb_lint
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdeprecated_classes.rb
122 lines (104 loc) · 3.61 KB
/
deprecated_classes.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# frozen_string_literal: true
require 'better_html'
require 'better_html/parser'
module ERBLint
module Linters
# Checks for deprecated classes in the start tags of HTML elements.
class DeprecatedClasses < Linter
include LinterRegistry
class RuleSet
include SmartProperties
property :suggestion, accepts: String, default: ''
property :deprecated, accepts: LinterConfig.array_of?(String), default: -> { [] }
end
class ConfigSchema < LinterConfig
property :rule_set,
default: -> { [] },
accepts: array_of?(RuleSet),
converts: to_array_of(RuleSet)
property :addendum, accepts: String
end
self.config_schema = ConfigSchema
def initialize(file_loader, config)
super
@addendum = @config.addendum
end
def run(processed_source)
process_nested_offenses(
source: processed_source,
offset: 0,
parent_source: processed_source,
)
end
private
def process_nested_offenses(source:, offset:, parent_source:)
class_name_with_loc(source).each do |class_name, loc|
range = parent_source.to_source_range(loc).offset(offset)
generate_offenses(class_name, range)
end
text_tags_content(source).each do |content_node|
sub_source = ProcessedSource.new(source.filename, content_node.loc.source)
process_nested_offenses(
source: sub_source,
offset: offset + content_node.loc.begin_pos,
parent_source: parent_source
)
end
end
def class_name_with_loc(processed_source)
Enumerator.new do |yielder|
tags(processed_source).each do |tag|
class_value = tag.attributes['class']&.value
next unless class_value
class_value.split(' ').each do |class_name|
yielder.yield(class_name, tag.loc)
end
end
end
end
def text_tags_content(processed_source)
Enumerator.new do |yielder|
script_tags(processed_source)
.select { |tag| tag.attributes['type']&.value == 'text/html' }
.each do |tag|
index = processed_source.ast.to_a.find_index(tag.node)
next_node = processed_source.ast.to_a[index + 1]
yielder.yield(next_node) if next_node.type == :text
end
end
end
def script_tags(processed_source)
tags(processed_source).select { |tag| tag.name == 'script' }
end
def tags(processed_source)
tag_nodes(processed_source).map { |tag_node| BetterHtml::Tree::Tag.from_node(tag_node) }
end
def tag_nodes(processed_source)
processed_source.parser.nodes_with_type(:tag)
end
def generate_offenses(class_name, range)
violated_rules(class_name).each do |violated_rule|
suggestion = " #{violated_rule[:suggestion]}".rstrip
message = "Deprecated class `%s` detected matching the pattern `%s`.%s #{@addendum}".strip
add_offense(
range,
format(message, class_name, violated_rule[:class_expr], suggestion)
)
end
end
def violated_rules(class_name)
[].tap do |result|
@config.rule_set.each do |rule|
rule.deprecated.each do |deprecated|
next unless /\A#{deprecated}\z/ =~ class_name
result << {
suggestion: rule.suggestion,
class_expr: deprecated,
}
end
end
end
end
end
end
end