html-proofer 5.0.5 → 5.0.6

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: 5480fbee53b67ca43a6a5896919e8d6ab82bca8e31b20e0734b60c78a71747c6
4
- data.tar.gz: 12d2e42cefdab3b45b8048e7293e2bb0ccb2235fb6ac2b1722b662f1129a7202
3
+ metadata.gz: 96326ab8326657b7f11d5cc1baddfb82e8dcb115e4a2aa576161642ebbe9afe4
4
+ data.tar.gz: ce761e92fcfe407fa4711558b60a6453545cdba6b7b97f8be8da22109e451256
5
5
  SHA512:
6
- metadata.gz: 6789de48b6d391cb36fd1c7b05bba18960e1782e4ef20b9cf15c076efdb147231a7b56c7c79eaad0c4c267a58fbc3355cb8a063334a7224a8901ac8a594908d8
7
- data.tar.gz: 17fb7a62b96b1eb3b47dd297f8aedab8e02ffb2eacf3880d1b4ea7b0061ab4ba13dcc20993e76ba36d766d29f6ef8726823e5f73524b01eea5de96cba2455e96
6
+ metadata.gz: 2c66bf1334c00dacb13d2789f119cf5e0f2da723afd59e82948589e26e54e6e12bb5778b23cc758fe81a9d99d112c501e607eacc2247a3258bda60904d7ec65d
7
+ data.tar.gz: c81c4482233aa8fe86721de10332dc9f42ed03c4103ff0c77144a6fffc0f715e7964be2a0b935a04fbc586b4f78a2b445eebb9df5912076e910ab387add299cd
@@ -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
@@ -115,9 +118,29 @@ module HTMLProofer
115
118
  def exists?
116
119
  return true if base64?
117
120
 
118
- return @runner.checked_paths[absolute_path] if @runner.checked_paths.key?(absolute_path)
121
+ !resolved_path.nil?
122
+ end
123
+
124
+ def resolved_path
125
+ path_to_resolve = absolute_path
126
+
127
+ return @runner.resolved_paths[path_to_resolve] if @runner.resolved_paths.key?(path_to_resolve)
128
+
129
+ # extensionless URLs
130
+ path_with_extension = "#{path_to_resolve}#{@runner.options[:assume_extension]}"
131
+ resolved = if @runner.options[:assume_extension] && File.file?(path_with_extension)
132
+ path_with_extension # existence checked implicitly by File.file?
133
+ # implicit index support
134
+ elsif File.directory?(path_to_resolve) && !unslashed_directory?(path_to_resolve)
135
+ path_with_index = File.join(path_to_resolve, @runner.options[:directory_index_file])
136
+ path_with_index if File.file?(path_with_index)
137
+ # explicit file or directory
138
+ elsif File.exist?(path_to_resolve)
139
+ path_to_resolve
140
+ end
141
+ @runner.resolved_paths[path_to_resolve] = resolved
119
142
 
120
- @runner.checked_paths[absolute_path] = File.exist?(absolute_path)
143
+ resolved
121
144
  end
122
145
 
123
146
  def base64?
@@ -125,47 +148,23 @@ module HTMLProofer
125
148
  end
126
149
 
127
150
  def absolute_path
128
- path = file_path || @runner.current_filename
151
+ path = full_path || @filename
129
152
 
130
153
  File.expand_path(path, Dir.pwd)
131
154
  end
132
155
 
133
- def file_path
156
+ def full_path
134
157
  return if path.nil? || path.empty?
135
158
 
136
- path_dot_ext = ""
137
-
138
- path_dot_ext = path + @runner.options[:assume_extension] unless blank?(@runner.options[:assume_extension])
139
-
140
159
  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(
145
- path,
146
- @runner.current_source,
147
- )) || File.exist?(File.expand_path(path_dot_ext, @runner.current_source))
148
- File.dirname(@runner.current_filename)
149
- # relative links in nested dir, path is a file
150
- elsif File.exist?(File.join(
151
- File.dirname(@runner.current_filename),
152
- path,
153
- )) || File.exist?(File.join(File.dirname(@runner.current_filename), path_dot_ext))
154
- File.dirname(@runner.current_filename)
155
- # relative link, path is a directory
160
+ # either overwrite with root_dir; or, if source is directory, use that; or, just get the source file's dirname
161
+ @runner.options[:root_dir] || (File.directory?(@source) ? @source : File.dirname(@source))
156
162
  else
157
- @runner.current_filename
158
- end
159
-
160
- file = File.join(base, path)
161
-
162
- if @runner.options[:assume_extension] && File.file?("#{file}#{@runner.options[:assume_extension]}")
163
- file = "#{file}#{@runner.options[:assume_extension]}"
164
- elsif File.directory?(file) && !unslashed_directory?(file) # implicit index support
165
- file = File.join(file, @runner.options[:directory_index_file])
163
+ # path relative to the file where the link is defined
164
+ File.dirname(@filename)
166
165
  end
167
166
 
168
- file
167
+ File.join(base, path)
169
168
  end
170
169
 
171
170
  def unslashed_directory?(file)
@@ -19,16 +19,14 @@ module HTMLProofer
19
19
  if @favicon.url.protocol_relative?
20
20
  add_failure(
21
21
  "favicon link #{@favicon.url} is a protocol-relative URL, use explicit https:// instead",
22
- line: @favicon.line,
23
- content: @favicon.content,
22
+ element: @favicon,
24
23
  )
25
24
  elsif @favicon.url.remote?
26
25
  add_to_external_urls(@favicon.url, @favicon.line)
27
26
  elsif !@favicon.url.exists?
28
27
  add_failure(
29
28
  "internal favicon #{@favicon.url.raw_attribute} does not exist",
30
- line: @favicon.line,
31
- content: @favicon.content,
29
+ element: @favicon,
32
30
  )
33
31
  end
34
32
  else
@@ -14,41 +14,37 @@ module HTMLProofer
14
14
  # screenshot filenames should return because of terrible names
15
15
  add_failure(
16
16
  "image has a terrible filename (#{@img.url.raw_attribute})",
17
- line: @img.line,
18
- content: @img.content,
17
+ element: @img,
19
18
  ) if terrible_filename?
20
19
 
21
20
  # does the image exist?
22
21
  if missing_src?
23
- 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)
24
23
  elsif @img.url.protocol_relative?
25
24
  add_failure(
26
25
  "image link #{@img.url} is a protocol-relative URL, use explicit https:// instead",
27
- line: @img.line,
28
- content: @img.content,
26
+ element: @img,
29
27
  )
30
28
  elsif @img.url.remote?
31
29
  add_to_external_urls(@img.url, @img.line)
32
30
  elsif !@img.url.exists? && !@img.multiple_srcsets? && !@img.multiple_sizes?
33
31
  add_failure(
34
32
  "internal image #{@img.url.raw_attribute} does not exist",
35
- line: @img.line,
36
- content: @img.content,
33
+ element: @img,
37
34
  )
38
35
  elsif @img.multiple_srcsets? || @img.multiple_sizes?
39
36
  @img.srcsets_wo_sizes.each do |srcset|
40
- 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)
41
38
 
42
39
  if srcset_url.protocol_relative?
43
40
  add_failure(
44
41
  "image link #{srcset_url.url} is a protocol-relative URL, use explicit https:// instead",
45
- line: @img.line,
46
- content: @img.content,
42
+ element: @img,
47
43
  )
48
44
  elsif srcset_url.remote?
49
45
  add_to_external_urls(srcset_url.url, @img.line)
50
46
  elsif !srcset_url.exists?
51
- 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)
52
48
  end
53
49
  end
54
50
  end
@@ -58,22 +54,19 @@ module HTMLProofer
58
54
  if missing_alt_tag? && !ignore_missing_alt?
59
55
  add_failure(
60
56
  "image #{@img.url.raw_attribute} does not have an alt attribute",
61
- line: @img.line,
62
- content: @img.content,
57
+ element: @img,
63
58
  )
64
59
  elsif (empty_alt_tag? || alt_all_spaces?) && !ignore_empty_alt?
65
60
  add_failure(
66
61
  "image #{@img.url.raw_attribute} has an alt attribute, but no content",
67
- line: @img.line,
68
- content: @img.content,
62
+ element: @img,
69
63
  )
70
64
  end
71
65
  end
72
66
 
73
67
  add_failure(
74
68
  "image #{@img.url.raw_attribute} uses the http scheme",
75
- line: @img.line,
76
- content: @img.content,
69
+ element: @img,
77
70
  ) if @runner.enforce_https? && @img.url.http?
78
71
  end
79
72
 
@@ -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,21 +18,20 @@ 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
32
  add_failure(
33
33
  "#{@link.url} is a protocol-relative URL, use explicit https:// instead",
34
- line: @link.line,
35
- content: @link.content,
34
+ element: @link,
36
35
  )
37
36
  next
38
37
  end
@@ -50,7 +49,7 @@ module HTMLProofer
50
49
  next if @link.node["rel"] == "dns-prefetch"
51
50
 
52
51
  unless @link.url.path?
53
- 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)
54
53
  next
55
54
  end
56
55
 
@@ -60,8 +59,7 @@ module HTMLProofer
60
59
  if @link.url.unslashed_directory?(@link.url.absolute_path)
61
60
  add_failure(
62
61
  "internally linking to a directory #{@link.url.raw_attribute} without trailing slash",
63
- line: @link.line,
64
- content: @link.content,
62
+ element: @link,
65
63
  )
66
64
  next
67
65
  end
@@ -88,7 +86,7 @@ module HTMLProofer
88
86
  when "http"
89
87
  return unless @runner.options[:enforce_https]
90
88
 
91
- 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)
92
90
  end
93
91
  end
94
92
 
@@ -96,14 +94,12 @@ module HTMLProofer
96
94
  if @link.url.path.empty?
97
95
  add_failure(
98
96
  "#{@link.url.raw_attribute} contains no email address",
99
- line: @link.line,
100
- content: @link.content,
97
+ element: @link,
101
98
  ) unless ignore_empty_mailto?
102
99
  elsif !/#{URI::MailTo::EMAIL_REGEXP}/o.match?(@link.url.path)
103
100
  add_failure(
104
101
  "#{@link.url.raw_attribute} contains an invalid email address",
105
- line: @link.line,
106
- content: @link.content,
102
+ element: @link,
107
103
  )
108
104
  end
109
105
  end
@@ -111,8 +107,7 @@ module HTMLProofer
111
107
  def handle_tel
112
108
  add_failure(
113
109
  "#{@link.url.raw_attribute} contains no phone number",
114
- line: @link.line,
115
- content: @link.content,
110
+ element: @link,
116
111
  ) if @link.url.path.empty?
117
112
  end
118
113
 
@@ -130,16 +125,14 @@ module HTMLProofer
130
125
  if blank?(@link.node["integrity"]) && blank?(@link.node["crossorigin"])
131
126
  add_failure(
132
127
  "SRI and CORS not provided in: #{@link.url.raw_attribute}",
133
- line: @link.line,
134
- content: @link.content,
128
+ element: @link,
135
129
  )
136
130
  elsif blank?(@link.node["integrity"])
137
- add_failure("Integrity is missing in: #{@link.url.raw_attribute}", line: @link.line, content: @link.content)
131
+ add_failure("Integrity is missing in: #{@link.url.raw_attribute}", element: @link)
138
132
  elsif blank?(@link.node["crossorigin"])
139
133
  add_failure(
140
134
  "CORS not provided for external resource in: #{@link.link.url.raw_attribute}",
141
- line: @link.line,
142
- content: @link.content,
135
+ element: @link,
143
136
  )
144
137
  end
145
138
  end
@@ -11,24 +11,22 @@ 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
20
  add_failure(
21
21
  "open graph link #{@open_graph.url} is a protocol-relative URL, use explicit https:// instead",
22
- line: @open_graph.line,
23
- content: @open_graph.content,
22
+ element: @open_graph,
24
23
  )
25
24
  elsif @open_graph.url.remote?
26
25
  add_to_external_urls(@open_graph.url, @open_graph.line)
27
26
  else
28
27
  add_failure(
29
28
  "internal open graph #{@open_graph.url.raw_attribute} does not exist",
30
- line: @open_graph.line,
31
- content: @open_graph.content,
29
+ element: @open_graph,
32
30
  ) unless @open_graph.url.exists?
33
31
  end
34
32
  end
@@ -12,12 +12,11 @@ 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
17
  add_failure(
18
18
  "script link #{@script.url} is a protocol-relative URL, use explicit https:// instead",
19
- line: @script.line,
20
- content: @script.content,
19
+ element: @script,
21
20
  )
22
21
  elsif @script.url.remote?
23
22
  add_to_external_urls(@script.url, @script.line)
@@ -25,8 +24,7 @@ module HTMLProofer
25
24
  elsif !@script.url.exists?
26
25
  add_failure(
27
26
  "internal script reference #{@script.src} does not exist",
28
- line: @script.line,
29
- content: @script.content,
27
+ element: @script,
30
28
  )
31
29
  end
32
30
  end
@@ -42,20 +40,17 @@ module HTMLProofer
42
40
  if blank?(@script.node["integrity"]) && blank?(@script.node["crossorigin"])
43
41
  add_failure(
44
42
  "SRI and CORS not provided in: #{@script.url.raw_attribute}",
45
- line: @script.line,
46
- content: @script.content,
43
+ element: @script,
47
44
  )
48
45
  elsif blank?(@script.node["integrity"])
49
46
  add_failure(
50
47
  "Integrity is missing in: #{@script.url.raw_attribute}",
51
- line: @script.line,
52
- content: @script.content,
48
+ element: @script,
53
49
  )
54
50
  elsif blank?(@script.node["crossorigin"])
55
51
  add_failure(
56
52
  "CORS not provided for external resource in: #{@script.url.raw_attribute}",
57
- line: @script.line,
58
- content: @script.content,
53
+ element: @script,
59
54
  )
60
55
  end
61
56
  end
@@ -24,14 +24,14 @@ 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)
27
+ def add_failure(description, element: nil, line: nil, status: nil, content: nil)
28
28
  @failures << Failure.new(
29
29
  @runner.current_filename,
30
30
  short_name,
31
31
  description,
32
- line: line,
32
+ line: element.nil? ? line : element.line,
33
33
  status: status,
34
- content: content,
34
+ content: element.nil? ? content : element.content,
35
35
  )
36
36
  end
37
37
 
@@ -45,8 +45,8 @@ module HTMLProofer
45
45
  @internal_urls[url_string] = [] if @internal_urls[url_string].nil?
46
46
 
47
47
  metadata = {
48
- source: @runner.current_source,
49
- filename: @runner.current_filename,
48
+ source: url.source,
49
+ filename: url.filename,
50
50
  line: line,
51
51
  base_url: base_url,
52
52
  found: false,
@@ -59,7 +59,7 @@ module HTMLProofer
59
59
 
60
60
  @external_urls[url_string] = [] if @external_urls[url_string].nil?
61
61
 
62
- @external_urls[url_string] << { filename: @runner.current_filename, line: line }
62
+ @external_urls[url_string] << { filename: url.filename, line: line }
63
63
  end
64
64
 
65
65
  class << self
@@ -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
@@ -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
@@ -29,15 +29,11 @@ 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])
32
+ url = HTMLProofer::Attribute::Url.new(@runner, link, base_url: metadata[:base_url], source: metadata[:source], filename: metadata[:filename])
33
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)
34
+ unless url.exists?
39
35
  @failed_checks << Failure.new(
40
- @runner.current_filename,
36
+ metadata[:filename],
41
37
  "Links > Internal",
42
38
  "internally linking to #{url}, which does not exist",
43
39
  line: metadata[:line],
@@ -51,6 +47,7 @@ module HTMLProofer
51
47
  hash_exists = hash_exists_for_url?(url)
52
48
  if hash_exists.nil?
53
49
  # the hash needs to be checked in the target file, we collect the url and metadata
50
+ target_file_path = url.resolved_path
54
51
  unless file_paths_hashes_to_check.key?(target_file_path)
55
52
  file_paths_hashes_to_check[target_file_path] = {}
56
53
  end
@@ -62,7 +59,7 @@ module HTMLProofer
62
59
  end
63
60
  unless hash_exists
64
61
  @failed_checks << Failure.new(
65
- @runner.current_filename,
62
+ metadata[:filename],
66
63
  "Links > Internal",
67
64
  "internally linking to #{url}; the file exists, but the hash '#{url.hash}' does not",
68
65
  line: metadata[:line],
@@ -109,12 +106,6 @@ module HTMLProofer
109
106
  @failed_checks
110
107
  end
111
108
 
112
- private def file_exists?(absolute_path)
113
- return @runner.checked_paths[absolute_path] if @runner.checked_paths.key?(absolute_path)
114
-
115
- @runner.checked_paths[absolute_path] = File.exist?(absolute_path)
116
- end
117
-
118
109
  # verify the hash w/o just based on the URL, w/o looking at the target file
119
110
  # => returns nil if the has could not be verified
120
111
  private def hash_exists_for_url?(url)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTMLProofer
4
- VERSION = "5.0.5"
4
+ VERSION = "5.0.6"
5
5
  end
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.5
4
+ version: 5.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen Torikian
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-07 00:00:00.000000000 Z
11
+ date: 2023-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable