erb_lint 0.0.19 → 0.0.20
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/cli.rb +16 -5
- data/lib/erb_lint/linters/closing_erb_tag_indent.rb +59 -0
- data/lib/erb_lint/linters/extra_newline.rb +36 -0
- data/lib/erb_lint/linters/final_newline.rb +21 -10
- data/lib/erb_lint/linters/rubocop.rb +4 -4
- data/lib/erb_lint/linters/self_closing_tag.rb +46 -0
- data/lib/erb_lint/linters/space_around_erb_tag.rb +30 -9
- data/lib/erb_lint/linters/space_in_html_tag.rb +119 -0
- data/lib/erb_lint/linters/space_indentation.rb +42 -0
- data/lib/erb_lint/linters/trailing_whitespace.rb +34 -0
- data/lib/erb_lint/runner_config.rb +20 -3
- data/lib/erb_lint/version.rb +1 -1
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab337195cd313e04726f3c3ca31cc6fd77e661ce
|
4
|
+
data.tar.gz: 52de035725a4a39e533552a056f17e2f6d87e980
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35dc5da94ef03e07d04a933ac5ded2951fde3437c20410d333e28b5c0fde51a6fe7df2b743f80cc0e13840c73b18b2309fdcd3a15c8ae9ecb0de30bcaad5270d
|
7
|
+
data.tar.gz: f1a74b863008b9ed36f56d7ecbf12533b9ee8b3bb89aca0da8c118306013ea0e54c9d1ea84def71b49b912d41c3347163fa284029cab98c36a3ec6132ccc35c9
|
data/lib/erb_lint/cli.rb
CHANGED
@@ -35,11 +35,12 @@ module ERBLint
|
|
35
35
|
load_options(args)
|
36
36
|
@files = args.dup
|
37
37
|
|
38
|
+
load_config
|
39
|
+
|
38
40
|
if lint_files.empty?
|
39
41
|
success!("no files given...\n#{option_parser}")
|
40
42
|
end
|
41
43
|
|
42
|
-
load_config
|
43
44
|
ensure_files_exist(lint_files)
|
44
45
|
|
45
46
|
if enabled_linter_classes.empty?
|
@@ -50,8 +51,7 @@ module ERBLint
|
|
50
51
|
"#{enabled_linter_classes.size} #{'autocorrectable ' if autocorrect?}linters..."
|
51
52
|
puts
|
52
53
|
|
53
|
-
|
54
|
-
runner = ERBLint::Runner.new(file_loader, runner_config)
|
54
|
+
runner = ERBLint::Runner.new(file_loader, @config)
|
55
55
|
lint_files.each do |filename|
|
56
56
|
begin
|
57
57
|
run_with_corrections(runner, filename)
|
@@ -142,6 +142,7 @@ module ERBLint
|
|
142
142
|
warn "#{config_filename} not found: using default config".yellow
|
143
143
|
@config = RunnerConfig.default
|
144
144
|
end
|
145
|
+
@config.merge!(runner_config_override)
|
145
146
|
rescue Psych::SyntaxError => e
|
146
147
|
failure!("error parsing config: #{e.message}")
|
147
148
|
end
|
@@ -157,9 +158,19 @@ module ERBLint
|
|
157
158
|
def lint_files
|
158
159
|
if @options[:lint_all]
|
159
160
|
pattern = File.expand_path(DEFAULT_LINT_ALL_GLOB, Dir.pwd)
|
160
|
-
Dir[pattern]
|
161
|
+
Dir[pattern].select { |filename| !excluded?(filename) }
|
161
162
|
else
|
162
|
-
@files
|
163
|
+
@files
|
164
|
+
.map { |f| f.include?('*') ? Dir[f] : f }
|
165
|
+
.flatten
|
166
|
+
.map { |f| File.expand_path(f, Dir.pwd) }
|
167
|
+
.select { |filename| !excluded?(filename) }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def excluded?(filename)
|
172
|
+
@config.global_exclude.any? do |path|
|
173
|
+
File.fnmatch?(path, filename)
|
163
174
|
end
|
164
175
|
end
|
165
176
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
module Linters
|
5
|
+
# When `<%` isn't followed by a newline, ensure `%>` isn't preceeded by a newline.
|
6
|
+
# When `%>` is preceeded by a newline, indent it at the same level as the corresponding `<%`.
|
7
|
+
class ClosingErbTagIndent < Linter
|
8
|
+
include LinterRegistry
|
9
|
+
|
10
|
+
START_SPACES = /\A([[:space:]]*)/m
|
11
|
+
END_SPACES = /([[:space:]]*)\z/m
|
12
|
+
|
13
|
+
def offenses(processed_source)
|
14
|
+
processed_source.ast.descendants(:erb).each_with_object([]) do |erb_node, offenses|
|
15
|
+
_, _, code_node, = *erb_node
|
16
|
+
code = code_node.children.first
|
17
|
+
|
18
|
+
start_spaces = code.match(START_SPACES)&.captures&.first || ""
|
19
|
+
end_spaces = code.match(END_SPACES)&.captures&.first || ""
|
20
|
+
|
21
|
+
start_with_newline = start_spaces.include?("\n")
|
22
|
+
end_with_newline = end_spaces.include?("\n")
|
23
|
+
|
24
|
+
if !start_with_newline && end_with_newline
|
25
|
+
offenses << Offense.new(
|
26
|
+
self,
|
27
|
+
processed_source.to_source_range(code_node.loc.stop - end_spaces.size + 1, code_node.loc.stop),
|
28
|
+
"Remove newline before `%>` to match start of tag.",
|
29
|
+
' '
|
30
|
+
)
|
31
|
+
elsif start_with_newline && !end_with_newline
|
32
|
+
offenses << Offense.new(
|
33
|
+
self,
|
34
|
+
processed_source.to_source_range(code_node.loc.stop, code_node.loc.stop),
|
35
|
+
"Insert newline before `%>` to match start of tag.",
|
36
|
+
"\n"
|
37
|
+
)
|
38
|
+
elsif start_with_newline && end_with_newline
|
39
|
+
current_indent = end_spaces.split("\n", -1).last
|
40
|
+
if erb_node.loc.column != current_indent.size
|
41
|
+
offenses << Offense.new(
|
42
|
+
self,
|
43
|
+
processed_source.to_source_range(code_node.loc.stop - current_indent.size + 1, code_node.loc.stop),
|
44
|
+
"Indent `%>` on column #{erb_node.loc.column} to match start of tag.",
|
45
|
+
' ' * erb_node.loc.column
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def autocorrect(_processed_source, offense)
|
53
|
+
lambda do |corrector|
|
54
|
+
corrector.replace(offense.source_range, offense.context)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
module Linters
|
5
|
+
# Detects multiple blank lines
|
6
|
+
class ExtraNewline < Linter
|
7
|
+
include LinterRegistry
|
8
|
+
|
9
|
+
EXTRA_NEWLINES = /(\n{3,})/m
|
10
|
+
|
11
|
+
def offenses(processed_source)
|
12
|
+
matches = processed_source.file_content.match(EXTRA_NEWLINES)
|
13
|
+
return [] unless matches
|
14
|
+
|
15
|
+
offenses = []
|
16
|
+
matches.captures.each_index do |index|
|
17
|
+
offenses << Offense.new(
|
18
|
+
self,
|
19
|
+
processed_source.to_source_range(
|
20
|
+
matches.begin(index) + 2,
|
21
|
+
matches.end(index) - 1
|
22
|
+
),
|
23
|
+
"Extra blank line detected."
|
24
|
+
)
|
25
|
+
end
|
26
|
+
offenses
|
27
|
+
end
|
28
|
+
|
29
|
+
def autocorrect(_processed_source, offense)
|
30
|
+
lambda do |corrector|
|
31
|
+
corrector.replace(offense.source_range, '')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -23,19 +23,30 @@ module ERBLint
|
|
23
23
|
return offenses if file_content.empty?
|
24
24
|
|
25
25
|
match = file_content.match(/(\n+)\z/)
|
26
|
-
|
26
|
+
final_newline = match&.captures&.first || ""
|
27
27
|
|
28
|
-
if @new_lines_should_be_present &&
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
if @new_lines_should_be_present && final_newline.size != 1
|
29
|
+
if final_newline.empty?
|
30
|
+
offenses << Offense.new(
|
31
|
+
self,
|
32
|
+
processed_source.to_source_range(file_content.size, file_content.size - 1),
|
33
|
+
'Missing a trailing newline at the end of the file.',
|
34
|
+
:insert
|
35
|
+
)
|
36
|
+
else
|
37
|
+
offenses << Offense.new(
|
38
|
+
self,
|
39
|
+
processed_source.to_source_range(file_content.size - final_newline.size + 1, file_content.size - 1),
|
40
|
+
'Remove multiple trailing newline at the end of the file.',
|
41
|
+
:remove
|
42
|
+
)
|
43
|
+
end
|
44
|
+
elsif !@new_lines_should_be_present && !final_newline.empty?
|
35
45
|
offenses << Offense.new(
|
36
46
|
self,
|
37
47
|
processed_source.to_source_range(match.begin(0), match.end(0) - 1),
|
38
|
-
"Remove #{
|
48
|
+
"Remove #{final_newline.size} trailing newline at the end of the file.",
|
49
|
+
:remove
|
39
50
|
)
|
40
51
|
end
|
41
52
|
offenses
|
@@ -43,7 +54,7 @@ module ERBLint
|
|
43
54
|
|
44
55
|
def autocorrect(_processed_source, offense)
|
45
56
|
lambda do |corrector|
|
46
|
-
if
|
57
|
+
if offense.context == :insert
|
47
58
|
corrector.insert_after(offense.source_range, "\n")
|
48
59
|
else
|
49
60
|
corrector.remove_trailing(offense.source_range, offense.source_range.size)
|
@@ -67,14 +67,14 @@ module ERBLint
|
|
67
67
|
|
68
68
|
def inspect_content(processed_source, erb_node)
|
69
69
|
indicator, _, code_node, = *erb_node
|
70
|
-
return if indicator == '#'
|
70
|
+
return if indicator&.children&.first == '#'
|
71
71
|
|
72
72
|
original_source = code_node.loc.source
|
73
73
|
trimmed_source = original_source.sub(BLOCK_EXPR, '').sub(SUFFIX_EXPR, '')
|
74
74
|
alignment_column = code_node.loc.column
|
75
75
|
aligned_source = "#{' ' * alignment_column}#{trimmed_source}"
|
76
76
|
|
77
|
-
source = rubocop_processed_source(aligned_source)
|
77
|
+
source = rubocop_processed_source(aligned_source, processed_source.filename)
|
78
78
|
return unless source.valid_syntax?
|
79
79
|
|
80
80
|
team = build_team
|
@@ -112,11 +112,11 @@ module ERBLint
|
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
115
|
-
def rubocop_processed_source(content)
|
115
|
+
def rubocop_processed_source(content, filename)
|
116
116
|
RuboCop::ProcessedSource.new(
|
117
117
|
content,
|
118
118
|
@rubocop_config.target_ruby_version,
|
119
|
-
|
119
|
+
filename
|
120
120
|
)
|
121
121
|
end
|
122
122
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
module Linters
|
5
|
+
# Warns when a tag is not self-closed properly.
|
6
|
+
class SelfClosingTag < Linter
|
7
|
+
include LinterRegistry
|
8
|
+
|
9
|
+
SELF_CLOSING_TAGS = %w(
|
10
|
+
area base br col command embed hr input keygen
|
11
|
+
link menuitem meta param source track wbr img
|
12
|
+
)
|
13
|
+
|
14
|
+
def offenses(processed_source)
|
15
|
+
processed_source.ast.descendants(:tag).each_with_object([]) do |tag_node, offenses|
|
16
|
+
tag = BetterHtml::Tree::Tag.from_node(tag_node)
|
17
|
+
next unless SELF_CLOSING_TAGS.include?(tag.name)
|
18
|
+
|
19
|
+
if tag.closing?
|
20
|
+
start_solidus = tag_node.children.first
|
21
|
+
offenses << Offense.new(
|
22
|
+
self,
|
23
|
+
processed_source.to_source_range(start_solidus.loc.start, start_solidus.loc.stop),
|
24
|
+
"Tag `#{tag.name}` is self-closing, it must not start with `</`.",
|
25
|
+
''
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
next if tag.self_closing?
|
30
|
+
offenses << Offense.new(
|
31
|
+
self,
|
32
|
+
processed_source.to_source_range(tag_node.loc.stop, tag_node.loc.stop - 1),
|
33
|
+
"Tag `#{tag.name}` is self-closing, it must end with `/>`.",
|
34
|
+
'/'
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def autocorrect(_processed_source, offense)
|
40
|
+
lambda do |corrector|
|
41
|
+
corrector.replace(offense.source_range, offense.context)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -23,25 +23,46 @@ module ERBLint
|
|
23
23
|
self,
|
24
24
|
processed_source.to_source_range(code_node.loc.start, code_node.loc.start + start_spaces.size - 1),
|
25
25
|
"Use 1 space after `<%#{indicator&.loc&.source}#{ltrim&.loc&.source}` "\
|
26
|
-
"instead of #{start_spaces.size} space#{'s' if start_spaces.size > 1}."
|
26
|
+
"instead of #{start_spaces.size} space#{'s' if start_spaces.size > 1}.",
|
27
|
+
' '
|
28
|
+
)
|
29
|
+
elsif start_spaces.count("\n") > 1
|
30
|
+
lines = start_spaces.split("\n", -1)
|
31
|
+
offenses << Offense.new(
|
32
|
+
self,
|
33
|
+
processed_source.to_source_range(code_node.loc.start, code_node.loc.start + start_spaces.size - 1),
|
34
|
+
"Use 1 newline after `<%#{indicator&.loc&.source}#{ltrim&.loc&.source}` "\
|
35
|
+
"instead of #{start_spaces.count("\n")}.",
|
36
|
+
"#{lines.first}\n#{lines.last}"
|
27
37
|
)
|
28
38
|
end
|
29
39
|
|
30
40
|
end_spaces = code.match(END_SPACES)&.captures&.first || ""
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
41
|
+
if end_spaces.size != 1 && !end_spaces.include?("\n")
|
42
|
+
offenses << Offense.new(
|
43
|
+
self,
|
44
|
+
processed_source.to_source_range(code_node.loc.stop - end_spaces.size + 1, code_node.loc.stop),
|
45
|
+
"Use 1 space before `#{rtrim&.loc&.source}%>` "\
|
46
|
+
"instead of #{end_spaces.size} space#{'s' if start_spaces.size > 1}.",
|
47
|
+
' '
|
48
|
+
)
|
49
|
+
elsif end_spaces.count("\n") > 1
|
50
|
+
lines = end_spaces.split("\n", -1)
|
51
|
+
offenses << Offense.new(
|
52
|
+
self,
|
53
|
+
processed_source.to_source_range(code_node.loc.stop - end_spaces.size + 1, code_node.loc.stop),
|
54
|
+
"Use 1 newline before `#{rtrim&.loc&.source}%>` "\
|
55
|
+
"instead of #{end_spaces.count("\n")}.",
|
56
|
+
"#{lines.first}\n#{lines.last}"
|
57
|
+
)
|
58
|
+
end
|
38
59
|
end
|
39
60
|
end
|
40
61
|
end
|
41
62
|
|
42
63
|
def autocorrect(_processed_source, offense)
|
43
64
|
lambda do |corrector|
|
44
|
-
corrector.replace(offense.source_range,
|
65
|
+
corrector.replace(offense.source_range, offense.context)
|
45
66
|
end
|
46
67
|
end
|
47
68
|
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
module Linters
|
5
|
+
# Detects extra or missing whitespace in html tags.
|
6
|
+
class SpaceInHtmlTag < Linter
|
7
|
+
include LinterRegistry
|
8
|
+
|
9
|
+
def offenses(processed_source)
|
10
|
+
offenses = []
|
11
|
+
processed_source.ast.descendants(:tag).each do |tag_node|
|
12
|
+
start_solidus, name, attributes, end_solidus = *tag_node
|
13
|
+
|
14
|
+
next_loc = name&.loc&.start || attributes&.loc&.start ||
|
15
|
+
end_solidus&.loc&.start || tag_node.loc.stop
|
16
|
+
if start_solidus
|
17
|
+
offenses << no_space(processed_source, tag_node.loc.start + 1, start_solidus.loc.start)
|
18
|
+
offenses << no_space(processed_source, start_solidus.loc.stop + 1, next_loc)
|
19
|
+
else
|
20
|
+
offenses << no_space(processed_source, tag_node.loc.start + 1, next_loc)
|
21
|
+
end
|
22
|
+
|
23
|
+
if attributes
|
24
|
+
offenses << single_space_or_newline(processed_source, name.loc.stop + 1, attributes.loc.start) if name
|
25
|
+
offenses.concat(process_attributes(processed_source, attributes) || [])
|
26
|
+
end
|
27
|
+
|
28
|
+
previous_loc = attributes&.loc&.stop || name&.loc&.stop ||
|
29
|
+
start_solidus&.loc&.stop || tag_node.loc.start
|
30
|
+
if end_solidus
|
31
|
+
offenses << single_space(processed_source, previous_loc + 1, end_solidus.loc.start)
|
32
|
+
offenses << no_space(processed_source, end_solidus.loc.stop + 1, tag_node.loc.stop)
|
33
|
+
else
|
34
|
+
offenses << no_space(processed_source, previous_loc + 1, tag_node.loc.stop)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
offenses.compact
|
38
|
+
end
|
39
|
+
|
40
|
+
def autocorrect(_processed_source, offense)
|
41
|
+
lambda do |corrector|
|
42
|
+
corrector.replace(offense.source_range, offense.context)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def no_space(processed_source, begin_pos, end_pos)
|
49
|
+
range = Range.new(begin_pos, end_pos - 1)
|
50
|
+
chars = processed_source.file_content[range]
|
51
|
+
return if chars.empty?
|
52
|
+
|
53
|
+
Offense.new(
|
54
|
+
self,
|
55
|
+
processed_source.to_source_range(begin_pos, end_pos - 1),
|
56
|
+
"Extra space detected where there should be no space.",
|
57
|
+
''
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def single_space_or_newline(processed_source, begin_pos, end_pos)
|
62
|
+
single_space(processed_source, begin_pos, end_pos, accept_newline: true)
|
63
|
+
end
|
64
|
+
|
65
|
+
def single_space(processed_source, begin_pos, end_pos, accept_newline: false)
|
66
|
+
range = Range.new(begin_pos, end_pos - 1)
|
67
|
+
chars = processed_source.file_content[range]
|
68
|
+
return if chars == ' '
|
69
|
+
|
70
|
+
newlines = chars.include?("\n")
|
71
|
+
expected = newlines && accept_newline ? "\n#{chars.split("\n", -1).last}" : ' '
|
72
|
+
non_space = chars.match(/([^[[:space:]]])/m)
|
73
|
+
|
74
|
+
if non_space && !non_space.captures.empty?
|
75
|
+
Offense.new(
|
76
|
+
self,
|
77
|
+
processed_source.to_source_range(begin_pos, end_pos - 1),
|
78
|
+
"Non-whitespace character(s) detected: "\
|
79
|
+
"#{non_space.captures.map(&:inspect).join(', ')}.",
|
80
|
+
expected
|
81
|
+
)
|
82
|
+
elsif newlines && accept_newline
|
83
|
+
if expected != chars
|
84
|
+
Offense.new(
|
85
|
+
self,
|
86
|
+
processed_source.to_source_range(begin_pos, end_pos - 1),
|
87
|
+
"#{chars.empty? ? 'No' : 'Extra'} space detected where there should be "\
|
88
|
+
"a single space or a single line break.",
|
89
|
+
expected
|
90
|
+
)
|
91
|
+
end
|
92
|
+
else
|
93
|
+
Offense.new(
|
94
|
+
self,
|
95
|
+
processed_source.to_source_range(begin_pos, end_pos - 1),
|
96
|
+
"#{chars.empty? ? 'No' : 'Extra'} space detected where there should be a single space.",
|
97
|
+
expected
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def process_attributes(processed_source, attributes)
|
103
|
+
offenses = []
|
104
|
+
attributes.children.each_with_index do |attribute, index|
|
105
|
+
name, equal, value = *attribute
|
106
|
+
offenses << no_space(processed_source, name.loc.stop + 1, equal.loc.start) if equal
|
107
|
+
offenses << no_space(processed_source, equal.loc.stop + 1, value.loc.start) if equal && value
|
108
|
+
|
109
|
+
next if index >= attributes.children.size - 1
|
110
|
+
next_attribute = attributes.children[index + 1]
|
111
|
+
|
112
|
+
offenses << single_space_or_newline(processed_source,
|
113
|
+
attribute.loc.stop + 1, next_attribute.loc.start)
|
114
|
+
end
|
115
|
+
offenses
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
module Linters
|
5
|
+
# Detects indentation with tabs and autocorrect them to spaces
|
6
|
+
class SpaceIndentation < Linter
|
7
|
+
include LinterRegistry
|
8
|
+
|
9
|
+
class ConfigSchema < LinterConfig
|
10
|
+
property :tab_width, converts: :to_i, accepts: Integer, default: 2
|
11
|
+
end
|
12
|
+
self.config_schema = ConfigSchema
|
13
|
+
|
14
|
+
START_SPACES = /\A([[:blank:]]*)/
|
15
|
+
|
16
|
+
def offenses(processed_source)
|
17
|
+
lines = processed_source.file_content.split("\n", -1)
|
18
|
+
document_pos = 0
|
19
|
+
lines.each_with_object([]) do |line, offenses|
|
20
|
+
spaces = line.match(START_SPACES)&.captures&.first
|
21
|
+
|
22
|
+
if spaces.include?("\t")
|
23
|
+
offenses << Offense.new(
|
24
|
+
self,
|
25
|
+
processed_source.to_source_range(document_pos, document_pos + spaces.length - 1),
|
26
|
+
"Indent with spaces instead of tabs.",
|
27
|
+
spaces.gsub("\t", ' ' * @config.tab_width)
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
document_pos += line.length + 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def autocorrect(_processed_source, offense)
|
36
|
+
lambda do |corrector|
|
37
|
+
corrector.replace(offense.source_range, offense.context)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ERBLint
|
4
|
+
module Linters
|
5
|
+
# Detects trailing whitespace at the end of a line
|
6
|
+
class TrailingWhitespace < Linter
|
7
|
+
include LinterRegistry
|
8
|
+
|
9
|
+
TRAILING_WHITESPACE = /([[:space:]]*)\Z/
|
10
|
+
|
11
|
+
def offenses(processed_source)
|
12
|
+
lines = processed_source.file_content.split("\n", -1)
|
13
|
+
document_pos = 0
|
14
|
+
lines.each_with_object([]) do |line, offenses|
|
15
|
+
document_pos += line.length + 1
|
16
|
+
whitespace = line.match(TRAILING_WHITESPACE)&.captures&.first
|
17
|
+
next unless whitespace && !whitespace.empty?
|
18
|
+
|
19
|
+
offenses << Offense.new(
|
20
|
+
self,
|
21
|
+
processed_source.to_source_range(document_pos - whitespace.length - 1, document_pos - 2),
|
22
|
+
"Extra whitespace detected at end of line."
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def autocorrect(_processed_source, offense)
|
28
|
+
lambda do |corrector|
|
29
|
+
corrector.replace(offense.source_range, '')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -22,7 +22,11 @@ module ERBLint
|
|
22
22
|
end
|
23
23
|
linter_klass = LinterRegistry.find_by_name(klass_name)
|
24
24
|
raise Error, "#{klass_name}: linter not found (is it loaded?)" unless linter_klass
|
25
|
-
linter_klass.config_schema.new(
|
25
|
+
linter_klass.config_schema.new(config_hash_for_linter(klass_name))
|
26
|
+
end
|
27
|
+
|
28
|
+
def global_exclude
|
29
|
+
@config['exclude'] || []
|
26
30
|
end
|
27
31
|
|
28
32
|
def merge(other_config)
|
@@ -38,12 +42,18 @@ module ERBLint
|
|
38
42
|
def default
|
39
43
|
new(
|
40
44
|
linters: {
|
45
|
+
AllowedScriptType: { enabled: true },
|
46
|
+
ClosingErbTagIndent: { enabled: true },
|
47
|
+
ExtraNewline: { enabled: true },
|
41
48
|
FinalNewline: { enabled: true },
|
49
|
+
NoJavascriptTagHelper: { enabled: true },
|
42
50
|
ParserErrors: { enabled: true },
|
43
51
|
RightTrim: { enabled: true },
|
52
|
+
SelfClosingTag: { enabled: true },
|
44
53
|
SpaceAroundErbTag: { enabled: true },
|
45
|
-
|
46
|
-
|
54
|
+
SpaceIndentation: { enabled: true },
|
55
|
+
SpaceInHtmlTag: { enabled: true },
|
56
|
+
TrailingWhitespace: { enabled: true },
|
47
57
|
},
|
48
58
|
)
|
49
59
|
end
|
@@ -54,5 +64,12 @@ module ERBLint
|
|
54
64
|
def linters_config
|
55
65
|
@config['linters'] || {}
|
56
66
|
end
|
67
|
+
|
68
|
+
def config_hash_for_linter(klass_name)
|
69
|
+
config_hash = linters_config[klass_name] || {}
|
70
|
+
config_hash['exclude'] ||= []
|
71
|
+
config_hash['exclude'].concat(global_exclude) if config_hash['exclude'].is_a?(Array)
|
72
|
+
config_hash
|
73
|
+
end
|
57
74
|
end
|
58
75
|
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.20
|
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-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: better_html
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.0.
|
19
|
+
version: 1.0.5
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.0.
|
26
|
+
version: 1.0.5
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: html_tokenizer
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -139,8 +139,10 @@ files:
|
|
139
139
|
- lib/erb_lint/linter_config.rb
|
140
140
|
- lib/erb_lint/linter_registry.rb
|
141
141
|
- lib/erb_lint/linters/allowed_script_type.rb
|
142
|
+
- lib/erb_lint/linters/closing_erb_tag_indent.rb
|
142
143
|
- lib/erb_lint/linters/deprecated_classes.rb
|
143
144
|
- lib/erb_lint/linters/erb_safety.rb
|
145
|
+
- lib/erb_lint/linters/extra_newline.rb
|
144
146
|
- lib/erb_lint/linters/final_newline.rb
|
145
147
|
- lib/erb_lint/linters/hard_coded_string.rb
|
146
148
|
- lib/erb_lint/linters/no_javascript_tag_helper.rb
|
@@ -148,7 +150,11 @@ files:
|
|
148
150
|
- lib/erb_lint/linters/right_trim.rb
|
149
151
|
- lib/erb_lint/linters/rubocop.rb
|
150
152
|
- lib/erb_lint/linters/rubocop_text.rb
|
153
|
+
- lib/erb_lint/linters/self_closing_tag.rb
|
151
154
|
- lib/erb_lint/linters/space_around_erb_tag.rb
|
155
|
+
- lib/erb_lint/linters/space_in_html_tag.rb
|
156
|
+
- lib/erb_lint/linters/space_indentation.rb
|
157
|
+
- lib/erb_lint/linters/trailing_whitespace.rb
|
152
158
|
- lib/erb_lint/offense.rb
|
153
159
|
- lib/erb_lint/processed_source.rb
|
154
160
|
- lib/erb_lint/runner.rb
|