html-proofer 4.4.3 → 5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8768e7a66f028dafa241252204bb20e619606464fde5a28cd17674071e8eca5
4
- data.tar.gz: 560faec79c102460c6c27959c829500b0135bbb9b01143bede6fb896eb8e917b
3
+ metadata.gz: f2c984bfe049fba10c746d289d8d0256f219d6ae8f5af152f6f8af68e639301b
4
+ data.tar.gz: c407f3995e873bbbc42d90de1f082827a40caa90cb12e5cdba7fd514500d1124
5
5
  SHA512:
6
- metadata.gz: eb7f6b4ae1a97660479aba2ffef9c1ceaf7e8e45761fa8d3e5ee11378a130f30a30a4b7d4f8510456fade443947c6c791e61497f4e7a0d087b27a94f6758248f
7
- data.tar.gz: e331d2c03dd7f59d48a6ae09d499a97d5587c27adcceaf782dd0a0162a3fe459a9f1b2cd88436d927af72a4018125dfde5db845f3bee55bd38a3479c4cb4c17e
6
+ metadata.gz: 732365ae4207754f70b27843a52d4f8186673fe1282ddaae03244ebff07517816371e970b75e465539bac51e931d51d4b1856866fcd6a3a575ae6e980d7c6465
7
+ data.tar.gz: ab01a8425d206863584e6c84f395ab85a22eff9d2e1df0daa4e0f72771fc9bde95a8992da17d2ef8195439ad1de2412135758989bb9ba29766b1978d95e0fad4
data/exe/htmlproofer ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $stdout.sync = true
5
+
6
+ $LOAD_PATH.unshift("#{__dir__}/../lib")
7
+
8
+ require "html-proofer"
9
+ require "benchmark"
10
+
11
+ exit_status = -1
12
+ cli = HTMLProofer::CLI.new
13
+
14
+ time = Benchmark.realtime { exit_status = cli.run }
15
+
16
+ puts "Finished in #{time.round(2)} seconds"
17
+ exit exit_status
@@ -3,7 +3,7 @@
3
3
  module HTMLProofer
4
4
  class Check
5
5
  class Images < HTMLProofer::Check
6
- SCREEN_SHOT_REGEX = /Screen(?: |%20)Shot(?: |%20)\d+-\d+-\d+(?: |%20)at(?: |%20)\d+.\d+.\d+/.freeze
6
+ SCREEN_SHOT_REGEX = /Screen(?: |%20)Shot(?: |%20)\d+-\d+-\d+(?: |%20)at(?: |%20)\d+.\d+.\d+/
7
7
 
8
8
  def run
9
9
  @html.css("img, source").each do |node|
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTMLProofer
4
+ # The CLI is a class responsible of handling all the command line interface
5
+ # logic.
6
+ class CLI
7
+ attr_reader :options
8
+
9
+ def initialize
10
+ @options = {}
11
+ end
12
+
13
+ def run(args = ARGV)
14
+ @options, path = HTMLProofer::Configuration.new.parse_cli_options(args)
15
+
16
+ paths = path.split(",")
17
+
18
+ if @options[:as_links]
19
+ links = path.split(",").map(&:strip)
20
+ HTMLProofer.check_links(links, @options).run
21
+ elsif File.directory?(paths.first)
22
+ HTMLProofer.check_directories(paths, @options).run
23
+ else
24
+ HTMLProofer.check_file(path, @options).run
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "optparse"
4
+
3
5
  module HTMLProofer
4
- module Configuration
6
+ class Configuration
5
7
  DEFAULT_TESTS = ["Links", "Images", "Scripts"].freeze
6
8
 
7
9
  PROOFER_DEFAULTS = {
@@ -41,10 +43,6 @@ module HTMLProofer
41
43
  max_concurrency: 50,
42
44
  }.freeze
43
45
 
44
- PARALLEL_DEFAULTS = {
45
- enable: true,
46
- }.freeze
47
-
48
46
  CACHE_DEFAULTS = {}.freeze
49
47
 
50
48
  class << self
@@ -54,38 +52,270 @@ module HTMLProofer
54
52
  options[:typhoeus] = HTMLProofer::Configuration::TYPHOEUS_DEFAULTS.merge(opts[:typhoeus] || {})
55
53
  options[:hydra] = HTMLProofer::Configuration::HYDRA_DEFAULTS.merge(opts[:hydra] || {})
56
54
 
57
- options[:parallel] = HTMLProofer::Configuration::PARALLEL_DEFAULTS.merge(opts[:parallel] || {})
58
55
  options[:cache] = HTMLProofer::Configuration::CACHE_DEFAULTS.merge(opts[:cache] || {})
59
56
 
60
57
  options.delete(:src)
61
58
 
62
59
  options
63
60
  end
61
+ end
62
+
63
+ def initialize
64
+ @options = {}
65
+ end
66
+
67
+ def parse_cli_options(args)
68
+ define_options.parse!(args)
69
+
70
+ input = ARGV.empty? ? "." : ARGV.join(",")
71
+
72
+ [@options, input]
73
+ end
74
+
75
+ private def define_options
76
+ OptionParser.new do |opts|
77
+ opts.banner = "Usage: htmlproofer [options] PATH/LINK"
78
+
79
+ section(opts, "Input Options") do
80
+ set_option(opts, "--as-links") do |long_opt_symbol, arg|
81
+ @options[long_opt_symbol] = arg
82
+ end
83
+
84
+ set_option(opts, "--assume-extension EXT") do |long_opt_symbol, arg|
85
+ @options[long_opt_symbol] = arg
86
+ end
87
+
88
+ set_option(opts, "--directory-index-file FILENAME") do |long_opt_symbol, arg|
89
+ @options[long_opt_symbol] = arg
90
+ end
64
91
 
65
- def to_regex?(item)
66
- if item.start_with?("/") && item.end_with?("/")
67
- Regexp.new(item[1...-1])
68
- else
69
- item
92
+ set_option(opts, "--extensions [EXT1,EXT2,...]") do |long_opt_symbol, list|
93
+ @options[long_opt_symbol] = list.nil? ? [] : list.split(",")
94
+ end
70
95
  end
71
- end
72
96
 
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?
97
+ section(opts, "Check Configuration") do
98
+ set_option(opts, "--[no-]allow-hash-href") do |long_opt_symbol, arg|
99
+ @options[long_opt_symbol] = arg
100
+ end
101
+
102
+ set_option(opts, "--[no-]allow-missing-href") do |long_opt_symbol, arg|
103
+ @options[long_opt_symbol] = arg
104
+ end
105
+
106
+ set_option(opts, "--checks [CHECK1,CHECK2,...]") do |long_opt_symbol, list|
107
+ @options[long_opt_symbol] = list.nil? ? [] : list.split(",")
108
+ end
109
+
110
+ set_option(opts, "--[no-]check-external-hash") do |long_opt_symbol, arg|
111
+ @options[long_opt_symbol] = arg
112
+ end
76
113
 
77
- return {} if config.nil?
114
+ set_option(opts, "--[no-]check-internal-hash") do |long_opt_symbol, arg|
115
+ @options[long_opt_symbol] = arg
116
+ end
117
+
118
+ set_option(opts, "--[no-]check-sri") do |long_opt_symbol, arg|
119
+ @options[long_opt_symbol] = arg
120
+ end
121
+
122
+ set_option(opts, "--[no-]disable-external") do |long_opt_symbol, arg|
123
+ @options[long_opt_symbol] = arg
124
+ end
125
+
126
+ set_option(opts, "--[no-]enforce-https") do |long_opt_symbol, arg|
127
+ @options[long_opt_symbol] = arg
128
+ end
129
+
130
+ set_option(opts, "--root-dir <DIR>") do |long_opt_symbol, arg|
131
+ @options[long_opt_symbol] = arg
132
+ end
133
+ end
134
+
135
+ section(opts, "Ignore Configuration") do
136
+ set_option(opts, "--ignore-files [FILE1,FILE2,...]") do |long_opt_symbol, list|
137
+ @options[long_opt_symbol] = list.nil? ? [] : list.split(",")
138
+ end
139
+
140
+ set_option(opts, "--[no-]ignore-empty-alt") do |long_opt_symbol, arg|
141
+ @options[long_opt_symbol] = arg
142
+ end
143
+
144
+ set_option(opts, "--[no-]ignore-empty-mailto") do |long_opt_symbol, arg|
145
+ @options[long_opt_symbol] = arg
146
+ end
147
+
148
+ set_option(opts, "--[no-]ignore-missing-alt") do |long_opt_symbol, arg|
149
+ @options[long_opt_symbol] = arg
150
+ end
151
+
152
+ set_option(opts, "--ignore-status-codes [500,401,420,...]") do |long_opt_symbol, list|
153
+ @options[long_opt_symbol] = list.nil? ? [] : list.split(",").map(&:to_i)
154
+ end
155
+
156
+ set_option(opts, "--ignore-urls [URL1, URL2,...]") do |long_opt_symbol, list|
157
+ @options[long_opt_symbol] = if list.nil?
158
+ []
159
+ else
160
+ list.split(",").each_with_object([]) do |url, arr|
161
+ arr << to_regex?(url)
162
+ end
163
+ end
164
+ end
165
+
166
+ set_option(opts, "--only-status-codes [404,451,...]") do |long_opt_symbol, list|
167
+ @options[long_opt_symbol] = list.nil? ? [] : list.split(",")
168
+ end
169
+
170
+ set_option(opts, "--only-4xx") do |long_opt_symbol, arg|
171
+ @options[long_opt_symbol] = arg
172
+ end
173
+ end
174
+
175
+ section(opts, "Transforms Configuration") do
176
+ set_option(opts, "--swap-attributes <CONFIG>") do |long_opt_symbol, arg|
177
+ @options[long_opt_symbol] = parse_json_option("swap_attributes", arg, symbolize_names: false)
178
+ end
179
+
180
+ set_option(opts, "--swap-urls [re:string,re:string,...]") do |long_opt_symbol, arg|
181
+ @options[long_opt_symbol] = str_to_regexp_map(arg)
182
+ end
183
+ end
78
184
 
79
- raise ArgumentError, "Must provide a JSON configuration in string format." unless config.is_a?(String)
185
+ section(opts, "Dependencies Configuration") do
186
+ set_option(opts, "--typhoeus <CONFIG>") do |long_opt_symbol, arg|
187
+ @options[long_opt_symbol] = parse_json_option("typhoeus", arg, symbolize_names: false)
188
+ end
80
189
 
81
- return {} if config.strip.empty?
190
+ set_option(opts, "--hydra <CONFIG>") do |long_opt_symbol, _list|
191
+ @options[long_opt_symbol] = parse_json_option("hydra", arg, symbolize_names: false)
192
+ end
82
193
 
83
- begin
84
- JSON.parse(config, { symbolize_names: symbolize_names })
85
- rescue StandardError
86
- raise ArgumentError, "Option '#{option_name} did not contain valid JSON."
194
+ set_option(opts, "--cache <CONFIG>") do |long_opt_symbol, _list|
195
+ @options[long_opt_symbol] = parse_json_option("cache", arg, symbolize_names: false)
196
+ end
87
197
  end
198
+
199
+ section(opts, "Reporting Configuration") do
200
+ set_option(opts, "--log-level <LEVEL>") do |long_opt_symbol, arg|
201
+ @options[long_opt_symbol] = arg.to_sym
202
+ end
203
+ end
204
+
205
+ section(opts, "General Configuration") do
206
+ set_option(opts, "--version") do
207
+ puts HTMLProofer::VERSION
208
+ exit(0)
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ private def to_regex?(item)
215
+ if item.start_with?("/") && item.end_with?("/")
216
+ Regexp.new(item[1...-1])
217
+ else
218
+ item
219
+ end
220
+ end
221
+
222
+ private def str_to_regexp_map(arg)
223
+ arg.split(",").each_with_object({}) do |s, hsh|
224
+ split = s.split(/(?<!\\):/, 2)
225
+
226
+ re = split[0].gsub(/\\:/, ":")
227
+ string = split[1].gsub(/\\:/, ":")
228
+ hsh[Regexp.new(re)] = string
229
+ end
230
+ end
231
+
232
+ private def section(opts, heading, &_block)
233
+ opts.separator("\n#{heading}:\n")
234
+ yield
235
+ end
236
+
237
+ private def set_option(opts, long_arg, &block)
238
+ long_opt_symbol = parse_long_opt(long_arg)
239
+ args = []
240
+ args += Array(ConfigurationHelp::TEXT[long_opt_symbol])
241
+
242
+ opts.on(long_arg, *args) do |arg|
243
+ yield long_opt_symbol, arg
88
244
  end
89
245
  end
246
+
247
+ # Converts the option into a symbol,
248
+ # e.g. '--allow-hash-href' => :allow_hash_href.
249
+ private def parse_long_opt(long_opt)
250
+ long_opt[2..].sub("[no-]", "").sub(/ .*/, "").tr("-", "_").gsub(/[\[\]]/, "").to_sym
251
+ end
252
+
253
+ def parse_json_option(option_name, config, symbolize_names: true)
254
+ raise ArgumentError, "Must provide an option name in string format." unless option_name.is_a?(String)
255
+ raise ArgumentError, "Must provide an option name in string format." if option_name.strip.empty?
256
+
257
+ return {} if config.nil?
258
+
259
+ raise ArgumentError, "Must provide a JSON configuration in string format." unless config.is_a?(String)
260
+
261
+ return {} if config.strip.empty?
262
+
263
+ begin
264
+ JSON.parse(config, { symbolize_names: symbolize_names })
265
+ rescue StandardError
266
+ raise ArgumentError, "Option '#{option_name} did not contain valid JSON."
267
+ end
268
+ end
269
+
270
+ module ConfigurationHelp
271
+ TEXT = {
272
+ 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`).",],
275
+ 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`)",],
278
+
279
+ 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\"]",],
284
+ check_external_hash: ["Checks whether external hashes exist (even if the webpage exists) (default: `true`)."],
285
+ check_internal_hash: ["Checks whether internal hashes exist (even if the webpage exists) (default: `true`)."],
286
+ check_sri: ["Check that `<link>` and `<script>` external resources use SRI (default: `false`)."],
287
+ 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`)."],
289
+ root_dir: ["The absolute path to the directory serving your html-files."],
290
+
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`)'.",],
296
+ ignore_missing_alt: ["If `true`, ignores images with missing alt tags (default: `false`)."],
297
+ ignore_status_codes: ["A comma-separated list of numbers representing status codes to ignore."],
298
+ 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.",],
301
+ only_status_codes: ["A comma-separated list of numbers representing the only status codes to report on."],
302
+ only_4xx: ["Only reports errors for links that fall within the 4xx status code range."],
303
+
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.",],
309
+
310
+ typhoeus: ["JSON-formatted string of Typhoeus config; if set, overrides the html-proofer defaults."],
311
+ hydra: ["JSON-formatted string of Hydra config; if set, overrides the html-proofer defaults."],
312
+ cache: ["JSON-formatted string of cache config; if set, overrides the html-proofer defaults."],
313
+
314
+ log_level: ["Sets the logging level. One of `:debug`, `:info`, ",
315
+ "`:warn`, `:error`, or `:fatal`. (default: `:info`)",],
316
+
317
+ version: ["Prints the version of html-proofer."],
318
+ }.freeze
319
+ end
90
320
  end
91
321
  end
@@ -13,6 +13,8 @@ module HTMLProofer
13
13
  @runner = runner
14
14
  @node = node
15
15
 
16
+ swap_attributes!
17
+
16
18
  @base_url = base_url
17
19
  @url = Attribute::Url.new(runner, link_attribute, base_url: base_url)
18
20
 
@@ -26,7 +28,6 @@ module HTMLProofer
26
28
 
27
29
  def meta_content
28
30
  return nil unless meta_tag?
29
- return swap_attributes("content") if attribute_swapped?
30
31
 
31
32
  @node["content"]
32
33
  end
@@ -37,7 +38,6 @@ module HTMLProofer
37
38
 
38
39
  def src
39
40
  return nil if !img_tag? && !script_tag? && !source_tag?
40
- return swap_attributes("src") if attribute_swapped?
41
41
 
42
42
  @node["src"]
43
43
  end
@@ -52,7 +52,6 @@ module HTMLProofer
52
52
 
53
53
  def srcset
54
54
  return nil if !img_tag? && !source_tag?
55
- return swap_attributes("srcset") if attribute_swapped?
56
55
 
57
56
  @node["srcset"]
58
57
  end
@@ -63,7 +62,6 @@ module HTMLProofer
63
62
 
64
63
  def href
65
64
  return nil if !a_tag? && !link_tag?
66
- return swap_attributes("href") if attribute_swapped?
67
65
 
68
66
  @node["href"]
69
67
  end
@@ -123,16 +121,17 @@ module HTMLProofer
123
121
  return true unless blank?(attrs)
124
122
  end
125
123
 
126
- private def swap_attributes(old_attr)
127
- attrs = @runner.options[:swap_attributes][@node.name]
124
+ private def swap_attributes!
125
+ return unless attribute_swapped?
128
126
 
129
- new_attr = attrs.find do |(o, _)|
130
- o == old_attr
131
- end&.last
127
+ attr_swaps = @runner.options[:swap_attributes][@node.name]
132
128
 
133
- return nil if blank?(new_attr)
129
+ attr_swaps.flatten.each_slice(2) do |(old_attr, new_attr)|
130
+ next if blank?(node[old_attr])
134
131
 
135
- @node[new_attr]
132
+ node[new_attr] = node[old_attr]
133
+ node.delete(old_attr)
134
+ end
136
135
  end
137
136
 
138
137
  private def ancestors_ignorable?
@@ -2,7 +2,7 @@
2
2
 
3
3
  module HTMLProofer
4
4
  class Reporter
5
- class Cli < HTMLProofer::Reporter
5
+ class Terminal < HTMLProofer::Reporter
6
6
  def report
7
7
  msg = failures.each_with_object([]) do |(check_name, failures), arr|
8
8
  str = ["For the #{check_name} check, the following failures were found:\n"]
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "async"
4
+
3
5
  module HTMLProofer
4
6
  class Runner
5
7
  include HTMLProofer::Utils
@@ -30,18 +32,18 @@ module HTMLProofer
30
32
  @current_source = nil
31
33
  @current_filename = nil
32
34
 
33
- @reporter = Reporter::Cli.new(logger: @logger)
35
+ @reporter = Reporter::Terminal.new(logger: @logger)
34
36
  end
35
37
 
36
38
  def run
37
39
  check_text = pluralize(checks.length, "check", "checks")
38
40
 
39
41
  if @type == :links
40
- @logger.log(:info, "Running #{check_text} (#{format_checks_list(checks)}) on #{@source} ... \n\n")
42
+ @logger.log(:info, "Running #{check_text} (#{format_checks_list(checks)}) on #{@source} ...\n\n")
41
43
  check_list_of_links unless @options[:disable_external]
42
44
  else
43
45
  @logger.log(:info,
44
- "Running #{check_text} (#{format_checks_list(checks)}) in #{@source} on *#{@options[:extensions].join(", ")} files...\n\n")
46
+ "Running #{check_text} (#{format_checks_list(checks)}) in #{@source} on *#{@options[:extensions].join(", ")} files ...\n\n")
45
47
 
46
48
  check_files
47
49
  @logger.log(:info, "Ran on #{pluralize(files.length, "file", "files")}!\n\n")
@@ -97,13 +99,15 @@ module HTMLProofer
97
99
 
98
100
  # Walks over each implemented check and runs them on the files, in parallel.
99
101
  def process_files
100
- if @options[:parallel][:enable]
101
- Parallel.map(files, @options[:parallel]) { |file| load_file(file[:path], file[:source]) }
102
- else
103
- files.map do |file|
104
- load_file(file[:path], file[:source])
102
+ loaded_files = []
103
+ files.each do |file|
104
+ Async do |task|
105
+ task.async do
106
+ loaded_files << load_file(file[:path], file[:source])
107
+ end
105
108
  end
106
109
  end
110
+ loaded_files
107
111
  end
108
112
 
109
113
  def load_file(path, source)
@@ -238,7 +242,7 @@ module HTMLProofer
238
242
  private def format_checks_list(checks)
239
243
  checks.map do |check|
240
244
  check.sub(/HTMLProofer::Check::/, "")
241
- end.join(", ")
245
+ end.sort.join(", ")
242
246
  end
243
247
  end
244
248
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTMLProofer
4
- VERSION = "4.4.3"
4
+ VERSION = "5.0.0"
5
5
  end
data/lib/html_proofer.rb CHANGED
@@ -5,13 +5,13 @@ lib_dir = File.join(File.dirname(__dir__), "lib")
5
5
  gem_loader = Zeitwerk::Loader.for_gem
6
6
  gem_loader.inflector.inflect(
7
7
  "html_proofer" => "HTMLProofer",
8
+ "cli" => "CLI",
8
9
  )
9
10
  gem_loader.ignore(File.join(lib_dir, "html-proofer.rb"))
10
11
  gem_loader.setup
11
12
 
12
13
  require "html_proofer/version"
13
14
 
14
- require "parallel"
15
15
  require "fileutils"
16
16
 
17
17
  if ENV.fetch("DEBUG", false)
@@ -22,7 +22,7 @@ end
22
22
  module HTMLProofer
23
23
  class << self
24
24
  def check_file(file, options = {})
25
- raise ArgumentError unless file.is_a?(String)
25
+ raise ArgumentError, "File isn't a string" unless file.is_a?(String)
26
26
  raise ArgumentError, "#{file} does not exist" unless File.exist?(file)
27
27
 
28
28
  options[:type] = :file
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: html-proofer
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.4.3
4
+ version: 5.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garen Torikian
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
11
  date: 2022-10-08 00:00:00.000000000 Z
12
12
  dependencies:
@@ -25,19 +25,19 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.3'
27
27
  - !ruby/object:Gem::Dependency
28
- name: mercenary
28
+ name: async
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.3'
33
+ version: '2.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.3'
40
+ version: '2.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: nokogiri
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.13'
55
- - !ruby/object:Gem::Dependency
56
- name: parallel
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '1.10'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '1.10'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: rainbow
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -270,7 +256,7 @@ executables:
270
256
  extensions: []
271
257
  extra_rdoc_files: []
272
258
  files:
273
- - bin/htmlproofer
259
+ - exe/htmlproofer
274
260
  - lib/html-proofer.rb
275
261
  - lib/html_proofer.rb
276
262
  - lib/html_proofer/attribute.rb
@@ -282,12 +268,13 @@ files:
282
268
  - lib/html_proofer/check/links.rb
283
269
  - lib/html_proofer/check/open_graph.rb
284
270
  - lib/html_proofer/check/scripts.rb
271
+ - lib/html_proofer/cli.rb
285
272
  - lib/html_proofer/configuration.rb
286
273
  - lib/html_proofer/element.rb
287
274
  - lib/html_proofer/failure.rb
288
275
  - lib/html_proofer/log.rb
289
276
  - lib/html_proofer/reporter.rb
290
- - lib/html_proofer/reporter/cli.rb
277
+ - lib/html_proofer/reporter/terminal.rb
291
278
  - lib/html_proofer/runner.rb
292
279
  - lib/html_proofer/url_validator.rb
293
280
  - lib/html_proofer/url_validator/external.rb
@@ -309,7 +296,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
309
296
  requirements:
310
297
  - - ">="
311
298
  - !ruby/object:Gem::Version
312
- version: 2.6.0
299
+ version: '3.1'
313
300
  - - "<"
314
301
  - !ruby/object:Gem::Version
315
302
  version: '4.0'
data/bin/htmlproofer DELETED
@@ -1,102 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- $stdout.sync = true
5
-
6
- $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
7
-
8
- require 'html-proofer'
9
- require 'mercenary'
10
-
11
- Mercenary.program(:htmlproofer) do |p|
12
- p.version HTMLProofer::VERSION
13
- p.description %(Test your rendered HTML files to make sure they're accurate.)
14
- p.syntax 'htmlproofer PATH [options]'
15
-
16
- p.description 'Runs the HTML-Proofer suite on the files in PATH. For more details, see the README.'
17
-
18
- p.option 'allow_hash_href', '--allow-hash-href=<true|false>', 'String', 'If `true`, assumes `href="#"` anchors are valid (default: `true`)'
19
- p.option 'allow_missing_href', '--allow-missing-href=<true|false>', 'String', 'If `true`, does not flag `a` tags missing `href`. In HTML5, this is technically allowed, but could also be human error. (default: `false`)'
20
- p.option 'as_links', '--as-links', 'Assumes that `PATH` is a comma-separated array of links to check.'
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=<true|false>', 'String', 'Checks whether external hashes exist (even if the webpage exists) (default: `true`).'
24
- p.option 'check_internal_hash', '--check-internal-hash=<true|false>', 'String', 'Checks whether internal hashes exist (even if the webpage exists) (default: `true`).'
25
- p.option 'check_sri', '--check-sri=<true|false>', 'String', 'Check that `<link>` and `<script>` external resources use SRI (default: `false`).'
26
- 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`)'
27
- p.option 'disable_external', '--disable-external=<true|false>', String, 'If `true`, does not run the external link checker (default: `false`)'
28
- p.option 'enforce_https', '--enforce-https=<true|false>', String, 'Fails a link if it\'s not marked as `https` (default: `true`).'
29
- 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`)'
30
- p.option 'ignore_empty_alt', '--ignore-empty-alt=<true|false>', 'String', '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) (default: `true`)'
31
- p.option 'ignore_empty_mailto', '--ignore-empty-mailto=<true|false>', 'String', 'If `true`, allows `mailto:` `href`s which do not contain an email address (default: `false`)'
32
- 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'
33
- p.option 'ignore_missing_alt', '--ignore-missing-alt=<true|false>', 'String', 'If `true`, ignores images with missing alt tags (default: `false`)'
34
- p.option 'ignore_status_codes', '--ignore-status-codes 123,[xxx, ...]', Array, 'A comma-separated list of numbers representing status codes to ignore.'
35
- 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.'
36
- 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`)'
37
- p.option 'only_4xx', '--only-4xx', 'Only reports errors for links that fall within the 4xx status code range'
38
- p.option 'root_dir', '--root-dir PATH', String, 'The absolute path to the directory serving your html-files.'
39
- p.option 'swap_attributes', '--swap-attributes CONFIG', String, 'JSON-formatted config that maps element names to the preferred attribute to check (default: `{}`).'
40
- 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.'
41
-
42
- p.option 'typhoeus', '--typhoeus CONFIG', String, 'JSON-formatted string of Typhoeus config. Will override the html-proofer defaults.'
43
- p.option 'hydra', '--hydra CONFIG', String, 'JSON-formatted string of Hydra config. Will override the html-proofer defaults.'
44
- p.option 'parallel', '--parallel CONFIG', String, 'JSON-formatted string of Parallel config. Will override the html-proofer defaults.'
45
- p.option 'cache', '--cache CONFIG', String, 'JSON-formatted string of cache config. Will override the html-proofer defaults.'
46
-
47
- p.action do |args, opts|
48
- args = ['.'] if args.empty?
49
- path = args.first
50
-
51
- options = {}
52
-
53
- # prepare everything to go to proofer
54
- p.options.reject { |o| opts[o.config_key].nil? }.each do |option|
55
- opts[option.config_key] = opts[option.config_key].map { |i| HTMLProofer::Configuration.to_regex?(i) } if opts[option.config_key].is_a?(Array)
56
- options[option.config_key.to_sym] = opts[option.config_key]
57
- end
58
-
59
- # some minor manipulation of a special option
60
- unless opts['swap_urls'].nil?
61
- options[:swap_urls] = {}
62
- opts['swap_urls'].each do |s|
63
- splt = s.split(/(?<!\\):/, 2)
64
-
65
- re = splt[0].gsub(/\\:/, ':')
66
- string = splt[1].gsub(/\\:/, ':')
67
- options[:swap_urls][Regexp.new(re)] = string
68
- end
69
- end
70
-
71
- # check booleans
72
- [:allow_hash_href, :allow_missing_href, :check_external_hash, :check_internal_hash, :check_sri, :disable_external, :enforce_https, :ignore_empty_alt, :ignore_empty_mailto, :ignore_missing_alt].each do |option|
73
- next if (val = opts[option.to_s]).nil?
74
- if val == "false"
75
- options[option] = false
76
- else
77
- options[option] = true
78
- end
79
- end
80
-
81
- options[:log_level] = opts['log_level'].to_sym unless opts['log_level'].nil?
82
-
83
- options[:typhoeus] = HTMLProofer::Configuration.parse_json_option('typhoeus', opts['typhoeus'], symbolize_names: false) unless opts['typhoeus'].nil?
84
- options[:hydra] = HTMLProofer::Configuration.parse_json_option('hydra', opts['hydra']) unless opts['hydra'].nil?
85
- options[:parallel] = HTMLProofer::Configuration.parse_json_option('parallel', opts['parallel']) unless opts['parallel'].nil?
86
- options[:cache] = HTMLProofer::Configuration.parse_json_option('cache', opts['cache']) unless opts['cache'].nil?
87
-
88
- options[:swap_attributes] = HTMLProofer::Configuration.parse_json_option('swap_attributes', opts['swap_attributes'], symbolize_names: false) unless opts['swap_attributes'].nil?
89
-
90
- options[:ignore_status_codes] = Array(options[:ignore_status_codes]).map(&:to_i)
91
-
92
- paths = path.split(',')
93
- if opts['as_links']
94
- links = path.split(',').map(&:strip)
95
- HTMLProofer.check_links(links, options).run
96
- elsif File.directory?(paths.first)
97
- HTMLProofer.check_directories(paths, options).run
98
- else
99
- HTMLProofer.check_file(path, options).run
100
- end
101
- end
102
- end