grover 1.1.10 → 1.2.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: 89de769dbb4ef96309c640d664ac90d9771968c02f596b6e6370d2f77b10cd1e
4
- data.tar.gz: 5df2d5e82e1155757d859c95e01b0ed24dd49fdae6d4d8cb5c23276ef774603d
3
+ metadata.gz: 50a8ec2a93d7be70d77b4aefd45f6a87296cbd0201f3fba5a2d962fce511f4b7
4
+ data.tar.gz: 340a18b36da574c7cf57c897c3d767e890385279b681e345e2ba29cc5bf3430e
5
5
  SHA512:
6
- metadata.gz: 9a6b7be08fb0d719cd464033d1d9ebf63343f8cf7126ea4dfead19eb2b022d9bf124898b58b6842690714f7d7d05fc01e847340a9ed47f9966b6c8eddfb768fe
7
- data.tar.gz: 90acf98c742ad66686108735828b0a378776c07375562df8c4db6f483a9f1669c7a0409381764caddb9a788c6ddbe9cfc7802edfa05961bedeb4c7896da4558d
6
+ metadata.gz: 13124e1d70416020c29695bbf59f2618015def0d46d4d194c5ba514b4f4c48a2622adc6238c32254717601b50acaadc3beac5065fb19ffbdf864b5d8f4bda868
7
+ data.tar.gz: dae49b29ab27b64b990f8f71f7571afb00e01c18a7a3b94571af2a1b74bd578248db4b712241e571674683052b196a8b4164095db449058a95d9e8f2509d9119
@@ -7,9 +7,10 @@ class Grover
7
7
  class Configuration
8
8
  attr_accessor :options, :meta_tag_prefix, :ignore_path, :ignore_request,
9
9
  :root_url, :use_pdf_middleware, :use_png_middleware,
10
- :use_jpeg_middleware, :node_env_vars, :allow_file_uris
10
+ :use_jpeg_middleware, :js_runtime_bin,
11
+ :node_env_vars, :allow_file_uris
11
12
 
12
- def initialize
13
+ def initialize # rubocop:disable Metrics/MethodLength
13
14
  @options = {}
14
15
  @meta_tag_prefix = 'grover-'
15
16
  @ignore_path = nil
@@ -18,6 +19,7 @@ class Grover
18
19
  @use_pdf_middleware = true
19
20
  @use_png_middleware = false
20
21
  @use_jpeg_middleware = false
22
+ @js_runtime_bin = ['node']
21
23
  @node_env_vars = {}
22
24
  @allow_file_uris = false
23
25
  end
data/lib/grover/errors.rb CHANGED
@@ -11,6 +11,18 @@ class Grover
11
11
  module JavaScript # rubocop:disable Style/Documentation
12
12
  Error = Class.new(::Grover::Error)
13
13
  UnknownError = Class.new(Error)
14
+
15
+ ErrorWithDetails = Class.new(Error) do
16
+ def initialize(name, error_details)
17
+ super(name)
18
+ @error_details = Grover::Utils.deep_transform_keys_in_object error_details, &:to_sym
19
+ end
20
+
21
+ attr_reader :error_details
22
+ end
23
+ RequestFailedError = Class.new(ErrorWithDetails)
24
+ PageRenderError = Class.new(ErrorWithDetails)
25
+
14
26
  def self.const_missing(name)
15
27
  const_set name, Class.new(Error)
16
28
  end
@@ -26,8 +26,32 @@ const fs = require('fs');
26
26
  const os = require('os');
27
27
  const path = require('path');
28
28
 
29
+ function GroverError(name, errors) {
30
+ this.name = name;
31
+ this.message = errors.map(e => e.message).join("\n");
32
+ this.errors = errors;
33
+ }
34
+ GroverError.prototype = Error.prototype;
35
+
29
36
  const _processPage = (async (convertAction, uriOrHtml, options) => {
30
- let browser, page, errors = [], tmpDir, wsConnection = false;
37
+ let browser, page, tmpDir, wsConnection = false;
38
+ const requestErrors = [], pageErrors = [];
39
+
40
+ const captureRequestError = (request) => {
41
+ const requestError = { url: request.url() };
42
+
43
+ if (request.failure()) {
44
+ requestError.reason = request.failure().errorText;
45
+ requestError.message = requestError.reason + " at " + requestError.url;
46
+ } else if (request.response() && request.response().status()) {
47
+ requestError.status = request.response().status();
48
+ requestError.message = requestError.status + " " + requestError.url;
49
+ } else {
50
+ requestError.message = "UnknownError " + requestError.url;
51
+ }
52
+
53
+ requestErrors.push(requestError);
54
+ };
31
55
 
32
56
  try {
33
57
  // Configure puppeteer debugging options
@@ -163,12 +187,24 @@ const _processPage = (async (convertAction, uriOrHtml, options) => {
163
187
  const raiseOnRequestFailure = options.raiseOnRequestFailure; delete options.raiseOnRequestFailure;
164
188
  if (raiseOnRequestFailure) {
165
189
  page.on('requestfinished', (request) => {
166
- if (request.response() && !(request.response().ok() || request.response().status() === 304) && !request.redirectChain().length > 0) {
167
- errors.push(request);
190
+ if (request.response() &&
191
+ !(request.response().ok() || request.response().status() === 304) &&
192
+ !request.redirectChain().length > 0) {
193
+ captureRequestError(request);
168
194
  }
169
195
  });
170
196
  page.on('requestfailed', (request) => {
171
- errors.push(request);
197
+ captureRequestError(request);
198
+ });
199
+ }
200
+
201
+ const raiseOnJSError = options.raiseOnJSError; delete options.raiseOnJSError;
202
+ if (raiseOnJSError) {
203
+ page.on('pageerror', (error) => {
204
+ pageErrors.push({
205
+ message: error.toString().replace(new RegExp('^' + error.name + ': '), ''),
206
+ type: error.name || 'Error'
207
+ });
172
208
  });
173
209
  }
174
210
 
@@ -257,21 +293,12 @@ const _processPage = (async (convertAction, uriOrHtml, options) => {
257
293
  await page.hover(hoverSelector);
258
294
  }
259
295
 
260
- if (errors.length > 0) {
261
- function RequestFailedError(errors) {
262
- this.name = "RequestFailedError";
263
- this.message = errors.map(e => {
264
- if (e.failure()) {
265
- return e.failure().errorText + " at " + e.url();
266
- } else if (e.response() && e.response().status()) {
267
- return e.response().status() + " " + e.url();
268
- } else {
269
- return "UnknownError " + e.url()
270
- }
271
- }).join("\n");
272
- }
273
- RequestFailedError.prototype = Error.prototype;
274
- throw new RequestFailedError(errors);
296
+ if (requestErrors.length > 0) {
297
+ throw new GroverError("RequestFailedError", requestErrors);
298
+ }
299
+
300
+ if (pageErrors.length > 0) {
301
+ throw new GroverError("PageRenderError", pageErrors);
275
302
  }
276
303
 
277
304
  // Setup conversion timeout
@@ -301,7 +328,9 @@ const _processPage = (async (convertAction, uriOrHtml, options) => {
301
328
  });
302
329
 
303
330
  function _handleError(error) {
304
- if (error instanceof Error) {
331
+ if (error instanceof GroverError) {
332
+ process.stdout.write(JSON.stringify(['err', error.message, error.name, error.errors]));
333
+ } else if (error instanceof Error) {
305
334
  process.stdout.write(
306
335
  JSON.stringify(['err', error.toString().replace(new RegExp('^' + error.name + ': '), ''), error.name])
307
336
  );
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'combine_pdf'
4
-
5
3
  class Grover
6
4
  #
7
5
  # Rack middleware for catching PDF requests and returning the upstream HTML as a PDF
@@ -132,12 +130,19 @@ class Grover
132
130
  end
133
131
 
134
132
  def add_cover_content(grover)
133
+ load_combine_pdf
135
134
  pdf = CombinePDF.parse grover.to_pdf
136
135
  pdf >> fetch_cover_pdf(grover.front_cover_path) if grover.show_front_cover?
137
136
  pdf << fetch_cover_pdf(grover.back_cover_path) if grover.show_back_cover?
138
137
  pdf.to_pdf
139
138
  end
140
139
 
140
+ def load_combine_pdf
141
+ require 'combine_pdf'
142
+ rescue ::LoadError
143
+ raise Grover::Error, 'Please add/install the "combine_pdf" gem to use the front/back cover page feature'
144
+ end
145
+
141
146
  def fetch_cover_pdf(path)
142
147
  temp_env = env.deep_dup
143
148
  scrub_env! temp_env
@@ -34,7 +34,8 @@ class Grover
34
34
  def fix_boolean_options!
35
35
  fix_options!(
36
36
  'display_header_footer', 'full_page', 'landscape', 'omit_background', 'prefer_css_page_size',
37
- 'print_background', 'viewport.has_touch', 'viewport.is_landscape', 'viewport.is_mobile', 'bypass_csp'
37
+ 'print_background', 'viewport.has_touch', 'viewport.is_landscape', 'viewport.is_mobile', 'bypass_csp',
38
+ 'raise_on_request_failure', 'raise_on_js_error'
38
39
  ) { |value| !FALSE_VALUES.include?(value) }
39
40
  end
40
41
 
@@ -33,7 +33,7 @@ class Grover
33
33
  def spawn_process
34
34
  @stdin, @stdout, @stderr, @wait_thr = Open3.popen3(
35
35
  Grover.configuration.node_env_vars,
36
- 'node',
36
+ *Grover.configuration.js_runtime_bin,
37
37
  File.expand_path(File.join(__dir__, 'js/processor.cjs')),
38
38
  chdir: app_root
39
39
  )
@@ -83,14 +83,14 @@ class Grover
83
83
  input = stdout.gets
84
84
  raise Errno::EPIPE, "Can't read from worker" if input.nil?
85
85
 
86
- status, message, error_class = JSON.parse(input)
86
+ status, message, error_class, errors = JSON.parse(input)
87
87
 
88
88
  if status == 'ok'
89
89
  message
90
90
  elsif error_class.nil?
91
91
  raise Grover::JavaScript::UnknownError, message
92
92
  else
93
- raise Grover::JavaScript.const_get(error_class, false), message
93
+ raise Grover::JavaScript.const_get(error_class, false).new(*[message, errors].compact)
94
94
  end
95
95
  rescue JSON::ParserError
96
96
  raise Grover::Error, 'Malformed worker response'
data/lib/grover/utils.rb CHANGED
@@ -8,7 +8,8 @@ class Grover
8
8
  ACRONYMS = {
9
9
  'css' => 'CSS',
10
10
  'csp' => 'CSP',
11
- 'http' => 'HTTP'
11
+ 'http' => 'HTTP',
12
+ 'js' => 'JS'
12
13
  }.freeze
13
14
  private_constant :ACRONYMS
14
15
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Grover
4
- VERSION = '1.1.10'
4
+ VERSION = '1.2.0'
5
5
  end
metadata CHANGED
@@ -1,43 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grover
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.10
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Bromwich
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-01 00:00:00.000000000 Z
11
+ date: 2024-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: combine_pdf
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: nokogiri
29
15
  requirement: !ruby/object:Gem::Requirement
30
16
  requirements:
31
17
  - - "~>"
32
18
  - !ruby/object:Gem::Version
33
- version: '1.0'
19
+ version: '1'
34
20
  type: :runtime
35
21
  prerelease: false
36
22
  version_requirements: !ruby/object:Gem::Requirement
37
23
  requirements:
38
24
  - - "~>"
39
25
  - !ruby/object:Gem::Version
40
- version: '1.0'
26
+ version: '1'
41
27
  description: Transform HTML into PDF/PNG/JPEG using Google Puppeteer/Chromium
42
28
  email:
43
29
  - abromwich@studiosity.com