html-proofer 5.0.0 → 5.0.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2c984bfe049fba10c746d289d8d0256f219d6ae8f5af152f6f8af68e639301b
4
- data.tar.gz: c407f3995e873bbbc42d90de1f082827a40caa90cb12e5cdba7fd514500d1124
3
+ metadata.gz: d9ed4a509a2d120a3aff5ad4809f6b91feff3842dceb371015aca5fd08e69bb2
4
+ data.tar.gz: 0db88e877a8fc19022d4fd1f382e4d38bfeead1540c42817e33799ccf2527bc7
5
5
  SHA512:
6
- metadata.gz: 732365ae4207754f70b27843a52d4f8186673fe1282ddaae03244ebff07517816371e970b75e465539bac51e931d51d4b1856866fcd6a3a575ae6e980d7c6465
7
- data.tar.gz: ab01a8425d206863584e6c84f395ab85a22eff9d2e1df0daa4e0f72771fc9bde95a8992da17d2ef8195439ad1de2412135758989bb9ba29766b1978d95e0fad4
6
+ metadata.gz: 6c888e7975c91eb41124057eba57fb3bdd9f8d82f38be6958c82649458526d7a41cb9aac8d4221678c273db419708cb2abfce2ce0be7396acd2b97295e938844
7
+ data.tar.gz: 61acc2e9c73ba8ca51abca84e21524e145a3f98f8d0d8db4c9683d13407610ea13dfe04342cc861ca0109cd8f9ac1fa24300bb0df684a198787ee5a2d40c8cde
@@ -3,13 +3,16 @@
3
3
  module HTMLProofer
4
4
  class Attribute
5
5
  class Url < HTMLProofer::Attribute
6
- attr_reader :url, :size
6
+ attr_reader :url, :size, :source, :filename
7
7
 
8
8
  REMOTE_SCHEMES = ["http", "https"].freeze
9
9
 
10
- def initialize(runner, link_attribute, base_url: nil, extract_size: false)
10
+ def initialize(runner, link_attribute, base_url: nil, source: nil, filename: nil, extract_size: false)
11
11
  super
12
12
 
13
+ @source = source
14
+ @filename = filename
15
+
13
16
  if @raw_attribute.nil?
14
17
  @url = nil
15
18
  else
@@ -49,7 +52,8 @@ module HTMLProofer
49
52
 
50
53
  def ignore?
51
54
  return true if /^javascript:/.match?(@url)
52
- return true if ignores_pattern?(@runner.options[:ignore_urls])
55
+
56
+ true if ignores_pattern?(@runner.options[:ignore_urls])
53
57
  end
54
58
 
55
59
  def valid?
@@ -115,9 +119,29 @@ module HTMLProofer
115
119
  def exists?
116
120
  return true if base64?
117
121
 
118
- return @runner.checked_paths[absolute_path] if @runner.checked_paths.key?(absolute_path)
122
+ !resolved_path.nil?
123
+ end
124
+
125
+ def resolved_path
126
+ path_to_resolve = absolute_path
119
127
 
120
- @runner.checked_paths[absolute_path] = File.exist?(absolute_path)
128
+ return @runner.resolved_paths[path_to_resolve] if @runner.resolved_paths.key?(path_to_resolve)
129
+
130
+ # extensionless URLs
131
+ path_with_extension = "#{path_to_resolve}#{@runner.options[:assume_extension]}"
132
+ resolved = if @runner.options[:assume_extension] && File.file?(path_with_extension)
133
+ path_with_extension # existence checked implicitly by File.file?
134
+ # implicit index support
135
+ elsif File.directory?(path_to_resolve) && !unslashed_directory?(path_to_resolve)
136
+ path_with_index = File.join(path_to_resolve, @runner.options[:directory_index_file])
137
+ path_with_index if File.file?(path_with_index)
138
+ # explicit file or directory
139
+ elsif File.exist?(path_to_resolve)
140
+ path_to_resolve
141
+ end
142
+ @runner.resolved_paths[path_to_resolve] = resolved
143
+
144
+ resolved
121
145
  end
122
146
 
123
147
  def base64?
@@ -125,43 +149,23 @@ module HTMLProofer
125
149
  end
126
150
 
127
151
  def absolute_path
128
- path = file_path || @runner.current_filename
152
+ path = full_path || @filename
129
153
 
130
154
  File.expand_path(path, Dir.pwd)
131
155
  end
132
156
 
133
- def file_path
157
+ def full_path
134
158
  return if path.nil? || path.empty?
135
159
 
136
- path_dot_ext = ""
137
-
138
- path_dot_ext = path + @runner.options[:assume_extension] unless blank?(@runner.options[:assume_extension])
139
-
140
160
  base = if absolute_path?(path) # path relative to root
141
- # either overwrite with root_dir; or, if source is directory, use that; or, just get the current file's dirname
142
- @runner.options[:root_dir] || (File.directory?(@runner.current_source) ? @runner.current_source : File.dirname(@runner.current_source))
143
- # relative links, path is a file
144
- elsif File.exist?(File.expand_path(path,
145
- @runner.current_source)) || File.exist?(File.expand_path(path_dot_ext, @runner.current_source))
146
- File.dirname(@runner.current_filename)
147
- # relative links in nested dir, path is a file
148
- elsif File.exist?(File.join(File.dirname(@runner.current_filename),
149
- path)) || File.exist?(File.join(File.dirname(@runner.current_filename), path_dot_ext))
150
- File.dirname(@runner.current_filename)
151
- # relative link, path is a directory
161
+ # either overwrite with root_dir; or, if source is directory, use that; or, just get the source file's dirname
162
+ @runner.options[:root_dir] || (File.directory?(@source) ? @source : File.dirname(@source))
152
163
  else
153
- @runner.current_filename
154
- end
155
-
156
- file = File.join(base, path)
157
-
158
- if @runner.options[:assume_extension] && File.file?("#{file}#{@runner.options[:assume_extension]}")
159
- file = "#{file}#{@runner.options[:assume_extension]}"
160
- elsif File.directory?(file) && !unslashed_directory?(file) # implicit index support
161
- file = File.join(file, @runner.options[:directory_index_file])
164
+ # path relative to the file where the link is defined
165
+ File.dirname(@filename)
162
166
  end
163
167
 
164
- file
168
+ File.join(base, path)
165
169
  end
166
170
 
167
171
  def unslashed_directory?(file)
@@ -213,15 +217,28 @@ module HTMLProofer
213
217
  url.start_with?("?")
214
218
  end
215
219
 
216
- def sans_hash
220
+ def without_hash
217
221
  @url.to_s.sub(/##{hash}/, "")
218
222
  end
219
223
 
220
- # catch any obvious issues, like strings in port numbers
224
+ # catch any obvious issues
221
225
  private def clean_url!
222
- return if @url =~ /^([!#{Regexp.last_match(0)}-;=?-\[\]_a-z~]|%[0-9a-fA-F]{2})+$/
223
-
224
- @url = Addressable::URI.parse(@url).normalize.to_s
226
+ parsed_url = Addressable::URI.parse(@url)
227
+ url = if parsed_url.scheme.nil?
228
+ parsed_url
229
+ else
230
+ parsed_url.normalize
231
+ end.to_s
232
+
233
+ # normalize strips this off, which causes issues with cache
234
+ @url = if @url.end_with?("/") && !url.end_with?("/")
235
+ "#{url}/"
236
+ elsif !@url.end_with?("/") && url.end_with?("/")
237
+ url.chop
238
+ else
239
+ url
240
+ end
241
+ rescue Addressable::URI::InvalidURIError # rubocop:disable Lint/SuppressedException; error will be reported at check time
225
242
  end
226
243
 
227
244
  private def swap_urls!
@@ -41,7 +41,7 @@ module HTMLProofer
41
41
  end
42
42
 
43
43
  def parsed_timeframe(timeframe)
44
- return nil if timeframe.nil?
44
+ return if timeframe.nil?
45
45
 
46
46
  time, date = timeframe.match(/(\d+)(\D)/).captures
47
47
  time = time.to_i
@@ -93,8 +93,10 @@ module HTMLProofer
93
93
  # if there are no urls, bail
94
94
  return {} if urls_detected.empty?
95
95
 
96
- urls_detected = urls_detected.transform_keys do |url|
97
- cleaned_url(url)
96
+ if type == :external
97
+ urls_detected = urls_detected.transform_keys do |url|
98
+ cleaned_url(url)
99
+ end
98
100
  end
99
101
 
100
102
  urls_to_check = detect_url_changes(urls_detected, type)
@@ -250,7 +252,7 @@ module HTMLProofer
250
252
  SECONDS_PER_HOUR = 3600
251
253
  SECONDS_PER_DAY = 86400
252
254
  SECONDS_PER_WEEK = 604800
253
- SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year
255
+ SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year
254
256
 
255
257
  private def time_ago(measurement, unit)
256
258
  case unit
@@ -267,7 +269,8 @@ module HTMLProofer
267
269
 
268
270
  private def url_matches_type?(url, type)
269
271
  return true if type == :internal && url !~ URI_REGEXP
270
- return true if type == :external && url =~ URI_REGEXP
272
+
273
+ true if type == :external && url =~ URI_REGEXP
271
274
  end
272
275
 
273
276
  private def cleaned_url(url)
@@ -17,13 +17,17 @@ module HTMLProofer
17
17
 
18
18
  if found
19
19
  if @favicon.url.protocol_relative?
20
- add_failure("favicon link #{@favicon.url} is a protocol-relative URL, use explicit https:// instead",
21
- line: @favicon.line, content: @favicon.content)
20
+ add_failure(
21
+ "favicon link #{@favicon.url} is a protocol-relative URL, use explicit https:// instead",
22
+ element: @favicon,
23
+ )
22
24
  elsif @favicon.url.remote?
23
25
  add_to_external_urls(@favicon.url, @favicon.line)
24
26
  elsif !@favicon.url.exists?
25
- add_failure("internal favicon #{@favicon.url.raw_attribute} does not exist", line: @favicon.line,
26
- content: @favicon.content)
27
+ add_failure(
28
+ "internal favicon #{@favicon.url.raw_attribute} does not exist",
29
+ element: @favicon,
30
+ )
27
31
  end
28
32
  else
29
33
  add_failure("no favicon provided")
@@ -12,31 +12,39 @@ module HTMLProofer
12
12
  next if @img.ignore?
13
13
 
14
14
  # screenshot filenames should return because of terrible names
15
- add_failure("image has a terrible filename (#{@img.url.raw_attribute})", line: @img.line,
16
- content: @img.content) if terrible_filename?
15
+ add_failure(
16
+ "image has a terrible filename (#{@img.url.raw_attribute})",
17
+ element: @img,
18
+ ) if terrible_filename?
17
19
 
18
20
  # does the image exist?
19
21
  if missing_src?
20
- add_failure("image has no src or srcset attribute", line: @img.line, content: @img.content)
22
+ add_failure("image has no src or srcset attribute", element: @img)
21
23
  elsif @img.url.protocol_relative?
22
- add_failure("image link #{@img.url} is a protocol-relative URL, use explicit https:// instead",
23
- line: @img.line, content: @img.content)
24
+ add_failure(
25
+ "image link #{@img.url} is a protocol-relative URL, use explicit https:// instead",
26
+ element: @img,
27
+ )
24
28
  elsif @img.url.remote?
25
29
  add_to_external_urls(@img.url, @img.line)
26
30
  elsif !@img.url.exists? && !@img.multiple_srcsets? && !@img.multiple_sizes?
27
- add_failure("internal image #{@img.url.raw_attribute} does not exist", line: @img.line,
28
- content: @img.content)
31
+ add_failure(
32
+ "internal image #{@img.url.raw_attribute} does not exist",
33
+ element: @img,
34
+ )
29
35
  elsif @img.multiple_srcsets? || @img.multiple_sizes?
30
36
  @img.srcsets_wo_sizes.each do |srcset|
31
- srcset_url = HTMLProofer::Attribute::Url.new(@runner, srcset, base_url: @img.base_url, extract_size: true)
37
+ srcset_url = HTMLProofer::Attribute::Url.new(@runner, srcset, base_url: @img.base_url, source: @img.url.source, filename: @img.url.filename, extract_size: true)
32
38
 
33
39
  if srcset_url.protocol_relative?
34
- add_failure("image link #{srcset_url.url} is a protocol-relative URL, use explicit https:// instead",
35
- line: @img.line, content: @img.content)
40
+ add_failure(
41
+ "image link #{srcset_url.url} is a protocol-relative URL, use explicit https:// instead",
42
+ element: @img,
43
+ )
36
44
  elsif srcset_url.remote?
37
45
  add_to_external_urls(srcset_url.url, @img.line)
38
46
  elsif !srcset_url.exists?
39
- add_failure("internal image #{srcset} does not exist", line: @img.line, content: @img.content)
47
+ add_failure("internal image #{srcset} does not exist", element: @img)
40
48
  end
41
49
  end
42
50
  end
@@ -44,16 +52,22 @@ module HTMLProofer
44
52
  # if this is an img element, check that the alt attribute is present
45
53
  if @img.img_tag? && !ignore_element?
46
54
  if missing_alt_tag? && !ignore_missing_alt?
47
- add_failure("image #{@img.url.raw_attribute} does not have an alt attribute", line: @img.line,
48
- content: @img.content)
55
+ add_failure(
56
+ "image #{@img.url.raw_attribute} does not have an alt attribute",
57
+ element: @img,
58
+ )
49
59
  elsif (empty_alt_tag? || alt_all_spaces?) && !ignore_empty_alt?
50
- add_failure("image #{@img.url.raw_attribute} has an alt attribute, but no content", line: @img.line,
51
- content: @img.content)
60
+ add_failure(
61
+ "image #{@img.url.raw_attribute} has an alt attribute, but no content",
62
+ element: @img,
63
+ )
52
64
  end
53
65
  end
54
66
 
55
- add_failure("image #{@img.url.raw_attribute} uses the http scheme", line: @img.line,
56
- content: @img.content) if @runner.enforce_https? && @img.url.http?
67
+ add_failure(
68
+ "image #{@img.url.raw_attribute} uses the http scheme",
69
+ element: @img,
70
+ ) if @runner.enforce_https? && @img.url.http?
57
71
  end
58
72
 
59
73
  external_urls
@@ -10,7 +10,7 @@ module HTMLProofer
10
10
  next if @link.ignore?
11
11
 
12
12
  if !allow_hash_href? && @link.node["href"] == "#"
13
- add_failure("linking to internal hash #, which points to nowhere", line: @link.line, content: @link.content)
13
+ add_failure("linking to internal hash #, which points to nowhere", element: @link)
14
14
  next
15
15
  end
16
16
 
@@ -18,19 +18,21 @@ module HTMLProofer
18
18
  if blank?(@link.url.raw_attribute)
19
19
  next if allow_missing_href?
20
20
 
21
- add_failure("'#{@link.node.name}' tag is missing a reference", line: @link.line, content: @link.content)
21
+ add_failure("'#{@link.node.name}' tag is missing a reference", element: @link)
22
22
  next
23
23
  end
24
24
 
25
25
  # is it even a valid URL?
26
26
  unless @link.url.valid?
27
- add_failure("#{@link.href} is an invalid URL", line: @link.line, content: @link.content)
27
+ add_failure("#{@link.href} is an invalid URL", element: @link)
28
28
  next
29
29
  end
30
30
 
31
31
  if @link.url.protocol_relative?
32
- add_failure("#{@link.url} is a protocol-relative URL, use explicit https:// instead",
33
- line: @link.line, content: @link.content)
32
+ add_failure(
33
+ "#{@link.url} is a protocol-relative URL, use explicit https:// instead",
34
+ element: @link,
35
+ )
34
36
  next
35
37
  end
36
38
 
@@ -47,7 +49,7 @@ module HTMLProofer
47
49
  next if @link.node["rel"] == "dns-prefetch"
48
50
 
49
51
  unless @link.url.path?
50
- add_failure("#{@link.url.raw_attribute} is an invalid URL", line: @link.line, content: @link.content)
52
+ add_failure("#{@link.url.raw_attribute} is an invalid URL", element: @link)
51
53
  next
52
54
  end
53
55
 
@@ -55,8 +57,10 @@ module HTMLProofer
55
57
  elsif @link.url.internal?
56
58
  # does the local directory have a trailing slash?
57
59
  if @link.url.unslashed_directory?(@link.url.absolute_path)
58
- add_failure("internally linking to a directory #{@link.url.raw_attribute} without trailing slash",
59
- line: @link.line, content: @link.content)
60
+ add_failure(
61
+ "internally linking to a directory #{@link.url.raw_attribute} without trailing slash",
62
+ element: @link,
63
+ )
60
64
  next
61
65
  end
62
66
 
@@ -82,23 +86,30 @@ module HTMLProofer
82
86
  when "http"
83
87
  return unless @runner.options[:enforce_https]
84
88
 
85
- add_failure("#{@link.url.raw_attribute} is not an HTTPS link", line: @link.line, content: @link.content)
89
+ add_failure("#{@link.url.raw_attribute} is not an HTTPS link", element: @link)
86
90
  end
87
91
  end
88
92
 
89
93
  def handle_mailto
90
94
  if @link.url.path.empty?
91
- add_failure("#{@link.url.raw_attribute} contains no email address", line: @link.line,
92
- content: @link.content) unless ignore_empty_mailto?
93
- elsif !/#{URI::MailTo::EMAIL_REGEXP}/o.match?(@link.url.path)
94
- add_failure("#{@link.url.raw_attribute} contains an invalid email address", line: @link.line,
95
- content: @link.content)
95
+ add_failure(
96
+ "#{@link.url.raw_attribute} contains no email address",
97
+ element: @link,
98
+ ) unless ignore_empty_mailto?
99
+ # eg., if any do not match a valid URL
100
+ elsif @link.url.path.split(",").any? { |email| !/#{URI::MailTo::EMAIL_REGEXP}/o.match?(email) }
101
+ add_failure(
102
+ "#{@link.url.raw_attribute} contains an invalid email address",
103
+ element: @link,
104
+ )
96
105
  end
97
106
  end
98
107
 
99
108
  def handle_tel
100
- add_failure("#{@link.url.raw_attribute} contains no phone number", line: @link.line,
101
- content: @link.content) if @link.url.path.empty?
109
+ add_failure(
110
+ "#{@link.url.raw_attribute} contains no phone number",
111
+ element: @link,
112
+ ) if @link.url.path.empty?
102
113
  end
103
114
 
104
115
  def ignore_empty_mailto?
@@ -113,13 +124,17 @@ module HTMLProofer
113
124
  return unless SRI_REL_TYPES.include?(@link.node["rel"])
114
125
 
115
126
  if blank?(@link.node["integrity"]) && blank?(@link.node["crossorigin"])
116
- add_failure("SRI and CORS not provided in: #{@link.url.raw_attribute}", line: @link.line,
117
- content: @link.content)
127
+ add_failure(
128
+ "SRI and CORS not provided in: #{@link.url.raw_attribute}",
129
+ element: @link,
130
+ )
118
131
  elsif blank?(@link.node["integrity"])
119
- add_failure("Integrity is missing in: #{@link.url.raw_attribute}", line: @link.line, content: @link.content)
132
+ add_failure("Integrity is missing in: #{@link.url.raw_attribute}", element: @link)
120
133
  elsif blank?(@link.node["crossorigin"])
121
- add_failure("CORS not provided for external resource in: #{@link.link.url.raw_attribute}", line: @link.line,
122
- content: @link.content)
134
+ add_failure(
135
+ "CORS not provided for external resource in: #{@link.link.url.raw_attribute}",
136
+ element: @link,
137
+ )
123
138
  end
124
139
  end
125
140
 
@@ -11,19 +11,23 @@ module HTMLProofer
11
11
 
12
12
  # does the open_graph exist?
13
13
  if missing_content?
14
- add_failure("open graph has no content attribute", line: @open_graph.line, content: @open_graph.content)
14
+ add_failure("open graph has no content attribute", element: @open_graph)
15
15
  elsif empty_content?
16
- add_failure("open graph content attribute is empty", line: @open_graph.line, content: @open_graph.content)
16
+ add_failure("open graph content attribute is empty", element: @open_graph)
17
17
  elsif !@open_graph.url.valid?
18
- add_failure("#{@open_graph.src} is an invalid URL", line: @open_graph.line)
18
+ add_failure("#{@open_graph.src} is an invalid URL", element: @open_graph)
19
19
  elsif @open_graph.url.protocol_relative?
20
- add_failure("open graph link #{@open_graph.url} is a protocol-relative URL, use explicit https:// instead",
21
- line: @open_graph.line, content: @open_graph.content)
20
+ add_failure(
21
+ "open graph link #{@open_graph.url} is a protocol-relative URL, use explicit https:// instead",
22
+ element: @open_graph,
23
+ )
22
24
  elsif @open_graph.url.remote?
23
25
  add_to_external_urls(@open_graph.url, @open_graph.line)
24
26
  else
25
- add_failure("internal open graph #{@open_graph.url.raw_attribute} does not exist", line: @open_graph.line,
26
- content: @open_graph.content) unless @open_graph.url.exists?
27
+ add_failure(
28
+ "internal open graph #{@open_graph.url.raw_attribute} does not exist",
29
+ element: @open_graph,
30
+ ) unless @open_graph.url.exists?
27
31
  end
28
32
  end
29
33
 
@@ -12,16 +12,20 @@ module HTMLProofer
12
12
 
13
13
  # does the script exist?
14
14
  if missing_src?
15
- add_failure("script is empty and has no src attribute", line: @script.line, content: @script.content)
15
+ add_failure("script is empty and has no src attribute", element: @script)
16
16
  elsif @script.url.protocol_relative?
17
- add_failure("script link #{@script.url} is a protocol-relative URL, use explicit https:// instead",
18
- line: @script.line, content: @script.content)
17
+ add_failure(
18
+ "script link #{@script.url} is a protocol-relative URL, use explicit https:// instead",
19
+ element: @script,
20
+ )
19
21
  elsif @script.url.remote?
20
22
  add_to_external_urls(@script.url, @script.line)
21
23
  check_sri if @runner.check_sri?
22
24
  elsif !@script.url.exists?
23
- add_failure("internal script reference #{@script.src} does not exist", line: @script.line,
24
- content: @script.content)
25
+ add_failure(
26
+ "internal script reference #{@script.src} does not exist",
27
+ element: @script,
28
+ )
25
29
  end
26
30
  end
27
31
 
@@ -34,14 +38,20 @@ module HTMLProofer
34
38
 
35
39
  def check_sri
36
40
  if blank?(@script.node["integrity"]) && blank?(@script.node["crossorigin"])
37
- add_failure("SRI and CORS not provided in: #{@script.url.raw_attribute}", line: @script.line,
38
- content: @script.content)
41
+ add_failure(
42
+ "SRI and CORS not provided in: #{@script.url.raw_attribute}",
43
+ element: @script,
44
+ )
39
45
  elsif blank?(@script.node["integrity"])
40
- add_failure("Integrity is missing in: #{@script.url.raw_attribute}", line: @script.line,
41
- content: @script.content)
46
+ add_failure(
47
+ "Integrity is missing in: #{@script.url.raw_attribute}",
48
+ element: @script,
49
+ )
42
50
  elsif blank?(@script.node["crossorigin"])
43
- add_failure("CORS not provided for external resource in: #{@script.url.raw_attribute}", line: @script.line,
44
- content: @script.content)
51
+ add_failure(
52
+ "CORS not provided for external resource in: #{@script.url.raw_attribute}",
53
+ element: @script,
54
+ )
45
55
  end
46
56
  end
47
57
  end
@@ -24,9 +24,15 @@ module HTMLProofer
24
24
  raise NotImplementedError, "HTMLProofer::Check subclasses must implement #run"
25
25
  end
26
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)
27
+ def add_failure(description, element: nil, line: nil, status: nil, content: nil)
28
+ @failures << Failure.new(
29
+ @runner.current_filename,
30
+ short_name,
31
+ description,
32
+ line: element.nil? ? line : element.line,
33
+ status: status,
34
+ content: element.nil? ? content : element.content,
35
+ )
30
36
  end
31
37
 
32
38
  def short_name
@@ -39,8 +45,8 @@ module HTMLProofer
39
45
  @internal_urls[url_string] = [] if @internal_urls[url_string].nil?
40
46
 
41
47
  metadata = {
42
- source: @runner.current_source,
43
- filename: @runner.current_filename,
48
+ source: url.source,
49
+ filename: url.filename,
44
50
  line: line,
45
51
  base_url: base_url,
46
52
  found: false,
@@ -53,7 +59,7 @@ module HTMLProofer
53
59
 
54
60
  @external_urls[url_string] = [] if @external_urls[url_string].nil?
55
61
 
56
- @external_urls[url_string] << { filename: @runner.current_filename, line: line }
62
+ @external_urls[url_string] << { filename: url.filename, line: line }
57
63
  end
58
64
 
59
65
  class << self
@@ -134,7 +134,17 @@ module HTMLProofer
134
134
 
135
135
  section(opts, "Ignore Configuration") do
136
136
  set_option(opts, "--ignore-files [FILE1,FILE2,...]") do |long_opt_symbol, list|
137
- @options[long_opt_symbol] = list.nil? ? [] : list.split(",")
137
+ @options[long_opt_symbol] = if list.nil?
138
+ []
139
+ else
140
+ list.split(",").map.each do |l|
141
+ if l.start_with?("/") && l.end_with?("/")
142
+ Regexp.new(l[1...-1])
143
+ else
144
+ l
145
+ end
146
+ end
147
+ end
138
148
  end
139
149
 
140
150
  set_option(opts, "--[no-]ignore-empty-alt") do |long_opt_symbol, arg|
@@ -187,12 +197,12 @@ module HTMLProofer
187
197
  @options[long_opt_symbol] = parse_json_option("typhoeus", arg, symbolize_names: false)
188
198
  end
189
199
 
190
- set_option(opts, "--hydra <CONFIG>") do |long_opt_symbol, _list|
191
- @options[long_opt_symbol] = parse_json_option("hydra", arg, symbolize_names: false)
200
+ set_option(opts, "--hydra <CONFIG>") do |long_opt_symbol, arg|
201
+ @options[long_opt_symbol] = parse_json_option("hydra", arg, symbolize_names: true)
192
202
  end
193
203
 
194
- set_option(opts, "--cache <CONFIG>") do |long_opt_symbol, _list|
195
- @options[long_opt_symbol] = parse_json_option("cache", arg, symbolize_names: false)
204
+ set_option(opts, "--cache <CONFIG>") do |long_opt_symbol, arg|
205
+ @options[long_opt_symbol] = parse_json_option("cache", arg, symbolize_names: true)
196
206
  end
197
207
  end
198
208
 
@@ -223,8 +233,8 @@ module HTMLProofer
223
233
  arg.split(",").each_with_object({}) do |s, hsh|
224
234
  split = s.split(/(?<!\\):/, 2)
225
235
 
226
- re = split[0].gsub(/\\:/, ":")
227
- string = split[1].gsub(/\\:/, ":")
236
+ re = split[0].gsub("\\:", ":")
237
+ string = split[1].gsub("\\:", ":")
228
238
  hsh[Regexp.new(re)] = string
229
239
  end
230
240
  end
@@ -270,49 +280,69 @@ module HTMLProofer
270
280
  module ConfigurationHelp
271
281
  TEXT = {
272
282
  as_links: ["Assumes that `PATH` is a comma-separated array of links to check."],
273
- assume_extension: ["Automatically add specified extension to files for internal links, ",
274
- "to allow extensionless URLs (as supported by most servers) (default: `.html`).",],
283
+ assume_extension: [
284
+ "Automatically add specified extension to files for internal links, ",
285
+ "to allow extensionless URLs (as supported by most servers) (default: `.html`).",
286
+ ],
275
287
  directory_index_file: ["Sets the file to look for when a link refers to a directory. (default: `index.html`)."],
276
- extensions: ["A comma-separated list of Strings indicating the file extensions you",
277
- "would like to check (default: `.html`)",],
288
+ extensions: [
289
+ "A comma-separated list of Strings indicating the file extensions you",
290
+ "would like to check (default: `.html`)",
291
+ ],
278
292
 
279
293
  allow_hash_href: ['"If `true`, assumes `href="#"` anchors are valid (default: `true`)"'],
280
- allow_missing_href: ["If `true`, does not flag `a` tags missing `href`. In HTML5, this is technically ",
281
- "allowed, but could also be human error. (default: `false`)",],
282
- checks: ["A comma-separated list of Strings indicating which checks you",
283
- "want to run (default: `[\"Links\", \"Images\", \"Scripts\"]",],
294
+ allow_missing_href: [
295
+ "If `true`, does not flag `a` tags missing `href`. In HTML5, this is technically ",
296
+ "allowed, but could also be human error. (default: `false`)",
297
+ ],
298
+ checks: [
299
+ "A comma-separated list of Strings indicating which checks you",
300
+ "want to run (default: `[\"Links\", \"Images\", \"Scripts\"]",
301
+ ],
284
302
  check_external_hash: ["Checks whether external hashes exist (even if the webpage exists) (default: `true`)."],
285
303
  check_internal_hash: ["Checks whether internal hashes exist (even if the webpage exists) (default: `true`)."],
286
304
  check_sri: ["Check that `<link>` and `<script>` external resources use SRI (default: `false`)."],
287
305
  disable_external: ["If `true`, does not run the external link checker (default: `false`)."],
288
- enforce_https: ["Fails a link if it\'s not marked as `https` (default: `true`)."],
306
+ enforce_https: ["Fails a link if it's not marked as `https` (default: `true`)."],
289
307
  root_dir: ["The absolute path to the directory serving your html-files."],
290
308
 
291
- ignore_empty_alt: ["If `true`, ignores images with empty/missing ",
292
- "alt tags (in other words, `<img alt>` and `<img alt=\"\">`",
293
- "are valid; set this to `false` to flag those) (default: `true`).",],
294
- ignore_empty_mailto: ["If `true`, allows `mailto:` `href`s which don't",
295
- "contain an email address (default: `false`)'.",],
309
+ ignore_empty_alt: [
310
+ "If `true`, ignores images with empty/missing ",
311
+ "alt tags (in other words, `<img alt>` and `<img alt=\"\">`",
312
+ "are valid; set this to `false` to flag those) (default: `true`).",
313
+ ],
314
+ ignore_empty_mailto: [
315
+ "If `true`, allows `mailto:` `href`s which don't",
316
+ "contain an email address (default: `false`)'.",
317
+ ],
296
318
  ignore_missing_alt: ["If `true`, ignores images with missing alt tags (default: `false`)."],
297
319
  ignore_status_codes: ["A comma-separated list of numbers representing status codes to ignore."],
298
320
  ignore_files: ["A comma-separated list of Strings or RegExps containing file paths that are safe to ignore"],
299
- ignore_urls: ["A comma-separated list of Strings or RegExps containing URLs that are",
300
- "safe to ignore. This affects all HTML attributes, such as `alt` tags on images.",],
321
+ ignore_urls: [
322
+ "A comma-separated list of Strings or RegExps containing URLs that are",
323
+ "safe to ignore. This affects all HTML attributes, such as `alt` tags on images.",
324
+ ],
301
325
  only_status_codes: ["A comma-separated list of numbers representing the only status codes to report on."],
302
326
  only_4xx: ["Only reports errors for links that fall within the 4xx status code range."],
303
327
 
304
- swap_attributes: ["JSON-formatted config that maps element names to the",
305
- "preferred attribute to check (default: `{}`).",],
306
- swap_urls: ["A comma-separated list containing key-value pairs of `RegExp => String`.",
307
- "It transforms URLs that match `RegExp` into `String` via `gsub`.",
308
- "The escape sequences `\\:` should be used to produce literal `:`s.",],
328
+ swap_attributes: [
329
+ "JSON-formatted config that maps element names to the",
330
+ "preferred attribute to check (default: `{}`).",
331
+ ],
332
+ swap_urls: [
333
+ "A comma-separated list containing key-value pairs of `RegExp => String`.",
334
+ "It transforms URLs that match `RegExp` into `String` via `gsub`.",
335
+ "The escape sequences `\\:` should be used to produce literal `:`s.",
336
+ ],
309
337
 
310
338
  typhoeus: ["JSON-formatted string of Typhoeus config; if set, overrides the html-proofer defaults."],
311
339
  hydra: ["JSON-formatted string of Hydra config; if set, overrides the html-proofer defaults."],
312
340
  cache: ["JSON-formatted string of cache config; if set, overrides the html-proofer defaults."],
313
341
 
314
- log_level: ["Sets the logging level. One of `:debug`, `:info`, ",
315
- "`:warn`, `:error`, or `:fatal`. (default: `:info`)",],
342
+ log_level: [
343
+ "Sets the logging level. One of `:debug`, `:info`, ",
344
+ "`:warn`, `:error`, or `:fatal`. (default: `:info`)",
345
+ ],
316
346
 
317
347
  version: ["Prints the version of html-proofer."],
318
348
  }.freeze
@@ -16,7 +16,7 @@ module HTMLProofer
16
16
  swap_attributes!
17
17
 
18
18
  @base_url = base_url
19
- @url = Attribute::Url.new(runner, link_attribute, base_url: base_url)
19
+ @url = Attribute::Url.new(runner, link_attribute, base_url: base_url, source: @runner.current_source, filename: @runner.current_filename)
20
20
 
21
21
  @line = node.line
22
22
  @content = node.content
@@ -27,7 +27,7 @@ module HTMLProofer
27
27
  end
28
28
 
29
29
  def meta_content
30
- return nil unless meta_tag?
30
+ return unless meta_tag?
31
31
 
32
32
  @node["content"]
33
33
  end
@@ -37,7 +37,7 @@ module HTMLProofer
37
37
  end
38
38
 
39
39
  def src
40
- return nil if !img_tag? && !script_tag? && !source_tag?
40
+ return if !img_tag? && !script_tag? && !source_tag?
41
41
 
42
42
  @node["src"]
43
43
  end
@@ -51,7 +51,7 @@ module HTMLProofer
51
51
  end
52
52
 
53
53
  def srcset
54
- return nil if !img_tag? && !source_tag?
54
+ return if !img_tag? && !source_tag?
55
55
 
56
56
  @node["srcset"]
57
57
  end
@@ -61,7 +61,7 @@ module HTMLProofer
61
61
  end
62
62
 
63
63
  def href
64
- return nil if !a_tag? && !link_tag?
64
+ return if !a_tag? && !link_tag?
65
65
 
66
66
  @node["href"]
67
67
  end
@@ -82,10 +82,25 @@ module HTMLProofer
82
82
  !blank?(srcset) && srcset.split(",").size > 1
83
83
  end
84
84
 
85
+ # From https://github.com/sindresorhus/srcset/blob/f7c48acd7facf18e94dec47e6b96e84e0f0e69dc/index.js#LL1-L16C71
86
+ # This regex represents a loose rule of an “image candidate string”; see https://html.spec.whatwg.org/multipage/images.html#srcset-attribute
87
+ # An “image candidate string” roughly consists of the following:
88
+ # 1. Zero or more whitespace characters.
89
+ # 2. A non-empty URL that does not start or end with `,`.
90
+ # 3. Zero or more whitespace characters.
91
+ # 4. An optional “descriptor” that starts with a whitespace character.
92
+ # 5. Zero or more whitespace characters.
93
+ # 6. Each image candidate string is separated by a `,`.
94
+ # We intentionally implement a loose rule here so that we can perform more aggressive error handling and reporting in the below code.
95
+
96
+ IMAGE_CANDIDATE_REGEX = /\s*([^,]\S*[^,](?:\s+[^,]+)?)\s*(?:,|$)/
97
+
85
98
  def srcsets
86
- return nil if blank?(srcset)
99
+ return if blank?(srcset)
87
100
 
88
- srcset.split(",").map(&:strip)
101
+ srcset.split(IMAGE_CANDIDATE_REGEX).select.with_index do |_part, idx|
102
+ idx.odd?
103
+ end.map(&:strip)
89
104
  end
90
105
 
91
106
  def multiple_sizes?
@@ -97,7 +112,7 @@ module HTMLProofer
97
112
  end
98
113
 
99
114
  def srcsets_wo_sizes
100
- return nil if blank?(srcsets)
115
+ return if blank?(srcsets)
101
116
 
102
117
  srcsets.map do |srcset|
103
118
  srcset.split(" ").first
@@ -118,7 +133,7 @@ module HTMLProofer
118
133
 
119
134
  attrs = @runner.options[:swap_attributes][@node.name]
120
135
 
121
- return true unless blank?(attrs)
136
+ true unless blank?(attrs)
122
137
  end
123
138
 
124
139
  private def swap_attributes!
@@ -11,9 +11,11 @@ module HTMLProofer
11
11
  STDERR_LEVELS = [:error, :fatal].freeze
12
12
 
13
13
  def initialize(log_level)
14
- @logger = Yell.new(format: false, \
14
+ @logger = Yell.new(
15
+ format: false,
15
16
  name: "HTMLProofer", \
16
- level: "gte.#{log_level}") do |l|
17
+ level: "gte.#{log_level}",
18
+ ) do |l|
17
19
  l.adapter(:stdout, level: "lte.warn")
18
20
  l.adapter(:stderr, level: "gte.error")
19
21
  end
@@ -39,8 +41,8 @@ module HTMLProofer
39
41
  :red
40
42
  end
41
43
 
42
- if (STDOUT_LEVELS.include?(level) && $stdout.isatty) || \
43
- (STDERR_LEVELS.include?(level) && $stderr.isatty)
44
+ if STDOUT_LEVELS.include?(level) ||
45
+ STDERR_LEVELS.include?(level)
44
46
  Rainbow(message).send(color)
45
47
  else
46
48
  message
@@ -11,8 +11,8 @@ module HTMLProofer
11
11
  end
12
12
 
13
13
  def failures=(failures)
14
- @failures = failures.group_by(&:check_name) \
15
- .transform_values { |issues| issues.sort_by { |issue| [issue.path, issue.line] } } \
14
+ @failures = failures.group_by(&:check_name)
15
+ .transform_values { |issues| issues.sort_by { |issue| [issue.path, issue.line] } }
16
16
  .sort
17
17
  end
18
18
 
@@ -6,8 +6,8 @@ module HTMLProofer
6
6
  class Runner
7
7
  include HTMLProofer::Utils
8
8
 
9
- attr_reader :options, :cache, :logger, :internal_urls, :external_urls, :checked_paths, :current_check
10
- attr_accessor :current_filename, :current_source, :reporter
9
+ attr_reader :options, :cache, :logger, :internal_urls, :external_urls, :resolved_paths, :current_check, :current_filename, :current_source
10
+ attr_accessor :reporter
11
11
 
12
12
  URL_TYPES = [:external, :internal].freeze
13
13
 
@@ -26,7 +26,7 @@ module HTMLProofer
26
26
 
27
27
  @before_request = []
28
28
 
29
- @checked_paths = {}
29
+ @resolved_paths = {}
30
30
 
31
31
  @current_check = nil
32
32
  @current_source = nil
@@ -42,8 +42,10 @@ module HTMLProofer
42
42
  @logger.log(:info, "Running #{check_text} (#{format_checks_list(checks)}) on #{@source} ...\n\n")
43
43
  check_list_of_links unless @options[:disable_external]
44
44
  else
45
- @logger.log(:info,
46
- "Running #{check_text} (#{format_checks_list(checks)}) in #{@source} on *#{@options[:extensions].join(", ")} files ...\n\n")
45
+ @logger.log(
46
+ :info,
47
+ "Running #{check_text} (#{format_checks_list(checks)}) in #{@source} on *#{@options[:extensions].join(", ")} files ...\n\n",
48
+ )
47
49
 
48
50
  check_files
49
51
  @logger.log(:info, "Ran on #{pluralize(files.length, "file", "files")}!\n\n")
@@ -166,8 +168,14 @@ module HTMLProofer
166
168
 
167
169
  def ignore_file?(file)
168
170
  @options[:ignore_files].each do |pattern|
169
- return true if pattern.is_a?(String) && pattern == file
170
- return true if pattern.is_a?(Regexp) && pattern =~ file
171
+ if pattern.is_a?(String) && pattern == file
172
+ @logger.log(:debug, "Ignoring #{file} because it matches #{pattern}")
173
+ return true
174
+ end
175
+ next unless pattern.is_a?(Regexp) && pattern.match(file)
176
+
177
+ @logger.log(:debug, "Ignoring #{file} because it matches regexp #{pattern}")
178
+ return true
171
179
  end
172
180
 
173
181
  false
@@ -228,7 +236,7 @@ module HTMLProofer
228
236
  end
229
237
 
230
238
  private def load_cache(type)
231
- ivar = instance_variable_get("@#{type}_urls")
239
+ ivar = instance_variable_get(:"@#{type}_urls")
232
240
 
233
241
  existing_urls_count = @cache.size(type)
234
242
  cache_text = pluralize(existing_urls_count, "#{type} link", "#{type} links")
@@ -241,7 +249,7 @@ module HTMLProofer
241
249
 
242
250
  private def format_checks_list(checks)
243
251
  checks.map do |check|
244
- check.sub(/HTMLProofer::Check::/, "")
252
+ check.sub("HTMLProofer::Check::", "")
245
253
  end.sort.join(", ")
246
254
  end
247
255
  end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "typhoeus"
4
- require "uri"
4
+ require "open-uri"
5
+ # require "uri"
6
+ require "pdf-reader"
5
7
 
6
8
  module HTMLProofer
7
9
  class UrlValidator
@@ -88,8 +90,12 @@ module HTMLProofer
88
90
  return if @runner.options[:ignore_status_codes].include?(response_code)
89
91
 
90
92
  if response_code.between?(200, 299)
91
- @cache.add_external(href, filenames, response_code, "OK", true) unless check_hash_in_2xx_response(href, url,
92
- response, filenames)
93
+ @cache.add_external(href, filenames, response_code, "OK", true) unless check_hash_in_2xx_response(
94
+ href,
95
+ url,
96
+ response,
97
+ filenames,
98
+ )
93
99
  elsif response.timed_out?
94
100
  handle_timeout(href, filenames, response_code)
95
101
  elsif response_code.zero?
@@ -115,6 +121,28 @@ module HTMLProofer
115
121
  return false unless url.hash?
116
122
 
117
123
  hash = url.hash
124
+ headers = response.options.fetch(:headers, {})
125
+ content_type = headers.find { |k, _| k.casecmp("content-type").zero? }
126
+
127
+ # attempt to verify PDF hash ref; see #787 for more details
128
+ # FIXME: this is re-reading the PDF response
129
+ if content_type && content_type[1].include?("pdf")
130
+ io = URI.parse(url.to_s).open
131
+ reader = PDF::Reader.new(io)
132
+
133
+ pages = reader.pages
134
+ if hash =~ /\Apage=(\d+)\z/
135
+ page = Regexp.last_match[1].to_i
136
+
137
+ unless pages[page - 1]
138
+ msg = "External link #{href} failed: #{url.without_hash} exists, but the hash '#{hash}' does not"
139
+ add_failure(filenames, msg, response.code)
140
+ @cache.add_external(href, filenames, response.code, msg, false)
141
+ end
142
+
143
+ return true
144
+ end
145
+ end
118
146
 
119
147
  body_doc = create_nokogiri(response.body)
120
148
 
@@ -130,7 +158,7 @@ module HTMLProofer
130
158
 
131
159
  return unless body_doc.xpath(xpath.join("|")).empty?
132
160
 
133
- msg = "External link #{href} failed: #{url.sans_hash} exists, but the hash '#{hash}' does not"
161
+ msg = "External link #{href} failed: #{url.without_hash} exists, but the hash '#{hash}' does not"
134
162
  add_failure(filenames, msg, response.code)
135
163
  @cache.add_external(href, filenames, response.code, msg, false)
136
164
  true
@@ -29,15 +29,17 @@ module HTMLProofer
29
29
  matched_count_to_log = pluralize(matched_files.count, "reference", "references")
30
30
  @logger.log(:debug, "(#{i + 1} / #{links.count}) Internal link #{link}: Checking #{matched_count_to_log}")
31
31
  matched_files.each do |metadata|
32
- url = HTMLProofer::Attribute::Url.new(@runner, link, base_url: metadata[:base_url])
33
-
34
- @runner.current_source = metadata[:source]
35
- @runner.current_filename = metadata[:filename]
36
-
37
- target_file_path = url.absolute_path
38
- unless file_exists?(target_file_path)
39
- @failed_checks << Failure.new(@runner.current_filename, "Links > Internal",
40
- "internally linking to #{url}, which does not exist", line: metadata[:line], status: nil, content: nil)
32
+ url = HTMLProofer::Attribute::Url.new(@runner, link, base_url: metadata[:base_url], source: metadata[:source], filename: metadata[:filename])
33
+
34
+ unless url.exists?
35
+ @failed_checks << Failure.new(
36
+ metadata[:filename],
37
+ "Links > Internal",
38
+ "internally linking to #{url}, which does not exist",
39
+ line: metadata[:line],
40
+ status: nil,
41
+ content: nil,
42
+ )
41
43
  to_add << [url, metadata, false]
42
44
  next
43
45
  end
@@ -45,6 +47,7 @@ module HTMLProofer
45
47
  hash_exists = hash_exists_for_url?(url)
46
48
  if hash_exists.nil?
47
49
  # the hash needs to be checked in the target file, we collect the url and metadata
50
+ target_file_path = url.resolved_path
48
51
  unless file_paths_hashes_to_check.key?(target_file_path)
49
52
  file_paths_hashes_to_check[target_file_path] = {}
50
53
  end
@@ -55,8 +58,14 @@ module HTMLProofer
55
58
  next
56
59
  end
57
60
  unless hash_exists
58
- @failed_checks << Failure.new(@runner.current_filename, "Links > Internal",
59
- "internally linking to #{url}; the file exists, but the hash '#{url.hash}' does not", line: metadata[:line], status: nil, content: nil)
61
+ @failed_checks << Failure.new(
62
+ metadata[:filename],
63
+ "Links > Internal",
64
+ "internally linking to #{url}; the file exists, but the hash '#{url.hash}' does not",
65
+ line: metadata[:line],
66
+ status: nil,
67
+ content: nil,
68
+ )
60
69
  to_add << [url, metadata, false]
61
70
  next
62
71
  end
@@ -75,8 +84,14 @@ module HTMLProofer
75
84
  exists = hash_exists_in_html?(href_hash, html)
76
85
  url_metadata.each do |(url, metadata)|
77
86
  unless exists
78
- @failed_checks << Failure.new(metadata[:filename], "Links > Internal",
79
- "internally linking to #{url}; the file exists, but the hash '#{href_hash}' does not", line: metadata[:line], status: nil, content: nil)
87
+ @failed_checks << Failure.new(
88
+ metadata[:filename],
89
+ "Links > Internal",
90
+ "internally linking to #{url}; the file exists, but the hash '#{href_hash}' does not",
91
+ line: metadata[:line],
92
+ status: nil,
93
+ content: nil,
94
+ )
80
95
  end
81
96
  to_add << [url, metadata, exists]
82
97
  end
@@ -91,12 +106,6 @@ module HTMLProofer
91
106
  @failed_checks
92
107
  end
93
108
 
94
- private def file_exists?(absolute_path)
95
- return @runner.checked_paths[absolute_path] if @runner.checked_paths.key?(absolute_path)
96
-
97
- @runner.checked_paths[absolute_path] = File.exist?(absolute_path)
98
- end
99
-
100
109
  # verify the hash w/o just based on the URL, w/o looking at the target file
101
110
  # => returns nil if the has could not be verified
102
111
  private def hash_exists_for_url?(url)
@@ -111,9 +120,7 @@ module HTMLProofer
111
120
  decoded_href_hash = Addressable::URI.unescape(href_hash)
112
121
  fragment_ids = [href_hash, decoded_href_hash]
113
122
  # https://www.w3.org/TR/html5/single-page.html#scroll-to-fragid
114
- return true if fragment_ids.include?("top")
115
-
116
- nil
123
+ true if fragment_ids.include?("top")
117
124
  end
118
125
 
119
126
  private def hash_exists_in_html?(href_hash, html)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTMLProofer
4
- VERSION = "5.0.0"
4
+ VERSION = "5.0.9"
5
5
  end
@@ -4,7 +4,7 @@ module HTMLProofer
4
4
  # https://stackoverflow.com/a/8812293
5
5
  class XpathFunctions
6
6
  def case_sensitive_equals(node_set, str_to_match)
7
- node_set.find_all { |node| node.to_s.== str_to_match.to_s }
7
+ node_set.find_all { |node| node.to_s == str_to_match.to_s }
8
8
  end
9
9
  end
10
10
  end
data/lib/html_proofer.rb CHANGED
@@ -15,8 +15,11 @@ require "html_proofer/version"
15
15
  require "fileutils"
16
16
 
17
17
  if ENV.fetch("DEBUG", false)
18
- require "awesome_print"
19
18
  require "debug"
19
+ begin
20
+ require "amazing_print"
21
+ rescue LoadError # rubocop:disable Lint/SuppressedException
22
+ end
20
23
  end
21
24
 
22
25
  module HTMLProofer
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: html-proofer
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen Torikian
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-08 00:00:00.000000000 Z
11
+ date: 2024-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -24,6 +24,7 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.3'
27
+ force_ruby_platform: false
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: async
29
30
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +39,7 @@ dependencies:
38
39
  - - "~>"
39
40
  - !ruby/object:Gem::Version
40
41
  version: '2.1'
42
+ force_ruby_platform: false
41
43
  - !ruby/object:Gem::Dependency
42
44
  name: nokogiri
43
45
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +54,22 @@ dependencies:
52
54
  - - "~>"
53
55
  - !ruby/object:Gem::Version
54
56
  version: '1.13'
57
+ force_ruby_platform: false
58
+ - !ruby/object:Gem::Dependency
59
+ name: pdf-reader
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - "~>"
63
+ - !ruby/object:Gem::Version
64
+ version: '2.11'
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '2.11'
72
+ force_ruby_platform: false
55
73
  - !ruby/object:Gem::Dependency
56
74
  name: rainbow
57
75
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +84,7 @@ dependencies:
66
84
  - - "~>"
67
85
  - !ruby/object:Gem::Version
68
86
  version: '3.0'
87
+ force_ruby_platform: false
69
88
  - !ruby/object:Gem::Dependency
70
89
  name: typhoeus
71
90
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +99,7 @@ dependencies:
80
99
  - - "~>"
81
100
  - !ruby/object:Gem::Version
82
101
  version: '1.3'
102
+ force_ruby_platform: false
83
103
  - !ruby/object:Gem::Dependency
84
104
  name: yell
85
105
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +114,7 @@ dependencies:
94
114
  - - "~>"
95
115
  - !ruby/object:Gem::Version
96
116
  version: '2.0'
117
+ force_ruby_platform: false
97
118
  - !ruby/object:Gem::Dependency
98
119
  name: zeitwerk
99
120
  requirement: !ruby/object:Gem::Requirement
@@ -108,62 +129,7 @@ dependencies:
108
129
  - - "~>"
109
130
  - !ruby/object:Gem::Version
110
131
  version: '2.5'
111
- - !ruby/object:Gem::Dependency
112
- name: awesome_print
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
- - !ruby/object:Gem::Dependency
126
- name: debug
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: '0'
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: '0'
139
- - !ruby/object:Gem::Dependency
140
- name: rake
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - ">="
144
- - !ruby/object:Gem::Version
145
- version: '0'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - ">="
151
- - !ruby/object:Gem::Version
152
- version: '0'
153
- - !ruby/object:Gem::Dependency
154
- name: redcarpet
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- version: '0'
160
- type: :development
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - ">="
165
- - !ruby/object:Gem::Version
166
- version: '0'
132
+ force_ruby_platform: false
167
133
  - !ruby/object:Gem::Dependency
168
134
  name: rspec
169
135
  requirement: !ruby/object:Gem::Requirement
@@ -178,48 +144,6 @@ dependencies:
178
144
  - - "~>"
179
145
  - !ruby/object:Gem::Version
180
146
  version: '3.1'
181
- - !ruby/object:Gem::Dependency
182
- name: rubocop
183
- requirement: !ruby/object:Gem::Requirement
184
- requirements:
185
- - - ">="
186
- - !ruby/object:Gem::Version
187
- version: '0'
188
- type: :development
189
- prerelease: false
190
- version_requirements: !ruby/object:Gem::Requirement
191
- requirements:
192
- - - ">="
193
- - !ruby/object:Gem::Version
194
- version: '0'
195
- - !ruby/object:Gem::Dependency
196
- name: rubocop-rspec
197
- requirement: !ruby/object:Gem::Requirement
198
- requirements:
199
- - - ">="
200
- - !ruby/object:Gem::Version
201
- version: '0'
202
- type: :development
203
- prerelease: false
204
- version_requirements: !ruby/object:Gem::Requirement
205
- requirements:
206
- - - ">="
207
- - !ruby/object:Gem::Version
208
- version: '0'
209
- - !ruby/object:Gem::Dependency
210
- name: rubocop-standard
211
- requirement: !ruby/object:Gem::Requirement
212
- requirements:
213
- - - ">="
214
- - !ruby/object:Gem::Version
215
- version: '0'
216
- type: :development
217
- prerelease: false
218
- version_requirements: !ruby/object:Gem::Requirement
219
- requirements:
220
- - - ">="
221
- - !ruby/object:Gem::Version
222
- version: '0'
223
147
  - !ruby/object:Gem::Dependency
224
148
  name: timecop
225
149
  requirement: !ruby/object:Gem::Requirement
@@ -288,7 +212,7 @@ licenses:
288
212
  metadata:
289
213
  funding_uri: https://github.com/sponsors/gjtorikian/
290
214
  rubygems_mfa_required: 'true'
291
- post_install_message:
215
+ post_install_message:
292
216
  rdoc_options: []
293
217
  require_paths:
294
218
  - lib
@@ -306,8 +230,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
306
230
  - !ruby/object:Gem::Version
307
231
  version: '0'
308
232
  requirements: []
309
- rubygems_version: 3.3.7
310
- signing_key:
233
+ rubygems_version: 3.5.3
234
+ signing_key:
311
235
  specification_version: 4
312
236
  summary: A set of tests to validate your HTML output. These tests check if your image
313
237
  references are legitimate, if they have alt tags, if your internal links are working,