html-proofer 3.19.4 → 4.0.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/bin/htmlproofer +31 -57
  3. data/lib/html-proofer.rb +1 -54
  4. data/lib/html_proofer/attribute/url.rb +237 -0
  5. data/lib/html_proofer/attribute.rb +15 -0
  6. data/lib/html_proofer/cache.rb +279 -0
  7. data/lib/html_proofer/check/favicon.rb +40 -0
  8. data/lib/html_proofer/check/images.rb +93 -0
  9. data/lib/html_proofer/check/links.rb +129 -0
  10. data/lib/html_proofer/check/open_graph.rb +39 -0
  11. data/lib/html_proofer/check/scripts.rb +46 -0
  12. data/lib/html_proofer/check.rb +92 -0
  13. data/lib/html_proofer/configuration.rb +88 -0
  14. data/lib/html_proofer/element.rb +122 -0
  15. data/lib/html_proofer/failure.rb +17 -0
  16. data/lib/{html-proofer → html_proofer}/log.rb +19 -19
  17. data/lib/html_proofer/reporter/cli.rb +33 -0
  18. data/lib/html_proofer/reporter.rb +23 -0
  19. data/lib/html_proofer/runner.rb +246 -0
  20. data/lib/html_proofer/url_validator/external.rb +194 -0
  21. data/lib/html_proofer/url_validator/internal.rb +96 -0
  22. data/lib/html_proofer/url_validator.rb +16 -0
  23. data/lib/{html-proofer → html_proofer}/utils.rb +9 -12
  24. data/lib/{html-proofer → html_proofer}/version.rb +1 -1
  25. data/lib/html_proofer/xpath_functions.rb +10 -0
  26. data/lib/html_proofer.rb +57 -0
  27. metadata +42 -22
  28. data/lib/html-proofer/cache.rb +0 -194
  29. data/lib/html-proofer/check/favicon.rb +0 -29
  30. data/lib/html-proofer/check/html.rb +0 -37
  31. data/lib/html-proofer/check/images.rb +0 -48
  32. data/lib/html-proofer/check/links.rb +0 -182
  33. data/lib/html-proofer/check/opengraph.rb +0 -46
  34. data/lib/html-proofer/check/scripts.rb +0 -42
  35. data/lib/html-proofer/check.rb +0 -75
  36. data/lib/html-proofer/configuration.rb +0 -88
  37. data/lib/html-proofer/element.rb +0 -265
  38. data/lib/html-proofer/issue.rb +0 -65
  39. data/lib/html-proofer/middleware.rb +0 -82
  40. data/lib/html-proofer/runner.rb +0 -249
  41. data/lib/html-proofer/url_validator.rb +0 -237
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTMLProofer
4
+ # Mostly handles issue management and collecting of external URLs.
5
+ class Check
6
+ include HTMLProofer::Utils
7
+
8
+ attr_reader :failures, :options, :internal_urls, :external_urls
9
+
10
+ def initialize(runner, html)
11
+ @runner = runner
12
+ @html = remove_ignored(html)
13
+
14
+ @external_urls = {}
15
+ @internal_urls = {}
16
+ @failures = []
17
+ end
18
+
19
+ def create_element(node)
20
+ Element.new(@runner, node, base_url: base_url)
21
+ end
22
+
23
+ def run
24
+ raise NotImplementedError, "HTMLProofer::Check subclasses must implement #run"
25
+ end
26
+
27
+ def add_failure(description, line: nil, status: nil, content: nil)
28
+ @failures << Failure.new(@runner.current_filename, short_name, description, line: line, status: status,
29
+ content: content)
30
+ end
31
+
32
+ def self.subchecks(runner_options)
33
+ # grab all known checks
34
+ checks = ObjectSpace.each_object(Class).select do |klass|
35
+ klass < self
36
+ end
37
+
38
+ # remove any checks not explicitly included
39
+ checks.each_with_object([]) do |check, arr|
40
+ next unless runner_options[:checks].include?(check.short_name)
41
+
42
+ arr << check
43
+ end
44
+ end
45
+
46
+ def short_name
47
+ self.class.name.split("::").last
48
+ end
49
+
50
+ def self.short_name
51
+ name.split("::").last
52
+ end
53
+
54
+ def add_to_internal_urls(url, line)
55
+ url_string = url.raw_attribute
56
+
57
+ @internal_urls[url_string] = [] if @internal_urls[url_string].nil?
58
+
59
+ metadata = {
60
+ source: @runner.current_source,
61
+ filename: @runner.current_filename,
62
+ line: line,
63
+ base_url: base_url,
64
+ found: false,
65
+ }
66
+ @internal_urls[url_string] << metadata
67
+ end
68
+
69
+ def add_to_external_urls(url, line)
70
+ url_string = url.to_s
71
+
72
+ @external_urls[url_string] = [] if @external_urls[url_string].nil?
73
+
74
+ @external_urls[url_string] << { filename: @runner.current_filename, line: line }
75
+ end
76
+
77
+ private def base_url
78
+ return @base_url if defined?(@base_url)
79
+
80
+ return (@base_url = "") if (base = @html.at_css("base")).nil?
81
+
82
+ @base_url = base["href"]
83
+ end
84
+
85
+ private def remove_ignored(html)
86
+ return if html.nil?
87
+
88
+ html.css("code, pre, tt").each(&:unlink)
89
+ html
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTMLProofer
4
+ module Configuration
5
+ DEFAULT_TESTS = ["Links", "Images", "Scripts"].freeze
6
+
7
+ PROOFER_DEFAULTS = {
8
+ allow_hash_href: true,
9
+ allow_missing_href: false,
10
+ assume_extension: ".html",
11
+ check_external_hash: true,
12
+ checks: DEFAULT_TESTS,
13
+ directory_index_file: "index.html",
14
+ disable_external: false,
15
+ ignore_empty_alt: true,
16
+ ignore_empty_mailto: false,
17
+ ignore_files: [],
18
+ ignore_missing_alt: false,
19
+ ignore_status_codes: [],
20
+ ignore_urls: [],
21
+ enforce_https: true,
22
+ extensions: [".html"],
23
+ log_level: :info,
24
+ only_4xx: false,
25
+ swap_attributes: {},
26
+ swap_urls: {},
27
+ }.freeze
28
+
29
+ TYPHOEUS_DEFAULTS = {
30
+ followlocation: true,
31
+ headers: {
32
+ "User-Agent" => "Mozilla/5.0 (compatible; HTML Proofer/#{HTMLProofer::VERSION}; +https://github.com/gjtorikian/html-proofer)",
33
+ "Accept" => "application/xml,application/xhtml+xml,text/html;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5",
34
+ },
35
+ connecttimeout: 10,
36
+ timeout: 30,
37
+ }.freeze
38
+
39
+ HYDRA_DEFAULTS = {
40
+ max_concurrency: 50,
41
+ }.freeze
42
+
43
+ PARALLEL_DEFAULTS = {
44
+ enable: true,
45
+ }.freeze
46
+
47
+ CACHE_DEFAULTS = {}.freeze
48
+
49
+ def self.generate_defaults(opts)
50
+ options = PROOFER_DEFAULTS.merge(opts)
51
+
52
+ options[:typhoeus] = HTMLProofer::Configuration::TYPHOEUS_DEFAULTS.merge(opts[:typhoeus] || {})
53
+ options[:hydra] = HTMLProofer::Configuration::HYDRA_DEFAULTS.merge(opts[:hydra] || {})
54
+
55
+ options[:parallel] = HTMLProofer::Configuration::PARALLEL_DEFAULTS.merge(opts[:parallel] || {})
56
+ options[:cache] = HTMLProofer::Configuration::CACHE_DEFAULTS.merge(opts[:cache] || {})
57
+
58
+ options.delete(:src)
59
+
60
+ options
61
+ end
62
+
63
+ def self.to_regex?(item)
64
+ if item.start_with?("/") && item.end_with?("/")
65
+ Regexp.new(item[1...-1])
66
+ else
67
+ item
68
+ end
69
+ end
70
+
71
+ def self.parse_json_option(option_name, config, symbolize_names: true)
72
+ raise ArgumentError, "Must provide an option name in string format." unless option_name.is_a?(String)
73
+ raise ArgumentError, "Must provide an option name in string format." if option_name.strip.empty?
74
+
75
+ return {} if config.nil?
76
+
77
+ raise ArgumentError, "Must provide a JSON configuration in string format." unless config.is_a?(String)
78
+
79
+ return {} if config.strip.empty?
80
+
81
+ begin
82
+ JSON.parse(config, { symbolize_names: symbolize_names })
83
+ rescue StandardError
84
+ raise ArgumentError, "Option '#{option_name} did not contain valid JSON."
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "addressable/uri"
4
+
5
+ module HTMLProofer
6
+ # Represents the element currently being processed
7
+ class Element
8
+ include HTMLProofer::Utils
9
+
10
+ attr_reader :node, :url, :base_url, :line, :content
11
+
12
+ def initialize(runner, node, base_url: nil)
13
+ @runner = runner
14
+ @node = node
15
+
16
+ @base_url = base_url
17
+ @url = Attribute::Url.new(runner, link_attribute, base_url: base_url)
18
+
19
+ @line = node.line
20
+ @content = node.content
21
+ end
22
+
23
+ def link_attribute
24
+ meta_content || src || srcset || href
25
+ end
26
+
27
+ def meta_content
28
+ return nil unless meta_tag?
29
+ return swap_attributes("content") if attribute_swapped?
30
+
31
+ @node["content"]
32
+ end
33
+
34
+ def meta_tag?
35
+ @node.name == "meta"
36
+ end
37
+
38
+ def src
39
+ return nil if !img_tag? && !script_tag? && !source_tag?
40
+ return swap_attributes("src") if attribute_swapped?
41
+
42
+ @node["src"]
43
+ end
44
+
45
+ def img_tag?
46
+ @node.name == "img"
47
+ end
48
+
49
+ def script_tag?
50
+ @node.name == "script"
51
+ end
52
+
53
+ def srcset
54
+ return nil if !img_tag? && !source_tag?
55
+ return swap_attributes("srcset") if attribute_swapped?
56
+
57
+ @node["srcset"]
58
+ end
59
+
60
+ def source_tag?
61
+ @node.name == "source"
62
+ end
63
+
64
+ def href
65
+ return nil if !a_tag? && !link_tag?
66
+ return swap_attributes("href") if attribute_swapped?
67
+
68
+ @node["href"]
69
+ end
70
+
71
+ def a_tag?
72
+ @node.name == "a"
73
+ end
74
+
75
+ def link_tag?
76
+ @node.name == "link"
77
+ end
78
+
79
+ def aria_hidden?
80
+ @node.attributes["aria-hidden"]&.value == "true"
81
+ end
82
+
83
+ def multiple_srcsets?
84
+ !blank?(srcset) && srcset.split(",").size > 1
85
+ end
86
+
87
+ def ignore?
88
+ return true if @node.attributes["data-proofer-ignore"]
89
+ return true if ancestors_ignorable?
90
+
91
+ return true if url&.ignore?
92
+
93
+ false
94
+ end
95
+
96
+ private def attribute_swapped?
97
+ return false if blank?(@runner.options[:swap_attributes])
98
+
99
+ attrs = @runner.options[:swap_attributes][@node.name]
100
+
101
+ return true unless blank?(attrs)
102
+ end
103
+
104
+ private def swap_attributes(old_attr)
105
+ attrs = @runner.options[:swap_attributes][@node.name]
106
+
107
+ new_attr = attrs.find do |(o, _)|
108
+ o == old_attr
109
+ end&.last
110
+
111
+ return nil if blank?(new_attr)
112
+
113
+ @node[new_attr]
114
+ end
115
+
116
+ private def ancestors_ignorable?
117
+ ancestors_attributes = @node.ancestors.map { |a| a.respond_to?(:attributes) && a.attributes }
118
+ ancestors_attributes.pop # remove document at the end
119
+ ancestors_attributes.any? { |a| !a["data-proofer-ignore"].nil? }
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTMLProofer
4
+ class Failure
5
+ attr_reader :path, :check_name, :description, :status, :line, :content
6
+
7
+ def initialize(path, check_name, description, line: nil, status: nil, content: nil)
8
+ @path = path
9
+ @check_name = check_name
10
+ @description = description
11
+
12
+ @line = line
13
+ @status = status
14
+ @content = content
15
+ end
16
+ end
17
+ end
@@ -1,21 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yell'
4
- require 'rainbow'
3
+ require "yell"
4
+ require "rainbow"
5
5
 
6
6
  module HTMLProofer
7
7
  class Log
8
8
  include Yell::Loggable
9
9
 
10
- STDOUT_LEVELS = %i[debug info warn].freeze
11
- STDERR_LEVELS = %i[error fatal].freeze
10
+ STDOUT_LEVELS = [:debug, :info, :warn].freeze
11
+ STDERR_LEVELS = [:error, :fatal].freeze
12
12
 
13
13
  def initialize(log_level)
14
14
  @logger = Yell.new(format: false, \
15
- name: 'HTMLProofer', \
16
- level: "gte.#{log_level}") do |l|
17
- l.adapter :stdout, level: 'lte.warn'
18
- l.adapter :stderr, level: 'gte.error'
15
+ name: "HTMLProofer", \
16
+ level: "gte.#{log_level}") do |l|
17
+ l.adapter(:stdout, level: "lte.warn")
18
+ l.adapter(:stderr, level: "gte.error")
19
19
  end
20
20
  end
21
21
 
@@ -24,23 +24,23 @@ module HTMLProofer
24
24
  end
25
25
 
26
26
  def log_with_color(level, message)
27
- @logger.send level, colorize(level, message)
27
+ @logger.send(level, colorize(level, message))
28
28
  end
29
29
 
30
30
  def colorize(level, message)
31
31
  color = case level
32
- when :debug
33
- :cyan
34
- when :info
35
- :blue
36
- when :warn
37
- :yellow
38
- when :error, :fatal
39
- :red
40
- end
32
+ when :debug
33
+ :cyan
34
+ when :info
35
+ :blue
36
+ when :warn
37
+ :yellow
38
+ when :error, :fatal
39
+ :red
40
+ end
41
41
 
42
42
  if (STDOUT_LEVELS.include?(level) && $stdout.isatty) || \
43
- (STDERR_LEVELS.include?(level) && $stderr.isatty)
43
+ (STDERR_LEVELS.include?(level) && $stderr.isatty)
44
44
  Rainbow(message).send(color)
45
45
  else
46
46
  message
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTMLProofer
4
+ class Reporter
5
+ class Cli < HTMLProofer::Reporter
6
+ def report
7
+ msg = failures.each_with_object([]) do |(check_name, failures), arr|
8
+ str = ["For the #{check_name} check, the following failures were found:\n"]
9
+
10
+ failures.each do |failure|
11
+ path_str = blank?(failure.path) ? "" : "At #{failure.path}"
12
+
13
+ line_str = failure.line.nil? ? "" : ":#{failure.line}"
14
+
15
+ path_and_line = "#{path_str}#{line_str}"
16
+ path_and_line = blank?(path_and_line) ? "" : "* #{path_and_line}:\n\n"
17
+
18
+ status_str = failure.status.nil? ? "" : " (status code #{failure.status})"
19
+
20
+ indent = blank?(path_and_line) ? "* " : " "
21
+ str << <<~MSG
22
+ #{path_and_line}#{indent}#{failure.description}#{status_str}
23
+ MSG
24
+ end
25
+
26
+ arr << str.join("\n")
27
+ end
28
+
29
+ @logger.log(:error, msg.join("\n"))
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTMLProofer
4
+ class Reporter
5
+ include HTMLProofer::Utils
6
+
7
+ attr_reader :failures
8
+
9
+ def initialize(logger: nil)
10
+ @logger = logger
11
+ end
12
+
13
+ def failures=(failures)
14
+ @failures = failures.group_by(&:check_name) \
15
+ .transform_values { |issues| issues.sort_by { |issue| [issue.path, issue.line] } } \
16
+ .sort
17
+ end
18
+
19
+ def report
20
+ raise NotImplementedError, "HTMLProofer::Reporter subclasses must implement #report"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTMLProofer
4
+ class Runner
5
+ include HTMLProofer::Utils
6
+
7
+ attr_reader :options, :cache, :logger, :internal_urls, :external_urls, :checked_paths, :current_check
8
+ attr_accessor :current_filename, :current_source, :reporter
9
+
10
+ URL_TYPES = [:external, :internal].freeze
11
+
12
+ def initialize(src, opts = {})
13
+ @options = HTMLProofer::Configuration.generate_defaults(opts)
14
+
15
+ @type = @options.delete(:type)
16
+ @source = src
17
+
18
+ @logger = HTMLProofer::Log.new(@options[:log_level])
19
+ @cache = Cache.new(self, @options[:cache])
20
+
21
+ @external_urls = {}
22
+ @internal_urls = {}
23
+ @failures = []
24
+
25
+ @before_request = []
26
+
27
+ @checked_paths = {}
28
+
29
+ @current_check = nil
30
+ @current_source = nil
31
+ @current_filename = nil
32
+
33
+ @reporter = Reporter::Cli.new(logger: @logger)
34
+ end
35
+
36
+ def run
37
+ check_text = pluralize(checks.length, "check", "checks")
38
+
39
+ if @type == :links
40
+ @logger.log(:info, "Running #{check_text} (#{format_checks_list(checks)}) on #{@source} ... \n\n")
41
+ check_list_of_links unless @options[:disable_external]
42
+ else
43
+ @logger.log(:info,
44
+ "Running #{check_text} (#{format_checks_list(checks)}) in #{@source} on *#{@options[:extensions].join(", ")} files...\n\n")
45
+
46
+ check_files
47
+ @logger.log(:info, "Ran on #{pluralize(files.length, "file", "files")}!\n\n")
48
+ end
49
+
50
+ @cache.write
51
+
52
+ @reporter.failures = @failures
53
+
54
+ if @failures.empty?
55
+ @logger.log(:info, "HTML-Proofer finished successfully.")
56
+ else
57
+ @failures.uniq!
58
+ report_failed_checks
59
+ end
60
+ end
61
+
62
+ def check_list_of_links
63
+ @external_urls = @source.uniq.each_with_object({}) do |link, hash|
64
+ url = Attribute::Url.new(self, link, base_url: nil).to_s
65
+
66
+ hash[url] = []
67
+ end
68
+
69
+ validate_external_urls
70
+ end
71
+
72
+ # Walks over each implemented check and runs them on the files, in parallel.
73
+ # Sends the collected external URLs to Typhoeus for batch processing.
74
+ def check_files
75
+ process_files.each do |result|
76
+ URL_TYPES.each do |url_type|
77
+ type = :"#{url_type}_urls"
78
+ ivar_name = "@#{type}"
79
+ ivar = instance_variable_get(ivar_name)
80
+
81
+ if ivar.empty?
82
+ instance_variable_set(ivar_name, result[type])
83
+ else
84
+ result[type].each do |url, metadata|
85
+ ivar[url] = [] if ivar[url].nil?
86
+ ivar[url].concat(metadata)
87
+ end
88
+ end
89
+ end
90
+ @failures.concat(result[:failures])
91
+ end
92
+
93
+ validate_external_urls unless @options[:disable_external]
94
+
95
+ validate_internal_urls
96
+ end
97
+
98
+ # Walks over each implemented check and runs them on the files, in parallel.
99
+ def process_files
100
+ if @options[:parallel][:enable]
101
+ Parallel.map(files, @options[:parallel]) { |file| load_file(file[:path], file[:source]) }
102
+ else
103
+ files.map do |file|
104
+ load_file(file[:path], file[:source])
105
+ end
106
+ end
107
+ end
108
+
109
+ def load_file(path, source)
110
+ @html = create_nokogiri(path)
111
+ check_parsed(path, source)
112
+ end
113
+
114
+ # Collects any external URLs found in a directory of files. Also collectes
115
+ # every failed test from process_files.
116
+ def check_parsed(path, source)
117
+ result = { internal_urls: {}, external_urls: {}, failures: [] }
118
+
119
+ checks.each do |klass|
120
+ @current_source = source
121
+ @current_filename = path
122
+
123
+ check = Object.const_get(klass).new(self, @html)
124
+ @logger.log(:debug, "Running #{check.short_name} in #{path}")
125
+
126
+ @current_check = check
127
+
128
+ check.run
129
+
130
+ result[:external_urls].merge!(check.external_urls) { |_key, old, current| old.concat(current) }
131
+ result[:internal_urls].merge!(check.internal_urls) { |_key, old, current| old.concat(current) }
132
+ result[:failures].concat(check.failures)
133
+ end
134
+ result
135
+ end
136
+
137
+ def validate_external_urls
138
+ external_url_validator = HTMLProofer::UrlValidator::External.new(self, @external_urls)
139
+ external_url_validator.before_request = @before_request
140
+ @failures.concat(external_url_validator.validate)
141
+ end
142
+
143
+ def validate_internal_urls
144
+ internal_link_validator = HTMLProofer::UrlValidator::Internal.new(self, @internal_urls)
145
+ @failures.concat(internal_link_validator.validate)
146
+ end
147
+
148
+ def files
149
+ @files ||= if @type == :directory
150
+ @source.map do |src|
151
+ pattern = File.join(src, "**", "*{#{@options[:extensions].join(",")}}")
152
+ Dir.glob(pattern).select do |f|
153
+ File.file?(f) && !ignore_file?(f)
154
+ end.map { |f| { source: src, path: f } }
155
+ end.flatten
156
+ elsif @type == :file && @options[:extensions].include?(File.extname(@source))
157
+ [@source].reject { |f| ignore_file?(f) }.map { |f| { source: f, path: f } }
158
+ else
159
+ []
160
+ end
161
+ end
162
+
163
+ def ignore_file?(file)
164
+ @options[:ignore_files].each do |pattern|
165
+ return true if pattern.is_a?(String) && pattern == file
166
+ return true if pattern.is_a?(Regexp) && pattern =~ file
167
+ end
168
+
169
+ false
170
+ end
171
+
172
+ def check_sri?
173
+ @options[:check_sri]
174
+ end
175
+
176
+ def enforce_https?
177
+ @options[:enforce_https]
178
+ end
179
+
180
+ def checks
181
+ return @checks if defined?(@checks) && !@checks.nil?
182
+
183
+ return (@checks = ["LinkCheck"]) if @type == :links
184
+
185
+ @checks = HTMLProofer::Check.subchecks(@options).map(&:name)
186
+
187
+ @checks
188
+ end
189
+
190
+ def failed_checks
191
+ @reporter.failures.flatten.select { |f| f.is_a?(Failure) }
192
+ end
193
+
194
+ def report_failed_checks
195
+ @reporter.report
196
+
197
+ failure_text = pluralize(@failures.length, "failure", "failures")
198
+ @logger.log(:fatal, "\nHTML-Proofer found #{failure_text}!")
199
+ exit(1)
200
+ end
201
+
202
+ # Set before_request callback.
203
+ #
204
+ # @example Set before_request.
205
+ # request.before_request { |request| p "yay" }
206
+ #
207
+ # @param [ Block ] block The block to execute.
208
+ #
209
+ # @yield [ Typhoeus::Request ]
210
+ #
211
+ # @return [ Array<Block> ] All before_request blocks.
212
+ def before_request(&block)
213
+ @before_request ||= []
214
+ @before_request << block if block
215
+ @before_request
216
+ end
217
+
218
+ def load_internal_cache
219
+ load_cache(:internal)
220
+ end
221
+
222
+ def load_external_cache
223
+ load_cache(:external)
224
+ end
225
+
226
+ private def load_cache(type)
227
+ ivar = instance_variable_get("@#{type}_urls")
228
+
229
+ existing_urls_count = @cache.size(type)
230
+ cache_text = pluralize(existing_urls_count, "#{type} link", "#{type} links")
231
+ @logger.log(:debug, "Found #{cache_text} in the cache")
232
+
233
+ urls_to_check = @cache.retrieve_urls(ivar, type)
234
+ urls_detected = pluralize(urls_to_check.count, "#{type} link", "#{type} links")
235
+ @logger.log(:info, "Checking #{urls_detected}")
236
+
237
+ urls_to_check
238
+ end
239
+
240
+ private def format_checks_list(checks)
241
+ checks.map do |check|
242
+ check.sub(/HTMLProofer::Check::/, "")
243
+ end.join(", ")
244
+ end
245
+ end
246
+ end