1 | "use strict";
|
2 |
|
3 | var _fs = _interopRequireDefault(require("fs"));
|
4 |
|
5 | var _debug = _interopRequireDefault(require("debug"));
|
6 |
|
7 | var _core = _interopRequireDefault(require("./core"));
|
8 |
|
9 | var _browser = require("./browser");
|
10 |
|
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
12 |
|
13 | const debuglog = (0, _debug.default)('penthouse');
|
14 | const DEFAULT_VIEWPORT_WIDTH = 1300;
|
15 |
|
16 | const DEFAULT_VIEWPORT_HEIGHT = 900;
|
17 |
|
18 | const DEFAULT_TIMEOUT = 30000;
|
19 |
|
20 | const DEFAULT_MAX_EMBEDDED_BASE64_LENGTH = 1000;
|
21 |
|
22 | const DEFAULT_USER_AGENT = 'Penthouse Critical Path CSS Generator';
|
23 | const DEFAULT_RENDER_WAIT_TIMEOUT = 100;
|
24 | const DEFAULT_BLOCK_JS_REQUESTS = true;
|
25 | const DEFAULT_PROPERTIES_TO_REMOVE = ['(.*)transition(.*)', 'cursor', 'pointer-events', '(-webkit-)?tap-highlight-color', '(.*)user-select'];
|
26 | const _UNSTABLE_KEEP_ALIVE_MAX_KEPT_OPEN_PAGES = 4;
|
27 |
|
28 | function exitHandler(exitCode) {
|
29 | (0, _browser.closeBrowser)({
|
30 | forceClose: true
|
31 | });
|
32 | process.exit(typeof exitCode === 'number' ? exitCode : 0);
|
33 | }
|
34 |
|
35 | function readFilePromise(filepath, encoding) {
|
36 | return new Promise((resolve, reject) => {
|
37 | _fs.default.readFile(filepath, encoding, (err, content) => {
|
38 | if (err) {
|
39 | return reject(err);
|
40 | }
|
41 |
|
42 | resolve(content);
|
43 | });
|
44 | });
|
45 | }
|
46 |
|
47 | function prepareForceSelectorsForSerialization(forceSelectors = []) {
|
48 |
|
49 | return forceSelectors.map(function (forceSelectorValue) {
|
50 | if (typeof forceSelectorValue === 'object' && forceSelectorValue.constructor.name === 'RegExp') {
|
51 | return {
|
52 | type: 'RegExp',
|
53 | source: forceSelectorValue.source,
|
54 | flags: forceSelectorValue.flags
|
55 | };
|
56 | }
|
57 |
|
58 | return {
|
59 | value: forceSelectorValue
|
60 | };
|
61 | });
|
62 | }
|
63 |
|
64 |
|
65 | const generateCriticalCssWrapped = async function generateCriticalCssWrapped(options, {
|
66 | forceTryRestartBrowser
|
67 | } = {}) {
|
68 | const width = parseInt(options.width || DEFAULT_VIEWPORT_WIDTH, 10);
|
69 | const height = parseInt(options.height || DEFAULT_VIEWPORT_HEIGHT, 10);
|
70 | const timeoutWait = options.timeout || DEFAULT_TIMEOUT;
|
71 |
|
72 | const propertiesToRemove = options.propertiesToRemove || DEFAULT_PROPERTIES_TO_REMOVE;
|
73 |
|
74 |
|
75 | const forceInclude = prepareForceSelectorsForSerialization(['*', '*:before', '*:after', 'html', 'body'].concat(options.forceInclude || []));
|
76 | const forceExclude = prepareForceSelectorsForSerialization(options.forceExclude || []);
|
77 |
|
78 |
|
79 | return new Promise(async (resolve, reject) => {
|
80 | debuglog('call generateCriticalCssWrapped');
|
81 | let formattedCss;
|
82 | let pagePromise;
|
83 |
|
84 | try {
|
85 | pagePromise = (0, _browser.getOpenBrowserPage)();
|
86 | formattedCss = await (0, _core.default)({
|
87 | pagePromise,
|
88 | url: options.url,
|
89 | cssString: options.cssString,
|
90 | width,
|
91 | height,
|
92 | forceInclude,
|
93 | forceExclude,
|
94 | strict: options.strict,
|
95 | userAgent: options.userAgent || DEFAULT_USER_AGENT,
|
96 | renderWaitTime: options.renderWaitTime || DEFAULT_RENDER_WAIT_TIMEOUT,
|
97 | timeout: timeoutWait,
|
98 | pageLoadSkipTimeout: options.pageLoadSkipTimeout,
|
99 | blockJSRequests: typeof options.blockJSRequests !== 'undefined' ? options.blockJSRequests : DEFAULT_BLOCK_JS_REQUESTS,
|
100 | customPageHeaders: options.customPageHeaders,
|
101 | cookies: options.cookies,
|
102 | screenshots: options.screenshots,
|
103 | keepLargerMediaQueries: options.keepLargerMediaQueries,
|
104 | maxElementsToCheckPerSelector: options.maxElementsToCheckPerSelector,
|
105 |
|
106 | propertiesToRemove,
|
107 | maxEmbeddedBase64Length: typeof options.maxEmbeddedBase64Length === 'number' ? options.maxEmbeddedBase64Length : DEFAULT_MAX_EMBEDDED_BASE64_LENGTH,
|
108 | debuglog,
|
109 | unstableKeepBrowserAlive: options.unstableKeepBrowserAlive,
|
110 | allowedResponseCode: options.allowedResponseCode,
|
111 | unstableKeepOpenPages: options.unstableKeepOpenPages || _UNSTABLE_KEEP_ALIVE_MAX_KEPT_OPEN_PAGES
|
112 | });
|
113 | } catch (e) {
|
114 | const page = await pagePromise.then(({
|
115 | page
|
116 | }) => page);
|
117 | await (0, _browser.closeBrowserPage)({
|
118 | page,
|
119 | error: e,
|
120 | unstableKeepBrowserAlive: options.unstableKeepBrowserAlive,
|
121 | unstableKeepOpenPages: options.unstableKeepOpenPages
|
122 | });
|
123 | const runningBrowswer = await (0, _browser.browserIsRunning)();
|
124 |
|
125 | if (!forceTryRestartBrowser && !runningBrowswer) {
|
126 | debuglog('Browser unexpecedly not opened - crashed? ' + '\nurl: ' + options.url + '\ncss length: ' + options.cssString.length);
|
127 |
|
128 | try {
|
129 | await (0, _browser.restartBrowser)({
|
130 | width,
|
131 | height,
|
132 | getBrowser: options.puppeteer && options.puppeteer.getBrowser
|
133 | });
|
134 |
|
135 | resolve(generateCriticalCssWrapped(options, {
|
136 | forceTryRestartBrowser: true
|
137 | }));
|
138 | } catch (e) {
|
139 | reject(e);
|
140 | }
|
141 |
|
142 | return;
|
143 | }
|
144 |
|
145 | reject(e);
|
146 | return;
|
147 | }
|
148 |
|
149 | const page = await pagePromise.then(({
|
150 | page
|
151 | }) => page);
|
152 | await (0, _browser.closeBrowserPage)({
|
153 | page,
|
154 | unstableKeepBrowserAlive: options.unstableKeepBrowserAlive,
|
155 | unstableKeepOpenPages: options.unstableKeepOpenPages
|
156 | });
|
157 | debuglog('generateCriticalCss done');
|
158 |
|
159 | if (formattedCss.trim().length === 0) {
|
160 |
|
161 | debuglog('Note: Generated critical css was empty for URL: ' + options.url);
|
162 | resolve('');
|
163 | return;
|
164 | }
|
165 |
|
166 | resolve(formattedCss);
|
167 | });
|
168 | };
|
169 |
|
170 | module.exports = function (options, callback) {
|
171 | process.on('exit', exitHandler);
|
172 | process.on('SIGTERM', exitHandler);
|
173 | process.on('SIGINT', exitHandler);
|
174 | return new Promise(async (resolve, reject) => {
|
175 | (0, _browser.addJob)();
|
176 |
|
177 | function cleanupAndExit({
|
178 | returnValue,
|
179 | error = null
|
180 | }) {
|
181 | process.removeListener('exit', exitHandler);
|
182 | process.removeListener('SIGTERM', exitHandler);
|
183 | process.removeListener('SIGINT', exitHandler);
|
184 | (0, _browser.removeJob)();
|
185 | (0, _browser.closeBrowser)({
|
186 | unstableKeepBrowserAlive: options.unstableKeepBrowserAlive
|
187 | });
|
188 |
|
189 | if (callback) {
|
190 | callback(error, returnValue);
|
191 | return;
|
192 | }
|
193 |
|
194 | if (error) {
|
195 | reject(error);
|
196 | } else {
|
197 | resolve(returnValue);
|
198 | }
|
199 | }
|
200 |
|
201 |
|
202 | if (!options.cssString && options.css) {
|
203 | try {
|
204 | const cssString = await readFilePromise(options.css, 'utf8');
|
205 | options = Object.assign({}, options, {
|
206 | cssString
|
207 | });
|
208 | } catch (err) {
|
209 | debuglog('error reading css file: ' + options.css + ', error: ' + err);
|
210 | cleanupAndExit({
|
211 | error: err
|
212 | });
|
213 | return;
|
214 | }
|
215 | }
|
216 |
|
217 | if (!options.cssString) {
|
218 | debuglog('Passed in css is empty');
|
219 | cleanupAndExit({
|
220 | error: new Error('css should not be empty')
|
221 | });
|
222 | return;
|
223 | }
|
224 |
|
225 | const width = parseInt(options.width || DEFAULT_VIEWPORT_WIDTH, 10);
|
226 | const height = parseInt(options.height || DEFAULT_VIEWPORT_HEIGHT, 10);
|
227 |
|
228 | try {
|
229 |
|
230 | await (0, _browser.launchBrowserIfNeeded)({
|
231 | getBrowser: options.puppeteer && options.puppeteer.getBrowser,
|
232 | width,
|
233 | height
|
234 | });
|
235 | const criticalCss = await generateCriticalCssWrapped(options);
|
236 | cleanupAndExit({
|
237 | returnValue: criticalCss
|
238 | });
|
239 | } catch (err) {
|
240 | cleanupAndExit({
|
241 | error: err
|
242 | });
|
243 | }
|
244 | });
|
245 | }; |
\ | No newline at end of file |