erb_lint 0.0.37 → 0.1.1

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/exe/erblint +1 -1
  3. data/lib/erb_lint/all.rb +26 -0
  4. data/lib/erb_lint/cli.rb +73 -24
  5. data/lib/erb_lint/corrector.rb +1 -1
  6. data/lib/erb_lint/linter.rb +5 -5
  7. data/lib/erb_lint/linter_config.rb +3 -3
  8. data/lib/erb_lint/linter_registry.rb +2 -2
  9. data/lib/erb_lint/linters/allowed_script_type.rb +7 -7
  10. data/lib/erb_lint/linters/closing_erb_tag_indent.rb +2 -2
  11. data/lib/erb_lint/linters/deprecated_classes.rb +7 -7
  12. data/lib/erb_lint/linters/erb_safety.rb +2 -2
  13. data/lib/erb_lint/linters/extra_newline.rb +1 -1
  14. data/lib/erb_lint/linters/final_newline.rb +2 -2
  15. data/lib/erb_lint/linters/hard_coded_string.rb +36 -16
  16. data/lib/erb_lint/linters/no_javascript_tag_helper.rb +8 -8
  17. data/lib/erb_lint/linters/partial_instance_variable.rb +23 -0
  18. data/lib/erb_lint/linters/require_input_autocomplete.rb +121 -0
  19. data/lib/erb_lint/linters/require_script_nonce.rb +92 -0
  20. data/lib/erb_lint/linters/right_trim.rb +1 -1
  21. data/lib/erb_lint/linters/rubocop.rb +11 -11
  22. data/lib/erb_lint/linters/rubocop_text.rb +1 -1
  23. data/lib/erb_lint/linters/self_closing_tag.rb +5 -7
  24. data/lib/erb_lint/linters/space_around_erb_tag.rb +5 -5
  25. data/lib/erb_lint/linters/space_in_html_tag.rb +6 -6
  26. data/lib/erb_lint/linters/space_indentation.rb +1 -1
  27. data/lib/erb_lint/linters/trailing_whitespace.rb +1 -1
  28. data/lib/erb_lint/offense.rb +7 -4
  29. data/lib/erb_lint/reporter.rb +2 -2
  30. data/lib/erb_lint/reporters/compact_reporter.rb +9 -3
  31. data/lib/erb_lint/reporters/json_reporter.rb +72 -0
  32. data/lib/erb_lint/reporters/multiline_reporter.rb +1 -1
  33. data/lib/erb_lint/runner.rb +1 -1
  34. data/lib/erb_lint/runner_config.rb +8 -7
  35. data/lib/erb_lint/runner_config_resolver.rb +4 -4
  36. data/lib/erb_lint/stats.rb +9 -6
  37. data/lib/erb_lint/utils/block_map.rb +2 -2
  38. data/lib/erb_lint/utils/offset_corrector.rb +1 -1
  39. data/lib/erb_lint/utils/ruby_to_erb.rb +5 -5
  40. data/lib/erb_lint/utils/severity_levels.rb +16 -0
  41. data/lib/erb_lint/version.rb +1 -1
  42. data/lib/erb_lint.rb +1 -24
  43. metadata +9 -3
@@ -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,19 +13,22 @@ 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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require 'active_support/core_ext/class'
3
- require 'active_support/core_ext/module/delegation'
2
+ require "active_support/core_ext/class"
3
+ require "active_support/core_ext/module/delegation"
4
4
 
5
5
  module ERBLint
6
6
  class Reporter
@@ -5,7 +5,7 @@ module ERBLint
5
5
  class CompactReporter < Reporter
6
6
  def preview
7
7
  puts "Linting #{stats.files} files with "\
8
- "#{stats.linters} #{'autocorrectable ' if autocorrect}linters..."
8
+ "#{stats.linters} #{"autocorrectable " if autocorrect}linters..."
9
9
  end
10
10
 
11
11
  def show
@@ -35,8 +35,14 @@ module ERBLint
35
35
  def summary
36
36
  if stats.corrected > 0
37
37
  report_corrected_offenses
38
- elsif stats.found > 0
39
- warn(Rainbow("#{stats.found} error(s) were found in ERB files").red)
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
40
46
  else
41
47
  puts Rainbow("No errors were found in ERB files").green
42
48
  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
@@ -9,7 +9,7 @@ module ERBLint
9
9
  def format_offense(filename, offense)
10
10
  <<~EOF
11
11
 
12
- #{offense.message}#{Rainbow(' (not autocorrected)').red if autocorrect}
12
+ #{offense.message}#{Rainbow(" (not autocorrected)").red if autocorrect}
13
13
  In file: #{filename}:#{offense.line_number}
14
14
  EOF
15
15
  end
@@ -8,7 +8,7 @@ module ERBLint
8
8
  def initialize(file_loader, config)
9
9
  @file_loader = file_loader
10
10
  @config = config || RunnerConfig.default
11
- raise ArgumentError, 'expect `config` to be a RunnerConfig instance' unless @config.is_a?(RunnerConfig)
11
+ raise ArgumentError, "expect `config` to be a RunnerConfig instance" unless @config.is_a?(RunnerConfig)
12
12
 
13
13
  linter_classes = LinterRegistry.linters.select { |klass| @config.for_linter(klass).enabled? }
14
14
  @linters = linter_classes.map do |linter_class|
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'erb_lint/runner_config_resolver'
3
+ require "erb_lint/runner_config_resolver"
4
4
 
5
5
  module ERBLint
6
6
  class RunnerConfig
@@ -9,7 +9,7 @@ module ERBLint
9
9
  def initialize(config = nil, file_loader = nil)
10
10
  @config = (config || {}).dup.deep_stringify_keys
11
11
 
12
- resolver.resolve_inheritance_from_gems(@config, @config.delete('inherit_gem'))
12
+ resolver.resolve_inheritance_from_gems(@config, @config.delete("inherit_gem"))
13
13
  resolver.resolve_inheritance(@config, file_loader) if file_loader
14
14
  @config.delete("inherit_from")
15
15
  end
@@ -24,7 +24,7 @@ module ERBLint
24
24
  elsif klass.is_a?(Class) && klass <= ERBLint::Linter
25
25
  klass.simple_name
26
26
  else
27
- raise ArgumentError, 'expected String or linter class'
27
+ raise ArgumentError, "expected String or linter class"
28
28
  end
29
29
  linter_klass = LinterRegistry.find_by_name(klass_name)
30
30
  raise Error, "#{klass_name}: linter not found (is it loaded?)" unless linter_klass
@@ -32,7 +32,7 @@ module ERBLint
32
32
  end
33
33
 
34
34
  def global_exclude
35
- @config['exclude'] || []
35
+ @config["exclude"] || []
36
36
  end
37
37
 
38
38
  def merge(other_config)
@@ -61,6 +61,7 @@ module ERBLint
61
61
  SpaceIndentation: { enabled: default_enabled },
62
62
  SpaceInHtmlTag: { enabled: default_enabled },
63
63
  TrailingWhitespace: { enabled: default_enabled },
64
+ RequireInputAutocomplete: { enabled: default_enabled },
64
65
  },
65
66
  )
66
67
  end
@@ -74,13 +75,13 @@ module ERBLint
74
75
  private
75
76
 
76
77
  def linters_config
77
- @config['linters'] || {}
78
+ @config["linters"] || {}
78
79
  end
79
80
 
80
81
  def config_hash_for_linter(klass_name)
81
82
  config_hash = linters_config[klass_name] || {}
82
- config_hash['exclude'] ||= []
83
- config_hash['exclude'].concat(global_exclude) if config_hash['exclude'].is_a?(Array)
83
+ config_hash["exclude"] ||= []
84
+ config_hash["exclude"].concat(global_exclude) if config_hash["exclude"].is_a?(Array)
84
85
  config_hash
85
86
  end
86
87
 
@@ -24,7 +24,7 @@
24
24
  module ERBLint
25
25
  class RunnerConfigResolver
26
26
  def resolve_inheritance(hash, file_loader)
27
- inherited_files = Array(hash['inherit_from'])
27
+ inherited_files = Array(hash["inherit_from"])
28
28
  base_configs(file_loader, inherited_files).reverse_each do |base_config|
29
29
  base_config.each do |k, v|
30
30
  next unless v.is_a?(Hash)
@@ -36,12 +36,12 @@ module ERBLint
36
36
 
37
37
  def resolve_inheritance_from_gems(hash, gems)
38
38
  (gems || {}).each_pair do |gem_name, config_path|
39
- raise(ArgumentError, "can't inherit configuration from the erb-lint gem") if gem_name == 'erb-lint'
39
+ raise(ArgumentError, "can't inherit configuration from the erb-lint gem") if gem_name == "erb-lint"
40
40
 
41
- hash['inherit_from'] = Array(hash['inherit_from'])
41
+ hash["inherit_from"] = Array(hash["inherit_from"])
42
42
  Array(config_path).reverse_each do |path|
43
43
  # Put gem configuration first so local configuration overrides it.
44
- hash['inherit_from'].unshift(gem_config_path(gem_name, path))
44
+ hash["inherit_from"].unshift(gem_config_path(gem_name, path))
45
45
  end
46
46
  end
47
47
  end
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
  module ERBLint
3
3
  class Stats
4
- attr_accessor :found,
5
- :corrected,
6
- :exceptions,
7
- :linters,
8
- :files,
9
- :processed_files
4
+ attr_accessor :ignored,
5
+ :found,
6
+ :corrected,
7
+ :exceptions,
8
+ :linters,
9
+ :files,
10
+ :processed_files
10
11
 
11
12
  def initialize(
13
+ ignored: 0,
12
14
  found: 0,
13
15
  corrected: 0,
14
16
  exceptions: 0,
@@ -16,6 +18,7 @@ module ERBLint
16
18
  files: 0,
17
19
  processed_files: {}
18
20
  )
21
+ @ignored = ignored
19
22
  @found = found
20
23
  @corrected = corrected
21
24
  @exceptions = exceptions
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'better_html/ast/node'
4
- require 'better_html/test_helper/ruby_node'
3
+ require "better_html/ast/node"
4
+ require "better_html/test_helper/ruby_node"
5
5
 
6
6
  module ERBLint
7
7
  module Utils
@@ -63,7 +63,7 @@ module ERBLint
63
63
  node_or_range
64
64
  else
65
65
  raise TypeError,
66
- 'Expected a Parser::Source::Range, Comment or ' \
66
+ "Expected a Parser::Source::Range, Comment or " \
67
67
  "Rubocop::AST::Node, got #{node_or_range.class}"
68
68
  end
69
69
  end