html-proofer 4.4.3 → 5.0.0

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: 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