erb_lint 0.0.37 → 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/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