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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f27b5c50ae5c1c77d5fbe36dbbdca327bcb96302912b726f7f955f643d1dfc48
4
- data.tar.gz: f09405cd0c70f1d2dc98f904c388bcab594f79107fdbe441c63f934821bef1b0
3
+ metadata.gz: b6e8293984dde05cd406326b21fa93a638b0a972efdab95f59adfe606e8be538
4
+ data.tar.gz: b9323e2c6d7cf91a08f45a7b9ecb77b826c02b2ad2b6bf74e6951701b41c3bdb
5
5
  SHA512:
6
- metadata.gz: 53a8c98438f2056e7e2d926e926e10a6d0aa840b1b6f790860631912a2146dc20c68ca2b303d799a8fbfa723476e0e95dd5bc89695ceddf09ecede6f9acafbd1
7
- data.tar.gz: f68269ba70facf5ede07452d1029f49d17baadff8c6b4fd1d9de520c0ede91ff360bacb0cf46b5c719c0ae35c50ad61c6ce5b36171867f6a1c9d8c675d805ebc
6
+ metadata.gz: 6d962362e0abc03b2deb15da0b06f2e42857756b045db568c959263346957b628dee73b719de2ed9f24a03762e2ae97e2ff208ea8f216c6d0323bca77864d43d
7
+ data.tar.gz: 2bb9ca1d81e1a088344c8df0c513d2c0e96187cf23b7895def81fba4ac1820f56a0099a4dd2caa861df6ecd75afd4388e4a5b37e18308c4641300ab07f704d15
data/bin/htmlproofer CHANGED
@@ -15,44 +15,33 @@ Mercenary.program(:htmlproofer) do |p|
15
15
 
16
16
  p.description 'Runs the HTML-Proofer suite on the files in PATH. For more details, see the README.'
17
17
 
18
- p.option 'allow_missing_href', '--allow-missing-href', 'If `true`, does not flag `a` tags missing `href` (this is the default for HTML5).'
19
- p.option 'allow_hash_href', '--allow-hash-href', 'If `true`, ignores the `href="#"`'
18
+ p.option 'allow_hash_href', '--allow-hash-href', 'If `true`, assumes `href="#"` anchors are valid'
19
+ p.option 'allow_missing_href', '--allow-missing-href', 'If `true`, does not flag `a` tags missing `href`. In HTML5, this is technically allowed, but could also be human error.'
20
20
  p.option 'as_links', '--as-links', 'Assumes that `PATH` is a comma-separated array of links to check.'
21
- p.option 'alt_ignore', '--alt-ignore image1,[image2,...]', Array, 'A comma-separated list of Strings or RegExps containing `img`s whose missing `alt` tags are safe to ignore'
22
- p.option 'assume_extension', '--assume-extension', 'Automatically add extension (e.g. `.html`) to file paths, to allow extensionless URLs (as supported by Jekyll 3 and GitHub Pages) (default: `false`).'
23
- p.option 'checks_to_ignore', '--checks-to-ignore check1,[check2,...]', Array, 'A comma-separated list of Strings indicating which checks you do not want to run (default: `[]`)'
24
- p.option 'check_external_hash', '--check-external-hash', 'Checks whether external hashes exist (even if the webpage exists). This slows the checker down (default: `false`).'
25
- p.option 'check_favicon', '--check-favicon', 'Enables the favicon checker (default: `false`).'
26
- p.option 'check_html', '--check-html', 'Enables HTML validation errors from Nokogumbo (default: `false`).'
27
- p.option 'check_img_http', '--check-img-http', 'Fails an image if it\'s marked as `http` (default: `false`).'
28
- p.option 'check_opengraph', '--check-opengraph', 'Enables the Open Graph checker (default: `false`).'
21
+ p.option 'assume_extension', '--assume-extension <ext>', 'Automatically add specified extension to files for internal links, to allow extensionless URLs (as supported by most servers) (default: `.html`).'
22
+ p.option 'checks', '--checks check1,[check2,...]', Array, 'A comma-separated list of Strings indicating which checks you want to run (default: `["Links", "Images", "Scripts"]`)'
23
+ p.option 'check_external_hash', '--check-external-hash', 'Checks whether external hashes exist (even if the webpage exists) (default: `true`).'
29
24
  p.option 'check_sri', '--check-sri', 'Check that `<link>` and `<script>` external resources use SRI (default: `false`).'
30
25
  p.option 'directory_index_file', '--directory-index-file <filename>', String, 'Sets the file to look for when a link refers to a directory. (default: `index.html`)'
31
- p.option 'disable_external', '--disable-external', 'If `true`, does not run the external link checker, which can take a lot of time (default: `false`)'
32
- p.option 'empty_alt_ignore', '--empty-alt-ignore', 'If `true`, ignores images with empty alt tags'
33
- p.option 'error_sort', '--error-sort <sort>', String, 'Defines the sort order for error output. Can be `:path`, `:desc`, or `:status` (default: `:path`).'
34
- p.option 'enforce_https', '--enforce-https', 'Fails a link if it\'s not marked as `https` (default: `false`).'
35
- p.option 'extension', '--extension <ext>', String, 'The extension of your HTML files including the dot. (default: `.html`)'
36
- p.option 'external_only', '--external_only', 'Only checks problems with external references'
37
- p.option 'file_ignore', '--file-ignore file1,[file2,...]', Array, 'A comma-separated list of Strings or RegExps containing file paths that are safe to ignore'
38
- p.option 'http_status_ignore', '--http-status-ignore 123,[xxx, ...]', Array, 'A comma-separated list of numbers representing status codes to ignore.'
39
- p.option 'internal_domains', '--internal-domains domain1,[domain2,...]', Array, 'A comma-separated list of Strings containing domains that will be treated as internal urls.'
26
+ p.option 'disable_external', '--disable-external', 'If `true`, does not run the external link checker (default: `false`)'
27
+ p.option 'enforce_https', '--enforce-https', 'Fails a link if it\'s not marked as `https` (default: `true`).'
28
+ p.option 'extensions', '--extensions ext1,[ext2,...[', Array, 'A comma-separated list of Strings indicating the file extensions you would like to check (including the dot) (default: `.html`)'
29
+ p.option 'ignore_empty_alt', '--ignore-empty-alt', ' If `true`, ignores images with empty/missing alt tags (in other words, `<img alt>` and `<img alt="">` are valid; set this to `false` to flag those)'
30
+ p.option 'ignore_files', '--ignore-files file1,[file2,...]', Array, 'A comma-separated list of Strings or RegExps containing file paths that are safe to ignore'
40
31
  p.option 'ignore_empty_mailto', '--ignore-empty-mailto', 'If `true`, allows `mailto:` `href`s which do not contain an email address'
41
- p.option 'report_invalid_tags', '--report-invalid-tags', 'When `check_html` is enabled, HTML markup that is unknown to Nokogumbo are reported as errors (default: `false`)'
42
- p.option 'report_missing_names', '--report-missing-names', 'When `check_html` is enabled, HTML markup that are missing entity names are reported as errors (default: `false`)'
43
- p.option 'report_script_embeds', '--report-script-embeds', 'When `check_html` is enabled, `script` tags containing markup are reported as errors (default: `false`)'
44
- p.option 'report_missing_doctype', '--report-missing-doctype', 'When `check_html` is enabled, HTML markup with missing or out-of-order `DOCTYPE` are reported as errors (default: `false`)'
45
- p.option 'report_eof_tags', '--report-eof-tags', 'When `check_html` is enabled, HTML markup with tags that are malformed are reported as errors (default: `false`)'
46
- p.option 'report_mismatched_tags', '--report-mismatched-tags', 'When `check_html` is enabled, HTML markup with mismatched tags are reported as errors (default: `false`)'
32
+ p.option 'ignore_missing_alt', '--ignore-missing-alt', 'If `true`, ignores images with missing alt tags'
33
+ p.option 'ignore_status_codes', '--ignore-status-codes 123,[xxx, ...]', Array, 'A comma-separated list of numbers representing status codes to ignore.'
34
+ p.option 'ignore_urls', '--ignore-urls link1,[link2,...]', Array, 'A comma-separated list of Strings or RegExps containing URLs that are safe to ignore. This affects all HTML attributes, such as `alt` tags on images.'
47
35
  p.option 'log_level', '--log-level <level>', String, 'Sets the logging level, as determined by Yell. One of `:debug`, `:info`, `:warn`, `:error`, or `:fatal`. (default: `:info`)'
48
36
  p.option 'only_4xx', '--only-4xx', 'Only reports errors for links that fall within the 4xx status code range'
49
- p.option 'storage_dir', '--storage-dir PATH', String, 'Directory where to store the cache log (default: "tmp/.htmlproofer")'
50
- p.option 'timeframe', '--timeframe <time>', String, 'A string representing the caching timeframe.'
51
- p.option 'typhoeus_config', '--typhoeus-config CONFIG', String, 'JSON-formatted string of Typhoeus config. Will override the html-proofer defaults.'
52
- p.option 'hydra_config', '--hydra-config CONFIG', String, 'JSON-formatted string of Hydra config. Will override the html-proofer defaults.'
53
- p.option 'url_ignore', '--url-ignore link1,[link2,...]', Array, 'A comma-separated list of Strings or RegExps containing URLs that are safe to ignore. It affects all HTML attributes. Note that non-HTTP(S) URIs are always ignored'
54
- p.option 'url_swap', '--url-swap re:string,[re:string,...]', Array, 'A comma-separated list containing key-value pairs of `RegExp => String`. It transforms URLs that match `RegExp` into `String` via `gsub`. The escape sequences `\\:` should be used to produce literal `:`s.'
55
37
  p.option 'root_dir', '--root-dir PATH', String, 'The absolute path to the directory serving your html-files.'
38
+ p.option 'swap_attributes', '--swap-attributes CONFIG', String, 'JSON-formatted config that maps element names to the preferred attribute to check (default: `{}`).'
39
+ p.option 'swap_urls', '--swap-urls re:string,[re:string,...]', Array, 'A comma-separated list containing key-value pairs of `RegExp => String`. It transforms URLs that match `RegExp` into `String` via `gsub`. The escape sequences `\\:` should be used to produce literal `:`s.'
40
+
41
+ p.option 'typhoeus', '--typhoeus CONFIG', String, 'JSON-formatted string of Typhoeus config. Will override the html-proofer defaults.'
42
+ p.option 'hydra', '--hydra CONFIG', String, 'JSON-formatted string of Hydra config. Will override the html-proofer defaults.'
43
+ p.option 'parallel', '--parallel CONFIG', String, 'JSON-formatted string of Parallel config. Will override the html-proofer defaults.'
44
+ p.option 'cache', '--cache CONFIG', String, 'JSON-formatted string of cache config. Will override the html-proofer defaults.'
56
45
 
57
46
  p.action do |args, opts|
58
47
  args = ['.'] if args.empty?
@@ -67,46 +56,31 @@ Mercenary.program(:htmlproofer) do |p|
67
56
  end
68
57
 
69
58
  # some minor manipulation of a special option
70
- unless opts['url_swap'].nil?
71
- options[:url_swap] = {}
72
- opts['url_swap'].each do |s|
59
+ unless opts['swap_urls'].nil?
60
+ options[:swap_urls] = {}
61
+ opts['swap_urls'].each do |s|
73
62
  splt = s.split(/(?<!\\):/, 2)
74
63
 
75
64
  re = splt[0].gsub(/\\:/, ':')
76
65
  string = splt[1].gsub(/\\:/, ':')
77
- options[:url_swap][Regexp.new(re)] = string
66
+ options[:swap_urls][Regexp.new(re)] = string
78
67
  end
79
68
  end
80
69
 
81
- options[:error_sort] = opts['error-sort'].to_sym unless opts['error-sort'].nil?
82
70
  options[:log_level] = opts['log_level'].to_sym unless opts['log_level'].nil?
83
71
 
84
- options[:validation] = HTMLProofer::Configuration::VALIDATION_DEFAULTS.dup
85
- options[:validation][:report_script_embeds] = opts['report_script_embeds'] unless opts['report_script_embeds'].nil?
86
- options[:validation][:report_missing_names] = opts['report_missing_names'] unless opts['report_missing_names'].nil?
87
- options[:validation][:report_invalid_tags] = opts['report_invalid_tags'] unless opts['report_invalid_tags'].nil?
88
- options[:validation][:report_missing_doctype] = opts['report_missing_doctype'] unless opts['report_missing_doctype'].nil?
89
- options[:validation][:report_eof_tags] = opts['report_eof_tags'] unless opts['report_eof_tags'].nil?
90
- options[:validation][:report_mismatched_tags] = opts['report_mismatched_tags'] unless opts['report_mismatched_tags'].nil?
91
-
92
- options[:typhoeus] = HTMLProofer::Configuration.parse_json_option('typhoeus_config', opts['typhoeus_config'], symbolize_names: false) unless opts['typhoeus_config'].nil?
93
- options[:hydra] = HTMLProofer::Configuration.parse_json_option('hydra_config', opts['hydra_config']) unless opts['hydra_config'].nil?
72
+ options[:typhoeus] = HTMLProofer::Configuration.parse_json_option('typhoeus', opts['typhoeus'], symbolize_names: false) unless opts['typhoeus'].nil?
73
+ options[:hydra] = HTMLProofer::Configuration.parse_json_option('hydra', opts['hydra']) unless opts['hydra'].nil?
74
+ options[:parallel] = HTMLProofer::Configuration.parse_json_option('parallel', opts['parallel']) unless opts['parallel'].nil?
75
+ options[:cache] = HTMLProofer::Configuration.parse_json_option('cache', opts['cache']) unless opts['cache'].nil?
94
76
 
95
- unless opts['timeframe'].nil?
96
- options[:cache] ||= {}
97
- options[:cache][:timeframe] = opts['timeframe'] unless opts['timeframe'].nil?
98
- end
99
-
100
- unless opts['storage_dir'].nil?
101
- options[:cache] ||= {}
102
- options[:cache][:storage_dir] = opts['storage_dir'] unless opts['storage_dir'].nil?
103
- end
77
+ options[:swap_attributes] = HTMLProofer::Configuration.parse_json_option('swap_attributes', opts['swap_attributes'], symbolize_names: false) unless opts['swap_attributes'].nil?
104
78
 
105
- options[:http_status_ignore] = Array(options[:http_status_ignore]).map(&:to_i)
79
+ options[:ignore_status_codes] = Array(options[:ignore_status_codes]).map(&:to_i)
106
80
 
107
81
  paths = path.split(',')
108
82
  if opts['as_links']
109
- links = path.delete(' ').split(',')
83
+ links = path.split(',').map(&:strip)
110
84
  HTMLProofer.check_links(links, options).run
111
85
  elsif File.directory?(paths.first)
112
86
  HTMLProofer.check_directories(paths, options).run
data/lib/html-proofer.rb CHANGED
@@ -1,56 +1,3 @@
1
- # rubocop:disable Naming/FileName
2
1
  # frozen_string_literal: true
3
2
 
4
- def require_all(path)
5
- dir = File.join(File.dirname(__FILE__), path)
6
- Dir[File.join(dir, '*.rb')].sort.each do |f|
7
- require f
8
- end
9
- end
10
-
11
- require_relative 'html-proofer/utils'
12
- require_all 'html-proofer'
13
- require_all 'html-proofer/check'
14
-
15
- require 'parallel'
16
- require 'fileutils'
17
-
18
- begin
19
- require 'awesome_print'
20
- require 'pry-byebug'
21
- rescue LoadError; end # rubocop:disable Lint/SuppressedException
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)
26
-
27
- options[:type] = :file
28
- HTMLProofer::Runner.new(file, options)
29
- end
30
-
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)
34
-
35
- options[:type] = :directory
36
- HTMLProofer::Runner.new([directory], options)
37
- end
38
-
39
- def self.check_directories(directories, options = {})
40
- raise ArgumentError unless directories.is_a?(Array)
41
-
42
- options[:type] = :directory
43
- directories.each do |directory|
44
- raise ArgumentError, "#{directory} does not exist" unless Dir.exist?(directory)
45
- end
46
- HTMLProofer::Runner.new(directories, options)
47
- end
48
-
49
- def self.check_links(links, options = {})
50
- raise ArgumentError unless links.is_a?(Array)
51
-
52
- options[:type] = :links
53
- HTMLProofer::Runner.new(links, options)
54
- end
55
- end
56
- # rubocop:enable Naming/FileName
3
+ require_relative "html_proofer"
@@ -0,0 +1,237 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTMLProofer
4
+ class Attribute
5
+ class Url < HTMLProofer::Attribute
6
+ attr_reader :url
7
+
8
+ REMOTE_SCHEMES = ["http", "https"].freeze
9
+
10
+ def initialize(runner, link_attribute, base_url: nil)
11
+ super
12
+
13
+ if @raw_attribute.nil?
14
+ @url = nil
15
+ else
16
+ @url = @raw_attribute.delete("\u200b").strip
17
+ @url = Addressable::URI.join(base_url, @url).to_s unless blank?(base_url)
18
+
19
+ swap_urls!
20
+ clean_url!
21
+
22
+ # convert "//" links to "https://"
23
+ @url.start_with?("//") ? @url = "https:#{@url}" : @url
24
+ end
25
+ end
26
+
27
+ def to_s
28
+ @url
29
+ end
30
+
31
+ def known_extension?
32
+ return true if hash_link?
33
+
34
+ ext = File.extname(path)
35
+
36
+ # no extension means we use the assumed one
37
+ return @runner.options[:extensions].include?(@runner.options[:assume_extension]) if blank?(ext)
38
+
39
+ @runner.options[:extensions].include?(ext)
40
+ end
41
+
42
+ def unknown_extension?
43
+ !known_extension?
44
+ end
45
+
46
+ def ignore?
47
+ return true if /^javascript:/.match?(@url)
48
+ return true if ignores_pattern?(@runner.options[:ignore_urls])
49
+ end
50
+
51
+ def valid?
52
+ !parts.nil?
53
+ end
54
+
55
+ def path?
56
+ !parts.host.nil? && !parts.path.nil?
57
+ end
58
+
59
+ def parts
60
+ @parts ||= Addressable::URI.parse(@url)
61
+ rescue URI::Error, Addressable::URI::InvalidURIError
62
+ @parts = nil
63
+ end
64
+
65
+ def path
66
+ Addressable::URI.unencode(parts.path) unless parts.nil?
67
+ end
68
+
69
+ def hash
70
+ parts&.fragment
71
+ end
72
+
73
+ # Does the URL have a hash?
74
+ def hash?
75
+ !blank?(hash)
76
+ end
77
+
78
+ def scheme
79
+ parts&.scheme
80
+ end
81
+
82
+ def remote?
83
+ REMOTE_SCHEMES.include?(scheme)
84
+ end
85
+
86
+ def http?
87
+ scheme == "http"
88
+ end
89
+
90
+ def https?
91
+ scheme == "https"
92
+ end
93
+
94
+ def non_http_remote?
95
+ !scheme.nil? && !remote?
96
+ end
97
+
98
+ def host
99
+ parts&.host
100
+ end
101
+
102
+ def domain_path
103
+ (host || "") + path
104
+ end
105
+
106
+ def query_values
107
+ parts&.query_values
108
+ end
109
+
110
+ # checks if a file exists relative to the current pwd
111
+ def exists?
112
+ return true if base64?
113
+
114
+ return @runner.checked_paths[absolute_path] if @runner.checked_paths.key?(absolute_path)
115
+
116
+ @runner.checked_paths[absolute_path] = File.exist?(absolute_path)
117
+ end
118
+
119
+ def base64?
120
+ /^data:image/.match?(@raw_attribute)
121
+ end
122
+
123
+ def absolute_path
124
+ path = file_path || @runner.current_filename
125
+
126
+ File.expand_path(path, Dir.pwd)
127
+ end
128
+
129
+ def file_path
130
+ return if path.nil? || path.empty?
131
+
132
+ path_dot_ext = ""
133
+
134
+ path_dot_ext = path + @runner.options[:assume_extension] unless blank?(@runner.options[:assume_extension])
135
+
136
+ base = if absolute_path?(path) # path relative to root
137
+ # either overwrite with root_dir; or, if source is directory, use that; or, just get the current file's dirname
138
+ @runner.options[:root_dir] || (File.directory?(@runner.current_source) ? @runner.current_source : File.dirname(@runner.current_source))
139
+ # relative links, path is a file
140
+ elsif File.exist?(File.expand_path(path,
141
+ @runner.current_source)) || File.exist?(File.expand_path(path_dot_ext, @runner.current_source))
142
+ File.dirname(@runner.current_filename)
143
+ # relative links in nested dir, path is a file
144
+ elsif File.exist?(File.join(File.dirname(@runner.current_filename),
145
+ path)) || File.exist?(File.join(File.dirname(@runner.current_filename), path_dot_ext))
146
+ File.dirname(@runner.current_filename)
147
+ # relative link, path is a directory
148
+ else
149
+ @runner.current_filename
150
+ end
151
+
152
+ file = File.join(base, path)
153
+
154
+ if @runner.options[:assume_extension] && File.file?("#{file}#{@runner.options[:assume_extension]}")
155
+ file = "#{file}#{@runner.options[:assume_extension]}"
156
+ elsif File.directory?(file) && !unslashed_directory?(file) # implicit index support
157
+ file = File.join(file, @runner.options[:directory_index_file])
158
+ end
159
+
160
+ file
161
+ end
162
+
163
+ def unslashed_directory?(file)
164
+ File.directory?(file) && !file.end_with?(File::SEPARATOR)
165
+ end
166
+
167
+ def absolute_path?(path)
168
+ path.start_with?("/")
169
+ end
170
+
171
+ # path is external to the file
172
+ def external?
173
+ !internal?
174
+ end
175
+
176
+ def internal?
177
+ relative_link? || internal_absolute_link? || hash_link?
178
+ end
179
+
180
+ def internal_absolute_link?
181
+ url.start_with?("/")
182
+ end
183
+
184
+ def relative_link?
185
+ return false if remote?
186
+
187
+ hash_link? || param_link? || url.start_with?(".") || url =~ /^\S/
188
+ end
189
+
190
+ def link_points_to_same_page?
191
+ hash_link || param_link
192
+ end
193
+
194
+ def hash_link?
195
+ url.start_with?("#")
196
+ end
197
+
198
+ def param_link?
199
+ url.start_with?("?")
200
+ end
201
+
202
+ def sans_hash
203
+ @url.to_s.sub(/##{hash}/, "")
204
+ end
205
+
206
+ # catch any obvious issues, like strings in port numbers
207
+ private def clean_url!
208
+ return if @url =~ /^([!#{Regexp.last_match(0)}-;=?-\[\]_a-z~]|%[0-9a-fA-F]{2})+$/
209
+
210
+ @url = Addressable::URI.parse(@url).normalize.to_s
211
+ end
212
+
213
+ private def swap_urls!
214
+ return @url if blank?(replacements = @runner.options[:swap_urls])
215
+
216
+ replacements.each do |link, replace|
217
+ @url = @url.gsub(link, replace)
218
+ end
219
+ end
220
+
221
+ private def ignores_pattern?(links_to_ignore)
222
+ return false unless links_to_ignore.is_a?(Array)
223
+
224
+ links_to_ignore.each do |link_to_ignore|
225
+ case link_to_ignore
226
+ when String
227
+ return true if link_to_ignore == @raw_attribute
228
+ when Regexp
229
+ return true if link_to_ignore&.match?(@raw_attribute)
230
+ end
231
+ end
232
+
233
+ false
234
+ end
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTMLProofer
4
+ # Represents an element currently being processed
5
+ class Attribute
6
+ include HTMLProofer::Utils
7
+
8
+ attr_reader :raw_attribute
9
+
10
+ def initialize(runner, raw_attribute, **_)
11
+ @runner = runner
12
+ @raw_attribute = raw_attribute
13
+ end
14
+ end
15
+ end