erb_lint 0.0.35 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/exe/erblint +1 -1
  3. data/lib/erb_lint.rb +1 -17
  4. data/lib/erb_lint/all.rb +26 -0
  5. data/lib/erb_lint/cli.rb +101 -54
  6. data/lib/erb_lint/corrector.rb +1 -1
  7. data/lib/erb_lint/linter.rb +6 -5
  8. data/lib/erb_lint/linter_config.rb +3 -3
  9. data/lib/erb_lint/linter_registry.rb +14 -5
  10. data/lib/erb_lint/linters/allowed_script_type.rb +7 -7
  11. data/lib/erb_lint/linters/closing_erb_tag_indent.rb +2 -2
  12. data/lib/erb_lint/linters/deprecated_classes.rb +7 -7
  13. data/lib/erb_lint/linters/erb_safety.rb +2 -2
  14. data/lib/erb_lint/linters/extra_newline.rb +1 -1
  15. data/lib/erb_lint/linters/final_newline.rb +2 -2
  16. data/lib/erb_lint/linters/hard_coded_string.rb +36 -16
  17. data/lib/erb_lint/linters/no_javascript_tag_helper.rb +8 -8
  18. data/lib/erb_lint/linters/partial_instance_variable.rb +23 -0
  19. data/lib/erb_lint/linters/require_input_autocomplete.rb +121 -0
  20. data/lib/erb_lint/linters/require_script_nonce.rb +92 -0
  21. data/lib/erb_lint/linters/right_trim.rb +1 -1
  22. data/lib/erb_lint/linters/rubocop.rb +11 -11
  23. data/lib/erb_lint/linters/rubocop_text.rb +1 -1
  24. data/lib/erb_lint/linters/self_closing_tag.rb +5 -7
  25. data/lib/erb_lint/linters/space_around_erb_tag.rb +5 -5
  26. data/lib/erb_lint/linters/space_in_html_tag.rb +6 -6
  27. data/lib/erb_lint/linters/space_indentation.rb +1 -1
  28. data/lib/erb_lint/linters/trailing_whitespace.rb +1 -1
  29. data/lib/erb_lint/offense.rb +15 -4
  30. data/lib/erb_lint/reporter.rb +39 -0
  31. data/lib/erb_lint/reporters/compact_reporter.rb +66 -0
  32. data/lib/erb_lint/reporters/json_reporter.rb +72 -0
  33. data/lib/erb_lint/reporters/multiline_reporter.rb +22 -0
  34. data/lib/erb_lint/runner.rb +1 -2
  35. data/lib/erb_lint/runner_config.rb +8 -7
  36. data/lib/erb_lint/runner_config_resolver.rb +4 -4
  37. data/lib/erb_lint/stats.rb +30 -0
  38. data/lib/erb_lint/utils/block_map.rb +2 -2
  39. data/lib/erb_lint/utils/offset_corrector.rb +1 -1
  40. data/lib/erb_lint/utils/ruby_to_erb.rb +5 -5
  41. data/lib/erb_lint/utils/severity_levels.rb +16 -0
  42. data/lib/erb_lint/version.rb +1 -1
  43. metadata +17 -7
@@ -8,7 +8,7 @@ module ERBLint
8
8
  include LinterRegistry
9
9
 
10
10
  class ConfigSchema < LinterConfig
11
- property :enforced_style, accepts: ['-', '='], default: '-'
11
+ property :enforced_style, accepts: ["-", "="], default: "-"
12
12
  end
13
13
  self.config_schema = ConfigSchema
14
14
 
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'better_html'
4
- require 'tempfile'
5
- require 'erb_lint/utils/offset_corrector'
3
+ require "better_html"
4
+ require "tempfile"
5
+ require "erb_lint/utils/offset_corrector"
6
6
 
7
7
  module ERBLint
8
8
  module Linters
@@ -25,7 +25,7 @@ module ERBLint
25
25
  super
26
26
  @only_cops = @config.only
27
27
  custom_config = config_from_hash(@config.rubocop_config)
28
- @rubocop_config = ::RuboCop::ConfigLoader.merge_with_default(custom_config, '')
28
+ @rubocop_config = ::RuboCop::ConfigLoader.merge_with_default(custom_config, "")
29
29
  end
30
30
 
31
31
  def run(processed_source)
@@ -68,13 +68,13 @@ module ERBLint
68
68
 
69
69
  def inspect_content(processed_source, erb_node)
70
70
  indicator, _, code_node, = *erb_node
71
- return if indicator&.children&.first == '#'
71
+ return if indicator&.children&.first == "#"
72
72
 
73
73
  original_source = code_node.loc.source
74
- trimmed_source = original_source.sub(BLOCK_EXPR, '').sub(SUFFIX_EXPR, '')
74
+ trimmed_source = original_source.sub(BLOCK_EXPR, "").sub(SUFFIX_EXPR, "")
75
75
  alignment_column = code_node.loc.column
76
76
  offset = code_node.loc.begin_pos - alignment_column
77
- aligned_source = "#{' ' * alignment_column}#{trimmed_source}"
77
+ aligned_source = "#{" " * alignment_column}#{trimmed_source}"
78
78
 
79
79
  source = rubocop_processed_source(aligned_source, processed_source.filename)
80
80
  return unless source.valid_syntax?
@@ -156,10 +156,10 @@ module ERBLint
156
156
  end
157
157
 
158
158
  def config_from_hash(hash)
159
- inherit_from = hash&.delete('inherit_from')
159
+ inherit_from = hash&.delete("inherit_from")
160
160
  resolve_inheritance(hash, inherit_from)
161
161
 
162
- tempfile_from('.erblint-rubocop', hash.to_yaml) do |tempfile|
162
+ tempfile_from(".erblint-rubocop", hash.to_yaml) do |tempfile|
163
163
  ::RuboCop::ConfigLoader.load_file(tempfile.path)
164
164
  end
165
165
  end
@@ -174,7 +174,7 @@ module ERBLint
174
174
  end
175
175
 
176
176
  def base_configs(inherit_from)
177
- regex = URI::DEFAULT_PARSER.make_regexp(%w(http https))
177
+ regex = URI::DEFAULT_PARSER.make_regexp(["http", "https"])
178
178
  configs = Array(inherit_from).compact.map do |base_name|
179
179
  if base_name =~ /\A#{regex}\z/
180
180
  ::RuboCop::ConfigLoader.load_file(::RuboCop::RemoteConfig.new(base_name, Dir.pwd))
@@ -191,7 +191,7 @@ module ERBLint
191
191
  { rubocop_correction: correction, offset: offset, bound_range: bound_range }
192
192
  end
193
193
 
194
- super(offense_range, rubocop_offense.message.strip, context)
194
+ super(offense_range, rubocop_offense.message.strip, context, rubocop_offense.severity.name)
195
195
  end
196
196
  end
197
197
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'rubocop'
3
+ require_relative "rubocop"
4
4
 
5
5
  module ERBLint
6
6
  module Linters
@@ -11,10 +11,8 @@ module ERBLint
11
11
  end
12
12
  self.config_schema = ConfigSchema
13
13
 
14
- SELF_CLOSING_TAGS = %w(
15
- area base br col command embed hr input keygen
16
- link menuitem meta param source track wbr img
17
- )
14
+ SELF_CLOSING_TAGS = ["area", "base", "br", "col", "command", "embed", "hr", "input", "keygen", "link",
15
+ "menuitem", "meta", "param", "source", "track", "wbr", "img",]
18
16
 
19
17
  def run(processed_source)
20
18
  processed_source.ast.descendants(:tag).each do |tag_node|
@@ -26,7 +24,7 @@ module ERBLint
26
24
  add_offense(
27
25
  start_solidus.loc,
28
26
  "Tag `#{tag.name}` is a void element, it must not start with `</`.",
29
- ''
27
+ ""
30
28
  )
31
29
  end
32
30
 
@@ -34,7 +32,7 @@ module ERBLint
34
32
  add_offense(
35
33
  tag_node.loc.end.offset(-1),
36
34
  "Tag `#{tag.name}` is self-closing, it must end with `/>`.",
37
- '/'
35
+ "/"
38
36
  )
39
37
  end
40
38
 
@@ -43,7 +41,7 @@ module ERBLint
43
41
  add_offense(
44
42
  end_solidus.loc,
45
43
  "Tag `#{tag.name}` is a void element, it must end with `>` and not `/>`.",
46
- ''
44
+ ""
47
45
  )
48
46
  end
49
47
  end
@@ -15,7 +15,7 @@ module ERBLint
15
15
  processed_source.ast.descendants(:erb).each do |erb_node|
16
16
  indicator_node, ltrim, code_node, rtrim = *erb_node
17
17
  indicator = indicator_node&.loc&.source
18
- next if indicator == '#' || indicator == '%'
18
+ next if indicator == "#" || indicator == "%"
19
19
  code = code_node.children.first
20
20
 
21
21
  start_spaces = code.match(START_SPACES)&.captures&.first || ""
@@ -23,8 +23,8 @@ module ERBLint
23
23
  add_offense(
24
24
  code_node.loc.resize(start_spaces.size),
25
25
  "Use 1 space after `<%#{indicator}#{ltrim&.loc&.source}` "\
26
- "instead of #{start_spaces.size} space#{'s' if start_spaces.size > 1}.",
27
- ' '
26
+ "instead of #{start_spaces.size} space#{"s" if start_spaces.size > 1}.",
27
+ " "
28
28
  )
29
29
  elsif start_spaces.count("\n") > 1
30
30
  lines = start_spaces.split("\n", -1)
@@ -41,8 +41,8 @@ module ERBLint
41
41
  add_offense(
42
42
  code_node.loc.end.adjust(begin_pos: -end_spaces.size),
43
43
  "Use 1 space before `#{rtrim&.loc&.source}%>` "\
44
- "instead of #{end_spaces.size} space#{'s' if start_spaces.size > 1}.",
45
- ' '
44
+ "instead of #{end_spaces.size} space#{"s" if start_spaces.size > 1}.",
45
+ " "
46
46
  )
47
47
  elsif end_spaces.count("\n") > 1
48
48
  lines = end_spaces.split("\n", -1)
@@ -50,7 +50,7 @@ module ERBLint
50
50
  add_offense(
51
51
  processed_source.to_source_range(range),
52
52
  "Extra space detected where there should be no space.",
53
- ''
53
+ ""
54
54
  )
55
55
  end
56
56
 
@@ -60,24 +60,24 @@ module ERBLint
60
60
 
61
61
  def single_space(processed_source, range, accept_newline: false)
62
62
  chars = processed_source.file_content[range]
63
- return if chars == ' '
63
+ return if chars == " "
64
64
 
65
65
  newlines = chars.include?("\n")
66
- expected = newlines && accept_newline ? "\n#{chars.split("\n", -1).last}" : ' '
66
+ expected = newlines && accept_newline ? "\n#{chars.split("\n", -1).last}" : " "
67
67
  non_space = chars.match(/([^[[:space:]]])/m)
68
68
 
69
69
  if non_space && !non_space.captures.empty?
70
70
  add_offense(
71
71
  processed_source.to_source_range(range),
72
72
  "Non-whitespace character(s) detected: "\
73
- "#{non_space.captures.map(&:inspect).join(', ')}.",
73
+ "#{non_space.captures.map(&:inspect).join(", ")}.",
74
74
  expected
75
75
  )
76
76
  elsif newlines && accept_newline
77
77
  if expected != chars
78
78
  add_offense(
79
79
  processed_source.to_source_range(range),
80
- "#{chars.empty? ? 'No' : 'Extra'} space detected where there should be "\
80
+ "#{chars.empty? ? "No" : "Extra"} space detected where there should be "\
81
81
  "a single space or a single line break.",
82
82
  expected
83
83
  )
@@ -85,7 +85,7 @@ module ERBLint
85
85
  else
86
86
  add_offense(
87
87
  processed_source.to_source_range(range),
88
- "#{chars.empty? ? 'No' : 'Extra'} space detected where there should be a single space.",
88
+ "#{chars.empty? ? "No" : "Extra"} space detected where there should be a single space.",
89
89
  expected
90
90
  )
91
91
  end
@@ -23,7 +23,7 @@ module ERBLint
23
23
  add_offense(
24
24
  processed_source.to_source_range(document_pos...(document_pos + spaces.length)),
25
25
  "Indent with spaces instead of tabs.",
26
- spaces.gsub("\t", ' ' * @config.tab_width)
26
+ spaces.gsub("\t", " " * @config.tab_width)
27
27
  )
28
28
  end
29
29
 
@@ -25,7 +25,7 @@ module ERBLint
25
25
 
26
26
  def autocorrect(_processed_source, offense)
27
27
  lambda do |corrector|
28
- corrector.replace(offense.source_range, '')
28
+ corrector.replace(offense.source_range, "")
29
29
  end
30
30
  end
31
31
  end
@@ -3,9 +3,9 @@
3
3
  module ERBLint
4
4
  # Defines common functionality available to all linters.
5
5
  class Offense
6
- attr_reader :linter, :source_range, :message, :context
6
+ attr_reader :linter, :source_range, :message, :context, :severity
7
7
 
8
- def initialize(linter, source_range, message, context = nil)
8
+ def initialize(linter, source_range, message, context = nil, severity = nil)
9
9
  unless source_range.is_a?(Parser::Source::Range)
10
10
  raise ArgumentError, "expected Parser::Source::Range for arg 2"
11
11
  end
@@ -13,23 +13,34 @@ module ERBLint
13
13
  @source_range = source_range
14
14
  @message = message
15
15
  @context = context
16
+ @severity = severity
16
17
  end
17
18
 
18
19
  def inspect
19
20
  "#<#{self.class.name} linter=#{linter.class.name} "\
20
21
  "source_range=#{source_range.begin_pos}...#{source_range.end_pos} "\
21
- "message=#{message}>"
22
+ "message=#{message}> "\
23
+ "severity=#{severity}"
22
24
  end
23
25
 
24
26
  def ==(other)
25
27
  other.class <= ERBLint::Offense &&
26
28
  other.linter == linter &&
27
29
  other.source_range == source_range &&
28
- other.message == message
30
+ other.message == message &&
31
+ other.severity == severity
29
32
  end
30
33
 
31
34
  def line_range
32
35
  Range.new(source_range.line, source_range.last_line)
33
36
  end
37
+
38
+ def line_number
39
+ line_range.begin
40
+ end
41
+
42
+ def column
43
+ source_range.column
44
+ end
34
45
  end
35
46
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ require "active_support/core_ext/class"
3
+ require "active_support/core_ext/module/delegation"
4
+
5
+ module ERBLint
6
+ class Reporter
7
+ def self.create_reporter(format, *args)
8
+ reporter_klass = "#{ERBLint::Reporters}::#{format.to_s.camelize}Reporter".constantize
9
+ reporter_klass.new(*args)
10
+ end
11
+
12
+ def self.available_format?(format)
13
+ available_formats.include?(format.to_s)
14
+ end
15
+
16
+ def self.available_formats
17
+ descendants
18
+ .map(&:to_s)
19
+ .map(&:demodulize)
20
+ .map(&:underscore)
21
+ .map { |klass_name| klass_name.sub("_reporter", "") }
22
+ .sort
23
+ end
24
+
25
+ def initialize(stats, autocorrect)
26
+ @stats = stats
27
+ @autocorrect = autocorrect
28
+ end
29
+
30
+ def preview; end
31
+
32
+ def show; end
33
+
34
+ private
35
+
36
+ attr_reader :stats, :autocorrect
37
+ delegate :processed_files, to: :stats
38
+ end
39
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ERBLint
4
+ module Reporters
5
+ class CompactReporter < Reporter
6
+ def preview
7
+ puts "Linting #{stats.files} files with "\
8
+ "#{stats.linters} #{"autocorrectable " if autocorrect}linters..."
9
+ end
10
+
11
+ def show
12
+ processed_files.each do |filename, offenses|
13
+ offenses.each do |offense|
14
+ puts format_offense(filename, offense)
15
+ end
16
+ end
17
+
18
+ footer
19
+ summary
20
+ end
21
+
22
+ private
23
+
24
+ def format_offense(filename, offense)
25
+ [
26
+ "#{filename}:",
27
+ "#{offense.line_number}:",
28
+ "#{offense.column}: ",
29
+ offense.message.to_s,
30
+ ].join
31
+ end
32
+
33
+ def footer; end
34
+
35
+ def summary
36
+ if stats.corrected > 0
37
+ report_corrected_offenses
38
+ elsif stats.ignored > 0 || stats.found > 0
39
+ if stats.ignored > 0
40
+ warn(Rainbow("#{stats.ignored} error(s) were ignored in ERB files").yellow)
41
+ end
42
+
43
+ if stats.found > 0
44
+ warn(Rainbow("#{stats.found} error(s) were found in ERB files").red)
45
+ end
46
+ else
47
+ puts Rainbow("No errors were found in ERB files").green
48
+ end
49
+ end
50
+
51
+ def report_corrected_offenses
52
+ corrected_found_diff = stats.found - stats.corrected
53
+
54
+ if corrected_found_diff > 0
55
+ message = Rainbow(
56
+ "#{stats.corrected} error(s) corrected and #{corrected_found_diff} error(s) remaining in ERB files"
57
+ ).red
58
+
59
+ warn(message)
60
+ else
61
+ puts Rainbow("#{stats.corrected} error(s) corrected in ERB files").green
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module ERBLint
6
+ module Reporters
7
+ class JsonReporter < Reporter
8
+ def preview; end
9
+
10
+ def show
11
+ puts formatted_data
12
+ end
13
+
14
+ private
15
+
16
+ def formatted_data
17
+ {
18
+ metadata: metadata,
19
+ files: formatted_files,
20
+ summary: summary,
21
+ }.to_json
22
+ end
23
+
24
+ def metadata
25
+ {
26
+ erb_lint_version: ERBLint::VERSION,
27
+ ruby_engine: RUBY_ENGINE,
28
+ ruby_version: RUBY_VERSION,
29
+ ruby_patchlevel: RUBY_PATCHLEVEL.to_s,
30
+ ruby_platform: RUBY_PLATFORM,
31
+ }
32
+ end
33
+
34
+ def summary
35
+ {
36
+ offenses: stats.found,
37
+ inspected_files: stats.processed_files.size,
38
+ corrected: stats.corrected,
39
+ }
40
+ end
41
+
42
+ def formatted_files
43
+ processed_files.map do |filename, offenses|
44
+ {
45
+ path: filename,
46
+ offenses: formatted_offenses(offenses),
47
+ }
48
+ end
49
+ end
50
+
51
+ def formatted_offenses(offenses)
52
+ offenses.map do |offense|
53
+ format_offense(offense)
54
+ end
55
+ end
56
+
57
+ def format_offense(offense)
58
+ {
59
+ linter: offense.linter.class.simple_name,
60
+ message: offense.message.to_s,
61
+ location: {
62
+ start_line: offense.line_number,
63
+ start_column: offense.column,
64
+ last_line: offense.source_range.last_line,
65
+ last_column: offense.source_range.last_column,
66
+ length: offense.source_range.length,
67
+ },
68
+ }
69
+ end
70
+ end
71
+ end
72
+ end