html-proofer 4.3.0 → 4.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e8afbf9c7f55055c38b77971166e852d6f7180481d1e9fb27f84e071c582d1a7
4
- data.tar.gz: 88db5f8e0bb8059ff3f6c50c8fcb4320bf0f6a4e1ef6fc053671591ff44a529f
3
+ metadata.gz: 98bde6dd5e32f42e5983fd1aaa7bf02d3359b40f2b4a8b5360a5826abf2cc674
4
+ data.tar.gz: 9b7d57fd18e625ab7cc91c46faceac510b05ba5290f665fb5dd333b92446b00b
5
5
  SHA512:
6
- metadata.gz: 3f765689616f7884c1a33685dceb08a0786c851839d4ec612023d3ed746ed7c7b7e9087bd91d8360d795fd54a2077fdbf9af9df2c0b25d246a9cc0a064579501
7
- data.tar.gz: 441a4f2b13e237505f9402a53fee5713a68d680824e6a976da42f26bfe1f57a0bdf661f6c3cafc45cb0fa28623c56d679c75044b10dd69db64ac824b7d911885
6
+ metadata.gz: a666be806bbb70028488b3ef89f1325d7b5faaec9f48e105220db2d2f7dd022c12dabac828949ffd2100bdc2b4e3bd4c1f9ce9df48bab17b9106d9453eb49f01
7
+ data.tar.gz: 8a58bb5b120ab3cf3eb0e692f3f52f4385bd5923d3a7c91e95dddd17d8b0e25d20dd207872099665ac37737afb6b4ed86d04d39bc7793c6af2abcd35390d2051
@@ -20,12 +20,13 @@ module HTMLProofer
20
20
 
21
21
  swap_urls!
22
22
  clean_url!
23
-
24
- # convert "//" links to "https://"
25
- @url.start_with?("//") ? @url = "https:#{@url}" : @url
26
23
  end
27
24
  end
28
25
 
26
+ def protocol_relative?
27
+ url.start_with?("//")
28
+ end
29
+
29
30
  def to_s
30
31
  @url
31
32
  end
@@ -16,7 +16,10 @@ module HTMLProofer
16
16
  return if immediate_redirect?
17
17
 
18
18
  if found
19
- if @favicon.url.remote?
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)
22
+ elsif @favicon.url.remote?
20
23
  add_to_external_urls(@favicon.url, @favicon.line)
21
24
  elsif !@favicon.url.exists?
22
25
  add_failure("internal favicon #{@favicon.url.raw_attribute} does not exist", line: @favicon.line,
@@ -6,7 +6,7 @@ module HTMLProofer
6
6
  SCREEN_SHOT_REGEX = /Screen(?: |%20)Shot(?: |%20)\d+-\d+-\d+(?: |%20)at(?: |%20)\d+.\d+.\d+/.freeze
7
7
 
8
8
  def run
9
- @html.css("img").each do |node|
9
+ @html.css("img, source").each do |node|
10
10
  @img = create_element(node)
11
11
 
12
12
  next if @img.ignore?
@@ -18,26 +18,22 @@ module HTMLProofer
18
18
  # does the image exist?
19
19
  if missing_src?
20
20
  add_failure("image has no src or srcset attribute", line: @img.line, content: @img.content)
21
+ 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)
21
24
  elsif @img.url.remote?
22
25
  add_to_external_urls(@img.url, @img.line)
23
26
  elsif !@img.url.exists? && !@img.multiple_srcsets? && !@img.multiple_sizes?
24
27
  add_failure("internal image #{@img.url.raw_attribute} does not exist", line: @img.line,
25
28
  content: @img.content)
26
- elsif @img.multiple_srcsets?
27
- @img.srcsets.each do |srcset|
28
- srcset_url = HTMLProofer::Attribute::Url.new(@runner, srcset, base_url: @img.base_url, extract_size: true)
29
-
30
- if srcset_url.remote?
31
- add_to_external_urls(srcset_url.url, @img.line)
32
- elsif !srcset_url.exists?
33
- add_failure("internal image #{srcset} does not exist", line: @img.line, content: @img.content)
34
- end
35
- end
36
- elsif @img.multiple_sizes?
29
+ elsif @img.multiple_srcsets? || @img.multiple_sizes?
37
30
  @img.srcsets_wo_sizes.each do |srcset|
38
31
  srcset_url = HTMLProofer::Attribute::Url.new(@runner, srcset, base_url: @img.base_url, extract_size: true)
39
32
 
40
- if srcset_url.remote?
33
+ 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)
36
+ elsif srcset_url.remote?
41
37
  add_to_external_urls(srcset_url.url, @img.line)
42
38
  elsif !srcset_url.exists?
43
39
  add_failure("internal image #{srcset} does not exist", line: @img.line, content: @img.content)
@@ -45,7 +41,8 @@ module HTMLProofer
45
41
  end
46
42
  end
47
43
 
48
- unless ignore_element?
44
+ # if this is an img element, check that the alt attribute is present
45
+ if @img.img_tag? && !ignore_element?
49
46
  if missing_alt_tag? && !ignore_missing_alt?
50
47
  add_failure("image #{@img.url.raw_attribute} does not have an alt attribute", line: @img.line,
51
48
  content: @img.content)
@@ -4,7 +4,7 @@ module HTMLProofer
4
4
  class Check
5
5
  class Links < HTMLProofer::Check
6
6
  def run
7
- @html.css("a, link, source").each do |node|
7
+ @html.css("a, link").each do |node|
8
8
  @link = create_element(node)
9
9
 
10
10
  next if @link.ignore?
@@ -28,6 +28,12 @@ module HTMLProofer
28
28
  next
29
29
  end
30
30
 
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)
34
+ next
35
+ end
36
+
31
37
  check_schemes
32
38
 
33
39
  # intentionally down here because we still want valid? & missing_href? to execute
@@ -16,6 +16,9 @@ module HTMLProofer
16
16
  add_failure("open graph content attribute is empty", line: @open_graph.line, content: @open_graph.content)
17
17
  elsif !@open_graph.url.valid?
18
18
  add_failure("#{@open_graph.src} is an invalid URL", line: @open_graph.line)
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)
19
22
  elsif @open_graph.url.remote?
20
23
  add_to_external_urls(@open_graph.url, @open_graph.line)
21
24
  else
@@ -13,8 +13,11 @@ module HTMLProofer
13
13
  # does the script exist?
14
14
  if missing_src?
15
15
  add_failure("script is empty and has no src attribute", line: @script.line, content: @script.content)
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)
16
19
  elsif @script.url.remote?
17
- add_to_external_urls(@script.src, @script.line)
20
+ add_to_external_urls(@script.url, @script.line)
18
21
  check_sri if @runner.check_sri?
19
22
  elsif !@script.url.exists?
20
23
  add_failure("internal script reference #{@script.src} does not exist", line: @script.line,
@@ -29,28 +29,10 @@ module HTMLProofer
29
29
  content: content)
30
30
  end
31
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
32
  def short_name
47
33
  self.class.name.split("::").last
48
34
  end
49
35
 
50
- def self.short_name
51
- name.split("::").last
52
- end
53
-
54
36
  def add_to_internal_urls(url, line)
55
37
  url_string = url.raw_attribute
56
38
 
@@ -74,6 +56,26 @@ module HTMLProofer
74
56
  @external_urls[url_string] << { filename: @runner.current_filename, line: line }
75
57
  end
76
58
 
59
+ class << self
60
+ def subchecks(runner_options)
61
+ # grab all known checks
62
+ checks = ObjectSpace.each_object(Class).select do |klass|
63
+ klass < self
64
+ end
65
+
66
+ # remove any checks not explicitly included
67
+ checks.each_with_object([]) do |check, arr|
68
+ next unless runner_options[:checks].include?(check.short_name)
69
+
70
+ arr << check
71
+ end
72
+ end
73
+
74
+ def short_name
75
+ name.split("::").last
76
+ end
77
+ end
78
+
77
79
  private def base_url
78
80
  return @base_url if defined?(@base_url)
79
81
 
@@ -47,42 +47,44 @@ module HTMLProofer
47
47
 
48
48
  CACHE_DEFAULTS = {}.freeze
49
49
 
50
- def self.generate_defaults(opts)
51
- options = PROOFER_DEFAULTS.merge(opts)
50
+ class << self
51
+ def generate_defaults(opts)
52
+ options = PROOFER_DEFAULTS.merge(opts)
52
53
 
53
- options[:typhoeus] = HTMLProofer::Configuration::TYPHOEUS_DEFAULTS.merge(opts[:typhoeus] || {})
54
- options[:hydra] = HTMLProofer::Configuration::HYDRA_DEFAULTS.merge(opts[:hydra] || {})
54
+ options[:typhoeus] = HTMLProofer::Configuration::TYPHOEUS_DEFAULTS.merge(opts[:typhoeus] || {})
55
+ options[:hydra] = HTMLProofer::Configuration::HYDRA_DEFAULTS.merge(opts[:hydra] || {})
55
56
 
56
- options[:parallel] = HTMLProofer::Configuration::PARALLEL_DEFAULTS.merge(opts[:parallel] || {})
57
- options[:cache] = HTMLProofer::Configuration::CACHE_DEFAULTS.merge(opts[:cache] || {})
57
+ options[:parallel] = HTMLProofer::Configuration::PARALLEL_DEFAULTS.merge(opts[:parallel] || {})
58
+ options[:cache] = HTMLProofer::Configuration::CACHE_DEFAULTS.merge(opts[:cache] || {})
58
59
 
59
- options.delete(:src)
60
+ options.delete(:src)
60
61
 
61
- options
62
- end
62
+ options
63
+ end
63
64
 
64
- def self.to_regex?(item)
65
- if item.start_with?("/") && item.end_with?("/")
66
- Regexp.new(item[1...-1])
67
- else
68
- item
65
+ def to_regex?(item)
66
+ if item.start_with?("/") && item.end_with?("/")
67
+ Regexp.new(item[1...-1])
68
+ else
69
+ item
70
+ end
69
71
  end
70
- end
71
72
 
72
- def self.parse_json_option(option_name, config, symbolize_names: true)
73
- raise ArgumentError, "Must provide an option name in string format." unless option_name.is_a?(String)
74
- raise ArgumentError, "Must provide an option name in string format." if option_name.strip.empty?
73
+ def parse_json_option(option_name, config, symbolize_names: true)
74
+ raise ArgumentError, "Must provide an option name in string format." unless option_name.is_a?(String)
75
+ raise ArgumentError, "Must provide an option name in string format." if option_name.strip.empty?
75
76
 
76
- return {} if config.nil?
77
+ return {} if config.nil?
77
78
 
78
- raise ArgumentError, "Must provide a JSON configuration in string format." unless config.is_a?(String)
79
+ raise ArgumentError, "Must provide a JSON configuration in string format." unless config.is_a?(String)
79
80
 
80
- return {} if config.strip.empty?
81
+ return {} if config.strip.empty?
81
82
 
82
- begin
83
- JSON.parse(config, { symbolize_names: symbolize_names })
84
- rescue StandardError
85
- raise ArgumentError, "Option '#{option_name} did not contain valid JSON."
83
+ begin
84
+ JSON.parse(config, { symbolize_names: symbolize_names })
85
+ rescue StandardError
86
+ raise ArgumentError, "Option '#{option_name} did not contain valid JSON."
87
+ end
86
88
  end
87
89
  end
88
90
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTMLProofer
4
- VERSION = "4.3.0"
4
+ VERSION = "4.4.0"
5
5
  end
data/lib/html_proofer.rb CHANGED
@@ -20,37 +20,39 @@ if ENV.fetch("DEBUG", false)
20
20
  end
21
21
 
22
22
  module HTMLProofer
23
- def self.check_file(file, options = {})
24
- raise ArgumentError unless file.is_a?(String)
25
- raise ArgumentError, "#{file} does not exist" unless File.exist?(file)
23
+ class << self
24
+ def check_file(file, options = {})
25
+ raise ArgumentError unless file.is_a?(String)
26
+ raise ArgumentError, "#{file} does not exist" unless File.exist?(file)
26
27
 
27
- options[:type] = :file
28
- HTMLProofer::Runner.new(file, options)
29
- end
28
+ options[:type] = :file
29
+ HTMLProofer::Runner.new(file, options)
30
+ end
30
31
 
31
- def self.check_directory(directory, options = {})
32
- raise ArgumentError unless directory.is_a?(String)
33
- raise ArgumentError, "#{directory} does not exist" unless Dir.exist?(directory)
32
+ def check_directory(directory, options = {})
33
+ raise ArgumentError unless directory.is_a?(String)
34
+ raise ArgumentError, "#{directory} does not exist" unless Dir.exist?(directory)
34
35
 
35
- options[:type] = :directory
36
- HTMLProofer::Runner.new([directory], options)
37
- end
36
+ options[:type] = :directory
37
+ HTMLProofer::Runner.new([directory], options)
38
+ end
38
39
 
39
- def self.check_directories(directories, options = {})
40
- raise ArgumentError unless directories.is_a?(Array)
40
+ def check_directories(directories, options = {})
41
+ raise ArgumentError unless directories.is_a?(Array)
41
42
 
42
- options[:type] = :directory
43
- directories.each do |directory|
44
- raise ArgumentError, "#{directory} does not exist" unless Dir.exist?(directory)
43
+ options[:type] = :directory
44
+ directories.each do |directory|
45
+ raise ArgumentError, "#{directory} does not exist" unless Dir.exist?(directory)
46
+ end
47
+ HTMLProofer::Runner.new(directories, options)
45
48
  end
46
- HTMLProofer::Runner.new(directories, options)
47
- end
48
49
 
49
- def self.check_links(links, options = {})
50
- raise ArgumentError unless links.is_a?(Array)
50
+ def check_links(links, options = {})
51
+ raise ArgumentError unless links.is_a?(Array)
51
52
 
52
- options[:type] = :links
53
- HTMLProofer::Runner.new(links, options)
53
+ options[:type] = :links
54
+ HTMLProofer::Runner.new(links, options)
55
+ end
54
56
  end
55
57
  end
56
58
 
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: 4.3.0
4
+ version: 4.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen Torikian
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-26 00:00:00.000000000 Z
11
+ date: 2022-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable