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 +4 -4
- data/lib/grover/configuration.rb +4 -2
- data/lib/grover/errors.rb +12 -0
- data/lib/grover/js/processor.cjs +49 -20
- data/lib/grover/middleware.rb +7 -2
- data/lib/grover/options_fixer.rb +2 -1
- data/lib/grover/processor.rb +3 -3
- data/lib/grover/utils.rb +2 -1
- data/lib/grover/version.rb +1 -1
- metadata +4 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50a8ec2a93d7be70d77b4aefd45f6a87296cbd0201f3fba5a2d962fce511f4b7
|
4
|
+
data.tar.gz: 340a18b36da574c7cf57c897c3d767e890385279b681e345e2ba29cc5bf3430e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13124e1d70416020c29695bbf59f2618015def0d46d4d194c5ba514b4f4c48a2622adc6238c32254717601b50acaadc3beac5065fb19ffbdf864b5d8f4bda868
|
7
|
+
data.tar.gz: dae49b29ab27b64b990f8f71f7571afb00e01c18a7a3b94571af2a1b74bd578248db4b712241e571674683052b196a8b4164095db449058a95d9e8f2509d9119
|
data/lib/grover/configuration.rb
CHANGED
@@ -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, :
|
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
|
data/lib/grover/js/processor.cjs
CHANGED
@@ -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,
|
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() &&
|
167
|
-
|
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
|
-
|
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 (
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
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
|
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
|
);
|
data/lib/grover/middleware.rb
CHANGED
@@ -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
|
data/lib/grover/options_fixer.rb
CHANGED
@@ -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
|
|
data/lib/grover/processor.rb
CHANGED
@@ -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
|
-
|
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),
|
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
data/lib/grover/version.rb
CHANGED
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.
|
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-
|
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
|
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
|
26
|
+
version: '1'
|
41
27
|
description: Transform HTML into PDF/PNG/JPEG using Google Puppeteer/Chromium
|
42
28
|
email:
|
43
29
|
- abromwich@studiosity.com
|