erb_lint 0.0.13 → 0.0.14
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 +4 -4
- data/lib/erb_lint.rb +6 -3
- data/lib/erb_lint/cli.rb +90 -25
- data/lib/erb_lint/corrector.rb +27 -0
- data/lib/erb_lint/linter.rb +5 -10
- data/lib/erb_lint/linters/deprecated_classes.rb +39 -27
- data/lib/erb_lint/linters/erb_safety.rb +9 -13
- data/lib/erb_lint/linters/final_newline.rb +22 -11
- data/lib/erb_lint/linters/rubocop.rb +91 -42
- data/lib/erb_lint/offense.rb +13 -6
- data/lib/erb_lint/offset_corrector.rb +54 -0
- data/lib/erb_lint/processed_source.rb +29 -0
- data/lib/erb_lint/runner.rb +3 -3
- data/lib/erb_lint/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 185ec9589ef4da649b5f073871c8e1977a2820f6
|
4
|
+
data.tar.gz: 7588eff7bf3e74f3f887a5e81c6eaa72839c953a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e1d78d903307c1433b0c56a9ce63993fbda5802ba671cc4288c96096d18617dbf69c85837026b6222e4c8779c3f44fb65af44cb54d29f6fdfb6664ad2369f26
|
7
|
+
data.tar.gz: fe70e6836ed54f517b79d73261ac2c7101bfa29b335a4e09cfce39177d7b2f9d63c497e30016099793dd236266fd4e58b12a5c09a3475e555cb15f00a7dbd7b4
|
data/lib/erb_lint.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'erb_lint/
|
4
|
-
require 'erb_lint/
|
3
|
+
require 'erb_lint/corrector'
|
4
|
+
require 'erb_lint/file_loader'
|
5
5
|
require 'erb_lint/linter_config'
|
6
6
|
require 'erb_lint/linter_registry'
|
7
7
|
require 'erb_lint/linter'
|
8
|
+
require 'erb_lint/offense'
|
9
|
+
require 'erb_lint/offset_corrector'
|
10
|
+
require 'erb_lint/processed_source'
|
8
11
|
require 'erb_lint/runner_config'
|
9
12
|
require 'erb_lint/runner'
|
10
|
-
require 'erb_lint/
|
13
|
+
require 'erb_lint/version'
|
11
14
|
|
12
15
|
# Load linters
|
13
16
|
Dir[File.expand_path('erb_lint/linters/**/*.rb', File.dirname(__FILE__))].each do |file|
|
data/lib/erb_lint/cli.rb
CHANGED
@@ -16,10 +16,19 @@ module ERBLint
|
|
16
16
|
class ExitWithFailure < RuntimeError; end
|
17
17
|
class ExitWithSuccess < RuntimeError; end
|
18
18
|
|
19
|
+
class Stats
|
20
|
+
attr_accessor :found, :corrected
|
21
|
+
def initialize
|
22
|
+
@found = 0
|
23
|
+
@corrected = 0
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
19
27
|
def initialize
|
20
28
|
@options = {}
|
21
29
|
@config = nil
|
22
30
|
@files = []
|
31
|
+
@stats = Stats.new
|
23
32
|
end
|
24
33
|
|
25
34
|
def run(args = ARGV)
|
@@ -33,31 +42,40 @@ module ERBLint
|
|
33
42
|
load_config
|
34
43
|
ensure_files_exist(lint_files)
|
35
44
|
|
36
|
-
|
45
|
+
if enabled_linter_classes.empty?
|
46
|
+
failure!('no linter available with current configuration')
|
47
|
+
end
|
48
|
+
|
49
|
+
puts "Linting #{lint_files.size} files with "\
|
50
|
+
"#{enabled_linter_classes.size} #{'autocorrectable ' if autocorrect?}linters..."
|
37
51
|
puts
|
38
52
|
|
39
|
-
errors_found = false
|
40
53
|
runner_config = @config.merge(runner_config_override)
|
41
54
|
runner = ERBLint::Runner.new(file_loader, runner_config)
|
42
55
|
lint_files.each do |filename|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
errors_found = true
|
56
|
+
begin
|
57
|
+
run_with_corrections(runner, filename)
|
58
|
+
rescue => e
|
59
|
+
puts "Exception occured when processing: #{relative_filename(filename)}"
|
60
|
+
puts e.message
|
61
|
+
puts e.backtrace.join("\n").red
|
62
|
+
puts
|
51
63
|
end
|
52
64
|
end
|
53
65
|
|
54
|
-
if
|
55
|
-
|
66
|
+
if @stats.corrected > 0
|
67
|
+
if @stats.found > 0
|
68
|
+
warn "#{@stats.corrected} error(s) corrected and #{@stats.found} error(s) remaining in ERB files".red
|
69
|
+
else
|
70
|
+
puts "#{@stats.corrected} error(s) corrected in ERB files".green
|
71
|
+
end
|
72
|
+
elsif @stats.found > 0
|
73
|
+
warn "#{@stats.found} error(s) were found in ERB files".red
|
56
74
|
else
|
57
75
|
puts "No errors were found in ERB files".green
|
58
76
|
end
|
59
77
|
|
60
|
-
|
78
|
+
@stats.found == 0
|
61
79
|
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, ExitWithFailure => e
|
62
80
|
warn e.message.red
|
63
81
|
false
|
@@ -71,6 +89,48 @@ module ERBLint
|
|
71
89
|
|
72
90
|
private
|
73
91
|
|
92
|
+
def autocorrect?
|
93
|
+
@options[:autocorrect]
|
94
|
+
end
|
95
|
+
|
96
|
+
def run_with_corrections(runner, filename)
|
97
|
+
file_content = File.read(filename)
|
98
|
+
offenses = []
|
99
|
+
|
100
|
+
7.times do
|
101
|
+
processed_source = ERBLint::ProcessedSource.new(filename, file_content)
|
102
|
+
offenses = runner.run(processed_source)
|
103
|
+
break unless autocorrect? && offenses.any?
|
104
|
+
|
105
|
+
corrector = correct(processed_source, offenses)
|
106
|
+
break if corrector.corrections.empty?
|
107
|
+
break if processed_source.file_content == corrector.corrected_content
|
108
|
+
|
109
|
+
@stats.corrected += corrector.corrections.size
|
110
|
+
|
111
|
+
File.open(filename, "wb") do |file|
|
112
|
+
file.write(corrector.corrected_content)
|
113
|
+
end
|
114
|
+
|
115
|
+
file_content = corrector.corrected_content
|
116
|
+
end
|
117
|
+
|
118
|
+
@stats.found += offenses.size
|
119
|
+
offenses.each do |offense|
|
120
|
+
puts <<~EOF
|
121
|
+
#{offense.message}#{' (not autocorrected)'.red if autocorrect?}
|
122
|
+
In file: #{relative_filename(filename)}:#{offense.line_range.begin}
|
123
|
+
|
124
|
+
EOF
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def correct(processed_source, offenses)
|
129
|
+
corrector = ERBLint::Corrector.new(processed_source, offenses)
|
130
|
+
failure!(corrector.diagnostics.join(', ')) if corrector.diagnostics.any?
|
131
|
+
corrector
|
132
|
+
end
|
133
|
+
|
74
134
|
def config_filename
|
75
135
|
@config_filename ||= @options[:config] || DEFAULT_CONFIG_FILENAME
|
76
136
|
end
|
@@ -128,12 +188,17 @@ module ERBLint
|
|
128
188
|
def enabled_linter_names
|
129
189
|
@enabled_linter_names ||=
|
130
190
|
@options[:enabled_linters] ||
|
131
|
-
known_linter_names
|
191
|
+
known_linter_names
|
192
|
+
.select { |name| @config.for_linter(name.camelize).enabled? }
|
132
193
|
end
|
133
194
|
|
134
195
|
def enabled_linter_classes
|
135
196
|
@enabled_linter_classes ||= ERBLint::LinterRegistry.linters
|
136
|
-
.select { |klass| enabled_linter_names.include?(klass.simple_name.underscore) }
|
197
|
+
.select { |klass| linter_can_run?(klass) && enabled_linter_names.include?(klass.simple_name.underscore) }
|
198
|
+
end
|
199
|
+
|
200
|
+
def linter_can_run?(klass)
|
201
|
+
!autocorrect? || klass.support_autocorrect?
|
137
202
|
end
|
138
203
|
|
139
204
|
def relative_filename(filename)
|
@@ -141,17 +206,13 @@ module ERBLint
|
|
141
206
|
end
|
142
207
|
|
143
208
|
def runner_config_override
|
144
|
-
|
145
|
-
|
146
|
-
linters
|
147
|
-
|
148
|
-
linters[klass.simple_name] = { 'enabled' => enabled_linter_classes.include?(klass) }
|
149
|
-
end
|
209
|
+
RunnerConfig.new(
|
210
|
+
linters: {}.tap do |linters|
|
211
|
+
ERBLint::LinterRegistry.linters.map do |klass|
|
212
|
+
linters[klass.simple_name] = { 'enabled' => enabled_linter_classes.include?(klass) }
|
150
213
|
end
|
151
|
-
|
152
|
-
|
153
|
-
RunnerConfig.new
|
154
|
-
end
|
214
|
+
end
|
215
|
+
)
|
155
216
|
end
|
156
217
|
|
157
218
|
def option_parser
|
@@ -184,6 +245,10 @@ module ERBLint
|
|
184
245
|
@options[:enabled_linters] = linters
|
185
246
|
end
|
186
247
|
|
248
|
+
opts.on("--autocorrect", "Correct offenses that can be corrected automatically (default: false)") do |config|
|
249
|
+
@options[:autocorrect] = config
|
250
|
+
end
|
251
|
+
|
187
252
|
opts.on_tail("-h", "--help", "Show this message") do
|
188
253
|
success!(opts)
|
189
254
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
class Corrector
|
5
|
+
attr_reader :processed_source, :offenses, :corrected_content
|
6
|
+
|
7
|
+
def initialize(processed_source, offenses)
|
8
|
+
@processed_source = processed_source
|
9
|
+
@offenses = offenses
|
10
|
+
@corrected_content = corrector.rewrite
|
11
|
+
end
|
12
|
+
|
13
|
+
def corrections
|
14
|
+
@corrections ||= @offenses.map do |offense|
|
15
|
+
offense.linter.autocorrect(@processed_source, offense)
|
16
|
+
end.compact
|
17
|
+
end
|
18
|
+
|
19
|
+
def corrector
|
20
|
+
RuboCop::Cop::Corrector.new(@processed_source.source_buffer, corrections)
|
21
|
+
end
|
22
|
+
|
23
|
+
def diagnostics
|
24
|
+
corrector.diagnostics
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/erb_lint/linter.rb
CHANGED
@@ -23,6 +23,10 @@ module ERBLint
|
|
23
23
|
|
24
24
|
linter.config_schema = LinterConfig
|
25
25
|
end
|
26
|
+
|
27
|
+
def support_autocorrect?
|
28
|
+
method_defined?(:autocorrect)
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
32
|
# Must be implemented by the concrete inheriting class.
|
@@ -41,16 +45,7 @@ module ERBLint
|
|
41
45
|
@config.excludes_file?(filename)
|
42
46
|
end
|
43
47
|
|
44
|
-
def
|
45
|
-
lines = file_content.scan(/[^\n]*\n|[^\n]+/)
|
46
|
-
lint_lines(lines)
|
47
|
-
end
|
48
|
-
|
49
|
-
protected
|
50
|
-
|
51
|
-
# The lint_lines method that contains the logic for the linter and returns a list of errors.
|
52
|
-
# Must be implemented by the concrete inheriting class.
|
53
|
-
def lint_lines(_lines)
|
48
|
+
def offenses(_processed_source)
|
54
49
|
raise NotImplementedError, "must implement ##{__method__}"
|
55
50
|
end
|
56
51
|
end
|
@@ -28,27 +28,39 @@ module ERBLint
|
|
28
28
|
@addendum = @config.addendum
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
text_tags_content(parser).each do |content|
|
38
|
-
errors.push(*lint_file(content))
|
39
|
-
end
|
40
|
-
errors
|
31
|
+
def offenses(processed_source)
|
32
|
+
process_nested_offenses(
|
33
|
+
source: processed_source,
|
34
|
+
offset: 0,
|
35
|
+
parent_source: processed_source,
|
36
|
+
)
|
41
37
|
end
|
42
38
|
|
43
39
|
private
|
44
40
|
|
45
|
-
def
|
46
|
-
|
41
|
+
def process_nested_offenses(source:, offset:, parent_source:)
|
42
|
+
offenses = []
|
43
|
+
class_name_with_loc(source).each do |class_name, loc|
|
44
|
+
range = parent_source.to_source_range(
|
45
|
+
offset + loc.start,
|
46
|
+
offset + loc.stop
|
47
|
+
)
|
48
|
+
offenses += generate_errors(class_name, range)
|
49
|
+
end
|
50
|
+
text_tags_content(source).each do |content_node|
|
51
|
+
sub_source = ProcessedSource.new(source.filename, content_node.loc.source)
|
52
|
+
offenses += process_nested_offenses(
|
53
|
+
source: sub_source,
|
54
|
+
offset: offset + content_node.loc.start,
|
55
|
+
parent_source: parent_source
|
56
|
+
)
|
57
|
+
end
|
58
|
+
offenses
|
47
59
|
end
|
48
60
|
|
49
|
-
def class_name_with_loc(
|
61
|
+
def class_name_with_loc(processed_source)
|
50
62
|
Enumerator.new do |yielder|
|
51
|
-
tags(
|
63
|
+
tags(processed_source).each do |tag|
|
52
64
|
class_value = tag.attributes['class']&.value
|
53
65
|
next unless class_value
|
54
66
|
class_value.split(' ').each do |class_name|
|
@@ -58,39 +70,39 @@ module ERBLint
|
|
58
70
|
end
|
59
71
|
end
|
60
72
|
|
61
|
-
def text_tags_content(
|
73
|
+
def text_tags_content(processed_source)
|
62
74
|
Enumerator.new do |yielder|
|
63
|
-
script_tags(
|
75
|
+
script_tags(processed_source)
|
64
76
|
.select { |tag| tag.attributes['type']&.value == 'text/html' }
|
65
77
|
.each do |tag|
|
66
|
-
index =
|
67
|
-
next_node =
|
78
|
+
index = processed_source.ast.to_a.find_index(tag.node)
|
79
|
+
next_node = processed_source.ast.to_a[index + 1]
|
68
80
|
|
69
|
-
yielder.yield(next_node
|
81
|
+
yielder.yield(next_node) if next_node.type == :text
|
70
82
|
end
|
71
83
|
end
|
72
84
|
end
|
73
85
|
|
74
|
-
def script_tags(
|
75
|
-
tags(
|
86
|
+
def script_tags(processed_source)
|
87
|
+
tags(processed_source).select { |tag| tag.name == 'script' }
|
76
88
|
end
|
77
89
|
|
78
|
-
def tags(
|
79
|
-
tag_nodes(
|
90
|
+
def tags(processed_source)
|
91
|
+
tag_nodes(processed_source).map { |tag_node| BetterHtml::Tree::Tag.from_node(tag_node) }
|
80
92
|
end
|
81
93
|
|
82
|
-
def tag_nodes(
|
83
|
-
parser.nodes_with_type(:tag)
|
94
|
+
def tag_nodes(processed_source)
|
95
|
+
processed_source.parser.nodes_with_type(:tag)
|
84
96
|
end
|
85
97
|
|
86
|
-
def generate_errors(class_name,
|
98
|
+
def generate_errors(class_name, range)
|
87
99
|
violated_rules(class_name).map do |violated_rule|
|
88
100
|
suggestion = " #{violated_rule[:suggestion]}".rstrip
|
89
101
|
message = "Deprecated class `%s` detected matching the pattern `%s`.%s #{@addendum}".strip
|
90
102
|
|
91
103
|
Offense.new(
|
92
104
|
self,
|
93
|
-
|
105
|
+
range,
|
94
106
|
format(message, class_name, violated_rule[:class_expr], suggestion)
|
95
107
|
)
|
96
108
|
end
|
@@ -21,13 +21,17 @@ module ERBLint
|
|
21
21
|
@config_filename = @config.better_html_config
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
|
26
|
-
tester = Tester.new(file_content, config: better_html_config)
|
24
|
+
def offenses(processed_source)
|
25
|
+
offenses = []
|
26
|
+
tester = Tester.new(processed_source.file_content, config: better_html_config)
|
27
27
|
tester.errors.each do |error|
|
28
|
-
|
28
|
+
offenses << Offense.new(
|
29
|
+
self,
|
30
|
+
processed_source.to_source_range(error.location.start, error.location.stop),
|
31
|
+
error.message
|
32
|
+
)
|
29
33
|
end
|
30
|
-
|
34
|
+
offenses
|
31
35
|
end
|
32
36
|
|
33
37
|
private
|
@@ -43,14 +47,6 @@ module ERBLint
|
|
43
47
|
BetterHtml::Config.new(**config_hash)
|
44
48
|
end
|
45
49
|
end
|
46
|
-
|
47
|
-
def format_offense(error)
|
48
|
-
Offense.new(
|
49
|
-
self,
|
50
|
-
error.location.line_range,
|
51
|
-
error.message
|
52
|
-
)
|
53
|
-
end
|
54
50
|
end
|
55
51
|
end
|
56
52
|
end
|
@@ -16,28 +16,39 @@ module ERBLint
|
|
16
16
|
@new_lines_should_be_present = @config.present?
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
def offenses(processed_source)
|
20
|
+
file_content = processed_source.file_content
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
return errors if lines.empty?
|
22
|
+
offenses = []
|
23
|
+
return offenses if file_content.empty?
|
24
24
|
|
25
|
-
|
25
|
+
match = file_content.match(/(\n+)\z/)
|
26
|
+
ends_with_newline = match.present?
|
26
27
|
|
27
28
|
if @new_lines_should_be_present && !ends_with_newline
|
28
|
-
|
29
|
+
offenses << Offense.new(
|
29
30
|
self,
|
30
|
-
|
31
|
+
processed_source.to_source_range(file_content.size, file_content.size - 1),
|
31
32
|
'Missing a trailing newline at the end of the file.'
|
32
33
|
)
|
33
34
|
elsif !@new_lines_should_be_present && ends_with_newline
|
34
|
-
|
35
|
+
offenses << Offense.new(
|
35
36
|
self,
|
36
|
-
|
37
|
-
|
37
|
+
processed_source.to_source_range(match.begin(0), match.end(0) - 1),
|
38
|
+
"Remove #{match[0].size} trailing newline at the end of the file."
|
38
39
|
)
|
39
40
|
end
|
40
|
-
|
41
|
+
offenses
|
42
|
+
end
|
43
|
+
|
44
|
+
def autocorrect(_processed_source, offense)
|
45
|
+
lambda do |corrector|
|
46
|
+
if @new_lines_should_be_present
|
47
|
+
corrector.insert_after(offense.source_range, "\n")
|
48
|
+
else
|
49
|
+
corrector.remove_trailing(offense.source_range, offense.source_range.size)
|
50
|
+
end
|
51
|
+
end
|
41
52
|
end
|
42
53
|
end
|
43
54
|
end
|
@@ -17,6 +17,7 @@ module ERBLint
|
|
17
17
|
|
18
18
|
self.config_schema = ConfigSchema
|
19
19
|
|
20
|
+
SUFFIX_EXPR = /[[:blank:]]*\Z/
|
20
21
|
# copied from Rails: action_view/template/handlers/erb/erubi.rb
|
21
22
|
BLOCK_EXPR = /\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
22
23
|
|
@@ -27,24 +28,83 @@ module ERBLint
|
|
27
28
|
@rubocop_config = RuboCop::ConfigLoader.merge_with_default(custom_config, '')
|
28
29
|
end
|
29
30
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
31
|
+
def offenses(processed_source)
|
32
|
+
offenses = []
|
33
|
+
processed_source.ast.descendants(:erb).each do |erb_node|
|
34
|
+
offenses.push(*inspect_content(processed_source, erb_node))
|
35
|
+
end
|
36
|
+
offenses
|
37
|
+
end
|
38
|
+
|
39
|
+
def autocorrect(processed_source, offense)
|
40
|
+
return unless offense.correction
|
41
|
+
lambda do |corrector|
|
42
|
+
passthrough = OffsetCorrector.new(
|
43
|
+
processed_source,
|
44
|
+
corrector,
|
45
|
+
offense.offset,
|
46
|
+
offense.bound_range,
|
47
|
+
)
|
48
|
+
offense.correction.call(passthrough)
|
42
49
|
end
|
43
|
-
errors
|
44
50
|
end
|
45
51
|
|
46
52
|
private
|
47
53
|
|
54
|
+
class OffenseWithCorrection < Offense
|
55
|
+
attr_reader :correction, :offset, :bound_range
|
56
|
+
def initialize(linter, source_range, message, correction:, offset:, bound_range:)
|
57
|
+
super(linter, source_range, message)
|
58
|
+
@correction = correction
|
59
|
+
@offset = offset
|
60
|
+
@bound_range = bound_range
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def inspect_content(processed_source, erb_node)
|
65
|
+
indicator, _, code_node, = *erb_node
|
66
|
+
return if indicator == '#'
|
67
|
+
|
68
|
+
original_source = code_node.loc.source
|
69
|
+
trimmed_source = original_source.sub(BLOCK_EXPR, '').sub(SUFFIX_EXPR, '')
|
70
|
+
alignment_column = code_node.loc.column
|
71
|
+
aligned_source = "#{' ' * alignment_column}#{trimmed_source}"
|
72
|
+
|
73
|
+
source = rubocop_processed_source(aligned_source)
|
74
|
+
return unless source.valid_syntax?
|
75
|
+
|
76
|
+
offenses = []
|
77
|
+
team = build_team
|
78
|
+
team.inspect_file(source)
|
79
|
+
team.cops.each do |cop|
|
80
|
+
cop.offenses.reject(&:disabled?).each_with_index do |rubocop_offense, index|
|
81
|
+
correction = cop.corrections[index] if rubocop_offense.corrected?
|
82
|
+
|
83
|
+
offset = code_node.loc.start - alignment_column
|
84
|
+
offense_range = processed_source.to_source_range(
|
85
|
+
offset + rubocop_offense.location.begin_pos,
|
86
|
+
offset + rubocop_offense.location.end_pos - 1,
|
87
|
+
)
|
88
|
+
|
89
|
+
bound_range = processed_source.to_source_range(
|
90
|
+
code_node.loc.start,
|
91
|
+
code_node.loc.stop
|
92
|
+
)
|
93
|
+
|
94
|
+
offenses <<
|
95
|
+
OffenseWithCorrection.new(
|
96
|
+
self,
|
97
|
+
offense_range,
|
98
|
+
rubocop_offense.message.strip,
|
99
|
+
correction: correction,
|
100
|
+
offset: offset,
|
101
|
+
bound_range: bound_range,
|
102
|
+
)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
offenses
|
106
|
+
end
|
107
|
+
|
48
108
|
def tempfile_from(filename, content)
|
49
109
|
Tempfile.create(File.basename(filename), Dir.pwd) do |tempfile|
|
50
110
|
tempfile.write(content)
|
@@ -54,14 +114,7 @@ module ERBLint
|
|
54
114
|
end
|
55
115
|
end
|
56
116
|
|
57
|
-
def
|
58
|
-
source = processed_source(content)
|
59
|
-
return unless source.valid_syntax?
|
60
|
-
offenses = team.inspect_file(source)
|
61
|
-
offenses.reject(&:disabled?)
|
62
|
-
end
|
63
|
-
|
64
|
-
def processed_source(content)
|
117
|
+
def rubocop_processed_source(content)
|
65
118
|
RuboCop::ProcessedSource.new(
|
66
119
|
content,
|
67
120
|
@rubocop_config.target_ruby_version,
|
@@ -69,29 +122,25 @@ module ERBLint
|
|
69
122
|
)
|
70
123
|
end
|
71
124
|
|
72
|
-
def
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
RuboCop::Cop::Team.new(cop_classes, @rubocop_config, extra_details: true, display_cop_names: true)
|
125
|
+
def cop_classes
|
126
|
+
if @only_cops.present?
|
127
|
+
selected_cops = RuboCop::Cop::Cop.all.select { |cop| cop.match?(@only_cops) }
|
128
|
+
RuboCop::Cop::Registry.new(selected_cops)
|
129
|
+
elsif @rubocop_config['Rails']['Enabled']
|
130
|
+
RuboCop::Cop::Registry.new(RuboCop::Cop::Cop.all)
|
131
|
+
else
|
132
|
+
RuboCop::Cop::Cop.non_rails
|
133
|
+
end
|
83
134
|
end
|
84
135
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
loc.line_range,
|
94
|
-
offense.message.strip
|
136
|
+
def build_team
|
137
|
+
RuboCop::Cop::Team.new(
|
138
|
+
cop_classes,
|
139
|
+
@rubocop_config,
|
140
|
+
extra_details: true,
|
141
|
+
display_cop_names: true,
|
142
|
+
auto_correct: true,
|
143
|
+
stdin: "",
|
95
144
|
)
|
96
145
|
end
|
97
146
|
|
data/lib/erb_lint/offense.rb
CHANGED
@@ -3,25 +3,32 @@
|
|
3
3
|
module ERBLint
|
4
4
|
# Defines common functionality available to all linters.
|
5
5
|
class Offense
|
6
|
-
attr_reader :linter, :
|
6
|
+
attr_reader :linter, :source_range, :message
|
7
7
|
|
8
|
-
def initialize(linter,
|
8
|
+
def initialize(linter, source_range, message)
|
9
|
+
unless source_range.is_a?(Parser::Source::Range)
|
10
|
+
raise ArgumentError, "expected Parser::Source::Range for arg 2"
|
11
|
+
end
|
9
12
|
@linter = linter
|
10
|
-
@
|
13
|
+
@source_range = source_range
|
11
14
|
@message = message
|
12
15
|
end
|
13
16
|
|
14
17
|
def inspect
|
15
18
|
"#<#{self.class.name} linter=#{linter.class.name} "\
|
16
|
-
"
|
19
|
+
"source_range=#{source_range.begin_pos}..#{source_range.end_pos - 1} "\
|
17
20
|
"message=#{message}>"
|
18
21
|
end
|
19
22
|
|
20
23
|
def ==(other)
|
21
|
-
other.class
|
24
|
+
other.class <= ERBLint::Offense &&
|
22
25
|
other.linter == linter &&
|
23
|
-
other.
|
26
|
+
other.source_range == source_range &&
|
24
27
|
other.message == message
|
25
28
|
end
|
29
|
+
|
30
|
+
def line_range
|
31
|
+
Range.new(source_range.line, source_range.last_line)
|
32
|
+
end
|
26
33
|
end
|
27
34
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
class OffsetCorrector
|
5
|
+
def initialize(processed_source, corrector, offset, bound_range)
|
6
|
+
@processed_source = processed_source
|
7
|
+
@corrector = corrector
|
8
|
+
@offset = offset
|
9
|
+
@bound_range = bound_range
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove(range)
|
13
|
+
@corrector.remove(range_with_offset(range))
|
14
|
+
end
|
15
|
+
|
16
|
+
def insert_before(range, content)
|
17
|
+
@corrector.insert_before(range_with_offset(range), content)
|
18
|
+
end
|
19
|
+
|
20
|
+
def insert_after(range, content)
|
21
|
+
@corrector.insert_after(range_with_offset(range), content)
|
22
|
+
end
|
23
|
+
|
24
|
+
def replace(range, content)
|
25
|
+
@corrector.replace(range_with_offset(range), content)
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove_preceding(range, size)
|
29
|
+
@corrector.remove_preceding(range_with_offset(range), size)
|
30
|
+
end
|
31
|
+
|
32
|
+
def remove_leading(range, size)
|
33
|
+
@corrector.remove_leading(range_with_offset(range), size)
|
34
|
+
end
|
35
|
+
|
36
|
+
def remove_trailing(range, size)
|
37
|
+
@corrector.remove_trailing(range_with_offset(range), size)
|
38
|
+
end
|
39
|
+
|
40
|
+
def range_with_offset(range)
|
41
|
+
@processed_source.to_source_range(
|
42
|
+
bound(@offset + range.begin_pos),
|
43
|
+
bound(@offset + range.end_pos - 1),
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def bound(pos)
|
48
|
+
[
|
49
|
+
[pos, @bound_range.begin_pos].max,
|
50
|
+
@bound_range.end_pos - 1
|
51
|
+
].min
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
class ProcessedSource
|
5
|
+
attr_reader :filename, :file_content, :parser
|
6
|
+
|
7
|
+
def initialize(filename, file_content)
|
8
|
+
@filename = filename
|
9
|
+
@file_content = file_content
|
10
|
+
@parser = BetterHtml::Parser.new(file_content, template_language: :html)
|
11
|
+
end
|
12
|
+
|
13
|
+
def ast
|
14
|
+
@parser.ast
|
15
|
+
end
|
16
|
+
|
17
|
+
def source_buffer
|
18
|
+
@source_buffer ||= begin
|
19
|
+
buffer = Parser::Source::Buffer.new(filename)
|
20
|
+
buffer.source = file_content
|
21
|
+
buffer
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_source_range(begin_pos, end_pos)
|
26
|
+
Parser::Source::Range.new(source_buffer, begin_pos, end_pos + 1)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/erb_lint/runner.rb
CHANGED
@@ -15,12 +15,12 @@ module ERBLint
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def run(
|
18
|
+
def run(processed_source)
|
19
19
|
offenses = []
|
20
20
|
@linters
|
21
|
-
.reject { |linter| linter.excludes_file?(filename) }
|
21
|
+
.reject { |linter| linter.excludes_file?(processed_source.filename) }
|
22
22
|
.each do |linter|
|
23
|
-
offenses += linter.
|
23
|
+
offenses += linter.offenses(processed_source)
|
24
24
|
end
|
25
25
|
offenses
|
26
26
|
end
|
data/lib/erb_lint/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erb_lint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Chan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01-
|
11
|
+
date: 2018-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: better_html
|
@@ -133,6 +133,7 @@ files:
|
|
133
133
|
- exe/erblint
|
134
134
|
- lib/erb_lint.rb
|
135
135
|
- lib/erb_lint/cli.rb
|
136
|
+
- lib/erb_lint/corrector.rb
|
136
137
|
- lib/erb_lint/file_loader.rb
|
137
138
|
- lib/erb_lint/linter.rb
|
138
139
|
- lib/erb_lint/linter_config.rb
|
@@ -142,6 +143,8 @@ files:
|
|
142
143
|
- lib/erb_lint/linters/final_newline.rb
|
143
144
|
- lib/erb_lint/linters/rubocop.rb
|
144
145
|
- lib/erb_lint/offense.rb
|
146
|
+
- lib/erb_lint/offset_corrector.rb
|
147
|
+
- lib/erb_lint/processed_source.rb
|
145
148
|
- lib/erb_lint/runner.rb
|
146
149
|
- lib/erb_lint/runner_config.rb
|
147
150
|
- lib/erb_lint/version.rb
|