1 | #!/usr/bin/env node
|
2 |
|
3 | 'use strict';
|
4 |
|
5 | const os = require('os');
|
6 | const path = require('path');
|
7 | const chalk = require('chalk');
|
8 | const meow = require('meow');
|
9 | const groupArgs = require('group-args');
|
10 | const indentString = require('indent-string');
|
11 | const stdin = require('get-stdin');
|
12 | const {assign, escapeRegExp, isString, isRegExp, map, reduce} = require('lodash');
|
13 |
|
14 | const file = require('./lib/file-helper');
|
15 | const critical = require('.');
|
16 |
|
17 | let ok;
|
18 |
|
19 | const help = `
|
20 | Usage: critical <input> [<option>]
|
21 |
|
22 | Options:
|
23 | -b, --base Your base directory
|
24 | -c, --css Your CSS Files (optional)
|
25 | -w, --width Viewport width
|
26 | -h, --height Viewport height
|
27 | -i, --inline Generate the HTML with inlined critical-path CSS
|
28 | -e, --extract Extract inlined styles from referenced stylesheets
|
29 | -p, --pathPrefix Path to prepend CSS assets with (defaults to /)
|
30 | -f, --folder HTML Subfolder (default: '')
|
31 | --ii, --inlineImages Inline images
|
32 | --ua, --userAgent User agent to use when fetching remote src
|
33 | --ignore RegExp, @type or selector to ignore
|
34 | --include RegExp, @type or selector to include
|
35 | --maxFileSize Sets a max file size (in bytes) for base64 inlined images
|
36 | --assetPaths Directories/Urls where the inliner should start looking for assets.
|
37 | --timeout Sets the maximum timeout (in milliseconds) for the operation (defaults to 30000 ms).'
|
38 | --user RFC2617 basic authorization user
|
39 | --pass RFC2617 basic authorization password
|
40 | `;
|
41 |
|
42 | const minimistOpts = {
|
43 | flags: {
|
44 | base: {
|
45 | type: 'string',
|
46 | alias: 'b'
|
47 | },
|
48 | css: {
|
49 | type: 'string',
|
50 | alias: 'c'
|
51 | },
|
52 | width: {
|
53 | alias: 'w'
|
54 | },
|
55 | height: {
|
56 | alias: 'h'
|
57 | },
|
58 | folder: {
|
59 | type: 'string',
|
60 | alias: 'f'
|
61 | },
|
62 | inline: {
|
63 | type: 'boolean',
|
64 | alias: 'i'
|
65 | },
|
66 | ignore: {
|
67 | type: 'string',
|
68 | alias: 'I'
|
69 | },
|
70 | extract: {
|
71 | type: 'boolean',
|
72 | alias: 'e'
|
73 | },
|
74 | pathPrefix: {
|
75 | type: 'string',
|
76 | alias: 'p'
|
77 | },
|
78 | inlineImages: {
|
79 | type: 'boolean',
|
80 | alias: 'ii'
|
81 | },
|
82 | user: {
|
83 | type: 'string'
|
84 | },
|
85 | pass: {
|
86 | type: 'string'
|
87 | },
|
88 | userAgent: {
|
89 | type: 'string',
|
90 | alias: 'ua'
|
91 | }
|
92 | }
|
93 | };
|
94 |
|
95 | const cli = meow(help, minimistOpts);
|
96 |
|
97 |
|
98 | cli.flags = Object.assign({}, cli.flags, groupArgs(['inline', 'penthouse'], {
|
99 | delimiter: '-'
|
100 | }, minimistOpts));
|
101 |
|
102 |
|
103 | cli.flags = reduce(cli.flags, (res, val, key) => {
|
104 | if (key.length <= 1) {
|
105 | return res;
|
106 | }
|
107 |
|
108 | switch (key) {
|
109 | case 'pathprefix':
|
110 | res.pathPrefix = val;
|
111 | break;
|
112 | case 'inlineimages':
|
113 | res.inlineImages = val;
|
114 | break;
|
115 | case 'userAgent':
|
116 | res.userAgent = val;
|
117 | break;
|
118 | case 'maxfilesize':
|
119 | res.maxFileSize = val;
|
120 | break;
|
121 | case 'timeout':
|
122 | res.timeout = val;
|
123 | break;
|
124 | case 'assetpaths':
|
125 | case 'assetPaths':
|
126 | if (isString(val)) {
|
127 | val = [val];
|
128 | }
|
129 |
|
130 | res.assetPaths = val;
|
131 | break;
|
132 | case 'include':
|
133 | case 'ignore':
|
134 | if (isString(val) || isRegExp(val)) {
|
135 | val = [val];
|
136 | }
|
137 |
|
138 | res[key] = map(val || [], entry => {
|
139 |
|
140 | const match = entry.match(/^\/(.*)\/([igmy]+)?$/);
|
141 |
|
142 | if (match) {
|
143 | return new RegExp(escapeRegExp(match[1]), match[2]);
|
144 | }
|
145 |
|
146 | return entry;
|
147 | });
|
148 | break;
|
149 | default:
|
150 | res[key] = val;
|
151 | break;
|
152 | }
|
153 |
|
154 | return res;
|
155 | }, {});
|
156 |
|
157 | function error(err) {
|
158 | process.stderr.write(indentString((chalk.red('Error: ') + err.message || err), 3));
|
159 | process.stderr.write(os.EOL);
|
160 | process.stderr.write(indentString(help, 3));
|
161 | process.exit(1);
|
162 | }
|
163 |
|
164 | function run(data) {
|
165 | const opts = assign({base: process.cwd()}, cli.flags);
|
166 | ok = true;
|
167 |
|
168 | if (data) {
|
169 | opts.html = data;
|
170 | } else {
|
171 | opts.src = cli.input[0];
|
172 | if (opts.src && !file.isExternal(opts.src)) {
|
173 | opts.src = path.resolve(cli.input[0]);
|
174 | }
|
175 | }
|
176 |
|
177 | try {
|
178 | critical.generate(opts, (err, val) => {
|
179 | if (err) {
|
180 | error(err);
|
181 | } else {
|
182 | process.stdout.write(val, process.exit);
|
183 | }
|
184 | });
|
185 | } catch (error) {
|
186 | error(error);
|
187 | }
|
188 | }
|
189 |
|
190 | if (cli.input[0]) {
|
191 | run();
|
192 | } else {
|
193 |
|
194 | stdin().then(run);
|
195 | setTimeout(() => {
|
196 | if (ok) {
|
197 | return;
|
198 | }
|
199 |
|
200 | run();
|
201 | }, 100);
|
202 | }
|