erb_lint 0.3.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/erb_lint/cli.rb +7 -2
- data/lib/erb_lint/linter.rb +26 -0
- data/lib/erb_lint/linters/hard_coded_string.rb +3 -2
- data/lib/erb_lint/linters/no_unused_disable.rb +45 -0
- data/lib/erb_lint/offense.rb +7 -0
- data/lib/erb_lint/reporters/junit_reporter.rb +112 -0
- data/lib/erb_lint/runner.rb +32 -3
- data/lib/erb_lint/runner_config.rb +1 -1
- data/lib/erb_lint/utils/inline_configs.rb +15 -0
- data/lib/erb_lint/version.rb +1 -1
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98ba7a87f348e584502a9e4810f9311329c04aeddf34af0b05a37702648b495e
|
4
|
+
data.tar.gz: 8728892a4c09fcbdbddb60316b911bf73ce099cc04e9ac10534b76f8aa6708a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d01f3c07ddebb9482ee744c69df6e799b4c0cb06bd24ffd7939f3d86e612a4f1bb2d5b4040c8505d7c8f8e9cceaecaabcca7dc03cab6ea5f23e73f10e098345
|
7
|
+
data.tar.gz: '080398f6522352b669bf21ea1fbdd8850b7b6b8dcef60493d8349fbfeb98eceeefc77c15442cad339612e83c5056d5498a286046c3a3749a73428661c962b695'
|
data/lib/erb_lint/cli.rb
CHANGED
@@ -69,6 +69,7 @@ module ERBLint
|
|
69
69
|
|
70
70
|
@options[:format] ||= :multiline
|
71
71
|
@options[:fail_level] ||= severity_level_for_name(:refactor)
|
72
|
+
@options[:disable_inline_configs] ||= false
|
72
73
|
@stats.files = lint_files.size
|
73
74
|
@stats.linters = enabled_linter_classes.size
|
74
75
|
@stats.autocorrectable_linters = enabled_linter_classes.count(&:support_autocorrect?)
|
@@ -76,7 +77,7 @@ module ERBLint
|
|
76
77
|
reporter = Reporter.create_reporter(@options[:format], @stats, autocorrect?)
|
77
78
|
reporter.preview
|
78
79
|
|
79
|
-
runner = ERBLint::Runner.new(file_loader, @config)
|
80
|
+
runner = ERBLint::Runner.new(file_loader, @config, @options[:disable_inline_configs])
|
80
81
|
file_content = nil
|
81
82
|
|
82
83
|
lint_files.each do |filename|
|
@@ -221,7 +222,7 @@ module ERBLint
|
|
221
222
|
rescue Psych::SyntaxError => e
|
222
223
|
failure!("error parsing config: #{e.message}")
|
223
224
|
ensure
|
224
|
-
@config
|
225
|
+
@config&.merge!(runner_config_override)
|
225
226
|
end
|
226
227
|
|
227
228
|
def file_loader
|
@@ -374,6 +375,10 @@ module ERBLint
|
|
374
375
|
@options[:allow_no_files] = config
|
375
376
|
end
|
376
377
|
|
378
|
+
opts.on("--disable-inline-configs", "Report all offenses while ignoring inline disable comments") do
|
379
|
+
@options[:disable_inline_configs] = true
|
380
|
+
end
|
381
|
+
|
377
382
|
opts.on(
|
378
383
|
"-sFILE",
|
379
384
|
"--stdin FILE",
|
data/lib/erb_lint/linter.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "erb_lint/utils/inline_configs"
|
4
|
+
|
3
5
|
module ERBLint
|
4
6
|
# Defines common functionality available to all linters.
|
5
7
|
class Linter
|
@@ -53,6 +55,13 @@ module ERBLint
|
|
53
55
|
raise NotImplementedError, "must implement ##{__method__}"
|
54
56
|
end
|
55
57
|
|
58
|
+
def run_and_update_offense_status(processed_source, enable_inline_configs = true)
|
59
|
+
run(processed_source)
|
60
|
+
if @offenses.any? && enable_inline_configs
|
61
|
+
update_offense_status(processed_source)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
56
65
|
def add_offense(source_range, message, context = nil, severity = nil)
|
57
66
|
@offenses << Offense.new(self, source_range, message, context, severity)
|
58
67
|
end
|
@@ -60,5 +69,22 @@ module ERBLint
|
|
60
69
|
def clear_offenses
|
61
70
|
@offenses = []
|
62
71
|
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def update_offense_status(processed_source)
|
76
|
+
@offenses.each do |offense|
|
77
|
+
offense_line_range = offense.source_range.line_range
|
78
|
+
offense_lines = source_for_line_range(processed_source, offense_line_range)
|
79
|
+
|
80
|
+
if Utils::InlineConfigs.rule_disable_comment_for_lines?(self.class.simple_name, offense_lines)
|
81
|
+
offense.disabled = true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def source_for_line_range(processed_source, line_range)
|
87
|
+
processed_source.source_buffer.source_lines[line_range.first - 1..line_range.last - 1].join
|
88
|
+
end
|
63
89
|
end
|
64
90
|
end
|
@@ -17,7 +17,7 @@ module ERBLint
|
|
17
17
|
ALLOWED_CORRECTORS = ["I18nCorrector", "RuboCop::Corrector::I18n::HardCodedString"]
|
18
18
|
|
19
19
|
NON_TEXT_TAGS = Set.new(["script", "style", "xmp", "iframe", "noembed", "noframes", "listing"])
|
20
|
-
|
20
|
+
NO_TRANSLATION_NEEDED = Set.new([
|
21
21
|
" ",
|
22
22
|
"&",
|
23
23
|
"<",
|
@@ -40,6 +40,7 @@ module ERBLint
|
|
40
40
|
" ",
|
41
41
|
" ",
|
42
42
|
" ",
|
43
|
+
"×",
|
43
44
|
])
|
44
45
|
|
45
46
|
class ConfigSchema < LinterConfig
|
@@ -96,7 +97,7 @@ module ERBLint
|
|
96
97
|
|
97
98
|
def check_string?(str)
|
98
99
|
string = str.gsub(/\s*/, "")
|
99
|
-
string.length > 1 && !
|
100
|
+
string.length > 1 && !NO_TRANSLATION_NEEDED.include?(string)
|
100
101
|
end
|
101
102
|
|
102
103
|
def load_corrector
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb_lint/utils/inline_configs"
|
4
|
+
|
5
|
+
module ERBLint
|
6
|
+
module Linters
|
7
|
+
# Checks for unused disable comments.
|
8
|
+
class NoUnusedDisable < Linter
|
9
|
+
include LinterRegistry
|
10
|
+
|
11
|
+
def run(processed_source, offenses)
|
12
|
+
disabled_rules_and_line_number = {}
|
13
|
+
|
14
|
+
processed_source.source_buffer.source_lines.each_with_index do |line, index|
|
15
|
+
rule_disables = Utils::InlineConfigs.disabled_rules(line)
|
16
|
+
next unless rule_disables
|
17
|
+
|
18
|
+
rule_disables.split(",").each do |rule|
|
19
|
+
disabled_rules_and_line_number[rule.strip] =
|
20
|
+
(disabled_rules_and_line_number[rule.strip] ||= []).push(index + 1)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
offenses.each do |offense|
|
25
|
+
rule_name = offense.linter.class.simple_name
|
26
|
+
line_numbers = disabled_rules_and_line_number[rule_name]
|
27
|
+
next unless line_numbers
|
28
|
+
|
29
|
+
line_numbers.reject do |line_number|
|
30
|
+
if (offense.source_range.line_span.first..offense.source_range.line_span.last).include?(line_number)
|
31
|
+
disabled_rules_and_line_number[rule_name].delete(line_number)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
disabled_rules_and_line_number.each do |rule, line_numbers|
|
37
|
+
line_numbers.each do |line_number|
|
38
|
+
add_offense(processed_source.source_buffer.line_range(line_number),
|
39
|
+
"Unused erblint:disable comment for #{rule}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/erb_lint/offense.rb
CHANGED
@@ -15,6 +15,7 @@ module ERBLint
|
|
15
15
|
@message = message
|
16
16
|
@context = context
|
17
17
|
@severity = severity
|
18
|
+
@disabled = false
|
18
19
|
end
|
19
20
|
|
20
21
|
def to_cached_offense_hash
|
@@ -44,6 +45,12 @@ module ERBLint
|
|
44
45
|
line_range.begin
|
45
46
|
end
|
46
47
|
|
48
|
+
attr_writer :disabled
|
49
|
+
|
50
|
+
def disabled?
|
51
|
+
@disabled
|
52
|
+
end
|
53
|
+
|
47
54
|
def column
|
48
55
|
source_range.column
|
49
56
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rexml/document"
|
4
|
+
require "rexml/formatters/pretty"
|
5
|
+
|
6
|
+
module ERBLint
|
7
|
+
module Reporters
|
8
|
+
class JunitReporter < Reporter
|
9
|
+
def preview; end
|
10
|
+
|
11
|
+
def show
|
12
|
+
xml = create_junit_xml
|
13
|
+
formatted_xml_string = StringIO.new
|
14
|
+
REXML::Formatters::Pretty.new.write(xml, formatted_xml_string)
|
15
|
+
puts formatted_xml_string.string
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
CONTEXT = {
|
21
|
+
prologue_quote: :quote,
|
22
|
+
attribute_quote: :quote,
|
23
|
+
}
|
24
|
+
|
25
|
+
def create_junit_xml
|
26
|
+
# create prologue
|
27
|
+
xml = REXML::Document.new(nil, CONTEXT)
|
28
|
+
xml << REXML::XMLDecl.new("1.0", "UTF-8")
|
29
|
+
|
30
|
+
xml.add_element(create_testsuite_element)
|
31
|
+
|
32
|
+
xml
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_testsuite_element
|
36
|
+
tests = stats.processed_files.size
|
37
|
+
failures = stats.found
|
38
|
+
testsuite_element = REXML::Element.new("testsuite", nil, CONTEXT)
|
39
|
+
testsuite_element.add_attribute("name", "erblint")
|
40
|
+
testsuite_element.add_attribute("tests", tests.to_s)
|
41
|
+
testsuite_element.add_attribute("failures", failures.to_s)
|
42
|
+
|
43
|
+
testsuite_element.add_element(create_properties)
|
44
|
+
|
45
|
+
processed_files.each do |filename, offenses|
|
46
|
+
if offenses.empty?
|
47
|
+
testcase_element = REXML::Element.new("testcase", nil, CONTEXT)
|
48
|
+
testcase_element.add_attribute("name", filename.to_s)
|
49
|
+
testcase_element.add_attribute("file", filename.to_s)
|
50
|
+
|
51
|
+
testsuite_element.add_element(testcase_element)
|
52
|
+
end
|
53
|
+
|
54
|
+
offenses.each do |offense|
|
55
|
+
testsuite_element.add_element(create_testcase(filename, offense))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
testsuite_element
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_properties
|
63
|
+
properties_element = REXML::Element.new("properties", nil, CONTEXT)
|
64
|
+
|
65
|
+
[
|
66
|
+
["erb_lint_version", ERBLint::VERSION],
|
67
|
+
["ruby_engine", RUBY_ENGINE],
|
68
|
+
["ruby_version", RUBY_VERSION],
|
69
|
+
["ruby_patchlevel", RUBY_PATCHLEVEL.to_s],
|
70
|
+
["ruby_platform", RUBY_PLATFORM],
|
71
|
+
].each do |property_attribute|
|
72
|
+
properties_element.add_element(create_property(*property_attribute))
|
73
|
+
end
|
74
|
+
|
75
|
+
properties_element
|
76
|
+
end
|
77
|
+
|
78
|
+
def create_property(name, value)
|
79
|
+
property_element = REXML::Element.new("property")
|
80
|
+
property_element.add_attribute("name", name)
|
81
|
+
property_element.add_attribute("value", value)
|
82
|
+
|
83
|
+
property_element
|
84
|
+
end
|
85
|
+
|
86
|
+
def create_testcase(filename, offense)
|
87
|
+
testcase_element = REXML::Element.new("testcase", nil, CONTEXT)
|
88
|
+
testcase_element.add_attribute("name", filename.to_s)
|
89
|
+
testcase_element.add_attribute("file", filename.to_s)
|
90
|
+
testcase_element.add_attribute("lineno", offense.line_number.to_s)
|
91
|
+
|
92
|
+
testcase_element.add_element(create_failure(filename, offense))
|
93
|
+
|
94
|
+
testcase_element
|
95
|
+
end
|
96
|
+
|
97
|
+
def create_failure(filename, offense)
|
98
|
+
message = offense.message
|
99
|
+
type = offense.simple_name
|
100
|
+
|
101
|
+
failure_element = REXML::Element.new("failure", nil, CONTEXT)
|
102
|
+
failure_element.add_attribute("message", "#{type}: #{message}")
|
103
|
+
failure_element.add_attribute("type", type.to_s)
|
104
|
+
|
105
|
+
cdata_element = REXML::CData.new("#{type}: #{message} at #{filename}:#{offense.line_number}:#{offense.column}")
|
106
|
+
failure_element.add_text(cdata_element)
|
107
|
+
|
108
|
+
failure_element
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/erb_lint/runner.rb
CHANGED
@@ -5,15 +5,19 @@ module ERBLint
|
|
5
5
|
class Runner
|
6
6
|
attr_reader :offenses
|
7
7
|
|
8
|
-
def initialize(file_loader, config)
|
8
|
+
def initialize(file_loader, config, disable_inline_configs = false)
|
9
9
|
@file_loader = file_loader
|
10
10
|
@config = config || RunnerConfig.default
|
11
11
|
raise ArgumentError, "expect `config` to be a RunnerConfig instance" unless @config.is_a?(RunnerConfig)
|
12
12
|
|
13
|
-
linter_classes = LinterRegistry.linters.select
|
13
|
+
linter_classes = LinterRegistry.linters.select do |klass|
|
14
|
+
@config.for_linter(klass).enabled? && klass != ERBLint::Linters::NoUnusedDisable
|
15
|
+
end
|
14
16
|
@linters = linter_classes.map do |linter_class|
|
15
17
|
linter_class.new(@file_loader, @config.for_linter(linter_class))
|
16
18
|
end
|
19
|
+
@no_unused_disable = nil
|
20
|
+
@disable_inline_configs = disable_inline_configs
|
17
21
|
@offenses = []
|
18
22
|
end
|
19
23
|
|
@@ -21,18 +25,43 @@ module ERBLint
|
|
21
25
|
@linters
|
22
26
|
.reject { |linter| linter.excludes_file?(processed_source.filename) }
|
23
27
|
.each do |linter|
|
24
|
-
linter.
|
28
|
+
linter.run_and_update_offense_status(processed_source, enable_inline_configs?)
|
25
29
|
@offenses.concat(linter.offenses)
|
26
30
|
end
|
31
|
+
report_unused_disable(processed_source)
|
32
|
+
@offenses = @offenses.reject(&:disabled?)
|
27
33
|
end
|
28
34
|
|
29
35
|
def clear_offenses
|
30
36
|
@offenses = []
|
31
37
|
@linters.each(&:clear_offenses)
|
38
|
+
@no_unused_disable&.clear_offenses
|
32
39
|
end
|
33
40
|
|
34
41
|
def restore_offenses(offenses)
|
35
42
|
@offenses.concat(offenses)
|
36
43
|
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def enable_inline_configs?
|
48
|
+
!@disable_inline_configs
|
49
|
+
end
|
50
|
+
|
51
|
+
def no_unused_disable_enabled?
|
52
|
+
LinterRegistry.linters.include?(ERBLint::Linters::NoUnusedDisable) &&
|
53
|
+
@config.for_linter(ERBLint::Linters::NoUnusedDisable).enabled?
|
54
|
+
end
|
55
|
+
|
56
|
+
def report_unused_disable(processed_source)
|
57
|
+
if no_unused_disable_enabled? && enable_inline_configs?
|
58
|
+
@no_unused_disable = ERBLint::Linters::NoUnusedDisable.new(
|
59
|
+
@file_loader,
|
60
|
+
@config.for_linter(ERBLint::Linters::NoUnusedDisable)
|
61
|
+
)
|
62
|
+
@no_unused_disable.run(processed_source, @offenses)
|
63
|
+
@offenses.concat(@no_unused_disable.offenses)
|
64
|
+
end
|
65
|
+
end
|
37
66
|
end
|
38
67
|
end
|
@@ -83,7 +83,7 @@ module ERBLint
|
|
83
83
|
def config_hash_for_linter(klass_name)
|
84
84
|
config_hash = linters_config[klass_name] || {}
|
85
85
|
config_hash["exclude"] ||= []
|
86
|
-
config_hash["exclude"].concat(global_exclude) if config_hash["exclude"].is_a?(Array)
|
86
|
+
config_hash["exclude"].concat(global_exclude).uniq! if config_hash["exclude"].is_a?(Array)
|
87
87
|
config_hash
|
88
88
|
end
|
89
89
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
module Utils
|
5
|
+
class InlineConfigs
|
6
|
+
def self.rule_disable_comment_for_lines?(rule, lines)
|
7
|
+
lines.match?(/# erblint:disable (?<rules>.*#{rule}).*/)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.disabled_rules(line)
|
11
|
+
line.match(/# erblint:disable (?<rules>.*) %>/)&.named_captures&.fetch("rules")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/erb_lint/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erb_lint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Chan
|
8
|
+
- Shopify Developers
|
8
9
|
autorequire:
|
9
10
|
bindir: exe
|
10
11
|
cert_chain: []
|
11
|
-
date:
|
12
|
+
date: 2023-08-25 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: activesupport
|
@@ -138,7 +139,7 @@ dependencies:
|
|
138
139
|
version: '0'
|
139
140
|
description: ERB Linter tool.
|
140
141
|
email:
|
141
|
-
-
|
142
|
+
- ruby@shopify.com
|
142
143
|
executables:
|
143
144
|
- erblint
|
144
145
|
extensions: []
|
@@ -164,6 +165,7 @@ files:
|
|
164
165
|
- lib/erb_lint/linters/final_newline.rb
|
165
166
|
- lib/erb_lint/linters/hard_coded_string.rb
|
166
167
|
- lib/erb_lint/linters/no_javascript_tag_helper.rb
|
168
|
+
- lib/erb_lint/linters/no_unused_disable.rb
|
167
169
|
- lib/erb_lint/linters/parser_errors.rb
|
168
170
|
- lib/erb_lint/linters/partial_instance_variable.rb
|
169
171
|
- lib/erb_lint/linters/require_input_autocomplete.rb
|
@@ -181,12 +183,14 @@ files:
|
|
181
183
|
- lib/erb_lint/reporter.rb
|
182
184
|
- lib/erb_lint/reporters/compact_reporter.rb
|
183
185
|
- lib/erb_lint/reporters/json_reporter.rb
|
186
|
+
- lib/erb_lint/reporters/junit_reporter.rb
|
184
187
|
- lib/erb_lint/reporters/multiline_reporter.rb
|
185
188
|
- lib/erb_lint/runner.rb
|
186
189
|
- lib/erb_lint/runner_config.rb
|
187
190
|
- lib/erb_lint/runner_config_resolver.rb
|
188
191
|
- lib/erb_lint/stats.rb
|
189
192
|
- lib/erb_lint/utils/block_map.rb
|
193
|
+
- lib/erb_lint/utils/inline_configs.rb
|
190
194
|
- lib/erb_lint/utils/offset_corrector.rb
|
191
195
|
- lib/erb_lint/utils/ruby_to_erb.rb
|
192
196
|
- lib/erb_lint/utils/severity_levels.rb
|
@@ -211,7 +215,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
215
|
- !ruby/object:Gem::Version
|
212
216
|
version: '0'
|
213
217
|
requirements: []
|
214
|
-
rubygems_version: 3.
|
218
|
+
rubygems_version: 3.4.18
|
215
219
|
signing_key:
|
216
220
|
specification_version: 4
|
217
221
|
summary: ERB lint tool
|