erb_lint 0.0.37 → 0.1.2
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/exe/erblint +1 -1
- data/lib/erb_lint/all.rb +26 -0
- data/lib/erb_lint/cli.rb +75 -29
- data/lib/erb_lint/corrector.rb +1 -1
- data/lib/erb_lint/linter.rb +5 -5
- data/lib/erb_lint/linter_config.rb +5 -3
- data/lib/erb_lint/linter_registry.rb +2 -2
- data/lib/erb_lint/linters/allowed_script_type.rb +9 -8
- data/lib/erb_lint/linters/closing_erb_tag_indent.rb +2 -2
- data/lib/erb_lint/linters/deprecated_classes.rb +8 -7
- data/lib/erb_lint/linters/erb_safety.rb +2 -2
- data/lib/erb_lint/linters/extra_newline.rb +1 -1
- data/lib/erb_lint/linters/final_newline.rb +2 -2
- data/lib/erb_lint/linters/hard_coded_string.rb +39 -16
- data/lib/erb_lint/linters/no_javascript_tag_helper.rb +11 -9
- data/lib/erb_lint/linters/partial_instance_variable.rb +23 -0
- data/lib/erb_lint/linters/require_input_autocomplete.rb +121 -0
- data/lib/erb_lint/linters/require_script_nonce.rb +92 -0
- data/lib/erb_lint/linters/right_trim.rb +1 -1
- data/lib/erb_lint/linters/rubocop.rb +13 -11
- data/lib/erb_lint/linters/rubocop_text.rb +1 -1
- data/lib/erb_lint/linters/self_closing_tag.rb +6 -7
- data/lib/erb_lint/linters/space_around_erb_tag.rb +8 -7
- data/lib/erb_lint/linters/space_in_html_tag.rb +7 -6
- data/lib/erb_lint/linters/space_indentation.rb +1 -1
- data/lib/erb_lint/linters/trailing_whitespace.rb +1 -1
- data/lib/erb_lint/offense.rb +8 -4
- data/lib/erb_lint/reporter.rb +4 -2
- data/lib/erb_lint/reporters/compact_reporter.rb +17 -4
- data/lib/erb_lint/reporters/json_reporter.rb +72 -0
- data/lib/erb_lint/reporters/multiline_reporter.rb +2 -1
- data/lib/erb_lint/runner.rb +3 -2
- data/lib/erb_lint/runner_config.rb +9 -7
- data/lib/erb_lint/runner_config_resolver.rb +5 -4
- data/lib/erb_lint/stats.rb +13 -6
- data/lib/erb_lint/utils/block_map.rb +3 -2
- data/lib/erb_lint/utils/offset_corrector.rb +2 -2
- data/lib/erb_lint/utils/ruby_to_erb.rb +6 -5
- data/lib/erb_lint/utils/severity_levels.rb +16 -0
- data/lib/erb_lint/version.rb +1 -1
- data/lib/erb_lint.rb +1 -24
- metadata +23 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97583caf4e48c7c5b8a6d2e1bf4c17add7bac9693ef6032be0fb22f9d46f4fa4
|
4
|
+
data.tar.gz: 3d1dba73d37680a0339936a12a6c2a048b473a7c9077518f54edd1aa037c4ce5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a043d8859ef059100dd809112f8768908f2ec8df59a52991c09ea995d2181aeabf06c105b3d12eef13ddbbed15d220edbdd88941fe6a55a23fd5c600aa55623d
|
7
|
+
data.tar.gz: 805102e771aac93e6a75017dde3f88deee018dceff3c8c57f2ddcdea88f97f241980efc17885999989733952e771e0538a75eaf67566499e61534c9e64476f75
|
data/exe/erblint
CHANGED
data/lib/erb_lint/all.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rubocop"
|
4
|
+
|
5
|
+
require "erb_lint"
|
6
|
+
require "erb_lint/corrector"
|
7
|
+
require "erb_lint/file_loader"
|
8
|
+
require "erb_lint/linter_config"
|
9
|
+
require "erb_lint/linter_registry"
|
10
|
+
require "erb_lint/linter"
|
11
|
+
require "erb_lint/offense"
|
12
|
+
require "erb_lint/processed_source"
|
13
|
+
require "erb_lint/runner_config"
|
14
|
+
require "erb_lint/runner"
|
15
|
+
require "erb_lint/stats"
|
16
|
+
require "erb_lint/reporter"
|
17
|
+
|
18
|
+
# Load linters
|
19
|
+
Dir[File.expand_path("linters/**/*.rb", __dir__)].each do |file|
|
20
|
+
require file
|
21
|
+
end
|
22
|
+
|
23
|
+
# Load reporters
|
24
|
+
Dir[File.expand_path("reporters/**/*.rb", __dir__)].each do |file|
|
25
|
+
require file
|
26
|
+
end
|
data/lib/erb_lint/cli.rb
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
3
|
+
require "erb_lint/all"
|
4
|
+
require "active_support"
|
5
|
+
require "active_support/inflector"
|
6
|
+
require "optparse"
|
7
|
+
require "psych"
|
8
|
+
require "yaml"
|
9
|
+
require "rainbow"
|
10
|
+
require "erb_lint/utils/severity_levels"
|
10
11
|
|
11
12
|
module ERBLint
|
12
13
|
class CLI
|
13
|
-
|
14
|
+
include Utils::SeverityLevels
|
15
|
+
|
16
|
+
DEFAULT_CONFIG_FILENAME = ".erb-lint.yml"
|
14
17
|
DEFAULT_LINT_ALL_GLOB = "**/*.html{+*,}.erb"
|
15
18
|
|
16
19
|
class ExitWithFailure < RuntimeError; end
|
@@ -27,7 +30,7 @@ module ERBLint
|
|
27
30
|
def run(args = ARGV)
|
28
31
|
dupped_args = args.dup
|
29
32
|
load_options(dupped_args)
|
30
|
-
@files = dupped_args
|
33
|
+
@files = @options[:stdin] || dupped_args
|
31
34
|
|
32
35
|
load_config
|
33
36
|
|
@@ -40,25 +43,28 @@ module ERBLint
|
|
40
43
|
ensure_files_exist(lint_files)
|
41
44
|
|
42
45
|
if enabled_linter_classes.empty?
|
43
|
-
failure!(
|
46
|
+
failure!("no linter available with current configuration")
|
44
47
|
end
|
45
48
|
|
46
49
|
@options[:format] ||= :multiline
|
50
|
+
@options[:fail_level] ||= severity_level_for_name(:refactor)
|
47
51
|
@stats.files = lint_files.size
|
48
52
|
@stats.linters = enabled_linter_classes.size
|
53
|
+
@stats.autocorrectable_linters = enabled_linter_classes.count(&:support_autocorrect?)
|
49
54
|
|
50
55
|
reporter = Reporter.create_reporter(@options[:format], @stats, autocorrect?)
|
51
56
|
reporter.preview
|
52
57
|
|
53
58
|
runner = ERBLint::Runner.new(file_loader, @config)
|
59
|
+
file_content = nil
|
54
60
|
|
55
61
|
lint_files.each do |filename|
|
56
62
|
runner.clear_offenses
|
57
63
|
begin
|
58
|
-
run_with_corrections(runner, filename)
|
64
|
+
file_content = run_with_corrections(runner, filename)
|
59
65
|
rescue => e
|
60
66
|
@stats.exceptions += 1
|
61
|
-
puts "Exception
|
67
|
+
puts "Exception occurred when processing: #{relative_filename(filename)}"
|
62
68
|
puts "If this file cannot be processed by erb-lint, "\
|
63
69
|
"you can exclude it in your configuration file."
|
64
70
|
puts e.message
|
@@ -69,6 +75,12 @@ module ERBLint
|
|
69
75
|
|
70
76
|
reporter.show
|
71
77
|
|
78
|
+
if stdin? && autocorrect?
|
79
|
+
# When running from stdin, we only lint a single file
|
80
|
+
puts "================ #{lint_files.first} ==================\n"
|
81
|
+
puts file_content
|
82
|
+
end
|
83
|
+
|
72
84
|
@stats.found == 0 && @stats.exceptions == 0
|
73
85
|
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, ExitWithFailure => e
|
74
86
|
warn(Rainbow(e.message).red)
|
@@ -88,7 +100,7 @@ module ERBLint
|
|
88
100
|
end
|
89
101
|
|
90
102
|
def run_with_corrections(runner, filename)
|
91
|
-
file_content =
|
103
|
+
file_content = read_content(filename)
|
92
104
|
|
93
105
|
7.times do
|
94
106
|
processed_source = ERBLint::ProcessedSource.new(filename, file_content)
|
@@ -101,8 +113,11 @@ module ERBLint
|
|
101
113
|
|
102
114
|
@stats.corrected += corrector.corrections.size
|
103
115
|
|
104
|
-
|
105
|
-
|
116
|
+
# Don't overwrite the file if the input comes from stdin
|
117
|
+
unless stdin?
|
118
|
+
File.open(filename, "wb") do |file|
|
119
|
+
file.write(corrector.corrected_content)
|
120
|
+
end
|
106
121
|
end
|
107
122
|
|
108
123
|
file_content = corrector.corrected_content
|
@@ -111,14 +126,27 @@ module ERBLint
|
|
111
126
|
offenses_filename = relative_filename(filename)
|
112
127
|
offenses = runner.offenses || []
|
113
128
|
|
114
|
-
@stats.found
|
129
|
+
@stats.ignored, @stats.found = offenses.partition do |offense|
|
130
|
+
severity_level_for_name(offense.severity) < @options[:fail_level]
|
131
|
+
end.map(&:size)
|
132
|
+
.zip([@stats.ignored, @stats.found])
|
133
|
+
.map(&:sum)
|
134
|
+
|
115
135
|
@stats.processed_files[offenses_filename] ||= []
|
116
136
|
@stats.processed_files[offenses_filename] |= offenses
|
137
|
+
|
138
|
+
file_content
|
139
|
+
end
|
140
|
+
|
141
|
+
def read_content(filename)
|
142
|
+
return File.read(filename, encoding: Encoding::UTF_8) unless stdin?
|
143
|
+
|
144
|
+
$stdin.binmode.read.force_encoding(Encoding::UTF_8)
|
117
145
|
end
|
118
146
|
|
119
147
|
def correct(processed_source, offenses)
|
120
148
|
corrector = ERBLint::Corrector.new(processed_source, offenses)
|
121
|
-
failure!(corrector.diagnostics.join(
|
149
|
+
failure!(corrector.diagnostics.join(", ")) if corrector.diagnostics.any?
|
122
150
|
corrector
|
123
151
|
end
|
124
152
|
|
@@ -156,7 +184,7 @@ module ERBLint
|
|
156
184
|
else
|
157
185
|
@files
|
158
186
|
.map { |f| Dir.exist?(f) ? Dir[File.join(f, glob)] : f }
|
159
|
-
.map { |f| f.include?(
|
187
|
+
.map { |f| f.include?("*") ? Dir[f] : f }
|
160
188
|
.flatten
|
161
189
|
.map { |f| File.expand_path(f, Dir.pwd) }
|
162
190
|
.select { |filename| !excluded?(filename) }
|
@@ -169,7 +197,8 @@ module ERBLint
|
|
169
197
|
|
170
198
|
def excluded?(filename)
|
171
199
|
@config.global_exclude.any? do |path|
|
172
|
-
File.
|
200
|
+
expanded_path = File.expand_path(path, Dir.pwd)
|
201
|
+
File.fnmatch?(expanded_path, filename)
|
173
202
|
end
|
174
203
|
end
|
175
204
|
|
@@ -204,22 +233,18 @@ module ERBLint
|
|
204
233
|
|
205
234
|
def enabled_linter_classes
|
206
235
|
@enabled_linter_classes ||= ERBLint::LinterRegistry.linters
|
207
|
-
.select { |klass|
|
208
|
-
end
|
209
|
-
|
210
|
-
def linter_can_run?(klass)
|
211
|
-
!autocorrect? || klass.support_autocorrect?
|
236
|
+
.select { |klass| enabled_linter_names.include?(klass.simple_name.underscore) }
|
212
237
|
end
|
213
238
|
|
214
239
|
def relative_filename(filename)
|
215
|
-
filename.sub("#{File.expand_path(
|
240
|
+
filename.sub("#{File.expand_path(".", Dir.pwd)}/", "")
|
216
241
|
end
|
217
242
|
|
218
243
|
def runner_config_override
|
219
244
|
RunnerConfig.new(
|
220
245
|
linters: {}.tap do |linters|
|
221
246
|
ERBLint::LinterRegistry.linters.map do |klass|
|
222
|
-
linters[klass.simple_name] = {
|
247
|
+
linters[klass.simple_name] = { "enabled" => enabled_linter_classes.include?(klass) }
|
223
248
|
end
|
224
249
|
end
|
225
250
|
)
|
@@ -255,19 +280,36 @@ module ERBLint
|
|
255
280
|
end
|
256
281
|
|
257
282
|
opts.on("--enable-linters LINTER[,LINTER,...]", Array,
|
258
|
-
"Only use specified linter", "Known linters are: #{known_linter_names.join(
|
283
|
+
"Only use specified linter", "Known linters are: #{known_linter_names.join(", ")}") do |linters|
|
259
284
|
linters.each do |linter|
|
260
285
|
unless known_linter_names.include?(linter)
|
261
|
-
failure!("#{linter}: not a valid linter name (#{known_linter_names.join(
|
286
|
+
failure!("#{linter}: not a valid linter name (#{known_linter_names.join(", ")})")
|
262
287
|
end
|
263
288
|
end
|
264
289
|
@options[:enabled_linters] = linters
|
265
290
|
end
|
266
291
|
|
292
|
+
opts.on("--fail-level SEVERITY", "Minimum severity for exit with error code") do |level|
|
293
|
+
parsed_severity = SEVERITY_CODE_TABLE[level.upcase.to_sym] || (SEVERITY_NAMES & [level.downcase]).first
|
294
|
+
|
295
|
+
if parsed_severity.nil?
|
296
|
+
failure!("#{level}: not a valid failure level (#{SEVERITY_NAMES.join(", ")})")
|
297
|
+
end
|
298
|
+
@options[:fail_level] = severity_level_for_name(parsed_severity)
|
299
|
+
end
|
300
|
+
|
267
301
|
opts.on("-a", "--autocorrect", "Correct offenses automatically if possible (default: false)") do |config|
|
268
302
|
@options[:autocorrect] = config
|
269
303
|
end
|
270
304
|
|
305
|
+
opts.on(
|
306
|
+
"-sFILE",
|
307
|
+
"--stdin FILE",
|
308
|
+
"Pipe source from STDIN. Takes the path to be used to check which rules to apply."
|
309
|
+
) do |file|
|
310
|
+
@options[:stdin] = [file]
|
311
|
+
end
|
312
|
+
|
271
313
|
opts.on_tail("-h", "--help", "Show this message") do
|
272
314
|
success!(opts)
|
273
315
|
end
|
@@ -280,12 +322,16 @@ module ERBLint
|
|
280
322
|
|
281
323
|
def format_options_help
|
282
324
|
"Report offenses in the given format: "\
|
283
|
-
|
325
|
+
"(#{Reporter.available_formats.join(", ")}) (default: multiline)"
|
284
326
|
end
|
285
327
|
|
286
328
|
def invalid_format_error_message(given_format)
|
287
329
|
formats = Reporter.available_formats.map { |format| " - #{format}\n" }
|
288
330
|
"#{given_format}: is not a valid format. Available formats:\n#{formats.join}"
|
289
331
|
end
|
332
|
+
|
333
|
+
def stdin?
|
334
|
+
@options[:stdin].present?
|
335
|
+
end
|
290
336
|
end
|
291
337
|
end
|
data/lib/erb_lint/corrector.rb
CHANGED
data/lib/erb_lint/linter.rb
CHANGED
@@ -15,9 +15,9 @@ module ERBLint
|
|
15
15
|
# `ERBLint::Linters::Compass::Bar.simple_name` #=> "Compass::Bar"
|
16
16
|
def inherited(linter)
|
17
17
|
super
|
18
|
-
linter.simple_name = if linter.name.start_with?(
|
19
|
-
name_parts = linter.name.split(
|
20
|
-
name_parts[2..-1].join(
|
18
|
+
linter.simple_name = if linter.name.start_with?("ERBLint::Linters::")
|
19
|
+
name_parts = linter.name.split("::")
|
20
|
+
name_parts[2..-1].join("::")
|
21
21
|
else
|
22
22
|
linter.name
|
23
23
|
end
|
@@ -53,8 +53,8 @@ module ERBLint
|
|
53
53
|
raise NotImplementedError, "must implement ##{__method__}"
|
54
54
|
end
|
55
55
|
|
56
|
-
def add_offense(source_range, message, context = nil)
|
57
|
-
@offenses << Offense.new(self, source_range, message, context)
|
56
|
+
def add_offense(source_range, message, context = nil, severity = nil)
|
57
|
+
@offenses << Offense.new(self, source_range, message, context, severity)
|
58
58
|
end
|
59
59
|
|
60
60
|
def clear_offenses
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "active_support"
|
4
|
+
require "smart_properties"
|
5
5
|
|
6
6
|
module ERBLint
|
7
7
|
class LinterConfig
|
@@ -27,8 +27,9 @@ module ERBLint
|
|
27
27
|
allowed_keys = self.class.properties.keys.map(&:to_s)
|
28
28
|
given_keys = config.keys
|
29
29
|
if (extra_keys = given_keys - allowed_keys).any?
|
30
|
-
raise Error, "Given key is not allowed: #{extra_keys.join(
|
30
|
+
raise Error, "Given key is not allowed: #{extra_keys.join(", ")}"
|
31
31
|
end
|
32
|
+
|
32
33
|
super(config)
|
33
34
|
rescue SmartProperties::InitializationError => e
|
34
35
|
raise Error, "The following properties are required to be set: #{e.properties}"
|
@@ -40,6 +41,7 @@ module ERBLint
|
|
40
41
|
unless self.class.properties.key?(name)
|
41
42
|
raise Error, "No such property: #{name}"
|
42
43
|
end
|
44
|
+
|
43
45
|
super
|
44
46
|
end
|
45
47
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ERBLint
|
4
4
|
# Stores all linters available to the application.
|
5
5
|
module LinterRegistry
|
6
|
-
CUSTOM_LINTERS_DIR =
|
6
|
+
CUSTOM_LINTERS_DIR = ".erb-linters"
|
7
7
|
@loaded_linters = []
|
8
8
|
|
9
9
|
class << self
|
@@ -27,7 +27,7 @@ module ERBLint
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def load_custom_linters(directory = CUSTOM_LINTERS_DIR)
|
30
|
-
ruby_files = Dir.glob(File.expand_path(File.join(directory,
|
30
|
+
ruby_files = Dir.glob(File.expand_path(File.join(directory, "**", "*.rb")))
|
31
31
|
ruby_files.each { |file| require file }
|
32
32
|
end
|
33
33
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "better_html"
|
4
|
+
require "better_html/tree/tag"
|
5
5
|
|
6
6
|
module ERBLint
|
7
7
|
module Linters
|
@@ -13,7 +13,7 @@ module ERBLint
|
|
13
13
|
|
14
14
|
class ConfigSchema < LinterConfig
|
15
15
|
property :allowed_types, accepts: array_of?(String),
|
16
|
-
default: -> { [
|
16
|
+
default: -> { ["text/javascript"] }
|
17
17
|
property :allow_blank, accepts: [true, false], default: true, reader: :allow_blank?
|
18
18
|
property :disallow_inline_scripts, accepts: [true, false], default: false, reader: :disallow_inline_scripts?
|
19
19
|
end
|
@@ -24,19 +24,19 @@ module ERBLint
|
|
24
24
|
parser.nodes_with_type(:tag).each do |tag_node|
|
25
25
|
tag = BetterHtml::Tree::Tag.from_node(tag_node)
|
26
26
|
next if tag.closing?
|
27
|
-
next unless tag.name ==
|
27
|
+
next unless tag.name == "script"
|
28
28
|
|
29
29
|
if @config.disallow_inline_scripts?
|
30
30
|
name_node = tag_node.to_a[1]
|
31
31
|
add_offense(
|
32
32
|
name_node.loc,
|
33
33
|
"Avoid using inline `<script>` tags altogether. "\
|
34
|
-
|
34
|
+
"Instead, move javascript code into a static file."
|
35
35
|
)
|
36
36
|
next
|
37
37
|
end
|
38
38
|
|
39
|
-
type_attribute = tag.attributes[
|
39
|
+
type_attribute = tag.attributes["type"]
|
40
40
|
type_present = type_attribute.present? && type_attribute.value_node.present?
|
41
41
|
|
42
42
|
if !type_present && !@config.allow_blank?
|
@@ -50,8 +50,8 @@ module ERBLint
|
|
50
50
|
add_offense(
|
51
51
|
type_attribute.loc,
|
52
52
|
"Avoid using #{type_attribute.value.inspect} as type for `<script>` tag. "\
|
53
|
-
|
54
|
-
|
53
|
+
"Must be one of: #{@config.allowed_types.join(", ")}"\
|
54
|
+
"#{" (or no type attribute)" if @config.allow_blank?}."
|
55
55
|
)
|
56
56
|
end
|
57
57
|
end
|
@@ -59,6 +59,7 @@ module ERBLint
|
|
59
59
|
|
60
60
|
def autocorrect(_processed_source, offense)
|
61
61
|
return unless offense.context
|
62
|
+
|
62
63
|
lambda do |corrector|
|
63
64
|
type_attribute, = *offense.context
|
64
65
|
if type_attribute.nil?
|
@@ -25,7 +25,7 @@ module ERBLint
|
|
25
25
|
add_offense(
|
26
26
|
code_node.loc.end.adjust(begin_pos: -end_spaces.size),
|
27
27
|
"Remove newline before `%>` to match start of tag.",
|
28
|
-
|
28
|
+
" "
|
29
29
|
)
|
30
30
|
elsif start_with_newline && !end_with_newline
|
31
31
|
add_offense(
|
@@ -39,7 +39,7 @@ module ERBLint
|
|
39
39
|
add_offense(
|
40
40
|
code_node.loc.end.adjust(begin_pos: -current_indent.size),
|
41
41
|
"Indent `%>` on column #{erb_node.loc.column} to match start of tag.",
|
42
|
-
|
42
|
+
" " * erb_node.loc.column
|
43
43
|
)
|
44
44
|
end
|
45
45
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "better_html"
|
4
|
+
require "better_html/parser"
|
5
5
|
|
6
6
|
module ERBLint
|
7
7
|
module Linters
|
@@ -11,7 +11,7 @@ module ERBLint
|
|
11
11
|
|
12
12
|
class RuleSet
|
13
13
|
include SmartProperties
|
14
|
-
property :suggestion, accepts: String, default:
|
14
|
+
property :suggestion, accepts: String, default: ""
|
15
15
|
property :deprecated, accepts: LinterConfig.array_of?(String), default: -> { [] }
|
16
16
|
end
|
17
17
|
|
@@ -57,9 +57,10 @@ module ERBLint
|
|
57
57
|
def class_name_with_loc(processed_source)
|
58
58
|
Enumerator.new do |yielder|
|
59
59
|
tags(processed_source).each do |tag|
|
60
|
-
class_value = tag.attributes[
|
60
|
+
class_value = tag.attributes["class"]&.value
|
61
61
|
next unless class_value
|
62
|
-
|
62
|
+
|
63
|
+
class_value.split(" ").each do |class_name|
|
63
64
|
yielder.yield(class_name, tag.loc)
|
64
65
|
end
|
65
66
|
end
|
@@ -69,7 +70,7 @@ module ERBLint
|
|
69
70
|
def text_tags_content(processed_source)
|
70
71
|
Enumerator.new do |yielder|
|
71
72
|
script_tags(processed_source)
|
72
|
-
.select { |tag| tag.attributes[
|
73
|
+
.select { |tag| tag.attributes["type"]&.value == "text/html" }
|
73
74
|
.each do |tag|
|
74
75
|
index = processed_source.ast.to_a.find_index(tag.node)
|
75
76
|
next_node = processed_source.ast.to_a[index + 1]
|
@@ -80,7 +81,7 @@ module ERBLint
|
|
80
81
|
end
|
81
82
|
|
82
83
|
def script_tags(processed_source)
|
83
|
-
tags(processed_source).select { |tag| tag.name ==
|
84
|
+
tags(processed_source).select { |tag| tag.name == "script" }
|
84
85
|
end
|
85
86
|
|
86
87
|
def tags(processed_source)
|
@@ -28,7 +28,7 @@ module ERBLint
|
|
28
28
|
if final_newline.empty?
|
29
29
|
add_offense(
|
30
30
|
processed_source.to_source_range(file_content.size...file_content.size),
|
31
|
-
|
31
|
+
"Missing a trailing newline at the end of the file.",
|
32
32
|
:insert
|
33
33
|
)
|
34
34
|
else
|
@@ -36,7 +36,7 @@ module ERBLint
|
|
36
36
|
processed_source.to_source_range(
|
37
37
|
(file_content.size - final_newline.size + 1)...file_content.size
|
38
38
|
),
|
39
|
-
|
39
|
+
"Remove multiple trailing newline at the end of the file.",
|
40
40
|
:remove
|
41
41
|
)
|
42
42
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "set"
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require "better_html/tree/tag"
|
5
|
+
require "active_support/core_ext/string/inflections"
|
5
6
|
|
6
7
|
module ERBLint
|
7
8
|
module Linters
|
@@ -13,17 +14,37 @@ module ERBLint
|
|
13
14
|
MissingCorrector = Class.new(StandardError)
|
14
15
|
MissingI18nLoadPath = Class.new(StandardError)
|
15
16
|
|
16
|
-
ALLOWED_CORRECTORS =
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
ALLOWED_CORRECTORS = ["I18nCorrector", "RuboCop::Corrector::I18n::HardCodedString"]
|
18
|
+
|
19
|
+
NON_TEXT_TAGS = Set.new(["script", "style", "xmp", "iframe", "noembed", "noframes", "listing"])
|
20
|
+
TEXT_NOT_ALLOWED = Set.new([
|
21
|
+
" ",
|
22
|
+
"&",
|
23
|
+
"<",
|
24
|
+
">",
|
25
|
+
""",
|
26
|
+
"©",
|
27
|
+
"®",
|
28
|
+
"™",
|
29
|
+
"…",
|
30
|
+
"—",
|
31
|
+
"•",
|
32
|
+
"“",
|
33
|
+
"”",
|
34
|
+
"‘",
|
35
|
+
"’",
|
36
|
+
"←",
|
37
|
+
"→",
|
38
|
+
"↓",
|
39
|
+
"↑",
|
40
|
+
" ",
|
41
|
+
" ",
|
42
|
+
" ",
|
43
|
+
])
|
23
44
|
|
24
45
|
class ConfigSchema < LinterConfig
|
25
46
|
property :corrector, accepts: Hash, required: false, default: -> { {} }
|
26
|
-
property :i18n_load_path, accepts: String, required: false, default:
|
47
|
+
property :i18n_load_path, accepts: String, required: false, default: ""
|
27
48
|
end
|
28
49
|
self.config_schema = ConfigSchema
|
29
50
|
|
@@ -63,9 +84,10 @@ module ERBLint
|
|
63
84
|
string = offense.source_range.source
|
64
85
|
return unless (klass = load_corrector)
|
65
86
|
return unless string.strip.length > 1
|
87
|
+
|
66
88
|
node = ::RuboCop::AST::StrNode.new(:str, [string])
|
67
89
|
corrector = klass.new(node, processed_source.filename, corrector_i18n_load_path, offense.source_range)
|
68
|
-
corrector.autocorrect(tag_start:
|
90
|
+
corrector.autocorrect(tag_start: "<%= ", tag_end: " %>")
|
69
91
|
rescue MissingCorrector, MissingI18nLoadPath
|
70
92
|
nil
|
71
93
|
end
|
@@ -73,20 +95,21 @@ module ERBLint
|
|
73
95
|
private
|
74
96
|
|
75
97
|
def check_string?(str)
|
76
|
-
string = str.gsub(/\s*/,
|
77
|
-
string.length > 1 && !
|
98
|
+
string = str.gsub(/\s*/, "")
|
99
|
+
string.length > 1 && !TEXT_NOT_ALLOWED.include?(string)
|
78
100
|
end
|
79
101
|
|
80
102
|
def load_corrector
|
81
|
-
corrector_name = @config[
|
103
|
+
corrector_name = @config["corrector"].fetch("name") { raise MissingCorrector }
|
82
104
|
raise ForbiddenCorrector unless ALLOWED_CORRECTORS.include?(corrector_name)
|
83
|
-
|
105
|
+
|
106
|
+
require @config["corrector"].fetch("path") { raise MissingCorrector }
|
84
107
|
|
85
108
|
corrector_name.safe_constantize
|
86
109
|
end
|
87
110
|
|
88
111
|
def corrector_i18n_load_path
|
89
|
-
@config[
|
112
|
+
@config["corrector"].fetch("i18n_load_path") { raise MissingI18nLoadPath }
|
90
113
|
end
|
91
114
|
|
92
115
|
def non_text_tag?(processed_source, text_node)
|