erb_lint 0.1.3 → 0.9.0

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/exe/erb_lint +10 -0
  3. data/exe/erblint +5 -4
  4. data/lib/erb_lint/all.rb +2 -0
  5. data/lib/erb_lint/cache.rb +88 -0
  6. data/lib/erb_lint/cached_offense.rb +58 -0
  7. data/lib/erb_lint/cli.rb +117 -22
  8. data/lib/erb_lint/corrector.rb +6 -17
  9. data/lib/erb_lint/file_loader.rb +2 -8
  10. data/lib/erb_lint/linter.rb +28 -2
  11. data/lib/erb_lint/linter_registry.rb +11 -1
  12. data/lib/erb_lint/linters/allowed_script_type.rb +8 -7
  13. data/lib/erb_lint/linters/closing_erb_tag_indent.rb +3 -3
  14. data/lib/erb_lint/linters/comment_syntax.rb +52 -0
  15. data/lib/erb_lint/linters/deprecated_classes.rb +2 -2
  16. data/lib/erb_lint/linters/erb_safety.rb +1 -1
  17. data/lib/erb_lint/linters/extra_newline.rb +1 -1
  18. data/lib/erb_lint/linters/final_newline.rb +4 -4
  19. data/lib/erb_lint/linters/hard_coded_string.rb +7 -4
  20. data/lib/erb_lint/linters/no_javascript_tag_helper.rb +6 -4
  21. data/lib/erb_lint/linters/no_unused_disable.rb +47 -0
  22. data/lib/erb_lint/linters/parser_errors.rb +1 -1
  23. data/lib/erb_lint/linters/partial_instance_variable.rb +2 -2
  24. data/lib/erb_lint/linters/require_input_autocomplete.rb +4 -4
  25. data/lib/erb_lint/linters/require_script_nonce.rb +2 -2
  26. data/lib/erb_lint/linters/right_trim.rb +1 -1
  27. data/lib/erb_lint/linters/rubocop.rb +32 -83
  28. data/lib/erb_lint/linters/rubocop_text.rb +2 -1
  29. data/lib/erb_lint/linters/self_closing_tag.rb +22 -5
  30. data/lib/erb_lint/linters/space_around_erb_tag.rb +8 -8
  31. data/lib/erb_lint/linters/space_in_html_tag.rb +7 -7
  32. data/lib/erb_lint/linters/space_indentation.rb +1 -1
  33. data/lib/erb_lint/linters/strict_locals.rb +50 -0
  34. data/lib/erb_lint/linters/trailing_whitespace.rb +1 -1
  35. data/lib/erb_lint/offense.rb +30 -3
  36. data/lib/erb_lint/processed_source.rb +1 -1
  37. data/lib/erb_lint/reporter.rb +3 -2
  38. data/lib/erb_lint/reporters/compact_reporter.rb +3 -2
  39. data/lib/erb_lint/reporters/gitlab_reporter.rb +55 -0
  40. data/lib/erb_lint/reporters/json_reporter.rb +4 -4
  41. data/lib/erb_lint/reporters/junit_reporter.rb +63 -0
  42. data/lib/erb_lint/reporters/multiline_reporter.rb +6 -1
  43. data/lib/erb_lint/runner.rb +36 -3
  44. data/lib/erb_lint/runner_config.rb +2 -1
  45. data/lib/erb_lint/runner_config_resolver.rb +1 -1
  46. data/lib/erb_lint/utils/block_map.rb +4 -4
  47. data/lib/erb_lint/utils/inline_configs.rb +15 -0
  48. data/lib/erb_lint/utils/severity_levels.rb +8 -2
  49. data/lib/erb_lint/version.rb +1 -1
  50. metadata +21 -28
  51. data/lib/erb_lint/utils/offset_corrector.rb +0 -72
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84ee5b7d05405468648ea7ea174c2f8f57ec1e56490873e1e89e1e9f618df585
4
- data.tar.gz: 3f7db38955673a6218517c8a031c97a9aed5a03fe8a1a9ed11c631cc8aa920f7
3
+ metadata.gz: 0eabdfa71b2841fc684663a79233451981e21fcffb175c3ae7a1e0bd8afdc8e4
4
+ data.tar.gz: c609b033d3354e27c938ef1972b1ac16c654b1900115bb4f6d6595c384d1cfb5
5
5
  SHA512:
6
- metadata.gz: e466a9178358d9400fa7a38cda19d1263efecaa1729391b1eea5c9dae2cf928686f97c5d84a0df85ba6774465f415ef94ec628cda8cc38834f2983fe525f41e7
7
- data.tar.gz: 1f7cc9b5a8994d9378f70085d69e0012baa78c0da7896b366dfd32f894f05b4c3978c831eab31f6b209c9c4a42bfe36dcf0bb413a872a91a2bb22bfc79effc16
6
+ metadata.gz: f3686f39426c32119e8a725b8320143b31c48f98969bb6f0135827c5ff7c260d5674cfad5190c2bc75111fd5a76d881e47d7aa18b6b48f3f903f698719601623
7
+ data.tar.gz: 727473a64473dad0adb3912bc7f7b7e5c84d51c60374f72e93f380d378635848a240e8f1f3f16ec8f88874290e5e16821e8445b882c09cec4c78081db9eb2bda
data/exe/erb_lint ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ lib_path = File.expand_path("#{__dir__}/../lib")
5
+ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
6
+
7
+ require "erb_lint/cli"
8
+
9
+ cli = ERBLint::CLI.new
10
+ exit(cli.run)
data/exe/erblint CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- $LOAD_PATH.unshift("#{__dir__}/../lib")
4
+ lib_path = File.expand_path("#{__dir__}/../lib")
5
+ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
5
6
 
6
- require "erb_lint/cli"
7
+ require "rainbow"
7
8
 
8
- cli = ERBLint::CLI.new
9
- exit(cli.run)
9
+ warn(Rainbow("Calling `erblint` is deprecated, please call the renamed executable `erb_lint` instead.").yellow)
10
+ exec(File.join(__dir__, "erb_lint"), *ARGV)
data/lib/erb_lint/all.rb CHANGED
@@ -3,6 +3,8 @@
3
3
  require "rubocop"
4
4
 
5
5
  require "erb_lint"
6
+ require "erb_lint/cache"
7
+ require "erb_lint/cached_offense"
6
8
  require "erb_lint/corrector"
7
9
  require "erb_lint/file_loader"
8
10
  require "erb_lint/linter_config"
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ERBLint
4
+ class Cache
5
+ CACHE_DIRECTORY = ".erb_lint_cache"
6
+
7
+ def initialize(config, cache_dir = nil)
8
+ @config = config
9
+ @cache_dir = cache_dir || CACHE_DIRECTORY
10
+ @hits = []
11
+ @new_results = []
12
+ puts "Cache mode is on"
13
+ end
14
+
15
+ def get(filename, file_content)
16
+ file_checksum = checksum(filename, file_content)
17
+ begin
18
+ cache_file_contents_as_offenses = JSON.parse(
19
+ File.read(File.join(@cache_dir, file_checksum)),
20
+ ).map do |offense_hash|
21
+ ERBLint::CachedOffense.new(offense_hash)
22
+ end
23
+ rescue Errno::ENOENT
24
+ return false
25
+ end
26
+ @hits.push(file_checksum)
27
+ cache_file_contents_as_offenses
28
+ end
29
+
30
+ def set(filename, file_content, offenses_as_json)
31
+ file_checksum = checksum(filename, file_content)
32
+ @new_results.push(file_checksum)
33
+
34
+ FileUtils.mkdir_p(@cache_dir)
35
+
36
+ File.open(File.join(@cache_dir, file_checksum), "wb") do |f|
37
+ f.write(offenses_as_json)
38
+ end
39
+ end
40
+
41
+ def close
42
+ prune_cache
43
+ end
44
+
45
+ def prune_cache
46
+ if hits.empty?
47
+ puts "Cache being created for the first time, skipping prune"
48
+ return
49
+ end
50
+
51
+ cache_files = Dir.new(@cache_dir).children
52
+ cache_files.each do |cache_file|
53
+ next if hits.include?(cache_file) || new_results.include?(cache_file)
54
+
55
+ File.delete(File.join(@cache_dir, cache_file))
56
+ end
57
+ end
58
+
59
+ def cache_dir_exists?
60
+ File.directory?(@cache_dir)
61
+ end
62
+
63
+ def clear
64
+ return unless cache_dir_exists?
65
+
66
+ puts "Clearing cache by deleting cache directory"
67
+ FileUtils.rm_r(@cache_dir)
68
+ end
69
+
70
+ private
71
+
72
+ attr_reader :config, :hits, :new_results
73
+
74
+ def checksum(filename, file_content)
75
+ digester = Digest::SHA1.new
76
+ mode = File.stat(filename).mode
77
+
78
+ digester.update(
79
+ "#{mode}#{config.to_hash}#{ERBLint::VERSION}#{file_content}",
80
+ )
81
+ digester.hexdigest
82
+ rescue Errno::ENOENT
83
+ # Spurious files that come and go should not cause a crash, at least not
84
+ # here.
85
+ "_"
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ERBLint
4
+ # A Cached version of an Offense with only essential information represented as strings
5
+ class CachedOffense
6
+ attr_reader(
7
+ :message,
8
+ :line_number,
9
+ :severity,
10
+ :column,
11
+ :simple_name,
12
+ :last_line,
13
+ :last_column,
14
+ :length,
15
+ )
16
+
17
+ def initialize(params)
18
+ params = params.transform_keys(&:to_sym)
19
+
20
+ @message = params[:message]
21
+ @line_number = params[:line_number]
22
+ @severity = params[:severity]&.to_sym
23
+ @column = params[:column]
24
+ @simple_name = params[:simple_name]
25
+ @last_line = params[:last_line]
26
+ @last_column = params[:last_column]
27
+ @length = params[:length]
28
+ end
29
+
30
+ def self.new_from_offense(offense)
31
+ new(
32
+ {
33
+ message: offense.message,
34
+ line_number: offense.line_number,
35
+ severity: offense.severity,
36
+ column: offense.column,
37
+ simple_name: offense.simple_name,
38
+ last_line: offense.last_line,
39
+ last_column: offense.last_column,
40
+ length: offense.length,
41
+ },
42
+ )
43
+ end
44
+
45
+ def to_h
46
+ {
47
+ message: message,
48
+ line_number: line_number,
49
+ severity: severity,
50
+ column: column,
51
+ simple_name: simple_name,
52
+ last_line: last_line,
53
+ last_column: last_column,
54
+ length: length,
55
+ }
56
+ end
57
+ end
58
+ end
data/lib/erb_lint/cli.rb CHANGED
@@ -13,7 +13,8 @@ module ERBLint
13
13
  class CLI
14
14
  include Utils::SeverityLevels
15
15
 
16
- DEFAULT_CONFIG_FILENAME = ".erb-lint.yml"
16
+ DEPRECATED_CONFIG_FILENAME = ".erb-lint.yml"
17
+ DEFAULT_CONFIG_FILENAME = ".erb_lint.yml"
17
18
  DEFAULT_LINT_ALL_GLOB = "**/*.html{+*,}.erb"
18
19
 
19
20
  class ExitWithFailure < RuntimeError; end
@@ -30,12 +31,33 @@ module ERBLint
30
31
  def run(args = ARGV)
31
32
  dupped_args = args.dup
32
33
  load_options(dupped_args)
34
+
35
+ if cache? && autocorrect?
36
+ failure!("cannot run autocorrect mode with cache")
37
+ end
38
+
33
39
  @files = @options[:stdin] || dupped_args
34
40
 
35
41
  load_config
36
42
 
43
+ cache_dir = @options[:cache_dir]
44
+ @cache = Cache.new(@config, cache_dir) if cache? || clear_cache?
45
+
46
+ if clear_cache?
47
+ if cache.cache_dir_exists?
48
+ cache.clear
49
+ success!("cache directory cleared")
50
+ else
51
+ failure!("cache directory doesn't exist, skipping deletion.")
52
+ end
53
+ end
54
+
37
55
  if !@files.empty? && lint_files.empty?
38
- failure!("no files found...\n")
56
+ if allow_no_files?
57
+ success!("no files found...\n")
58
+ else
59
+ failure!("no files found...\n")
60
+ end
39
61
  elsif lint_files.empty?
40
62
  failure!("no files found or given, specify files or config...\n#{option_parser}")
41
63
  end
@@ -48,24 +70,25 @@ module ERBLint
48
70
 
49
71
  @options[:format] ||= :multiline
50
72
  @options[:fail_level] ||= severity_level_for_name(:refactor)
73
+ @options[:disable_inline_configs] ||= false
51
74
  @stats.files = lint_files.size
52
75
  @stats.linters = enabled_linter_classes.size
53
76
  @stats.autocorrectable_linters = enabled_linter_classes.count(&:support_autocorrect?)
54
77
 
55
- reporter = Reporter.create_reporter(@options[:format], @stats, autocorrect?)
78
+ reporter = Reporter.create_reporter(@options[:format], @stats, autocorrect?, @options[:show_linter_names])
56
79
  reporter.preview
57
80
 
58
- runner = ERBLint::Runner.new(file_loader, @config)
81
+ runner = ERBLint::Runner.new(file_loader, @config, @options[:disable_inline_configs])
59
82
  file_content = nil
60
83
 
61
84
  lint_files.each do |filename|
62
85
  runner.clear_offenses
63
86
  begin
64
- file_content = run_with_corrections(runner, filename)
87
+ file_content = run_on_file(runner, filename)
65
88
  rescue => e
66
89
  @stats.exceptions += 1
67
90
  puts "Exception occurred when processing: #{relative_filename(filename)}"
68
- puts "If this file cannot be processed by erb-lint, "\
91
+ puts "If this file cannot be processed by erb_lint, " \
69
92
  "you can exclude it in your configuration file."
70
93
  puts e.message
71
94
  puts Rainbow(e.backtrace.join("\n")).red
@@ -73,6 +96,8 @@ module ERBLint
73
96
  end
74
97
  end
75
98
 
99
+ cache&.close
100
+
76
101
  reporter.show
77
102
 
78
103
  if stdin? && autocorrect?
@@ -95,19 +120,49 @@ module ERBLint
95
120
 
96
121
  private
97
122
 
123
+ attr_reader :cache, :config
124
+
125
+ def run_on_file(runner, filename)
126
+ file_content = read_content(filename)
127
+
128
+ if cache? && !autocorrect?
129
+ run_using_cache(runner, filename, file_content)
130
+ else
131
+ file_content = run_with_corrections(runner, filename, file_content)
132
+ end
133
+
134
+ log_offense_stats(runner, filename)
135
+ file_content
136
+ end
137
+
138
+ def run_using_cache(runner, filename, file_content)
139
+ if (cache_result_offenses = cache.get(filename, file_content))
140
+ runner.restore_offenses(cache_result_offenses)
141
+ else
142
+ run_with_corrections(runner, filename, file_content)
143
+ cache.set(filename, file_content, runner.offenses.map(&:to_cached_offense_hash).to_json)
144
+ end
145
+ end
146
+
98
147
  def autocorrect?
99
148
  @options[:autocorrect]
100
149
  end
101
150
 
102
- def run_with_corrections(runner, filename)
103
- file_content = read_content(filename)
151
+ def cache?
152
+ @options[:cache]
153
+ end
104
154
 
155
+ def clear_cache?
156
+ @options[:clear_cache]
157
+ end
158
+
159
+ def run_with_corrections(runner, filename, file_content)
105
160
  7.times do
106
161
  processed_source = ERBLint::ProcessedSource.new(filename, file_content)
107
162
  runner.run(processed_source)
108
163
  break unless autocorrect? && runner.offenses.any?
109
164
 
110
- corrector = correct(processed_source, runner.offenses)
165
+ corrector = corrector(processed_source, runner.offenses)
111
166
  break if corrector.corrections.empty?
112
167
  break if processed_source.file_content == corrector.corrected_content
113
168
 
@@ -123,6 +178,11 @@ module ERBLint
123
178
  file_content = corrector.corrected_content
124
179
  runner.clear_offenses
125
180
  end
181
+
182
+ file_content
183
+ end
184
+
185
+ def log_offense_stats(runner, filename)
126
186
  offenses_filename = relative_filename(filename)
127
187
  offenses = runner.offenses || []
128
188
 
@@ -134,8 +194,6 @@ module ERBLint
134
194
 
135
195
  @stats.processed_files[offenses_filename] ||= []
136
196
  @stats.processed_files[offenses_filename] |= offenses
137
-
138
- file_content
139
197
  end
140
198
 
141
199
  def read_content(filename)
@@ -144,10 +202,8 @@ module ERBLint
144
202
  $stdin.binmode.read.force_encoding(Encoding::UTF_8)
145
203
  end
146
204
 
147
- def correct(processed_source, offenses)
148
- corrector = ERBLint::Corrector.new(processed_source, offenses)
149
- failure!(corrector.diagnostics.join(", ")) if corrector.diagnostics.any?
150
- corrector
205
+ def corrector(processed_source, offenses)
206
+ ERBLint::Corrector.new(processed_source, offenses)
151
207
  end
152
208
 
153
209
  def config_filename
@@ -158,6 +214,13 @@ module ERBLint
158
214
  if File.exist?(config_filename)
159
215
  config = RunnerConfig.new(file_loader.yaml(config_filename), file_loader)
160
216
  @config = RunnerConfig.default_for(config)
217
+ elsif File.exist?(DEPRECATED_CONFIG_FILENAME)
218
+ deprecation_message = "The config file has been renamed to `#{DEFAULT_CONFIG_FILENAME}` and " \
219
+ "`#{DEPRECATED_CONFIG_FILENAME}` is deprecated. " \
220
+ "Please rename your config file to `#{DEFAULT_CONFIG_FILENAME}`."
221
+ warn(Rainbow(deprecation_message).yellow)
222
+ config = RunnerConfig.new(file_loader.yaml(DEPRECATED_CONFIG_FILENAME), file_loader)
223
+ @config = RunnerConfig.default_for(config)
161
224
  else
162
225
  warn(Rainbow("#{config_filename} not found: using default config").yellow)
163
226
  @config = RunnerConfig.default
@@ -165,7 +228,7 @@ module ERBLint
165
228
  rescue Psych::SyntaxError => e
166
229
  failure!("error parsing config: #{e.message}")
167
230
  ensure
168
- @config.merge!(runner_config_override)
231
+ @config&.merge!(runner_config_override)
169
232
  end
170
233
 
171
234
  def file_loader
@@ -246,7 +309,7 @@ module ERBLint
246
309
  ERBLint::LinterRegistry.linters.map do |klass|
247
310
  linters[klass.simple_name] = { "enabled" => enabled_linter_classes.include?(klass) }
248
311
  end
249
- end
312
+ end,
250
313
  )
251
314
  end
252
315
 
@@ -262,7 +325,7 @@ module ERBLint
262
325
  end
263
326
  end
264
327
 
265
- opts.on("--format FORMAT", format_options_help) do |format|
328
+ opts.on("-f", "--format FORMAT", format_options_help) do |format|
266
329
  unless Reporter.available_format?(format)
267
330
  error_message = invalid_format_error_message(format)
268
331
  failure!(error_message)
@@ -279,8 +342,24 @@ module ERBLint
279
342
  @options[:enabled_linters] = known_linter_names
280
343
  end
281
344
 
282
- opts.on("--enable-linters LINTER[,LINTER,...]", Array,
283
- "Only use specified linter", "Known linters are: #{known_linter_names.join(", ")}") do |linters|
345
+ opts.on("--cache", "Enable caching") do |config|
346
+ @options[:cache] = config
347
+ end
348
+
349
+ opts.on("--cache-dir DIR", "Set the cache directory") do |dir|
350
+ @options[:cache_dir] = dir
351
+ end
352
+
353
+ opts.on("--clear-cache", "Clear cache") do |config|
354
+ @options[:clear_cache] = config
355
+ end
356
+
357
+ opts.on(
358
+ "--enable-linters LINTER[,LINTER,...]",
359
+ Array,
360
+ "Only use specified linter",
361
+ "Known linters are: #{known_linter_names.join(", ")}",
362
+ ) do |linters|
284
363
  linters.each do |linter|
285
364
  unless known_linter_names.include?(linter)
286
365
  failure!("#{linter}: not a valid linter name (#{known_linter_names.join(", ")})")
@@ -302,10 +381,22 @@ module ERBLint
302
381
  @options[:autocorrect] = config
303
382
  end
304
383
 
384
+ opts.on("--show-linter-names", "Show linter names") do
385
+ @options[:show_linter_names] = true
386
+ end
387
+
388
+ opts.on("--allow-no-files", "When no matching files found, exit successfully (default: false)") do |config|
389
+ @options[:allow_no_files] = config
390
+ end
391
+
392
+ opts.on("--disable-inline-configs", "Report all offenses while ignoring inline disable comments") do
393
+ @options[:disable_inline_configs] = true
394
+ end
395
+
305
396
  opts.on(
306
397
  "-sFILE",
307
398
  "--stdin FILE",
308
- "Pipe source from STDIN. Takes the path to be used to check which rules to apply."
399
+ "Pipe source from STDIN. Takes the path to be used to check which rules to apply.",
309
400
  ) do |file|
310
401
  @options[:stdin] = [file]
311
402
  end
@@ -321,7 +412,7 @@ module ERBLint
321
412
  end
322
413
 
323
414
  def format_options_help
324
- "Report offenses in the given format: "\
415
+ "Report offenses in the given format: " \
325
416
  "(#{Reporter.available_formats.join(", ")}) (default: multiline)"
326
417
  end
327
418
 
@@ -333,5 +424,9 @@ module ERBLint
333
424
  def stdin?
334
425
  @options[:stdin].present?
335
426
  end
427
+
428
+ def allow_no_files?
429
+ @options[:allow_no_files]
430
+ end
336
431
  end
337
432
  end
@@ -7,31 +7,20 @@ module ERBLint
7
7
  def initialize(processed_source, offenses)
8
8
  @processed_source = processed_source
9
9
  @offenses = offenses
10
+ corrector = RuboCop::Cop::Corrector.new(@processed_source.source_buffer)
11
+ correct!(corrector)
10
12
  @corrected_content = corrector.rewrite
11
13
  end
12
14
 
13
15
  def corrections
14
16
  @corrections ||= @offenses.map do |offense|
15
- offense.linter.autocorrect(@processed_source, offense)
17
+ offense.linter.autocorrect(@processed_source, offense) if offense.linter.class.support_autocorrect?
16
18
  end.compact
17
19
  end
18
20
 
19
- def corrector
20
- BASE.new(@processed_source.source_buffer, corrections)
21
- end
22
-
23
- if ::RuboCop::Version::STRING.to_f >= 0.87
24
- require "rubocop/cop/legacy/corrector"
25
- BASE = ::RuboCop::Cop::Legacy::Corrector
26
-
27
- def diagnostics
28
- []
29
- end
30
- else
31
- BASE = ::RuboCop::Cop::Corrector
32
-
33
- def diagnostics
34
- corrector.diagnostics
21
+ def correct!(corrector)
22
+ corrections.each do |correction|
23
+ correction.call(corrector)
35
24
  end
36
25
  end
37
26
  end
@@ -9,14 +9,8 @@ module ERBLint
9
9
  @base_path = base_path
10
10
  end
11
11
 
12
- if RUBY_VERSION >= "2.6"
13
- def yaml(filename)
14
- YAML.safe_load(read_content(filename), permitted_classes: [Regexp, Symbol], filename: filename) || {}
15
- end
16
- else
17
- def yaml(filename)
18
- YAML.safe_load(read_content(filename), [Regexp, Symbol], [], false, filename) || {}
19
- end
12
+ def yaml(filename)
13
+ YAML.safe_load(read_content(filename), permitted_classes: [Regexp, Symbol], filename: filename) || {}
20
14
  end
21
15
 
22
16
  private
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "erb_lint/utils/inline_configs"
4
+
3
5
  module ERBLint
4
6
  # Defines common functionality available to all linters.
5
7
  class Linter
@@ -30,13 +32,13 @@ module ERBLint
30
32
  end
31
33
  end
32
34
 
33
- attr_reader :offenses
35
+ attr_reader :offenses, :config
34
36
 
35
37
  # Must be implemented by the concrete inheriting class.
36
38
  def initialize(file_loader, config)
37
39
  @file_loader = file_loader
38
40
  @config = config
39
- raise ArgumentError, "expect `config` to be #{self.class.config_schema} instance, "\
41
+ raise ArgumentError, "expect `config` to be #{self.class.config_schema} instance, " \
40
42
  "not #{config.class}" unless config.is_a?(self.class.config_schema)
41
43
  @offenses = []
42
44
  end
@@ -53,6 +55,13 @@ module ERBLint
53
55
  raise NotImplementedError, "must implement ##{__method__}"
54
56
  end
55
57
 
58
+ def run_and_update_offense_status(processed_source, enable_inline_configs = true)
59
+ run(processed_source)
60
+ if @offenses.any? && enable_inline_configs
61
+ update_offense_status(processed_source)
62
+ end
63
+ end
64
+
56
65
  def add_offense(source_range, message, context = nil, severity = nil)
57
66
  @offenses << Offense.new(self, source_range, message, context, severity)
58
67
  end
@@ -60,5 +69,22 @@ module ERBLint
60
69
  def clear_offenses
61
70
  @offenses = []
62
71
  end
72
+
73
+ private
74
+
75
+ def update_offense_status(processed_source)
76
+ @offenses.each do |offense|
77
+ offense_line_range = offense.source_range.line_range
78
+ offense_lines = source_for_line_range(processed_source, offense_line_range)
79
+
80
+ if Utils::InlineConfigs.rule_disable_comment_for_lines?(self.class.simple_name, offense_lines)
81
+ offense.disabled = true
82
+ end
83
+ end
84
+ end
85
+
86
+ def source_for_line_range(processed_source, line_range)
87
+ processed_source.source_buffer.source_lines[line_range.first - 1..line_range.last - 1].join
88
+ end
63
89
  end
64
90
  end
@@ -3,7 +3,8 @@
3
3
  module ERBLint
4
4
  # Stores all linters available to the application.
5
5
  module LinterRegistry
6
- CUSTOM_LINTERS_DIR = ".erb-linters"
6
+ DEPRECATED_CUSTOM_LINTERS_DIR = ".erb-linters"
7
+ CUSTOM_LINTERS_DIR = ".erb_linters"
7
8
  @loaded_linters = []
8
9
 
9
10
  class << self
@@ -28,6 +29,15 @@ module ERBLint
28
29
 
29
30
  def load_custom_linters(directory = CUSTOM_LINTERS_DIR)
30
31
  ruby_files = Dir.glob(File.expand_path(File.join(directory, "**", "*.rb")))
32
+
33
+ deprecated_ruby_files = Dir.glob(File.expand_path(File.join(DEPRECATED_CUSTOM_LINTERS_DIR, "**", "*.rb")))
34
+ if deprecated_ruby_files.any?
35
+ deprecation_message = "The '#{DEPRECATED_CUSTOM_LINTERS_DIR}' directory for custom linters is deprecated. " \
36
+ "Please rename it to '#{CUSTOM_LINTERS_DIR}'"
37
+ warn(Rainbow(deprecation_message).yellow)
38
+ ruby_files.concat(deprecated_ruby_files)
39
+ end
40
+
31
41
  ruby_files.each { |file| require file }
32
42
  end
33
43
  end
@@ -12,7 +12,8 @@ module ERBLint
12
12
  include LinterRegistry
13
13
 
14
14
  class ConfigSchema < LinterConfig
15
- property :allowed_types, accepts: array_of?(String),
15
+ property :allowed_types,
16
+ accepts: array_of?(String),
16
17
  default: -> { ["text/javascript"] }
17
18
  property :allow_blank, accepts: [true, false], default: true, reader: :allow_blank?
18
19
  property :disallow_inline_scripts, accepts: [true, false], default: false, reader: :disallow_inline_scripts?
@@ -30,8 +31,8 @@ module ERBLint
30
31
  name_node = tag_node.to_a[1]
31
32
  add_offense(
32
33
  name_node.loc,
33
- "Avoid using inline `<script>` tags altogether. "\
34
- "Instead, move javascript code into a static file."
34
+ "Avoid using inline `<script>` tags altogether. " \
35
+ "Instead, move javascript code into a static file.",
35
36
  )
36
37
  next
37
38
  end
@@ -44,14 +45,14 @@ module ERBLint
44
45
  add_offense(
45
46
  name_node.loc,
46
47
  "Missing a `type=\"text/javascript\"` attribute to `<script>` tag.",
47
- [type_attribute]
48
+ [type_attribute],
48
49
  )
49
50
  elsif type_present && !@config.allowed_types.include?(type_attribute.value)
50
51
  add_offense(
51
52
  type_attribute.loc,
52
- "Avoid using #{type_attribute.value.inspect} as type for `<script>` tag. "\
53
- "Must be one of: #{@config.allowed_types.join(", ")}"\
54
- "#{" (or no type attribute)" if @config.allow_blank?}."
53
+ "Avoid using #{type_attribute.value.inspect} as type for `<script>` tag. " \
54
+ "Must be one of: #{@config.allowed_types.join(", ")}" \
55
+ "#{" (or no type attribute)" if @config.allow_blank?}.",
55
56
  )
56
57
  end
57
58
  end
@@ -25,13 +25,13 @@ module ERBLint
25
25
  add_offense(
26
26
  code_node.loc.end.adjust(begin_pos: -end_spaces.size),
27
27
  "Remove newline before `%>` to match start of tag.",
28
- " "
28
+ " ",
29
29
  )
30
30
  elsif start_with_newline && !end_with_newline
31
31
  add_offense(
32
32
  code_node.loc.end.adjust(begin_pos: -end_spaces.size),
33
33
  "Insert newline before `%>` to match start of tag.",
34
- "\n"
34
+ "\n",
35
35
  )
36
36
  elsif start_with_newline && end_with_newline
37
37
  current_indent = end_spaces.split("\n", -1).last
@@ -39,7 +39,7 @@ module ERBLint
39
39
  add_offense(
40
40
  code_node.loc.end.adjust(begin_pos: -current_indent.size),
41
41
  "Indent `%>` on column #{erb_node.loc.column} to match start of tag.",
42
- " " * erb_node.loc.column
42
+ " " * erb_node.loc.column,
43
43
  )
44
44
  end
45
45
  end