erb_lint 0.0.19 → 0.0.20
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 +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
|