erb_lint 0.2.0 → 0.3.1
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/all.rb +2 -0
- data/lib/erb_lint/cache.rb +88 -0
- data/lib/erb_lint/cached_offense.rb +58 -0
- data/lib/erb_lint/cli.rb +70 -6
- data/lib/erb_lint/linter.rb +1 -1
- data/lib/erb_lint/linters/comment_syntax.rb +52 -0
- data/lib/erb_lint/linters/rubocop.rb +7 -1
- data/lib/erb_lint/offense.rb +20 -0
- data/lib/erb_lint/reporters/json_reporter.rb +4 -4
- data/lib/erb_lint/runner.rb +4 -0
- data/lib/erb_lint/runner_config.rb +1 -0
- data/lib/erb_lint/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1bd25c794b1028b09ecec8ce3b5e77600d36364d59b85f2e4b24a18d6bb08853
|
4
|
+
data.tar.gz: e41f353e930401a024d880fc6973beb1e462f2f5372edb094523eacde0030d72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94ed5b3479803ff2b51af6390c0e39c2fb37ef98e23fc2523ac204b638d8ff043665926081794ff27d77a60faae8ee7747cb4ef3d04125b7b3f7c2ae55ea268f
|
7
|
+
data.tar.gz: 9b423110f202bb9728ec57dbd60f04aa86e2901ecfa915d9ddc49fd58fac27b3daddea78780612c3f474ece4c41c01977bea72ad8353a9a113d5cee8c4a51914
|
data/lib/erb_lint/all.rb
CHANGED
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
class Cache
|
5
|
+
CACHE_DIRECTORY = ".erb-lint-cache"
|
6
|
+
|
7
|
+
def initialize(config, cache_dir = nil)
|
8
|
+
@config = config
|
9
|
+
@cache_dir = cache_dir || CACHE_DIRECTORY
|
10
|
+
@hits = []
|
11
|
+
@new_results = []
|
12
|
+
puts "Cache mode is on"
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(filename, file_content)
|
16
|
+
file_checksum = checksum(filename, file_content)
|
17
|
+
begin
|
18
|
+
cache_file_contents_as_offenses = JSON.parse(
|
19
|
+
File.read(File.join(@cache_dir, file_checksum))
|
20
|
+
).map do |offense_hash|
|
21
|
+
ERBLint::CachedOffense.new(offense_hash)
|
22
|
+
end
|
23
|
+
rescue Errno::ENOENT
|
24
|
+
return false
|
25
|
+
end
|
26
|
+
@hits.push(file_checksum)
|
27
|
+
cache_file_contents_as_offenses
|
28
|
+
end
|
29
|
+
|
30
|
+
def set(filename, file_content, offenses_as_json)
|
31
|
+
file_checksum = checksum(filename, file_content)
|
32
|
+
@new_results.push(file_checksum)
|
33
|
+
|
34
|
+
FileUtils.mkdir_p(@cache_dir)
|
35
|
+
|
36
|
+
File.open(File.join(@cache_dir, file_checksum), "wb") do |f|
|
37
|
+
f.write(offenses_as_json)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def close
|
42
|
+
prune_cache
|
43
|
+
end
|
44
|
+
|
45
|
+
def prune_cache
|
46
|
+
if hits.empty?
|
47
|
+
puts "Cache being created for the first time, skipping prune"
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
51
|
+
cache_files = Dir.new(@cache_dir).children
|
52
|
+
cache_files.each do |cache_file|
|
53
|
+
next if hits.include?(cache_file) || new_results.include?(cache_file)
|
54
|
+
|
55
|
+
File.delete(File.join(@cache_dir, cache_file))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def cache_dir_exists?
|
60
|
+
File.directory?(@cache_dir)
|
61
|
+
end
|
62
|
+
|
63
|
+
def clear
|
64
|
+
return unless cache_dir_exists?
|
65
|
+
|
66
|
+
puts "Clearing cache by deleting cache directory"
|
67
|
+
FileUtils.rm_r(@cache_dir)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
attr_reader :config, :hits, :new_results
|
73
|
+
|
74
|
+
def checksum(filename, file_content)
|
75
|
+
digester = Digest::SHA1.new
|
76
|
+
mode = File.stat(filename).mode
|
77
|
+
|
78
|
+
digester.update(
|
79
|
+
"#{mode}#{config.to_hash}#{ERBLint::VERSION}#{file_content}"
|
80
|
+
)
|
81
|
+
digester.hexdigest
|
82
|
+
rescue Errno::ENOENT
|
83
|
+
# Spurious files that come and go should not cause a crash, at least not
|
84
|
+
# here.
|
85
|
+
"_"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
# A Cached version of an Offense with only essential information represented as strings
|
5
|
+
class CachedOffense
|
6
|
+
attr_reader(
|
7
|
+
:message,
|
8
|
+
:line_number,
|
9
|
+
:severity,
|
10
|
+
:column,
|
11
|
+
:simple_name,
|
12
|
+
:last_line,
|
13
|
+
:last_column,
|
14
|
+
:length,
|
15
|
+
)
|
16
|
+
|
17
|
+
def initialize(params)
|
18
|
+
params = params.transform_keys(&:to_sym)
|
19
|
+
|
20
|
+
@message = params[:message]
|
21
|
+
@line_number = params[:line_number]
|
22
|
+
@severity = params[:severity]&.to_sym
|
23
|
+
@column = params[:column]
|
24
|
+
@simple_name = params[:simple_name]
|
25
|
+
@last_line = params[:last_line]
|
26
|
+
@last_column = params[:last_column]
|
27
|
+
@length = params[:length]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.new_from_offense(offense)
|
31
|
+
new(
|
32
|
+
{
|
33
|
+
message: offense.message,
|
34
|
+
line_number: offense.line_number,
|
35
|
+
severity: offense.severity,
|
36
|
+
column: offense.column,
|
37
|
+
simple_name: offense.simple_name,
|
38
|
+
last_line: offense.last_line,
|
39
|
+
last_column: offense.last_column,
|
40
|
+
length: offense.length,
|
41
|
+
}
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_h
|
46
|
+
{
|
47
|
+
message: message,
|
48
|
+
line_number: line_number,
|
49
|
+
severity: severity,
|
50
|
+
column: column,
|
51
|
+
simple_name: simple_name,
|
52
|
+
last_line: last_line,
|
53
|
+
last_column: last_column,
|
54
|
+
length: length,
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/erb_lint/cli.rb
CHANGED
@@ -30,10 +30,27 @@ module ERBLint
|
|
30
30
|
def run(args = ARGV)
|
31
31
|
dupped_args = args.dup
|
32
32
|
load_options(dupped_args)
|
33
|
+
|
34
|
+
if cache? && autocorrect?
|
35
|
+
failure!("cannot run autocorrect mode with cache")
|
36
|
+
end
|
37
|
+
|
33
38
|
@files = @options[:stdin] || dupped_args
|
34
39
|
|
35
40
|
load_config
|
36
41
|
|
42
|
+
cache_dir = @options[:cache_dir]
|
43
|
+
@cache = Cache.new(@config, cache_dir) if cache? || clear_cache?
|
44
|
+
|
45
|
+
if clear_cache?
|
46
|
+
if cache.cache_dir_exists?
|
47
|
+
cache.clear
|
48
|
+
success!("cache directory cleared")
|
49
|
+
else
|
50
|
+
failure!("cache directory doesn't exist, skipping deletion.")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
37
54
|
if !@files.empty? && lint_files.empty?
|
38
55
|
if allow_no_files?
|
39
56
|
success!("no files found...\n")
|
@@ -65,7 +82,7 @@ module ERBLint
|
|
65
82
|
lint_files.each do |filename|
|
66
83
|
runner.clear_offenses
|
67
84
|
begin
|
68
|
-
file_content =
|
85
|
+
file_content = run_on_file(runner, filename)
|
69
86
|
rescue => e
|
70
87
|
@stats.exceptions += 1
|
71
88
|
puts "Exception occurred when processing: #{relative_filename(filename)}"
|
@@ -77,6 +94,8 @@ module ERBLint
|
|
77
94
|
end
|
78
95
|
end
|
79
96
|
|
97
|
+
cache&.close
|
98
|
+
|
80
99
|
reporter.show
|
81
100
|
|
82
101
|
if stdin? && autocorrect?
|
@@ -99,13 +118,43 @@ module ERBLint
|
|
99
118
|
|
100
119
|
private
|
101
120
|
|
121
|
+
attr_reader :cache, :config
|
122
|
+
|
123
|
+
def run_on_file(runner, filename)
|
124
|
+
file_content = read_content(filename)
|
125
|
+
|
126
|
+
if cache? && !autocorrect?
|
127
|
+
run_using_cache(runner, filename, file_content)
|
128
|
+
else
|
129
|
+
file_content = run_with_corrections(runner, filename, file_content)
|
130
|
+
end
|
131
|
+
|
132
|
+
log_offense_stats(runner, filename)
|
133
|
+
file_content
|
134
|
+
end
|
135
|
+
|
136
|
+
def run_using_cache(runner, filename, file_content)
|
137
|
+
if (cache_result_offenses = cache.get(filename, file_content))
|
138
|
+
runner.restore_offenses(cache_result_offenses)
|
139
|
+
else
|
140
|
+
run_with_corrections(runner, filename, file_content)
|
141
|
+
cache.set(filename, file_content, runner.offenses.map(&:to_cached_offense_hash).to_json)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
102
145
|
def autocorrect?
|
103
146
|
@options[:autocorrect]
|
104
147
|
end
|
105
148
|
|
106
|
-
def
|
107
|
-
|
149
|
+
def cache?
|
150
|
+
@options[:cache]
|
151
|
+
end
|
108
152
|
|
153
|
+
def clear_cache?
|
154
|
+
@options[:clear_cache]
|
155
|
+
end
|
156
|
+
|
157
|
+
def run_with_corrections(runner, filename, file_content)
|
109
158
|
7.times do
|
110
159
|
processed_source = ERBLint::ProcessedSource.new(filename, file_content)
|
111
160
|
runner.run(processed_source)
|
@@ -127,6 +176,11 @@ module ERBLint
|
|
127
176
|
file_content = corrector.corrected_content
|
128
177
|
runner.clear_offenses
|
129
178
|
end
|
179
|
+
|
180
|
+
file_content
|
181
|
+
end
|
182
|
+
|
183
|
+
def log_offense_stats(runner, filename)
|
130
184
|
offenses_filename = relative_filename(filename)
|
131
185
|
offenses = runner.offenses || []
|
132
186
|
|
@@ -138,8 +192,6 @@ module ERBLint
|
|
138
192
|
|
139
193
|
@stats.processed_files[offenses_filename] ||= []
|
140
194
|
@stats.processed_files[offenses_filename] |= offenses
|
141
|
-
|
142
|
-
file_content
|
143
195
|
end
|
144
196
|
|
145
197
|
def read_content(filename)
|
@@ -266,7 +318,7 @@ module ERBLint
|
|
266
318
|
end
|
267
319
|
end
|
268
320
|
|
269
|
-
opts.on("--format FORMAT", format_options_help) do |format|
|
321
|
+
opts.on("-f", "--format FORMAT", format_options_help) do |format|
|
270
322
|
unless Reporter.available_format?(format)
|
271
323
|
error_message = invalid_format_error_message(format)
|
272
324
|
failure!(error_message)
|
@@ -283,6 +335,18 @@ module ERBLint
|
|
283
335
|
@options[:enabled_linters] = known_linter_names
|
284
336
|
end
|
285
337
|
|
338
|
+
opts.on("--cache", "Enable caching") do |config|
|
339
|
+
@options[:cache] = config
|
340
|
+
end
|
341
|
+
|
342
|
+
opts.on("--cache-dir DIR", "Set the cache directory") do |dir|
|
343
|
+
@options[:cache_dir] = dir
|
344
|
+
end
|
345
|
+
|
346
|
+
opts.on("--clear-cache", "Clear cache") do |config|
|
347
|
+
@options[:clear_cache] = config
|
348
|
+
end
|
349
|
+
|
286
350
|
opts.on("--enable-linters LINTER[,LINTER,...]", Array,
|
287
351
|
"Only use specified linter", "Known linters are: #{known_linter_names.join(", ")}") do |linters|
|
288
352
|
linters.each do |linter|
|
data/lib/erb_lint/linter.rb
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
module Linters
|
5
|
+
# Detects comment syntax that isn't valid ERB.
|
6
|
+
class CommentSyntax < Linter
|
7
|
+
include LinterRegistry
|
8
|
+
|
9
|
+
def initialize(file_loader, config)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def run(processed_source)
|
14
|
+
file_content = processed_source.file_content
|
15
|
+
return if file_content.empty?
|
16
|
+
|
17
|
+
processed_source.ast.descendants(:erb).each do |erb_node|
|
18
|
+
indicator_node, _, code_node, _ = *erb_node
|
19
|
+
next if code_node.nil?
|
20
|
+
|
21
|
+
indicator_node_str = indicator_node&.deconstruct&.last
|
22
|
+
next if indicator_node_str == "#"
|
23
|
+
|
24
|
+
code_node_str = code_node.deconstruct.last
|
25
|
+
next unless code_node_str.start_with?(" #")
|
26
|
+
|
27
|
+
range = find_range(erb_node, code_node_str)
|
28
|
+
source_range = processed_source.to_source_range(range)
|
29
|
+
|
30
|
+
correct_erb_tag = indicator_node_str == "=" ? "<%#=" : "<%#"
|
31
|
+
|
32
|
+
add_offense(
|
33
|
+
source_range,
|
34
|
+
<<~EOF.chomp
|
35
|
+
Bad ERB comment syntax. Should be #{correct_erb_tag} without a space between.
|
36
|
+
Leaving a space between ERB tags and the Ruby comment character can cause parser errors.
|
37
|
+
EOF
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_range(node, str)
|
43
|
+
match = node.loc.source.match(Regexp.new(Regexp.quote(str.strip)))
|
44
|
+
return unless match
|
45
|
+
|
46
|
+
range_begin = match.begin(0) + node.loc.begin_pos
|
47
|
+
range_end = match.end(0) + node.loc.begin_pos
|
48
|
+
(range_begin...range_end)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -131,11 +131,17 @@ module ERBLint
|
|
131
131
|
end
|
132
132
|
|
133
133
|
def rubocop_processed_source(content, filename)
|
134
|
-
::RuboCop::ProcessedSource.new(
|
134
|
+
source = ::RuboCop::ProcessedSource.new(
|
135
135
|
content,
|
136
136
|
@rubocop_config.target_ruby_version,
|
137
137
|
filename
|
138
138
|
)
|
139
|
+
if ::RuboCop::Version::STRING.to_f >= 1.38
|
140
|
+
registry = RuboCop::Cop::Registry.global
|
141
|
+
source.registry = registry
|
142
|
+
source.config = @rubocop_config
|
143
|
+
end
|
144
|
+
source
|
139
145
|
end
|
140
146
|
|
141
147
|
def cop_classes
|
data/lib/erb_lint/offense.rb
CHANGED
@@ -17,6 +17,10 @@ module ERBLint
|
|
17
17
|
@severity = severity
|
18
18
|
end
|
19
19
|
|
20
|
+
def to_cached_offense_hash
|
21
|
+
ERBLint::CachedOffense.new_from_offense(self).to_h
|
22
|
+
end
|
23
|
+
|
20
24
|
def inspect
|
21
25
|
"#<#{self.class.name} linter=#{linter.class.name} "\
|
22
26
|
"source_range=#{source_range.begin_pos}...#{source_range.end_pos} "\
|
@@ -43,5 +47,21 @@ module ERBLint
|
|
43
47
|
def column
|
44
48
|
source_range.column
|
45
49
|
end
|
50
|
+
|
51
|
+
def simple_name
|
52
|
+
linter.class.simple_name
|
53
|
+
end
|
54
|
+
|
55
|
+
def last_line
|
56
|
+
source_range.last_line
|
57
|
+
end
|
58
|
+
|
59
|
+
def last_column
|
60
|
+
source_range.last_column
|
61
|
+
end
|
62
|
+
|
63
|
+
def length
|
64
|
+
source_range.length
|
65
|
+
end
|
46
66
|
end
|
47
67
|
end
|
@@ -56,14 +56,14 @@ module ERBLint
|
|
56
56
|
|
57
57
|
def format_offense(offense)
|
58
58
|
{
|
59
|
-
linter: offense.
|
59
|
+
linter: offense.simple_name,
|
60
60
|
message: offense.message.to_s,
|
61
61
|
location: {
|
62
62
|
start_line: offense.line_number,
|
63
63
|
start_column: offense.column,
|
64
|
-
last_line: offense.
|
65
|
-
last_column: offense.
|
66
|
-
length: offense.
|
64
|
+
last_line: offense.last_line,
|
65
|
+
last_column: offense.last_column,
|
66
|
+
length: offense.length,
|
67
67
|
},
|
68
68
|
}
|
69
69
|
end
|
data/lib/erb_lint/runner.rb
CHANGED
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.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Chan
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -147,6 +147,8 @@ files:
|
|
147
147
|
- exe/erblint
|
148
148
|
- lib/erb_lint.rb
|
149
149
|
- lib/erb_lint/all.rb
|
150
|
+
- lib/erb_lint/cache.rb
|
151
|
+
- lib/erb_lint/cached_offense.rb
|
150
152
|
- lib/erb_lint/cli.rb
|
151
153
|
- lib/erb_lint/corrector.rb
|
152
154
|
- lib/erb_lint/file_loader.rb
|
@@ -155,6 +157,7 @@ files:
|
|
155
157
|
- lib/erb_lint/linter_registry.rb
|
156
158
|
- lib/erb_lint/linters/allowed_script_type.rb
|
157
159
|
- lib/erb_lint/linters/closing_erb_tag_indent.rb
|
160
|
+
- lib/erb_lint/linters/comment_syntax.rb
|
158
161
|
- lib/erb_lint/linters/deprecated_classes.rb
|
159
162
|
- lib/erb_lint/linters/erb_safety.rb
|
160
163
|
- lib/erb_lint/linters/extra_newline.rb
|