UNPKG

5.12 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3'use strict';
4
5const os = require('os');
6const path = require('path');
7const chalk = require('chalk');
8const meow = require('meow');
9const groupArgs = require('group-args');
10const indentString = require('indent-string');
11const stdin = require('get-stdin');
12const {assign, escapeRegExp, isString, isRegExp, map, reduce} = require('lodash');
13
14const file = require('./lib/file-helper');
15const critical = require('.');
16
17let ok;
18
19const help = `
20Usage: critical <input> [<option>]
21
22Options:
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
42const 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
95const cli = meow(help, minimistOpts);
96
97// Group args for inline-critical and penthouse
98cli.flags = Object.assign({}, cli.flags, groupArgs(['inline', 'penthouse'], {
99 delimiter: '-'
100}, minimistOpts));
101
102// Cleanup cli flags and assert cammelcase keeps camelcase
103cli.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 // Check regex
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
157function 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
164function 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
190if (cli.input[0]) {
191 run();
192} else {
193 // Get stdin
194 stdin().then(run);
195 setTimeout(() => {
196 if (ok) {
197 return;
198 }
199
200 run();
201 }, 100);
202}