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 | debuglog('call generateCriticalCssWrapped');
|
78 | let formattedCss;
|
79 | let pagePromise;
|
80 |
|
81 | try {
|
82 | pagePromise = (0, _browser.getOpenBrowserPage)();
|
83 | formattedCss = await (0, _core.default)({
|
84 | pagePromise,
|
85 | url: options.url,
|
86 | cssString: options.cssString,
|
87 | width,
|
88 | height,
|
89 | forceInclude,
|
90 | forceExclude,
|
91 | strict: options.strict,
|
92 | userAgent: options.userAgent || DEFAULT_USER_AGENT,
|
93 | renderWaitTime: options.renderWaitTime || DEFAULT_RENDER_WAIT_TIMEOUT,
|
94 | timeout: timeoutWait,
|
95 | pageLoadSkipTimeout: options.pageLoadSkipTimeout,
|
96 | blockJSRequests: typeof options.blockJSRequests !== 'undefined' ? options.blockJSRequests : DEFAULT_BLOCK_JS_REQUESTS,
|
97 | customPageHeaders: options.customPageHeaders,
|
98 | cookies: options.cookies,
|
99 | screenshots: options.screenshots,
|
100 | keepLargerMediaQueries: options.keepLargerMediaQueries,
|
101 | maxElementsToCheckPerSelector: options.maxElementsToCheckPerSelector,
|
102 |
|
103 | propertiesToRemove,
|
104 | maxEmbeddedBase64Length: typeof options.maxEmbeddedBase64Length === 'number' ? options.maxEmbeddedBase64Length : DEFAULT_MAX_EMBEDDED_BASE64_LENGTH,
|
105 | debuglog,
|
106 | unstableKeepBrowserAlive: options.unstableKeepBrowserAlive,
|
107 | allowedResponseCode: options.allowedResponseCode,
|
108 | unstableKeepOpenPages: options.unstableKeepOpenPages || _UNSTABLE_KEEP_ALIVE_MAX_KEPT_OPEN_PAGES
|
109 | });
|
110 | } catch (e) {
|
111 | const page = await pagePromise.then(({
|
112 | page
|
113 | }) => page);
|
114 | await (0, _browser.closeBrowserPage)({
|
115 | page,
|
116 | error: e,
|
117 | unstableKeepBrowserAlive: options.unstableKeepBrowserAlive,
|
118 | unstableKeepOpenPages: options.unstableKeepOpenPages
|
119 | });
|
120 | const runningBrowswer = await (0, _browser.browserIsRunning)();
|
121 |
|
122 | if (!forceTryRestartBrowser && !runningBrowswer) {
|
123 | debuglog('Browser unexpecedly not opened - crashed? ' + '\nurl: ' + options.url + '\ncss length: ' + options.cssString.length);
|
124 | await (0, _browser.restartBrowser)({
|
125 | width,
|
126 | height,
|
127 | getBrowser: options.puppeteer && options.puppeteer.getBrowser
|
128 | });
|
129 |
|
130 | return generateCriticalCssWrapped(options, {
|
131 | forceTryRestartBrowser: true
|
132 | });
|
133 | }
|
134 |
|
135 | throw e;
|
136 | }
|
137 |
|
138 | const page = await pagePromise.then(({
|
139 | page
|
140 | }) => page);
|
141 | await (0, _browser.closeBrowserPage)({
|
142 | page,
|
143 | unstableKeepBrowserAlive: options.unstableKeepBrowserAlive,
|
144 | unstableKeepOpenPages: options.unstableKeepOpenPages
|
145 | });
|
146 | debuglog('generateCriticalCss done');
|
147 |
|
148 | if (formattedCss.trim().length === 0) {
|
149 |
|
150 | debuglog('Note: Generated critical css was empty for URL: ' + options.url);
|
151 | return '';
|
152 | }
|
153 |
|
154 | return formattedCss;
|
155 | };
|
156 |
|
157 | module.exports = async function (options, callback) {
|
158 | process.on('exit', exitHandler);
|
159 | process.on('SIGTERM', exitHandler);
|
160 | process.on('SIGINT', exitHandler);
|
161 | (0, _browser.addJob)();
|
162 |
|
163 | function cleanupAndExit({
|
164 | returnValue,
|
165 | error = null
|
166 | }) {
|
167 | process.removeListener('exit', exitHandler);
|
168 | process.removeListener('SIGTERM', exitHandler);
|
169 | process.removeListener('SIGINT', exitHandler);
|
170 | (0, _browser.removeJob)();
|
171 | (0, _browser.closeBrowser)({
|
172 | unstableKeepBrowserAlive: options.unstableKeepBrowserAlive
|
173 | });
|
174 |
|
175 | if (callback) {
|
176 | callback(error, returnValue);
|
177 | return;
|
178 | }
|
179 |
|
180 | if (error) {
|
181 | throw error;
|
182 | } else {
|
183 | return returnValue;
|
184 | }
|
185 | }
|
186 |
|
187 |
|
188 | if (!options.cssString && options.css) {
|
189 | try {
|
190 | const cssString = await readFilePromise(options.css, 'utf8');
|
191 | options = Object.assign({}, options, {
|
192 | cssString
|
193 | });
|
194 | } catch (err) {
|
195 | debuglog('error reading css file: ' + options.css + ', error: ' + err);
|
196 | return cleanupAndExit({
|
197 | error: err
|
198 | });
|
199 | }
|
200 | }
|
201 |
|
202 | if (!options.cssString) {
|
203 | debuglog('Passed in css is empty');
|
204 | return cleanupAndExit({
|
205 | error: new Error('css should not be empty')
|
206 | });
|
207 | }
|
208 |
|
209 | const width = parseInt(options.width || DEFAULT_VIEWPORT_WIDTH, 10);
|
210 | const height = parseInt(options.height || DEFAULT_VIEWPORT_HEIGHT, 10);
|
211 |
|
212 | try {
|
213 |
|
214 | await (0, _browser.launchBrowserIfNeeded)({
|
215 | getBrowser: options.puppeteer && options.puppeteer.getBrowser,
|
216 | width,
|
217 | height
|
218 | });
|
219 | const criticalCss = await generateCriticalCssWrapped(options);
|
220 | return cleanupAndExit({
|
221 | returnValue: criticalCss
|
222 | });
|
223 | } catch (err) {
|
224 | return cleanupAndExit({
|
225 | error: err
|
226 | });
|
227 | }
|
228 | }; |
\ | No newline at end of file |