2015-06-25 06:27:18 +02:00
|
|
|
#!/usr/bin/env node
|
2019-08-08 18:10:41 +02:00
|
|
|
|
2015-06-25 06:27:18 +02:00
|
|
|
'use strict';
|
2019-08-08 18:10:41 +02:00
|
|
|
|
2017-03-20 22:42:19 +01:00
|
|
|
const os = require('os');
|
|
|
|
const fs = require('fs');
|
|
|
|
const meow = require('meow');
|
2018-12-30 23:41:29 +01:00
|
|
|
const chalk = require('chalk');
|
2017-03-20 22:42:19 +01:00
|
|
|
const indentString = require('indent-string');
|
|
|
|
const stdin = require('get-stdin');
|
|
|
|
const css = require('css');
|
2019-11-03 14:23:08 +01:00
|
|
|
const escapeRegExp = require('lodash.escaperegexp');
|
|
|
|
const defaults = require('lodash.defaults');
|
2018-03-05 20:56:57 +01:00
|
|
|
const inlineCritical = require('.');
|
2015-06-25 06:27:18 +02:00
|
|
|
|
2017-03-20 22:42:19 +01:00
|
|
|
let ok;
|
2018-03-05 20:56:57 +01:00
|
|
|
const help = `
|
|
|
|
Usage: inline-critical <input> [<option>]
|
2015-06-25 06:27:18 +02:00
|
|
|
|
2018-03-05 20:56:57 +01:00
|
|
|
Options:
|
|
|
|
-c, --css Path to CSS file
|
|
|
|
-h, --html Path to HTML file
|
|
|
|
-i, --ignore Skip matching stylesheets
|
|
|
|
-m, --minify Minify the styles before inlining (default)
|
2020-05-20 00:31:59 +02:00
|
|
|
-p, --preload Adds preload tags
|
|
|
|
|
2018-03-05 20:56:57 +01:00
|
|
|
-e, --extract Remove the inlined styles from any stylesheets referenced in the HTML
|
|
|
|
-b, --base Is used when extracting styles to find the files references by href attributes
|
|
|
|
-s, --selector Optionally defines the element used by loadCSS as a reference for inlining
|
2019-03-26 17:31:54 +01:00
|
|
|
|
2020-05-20 00:31:59 +02:00
|
|
|
--polyfill Use loadCSS polyfill instead of media=print
|
2019-03-26 17:31:54 +01:00
|
|
|
--noscript Position of noscript fallback ('body' - end of body, 'head' - end of head, false - no noscript)
|
2018-03-05 20:56:57 +01:00
|
|
|
`;
|
|
|
|
|
|
|
|
const cli = meow(help, {
|
2018-12-18 12:51:30 +01:00
|
|
|
autoHelp: true,
|
|
|
|
autoVersion: true,
|
|
|
|
flags: {
|
|
|
|
css: {
|
|
|
|
type: 'string',
|
|
|
|
alias: 'c',
|
|
|
|
},
|
|
|
|
html: {
|
|
|
|
type: 'string',
|
|
|
|
alias: 'h',
|
|
|
|
},
|
|
|
|
ignore: {
|
|
|
|
type: 'string',
|
|
|
|
alias: 'i',
|
2020-05-23 00:36:56 +02:00
|
|
|
isMultiple: true,
|
2018-12-18 12:51:30 +01:00
|
|
|
},
|
|
|
|
minify: {
|
|
|
|
type: 'boolean',
|
|
|
|
alias: 'm',
|
2018-12-30 23:41:29 +01:00
|
|
|
default: true,
|
2018-12-18 12:51:30 +01:00
|
|
|
},
|
|
|
|
extract: {
|
|
|
|
type: 'boolean',
|
|
|
|
alias: 'e',
|
|
|
|
},
|
|
|
|
base: {
|
|
|
|
type: 'string',
|
|
|
|
alias: 'b',
|
|
|
|
},
|
|
|
|
selector: {
|
|
|
|
type: 'string',
|
|
|
|
alias: 's',
|
|
|
|
},
|
2020-05-20 00:31:59 +02:00
|
|
|
preload: {
|
|
|
|
type: 'boolean',
|
|
|
|
alias: 'p',
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
polyfill: {
|
|
|
|
type: 'boolean',
|
|
|
|
default: false,
|
|
|
|
},
|
2019-03-26 17:31:54 +01:00
|
|
|
noscript: {
|
|
|
|
type: 'string',
|
|
|
|
},
|
2018-12-18 12:51:30 +01:00
|
|
|
},
|
2015-06-25 06:27:18 +02:00
|
|
|
});
|
|
|
|
|
2017-03-20 22:42:19 +01:00
|
|
|
// Cleanup cli flags
|
2020-05-23 00:36:56 +02:00
|
|
|
cli.flags = Object.entries(cli.flags).reduce((result, [key, value]) => {
|
2019-11-03 14:23:08 +01:00
|
|
|
if (key.length <= 1) {
|
2020-05-23 00:36:56 +02:00
|
|
|
return result;
|
2019-11-03 14:23:08 +01:00
|
|
|
}
|
2015-06-25 06:27:18 +02:00
|
|
|
|
2019-11-03 14:23:08 +01:00
|
|
|
switch (key) {
|
|
|
|
case 'css':
|
|
|
|
case 'html':
|
|
|
|
try {
|
2020-05-23 00:36:56 +02:00
|
|
|
result[key] = read(value);
|
2019-11-03 14:23:08 +01:00
|
|
|
} catch (_) {}
|
|
|
|
|
|
|
|
break;
|
|
|
|
case 'base':
|
2020-05-23 00:36:56 +02:00
|
|
|
result.basePath = value;
|
2019-11-03 14:23:08 +01:00
|
|
|
break;
|
|
|
|
case 'ignore':
|
2020-05-23 00:36:56 +02:00
|
|
|
if (!Array.isArray(value)) {
|
|
|
|
value = [value];
|
2019-11-03 14:23:08 +01:00
|
|
|
}
|
|
|
|
|
2020-05-23 00:36:56 +02:00
|
|
|
result.ignore = (value || []).map((ignore) => {
|
2019-11-03 14:23:08 +01:00
|
|
|
// Check regex
|
2020-05-20 07:28:30 +02:00
|
|
|
const {groups} = /^\/(?<expression>.*)\/(?<flags>[igmy]+)?$/.exec(ignore) || {};
|
|
|
|
const {expression, flags} = groups || {};
|
2019-11-03 14:23:08 +01:00
|
|
|
|
2020-05-20 07:28:30 +02:00
|
|
|
if (groups) {
|
|
|
|
return new RegExp(escapeRegExp(expression), flags);
|
2018-12-18 12:51:30 +01:00
|
|
|
}
|
2019-06-13 23:31:53 +02:00
|
|
|
|
2019-11-03 14:23:08 +01:00
|
|
|
return ignore;
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
default:
|
2020-05-23 00:36:56 +02:00
|
|
|
result[key] = value;
|
2019-11-03 14:23:08 +01:00
|
|
|
break;
|
|
|
|
}
|
2015-06-25 06:27:18 +02:00
|
|
|
|
2020-05-23 00:36:56 +02:00
|
|
|
return result;
|
2019-11-03 14:23:08 +01:00
|
|
|
}, {});
|
2015-06-25 06:27:18 +02:00
|
|
|
|
2018-09-15 23:16:53 +02:00
|
|
|
function processError(err) {
|
2019-08-08 18:10:41 +02:00
|
|
|
process.stderr.write(chalk.red(indentString(`Error: ${err.message || err}`, 2)));
|
2018-12-18 12:51:30 +01:00
|
|
|
process.stderr.write(os.EOL);
|
2018-12-30 23:41:29 +01:00
|
|
|
process.stderr.write(indentString(help, 2));
|
|
|
|
process.exit(1);
|
2015-06-25 06:27:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function read(file) {
|
2018-12-18 12:51:30 +01:00
|
|
|
try {
|
|
|
|
return fs.readFileSync(file, 'utf8');
|
|
|
|
} catch (error) {
|
|
|
|
processError(error);
|
|
|
|
}
|
2015-06-25 06:27:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function run(data) {
|
2020-05-23 00:36:56 +02:00
|
|
|
const options_ = defaults(cli.flags, {basePath: process.cwd()});
|
2018-12-18 12:51:30 +01:00
|
|
|
ok = true;
|
2015-11-13 22:59:10 +01:00
|
|
|
|
2018-12-18 12:51:30 +01:00
|
|
|
if (data) {
|
|
|
|
// Detect html
|
|
|
|
try {
|
|
|
|
css.parse(data);
|
2020-05-23 00:36:56 +02:00
|
|
|
options_.css = data;
|
2019-10-07 16:12:10 +02:00
|
|
|
} catch (_) {
|
2020-05-23 00:36:56 +02:00
|
|
|
options_.html = data;
|
2015-06-25 06:27:18 +02:00
|
|
|
}
|
2018-12-18 12:51:30 +01:00
|
|
|
}
|
2015-06-25 06:27:18 +02:00
|
|
|
|
2020-05-23 00:36:56 +02:00
|
|
|
(cli.input || []).forEach((file) => {
|
|
|
|
const temporary = read(file);
|
2015-06-25 06:27:18 +02:00
|
|
|
try {
|
2020-05-23 00:36:56 +02:00
|
|
|
css.parse(temporary);
|
|
|
|
options_.css = temporary;
|
2019-10-07 16:12:10 +02:00
|
|
|
} catch (_) {
|
2020-05-23 00:36:56 +02:00
|
|
|
options_.html = temporary;
|
2015-06-25 06:27:18 +02:00
|
|
|
}
|
2018-12-18 12:51:30 +01:00
|
|
|
});
|
|
|
|
|
2020-05-23 00:36:56 +02:00
|
|
|
if (!options_.html || !options_.css) {
|
2018-12-18 12:51:30 +01:00
|
|
|
cli.showHelp();
|
|
|
|
}
|
|
|
|
|
2020-05-23 00:36:56 +02:00
|
|
|
const {html, css: styles, ...options} = options_;
|
2018-12-18 12:51:30 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
const out = inlineCritical(html, styles, options);
|
|
|
|
process.stdout.write(out.toString(), process.exit);
|
|
|
|
} catch (error) {
|
|
|
|
processError(error);
|
|
|
|
}
|
2015-06-25 06:27:18 +02:00
|
|
|
}
|
|
|
|
|
2017-03-20 22:42:19 +01:00
|
|
|
// Get stdin
|
2015-09-16 06:28:54 +02:00
|
|
|
stdin().then(run);
|
2017-03-20 22:42:19 +01:00
|
|
|
setTimeout(() => {
|
2018-12-18 12:51:30 +01:00
|
|
|
if (ok) {
|
|
|
|
return;
|
|
|
|
}
|
2019-06-13 23:31:53 +02:00
|
|
|
|
2018-12-18 12:51:30 +01:00
|
|
|
run();
|
2015-06-25 23:27:56 +02:00
|
|
|
}, 100);
|