grover 0.14.1 → 1.0.2
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 +4 -4
- data/lib/grover.rb +10 -1
- data/lib/grover/js/processor.js +121 -9
- data/lib/grover/options_fixer.rb +3 -2
- data/lib/grover/processor.rb +1 -0
- data/lib/grover/utils.rb +8 -5
- data/lib/grover/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: afca67d672e63071ee66a65dc2d2a65e728eb06715b2c59b43cf44dce587ab1c
|
4
|
+
data.tar.gz: 50313e3201d5f1bbbf8e6d9c92147c3ddc373fbdd51145ee3315a616b69b303c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8aa164f8b52ef008c70fe7ff4918194859db0be10cee102556cc67d49fe9cf5035ee181f9dac64dce87dfa7b7295effe090b851504dd9774e33a23ecab7e15e
|
7
|
+
data.tar.gz: cd5b46accc2bbbdc5f3d149d62099c4d47c36744aa877876966680e687bfa0fc69f9ad6045c0a4b9a1e3f135c90279ffd595d526f373daddda02a5fb42db754d
|
data/lib/grover.rb
CHANGED
@@ -50,6 +50,15 @@ class Grover
|
|
50
50
|
processor.convert :pdf, @url, normalized_options(path: path)
|
51
51
|
end
|
52
52
|
|
53
|
+
#
|
54
|
+
# Request URL with provided options and render HTML
|
55
|
+
#
|
56
|
+
# @return [String] The resulting HTML string
|
57
|
+
#
|
58
|
+
def to_html
|
59
|
+
processor.convert :content, @url, normalized_options(path: nil)
|
60
|
+
end
|
61
|
+
|
53
62
|
#
|
54
63
|
# Request URL with provided options and create screenshot
|
55
64
|
#
|
@@ -135,7 +144,7 @@ class Grover
|
|
135
144
|
end
|
136
145
|
|
137
146
|
def normalized_options(path:)
|
138
|
-
normalized_options = Utils.normalize_object @options
|
147
|
+
normalized_options = Utils.normalize_object @options, excluding: ['extraHTTPHeaders']
|
139
148
|
normalized_options['path'] = path if path.is_a? ::String
|
140
149
|
normalized_options
|
141
150
|
end
|
data/lib/grover/js/processor.js
CHANGED
@@ -11,7 +11,7 @@ try {
|
|
11
11
|
process.stdout.write("[\"ok\"]\n");
|
12
12
|
|
13
13
|
const _processPage = (async (convertAction, urlOrHtml, options) => {
|
14
|
-
let browser;
|
14
|
+
let browser, errors = [];
|
15
15
|
try {
|
16
16
|
const launchParams = {
|
17
17
|
args: process.env.GROVER_NO_SANDBOX === 'true' ? ['--no-sandbox', '--disable-setuid-sandbox'] : []
|
@@ -66,12 +66,76 @@ const _processPage = (async (convertAction, urlOrHtml, options) => {
|
|
66
66
|
requestOptions.timeout = timeout;
|
67
67
|
}
|
68
68
|
|
69
|
+
// Setup user agent (if provided)
|
70
|
+
const userAgent = options.userAgent; delete options.userAgent;
|
71
|
+
if (userAgent !== undefined) {
|
72
|
+
await page.setUserAgent(userAgent);
|
73
|
+
}
|
74
|
+
|
69
75
|
// Setup viewport options (if provided)
|
70
76
|
const viewport = options.viewport; delete options.viewport;
|
71
77
|
if (viewport !== undefined) {
|
72
78
|
await page.setViewport(viewport);
|
73
79
|
}
|
74
80
|
|
81
|
+
// If specified, emulate the media type
|
82
|
+
const emulateMedia = options.emulateMedia; delete options.emulateMedia;
|
83
|
+
if (emulateMedia !== undefined) {
|
84
|
+
if (typeof page.emulateMediaType === 'function') {
|
85
|
+
await page.emulateMediaType(emulateMedia);
|
86
|
+
} else {
|
87
|
+
await page.emulateMedia(emulateMedia);
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
// Emulate the media features, if specified
|
92
|
+
const mediaFeatures = options.mediaFeatures; delete options.mediaFeatures;
|
93
|
+
if (Array.isArray(mediaFeatures)) {
|
94
|
+
page.emulateMediaFeatures(mediaFeatures);
|
95
|
+
}
|
96
|
+
|
97
|
+
// Emulate timezone (if provided)
|
98
|
+
const timezone = options.timezone; delete options.timezone;
|
99
|
+
if (timezone !== undefined) {
|
100
|
+
await page.emulateTimezone(timezone);
|
101
|
+
}
|
102
|
+
|
103
|
+
// Emulate vision deficiency (if provided)
|
104
|
+
const visionDeficiency = options.visionDeficiency; delete options.visionDeficiency;
|
105
|
+
if (visionDeficiency !== undefined) {
|
106
|
+
page.emulateVisionDeficiency(type);
|
107
|
+
}
|
108
|
+
|
109
|
+
// Bypass CSP (content security policy), if provided
|
110
|
+
const bypassCSP = options.bypassCSP; delete options.bypassCSP;
|
111
|
+
if (bypassCSP !== undefined) {
|
112
|
+
page.setBypassCSP(bypassCSP);
|
113
|
+
}
|
114
|
+
|
115
|
+
// Add extra HTTP headers (if provided)
|
116
|
+
const extraHTTPHeaders = options.extraHTTPHeaders; delete options.extraHTTPHeaders;
|
117
|
+
if (extraHTTPHeaders !== undefined) {
|
118
|
+
page.setExtraHTTPHeaders(extraHTTPHeaders);
|
119
|
+
}
|
120
|
+
|
121
|
+
// Set geolocation (if provided)
|
122
|
+
const geolocation = options.geolocation; delete options.geolocation;
|
123
|
+
if (geolocation !== undefined) {
|
124
|
+
page.setGeolocation(geolocation);
|
125
|
+
}
|
126
|
+
|
127
|
+
const raiseOnRequestFailure = options.raiseOnRequestFailure; delete options.raiseOnRequestFailure;
|
128
|
+
if (raiseOnRequestFailure) {
|
129
|
+
page.on('requestfinished', (request) => {
|
130
|
+
if (request.response() && !request.response().ok() && !request.redirectChain().length > 0) {
|
131
|
+
errors.push(request);
|
132
|
+
}
|
133
|
+
});
|
134
|
+
page.on('requestfailed', (request) => {
|
135
|
+
errors.push(request);
|
136
|
+
});
|
137
|
+
}
|
138
|
+
|
75
139
|
const waitUntil = options.waitUntil; delete options.waitUntil;
|
76
140
|
if (urlOrHtml.match(/^http/i)) {
|
77
141
|
// Request is for a URL, so request it
|
@@ -91,13 +155,19 @@ const _processPage = (async (convertAction, urlOrHtml, options) => {
|
|
91
155
|
await page.goto(displayUrl || 'http://example.com', requestOptions);
|
92
156
|
}
|
93
157
|
|
94
|
-
//
|
95
|
-
const
|
96
|
-
if (
|
97
|
-
|
98
|
-
await page.
|
99
|
-
}
|
100
|
-
|
158
|
+
// add styles (if provided)
|
159
|
+
const styleTagOptions = options.styleTagOptions; delete options.styleTagOptions;
|
160
|
+
if (Array.isArray(styleTagOptions)) {
|
161
|
+
for (const styleTagOption of styleTagOptions) {
|
162
|
+
await page.addStyleTag(styleTagOption);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
// add scripts (if provided)
|
167
|
+
const scriptTagOptions = options.scriptTagOptions; delete options.scriptTagOptions;
|
168
|
+
if (Array.isArray(scriptTagOptions)) {
|
169
|
+
for (const scriptTagOption of scriptTagOptions) {
|
170
|
+
await page.addScriptTag(scriptTagOption);
|
101
171
|
}
|
102
172
|
}
|
103
173
|
|
@@ -111,7 +181,49 @@ const _processPage = (async (convertAction, urlOrHtml, options) => {
|
|
111
181
|
const waitForSelector = options.waitForSelector; delete options.waitForSelector;
|
112
182
|
const waitForSelectorOptions = options.waitForSelectorOptions; delete options.waitForSelectorOptions;
|
113
183
|
if (waitForSelector !== undefined) {
|
114
|
-
await page.waitForSelector(waitForSelector, waitForSelectorOptions)
|
184
|
+
await page.waitForSelector(waitForSelector, waitForSelectorOptions);
|
185
|
+
}
|
186
|
+
|
187
|
+
// If specified, wait for function
|
188
|
+
const waitForFunction = options.waitForFunction; delete options.waitForFunction;
|
189
|
+
const waitForFunctionOptions = options.waitForFunctionOptions; delete options.waitForFunctionOptions;
|
190
|
+
if (waitForFunction !== undefined) {
|
191
|
+
await page.waitForFunction(waitForFunction, waitForFunctionOptions);
|
192
|
+
}
|
193
|
+
|
194
|
+
// If specified, wait for timeout
|
195
|
+
const waitForTimeout = options.waitForTimeout; delete options.waitForTimeout;
|
196
|
+
if (waitForTimeout !== undefined) {
|
197
|
+
await page.waitForTimeout(waitForTimeout);
|
198
|
+
}
|
199
|
+
|
200
|
+
// If specified, focus on the specified selector
|
201
|
+
const focusSelector = options.focus; delete options.focus;
|
202
|
+
if (focusSelector !== undefined) {
|
203
|
+
await page.focus(focusSelector);
|
204
|
+
}
|
205
|
+
|
206
|
+
// If specified, hover on the specified selector
|
207
|
+
const hoverSelector = options.hover; delete options.hover;
|
208
|
+
if (hoverSelector !== undefined) {
|
209
|
+
await page.hover(hoverSelector);
|
210
|
+
}
|
211
|
+
|
212
|
+
if (errors.length > 0) {
|
213
|
+
function RequestFailedError(errors) {
|
214
|
+
this.name = "RequestFailedError";
|
215
|
+
this.message = errors.map(e => {
|
216
|
+
if (e.failure()) {
|
217
|
+
return e.failure().errorText + " at " + e.url();
|
218
|
+
} else if (e.response() && e.response().status()) {
|
219
|
+
return e.response().status() + " " + e.url();
|
220
|
+
} else {
|
221
|
+
return "UnknownError " + e.url()
|
222
|
+
}
|
223
|
+
}).join("\n");
|
224
|
+
}
|
225
|
+
RequestFailedError.prototype = Error.prototype;
|
226
|
+
throw new RequestFailedError(errors);
|
115
227
|
}
|
116
228
|
|
117
229
|
// If we're running puppeteer in headless mode, return the converted PDF
|
data/lib/grover/options_fixer.rb
CHANGED
@@ -34,13 +34,13 @@ 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'
|
37
|
+
'print_background', 'viewport.has_touch', 'viewport.is_landscape', 'viewport.is_mobile', 'bypass_csp'
|
38
38
|
) { |value| !FALSE_VALUES.include?(value) }
|
39
39
|
end
|
40
40
|
|
41
41
|
def fix_integer_options!
|
42
42
|
fix_options!(
|
43
|
-
'viewport.height', 'viewport.width',
|
43
|
+
'viewport.height', 'viewport.width', 'wait_for_timeout',
|
44
44
|
&:to_i
|
45
45
|
)
|
46
46
|
end
|
@@ -48,6 +48,7 @@ class Grover
|
|
48
48
|
def fix_float_options!
|
49
49
|
fix_options!(
|
50
50
|
'clip.height', 'clip.width', 'clip.x', 'clip.y', 'quality', 'scale', 'viewport.device_scale_factor',
|
51
|
+
'geolocation.latitude', 'geolocation.longitude',
|
51
52
|
&:to_f
|
52
53
|
)
|
53
54
|
end
|
data/lib/grover/processor.rb
CHANGED
data/lib/grover/utils.rb
CHANGED
@@ -6,7 +6,9 @@ class Grover
|
|
6
6
|
#
|
7
7
|
class Utils
|
8
8
|
ACRONYMS = {
|
9
|
-
'css' => 'CSS'
|
9
|
+
'css' => 'CSS',
|
10
|
+
'csp' => 'CSP',
|
11
|
+
'http' => 'HTTP'
|
10
12
|
}.freeze
|
11
13
|
private_constant :ACRONYMS
|
12
14
|
|
@@ -41,11 +43,12 @@ class Grover
|
|
41
43
|
# Copied from active support
|
42
44
|
# @see active_support/core_ext/hash/keys.rb
|
43
45
|
#
|
44
|
-
def self.deep_transform_keys_in_object(object, &block)
|
46
|
+
def self.deep_transform_keys_in_object(object, excluding: [], &block) # rubocop:disable Metrics/MethodLength
|
45
47
|
case object
|
46
48
|
when Hash
|
47
49
|
object.each_with_object({}) do |(key, value), result|
|
48
|
-
|
50
|
+
new_key = yield(key)
|
51
|
+
result[new_key] = excluding.include?(new_key) ? value : deep_transform_keys_in_object(value, &block)
|
49
52
|
end
|
50
53
|
when Array
|
51
54
|
object.map { |e| deep_transform_keys_in_object(e, &block) }
|
@@ -80,8 +83,8 @@ class Grover
|
|
80
83
|
#
|
81
84
|
# Recursively normalizes hash objects with camelized string keys
|
82
85
|
#
|
83
|
-
def self.normalize_object(object)
|
84
|
-
deep_transform_keys_in_object(object) { |k| normalize_key(k) }
|
86
|
+
def self.normalize_object(object, excluding: [])
|
87
|
+
deep_transform_keys_in_object(object, excluding: excluding) { |k| normalize_key(k) }
|
85
88
|
end
|
86
89
|
|
87
90
|
#
|
data/lib/grover/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grover
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Bromwich
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: combine_pdf
|
@@ -181,7 +181,7 @@ homepage: https://github.com/Studiosity/grover
|
|
181
181
|
licenses:
|
182
182
|
- MIT
|
183
183
|
metadata: {}
|
184
|
-
post_install_message:
|
184
|
+
post_install_message:
|
185
185
|
rdoc_options: []
|
186
186
|
require_paths:
|
187
187
|
- lib
|
@@ -199,8 +199,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
199
199
|
- !ruby/object:Gem::Version
|
200
200
|
version: '0'
|
201
201
|
requirements: []
|
202
|
-
rubygems_version: 3.
|
203
|
-
signing_key:
|
202
|
+
rubygems_version: 3.0.6
|
203
|
+
signing_key:
|
204
204
|
specification_version: 4
|
205
205
|
summary: A Ruby gem to transform HTML into PDF, PNG or JPEG by wrapping the NodeJS
|
206
206
|
Google Puppeteer driver for Chromium
|