-
Notifications
You must be signed in to change notification settings - Fork 0
/
critical-css.js
executable file
·90 lines (80 loc) · 2.47 KB
/
critical-css.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env node
const yargs = require('yargs')
const penthouse = require('penthouse')
const fs = require("fs");
const path = require("path");
const args = yargs(process.argv.slice(2))
.usage('$0 [url..]', 'Extract the critical CSS from the passed blob of CSS', (yargs) => {
yargs.positional('url', {
describe: 'The URL to compare to',
type: 'string',
})
yargs.option('exclude', {
describe: 'Exclude css selectors to remove in critical css (the `forceExclude` option in penthouse)',
type: 'array',
default: [],
})
}).argv
main(args['url'])
async function main(urls) {
let cssString = ""
process.stdin.on('data', str => cssString += str)
process.stdin.on('end', async () => {
const cssPromises = urls.map(url => {
process.stderr.write(`Gathering critical CSS for ${url}\n`);
return findCriticalCss(cssString, url);
});
const cssArray = await Promise.all(cssPromises);
for (const css of cssArray) {
process.stdout.write(css);
}
})
}
function findCriticalCss(cssString, url) {
const excludeArr = validateArrayRegexes(args['exclude']);
return penthouse({
url,
cssString,
pageLoadSkipTimeout: 60000,
renderWaitTime: 60000,
timeout: 120000,
width: 4096,
height: 2160,
blockJSRequests: false,
forceExclude: excludeArr,
screenshots: {
basePath: getScreenshotPath(url),
type: 'jpeg',
quality: 100
}
})}
// Returns a relative path for the screenshot using the url
function getScreenshotPath(url) {
const screenshotsDir = "critical-css-screenshots";
// sanitize the url to use it as a filename
const filename = url.replace(/[?/:<>"|*]/g, "-");
if (!fs.existsSync(screenshotsDir)) {
fs.mkdirSync(screenshotsDir);
}
return path.join(screenshotsDir, filename)
}
// When a regex is passed from a command line it converts to a string,
// so we need to convert it back to a regex
function validateArrayRegexes(arr) {
return arr.map((str) => {
if (str.startsWith("/") && str.lastIndexOf("/") > 0) {
// Extract the regular expression pattern and flags from the string
const lastSlashIndex = str.lastIndexOf("/");
const pattern = str.substring(1, lastSlashIndex);
const flags = str.substring(lastSlashIndex + 1);
// Create a new RegExp object with the pattern and flags
try {
return new RegExp(pattern, flags);
} catch (error) {
return str;
}
} else {
return str;
}
});
}