iptv-stream-web/node_modules/csso-cli/index.js

392 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var fs = require('fs');
var path = require('path');
var cli = require('clap');
var csso = require('csso');
var SourceMapConsumer = require('source-map').SourceMapConsumer;
function unixPathname(pathname) {
return pathname.replace(/\\/g, '/');
}
function readFromStream(stream, minify) {
var buffer = [];
stream
.setEncoding('utf8')
.on('data', function(chunk) {
buffer.push(chunk);
})
.on('end', function() {
minify(buffer.join(''));
});
}
function showStat(filename, source, result, inputMap, map, time, mem) {
function fmt(size) {
return String(size).split('').reverse().reduce(function(size, digit, idx) {
if (idx && idx % 3 === 0) {
size = ' ' + size;
}
return digit + size;
}, '');
}
map = map || 0;
result -= map;
console.error('Source: ', filename === '<stdin>' ? filename : path.relative(process.cwd(), filename));
if (inputMap) {
console.error('Map source:', inputMap);
}
console.error('Original: ', fmt(source), 'bytes');
console.error('Compressed:', fmt(result), 'bytes', '(' + (100 * result / source).toFixed(2) + '%)');
console.error('Saving: ', fmt(source - result), 'bytes', '(' + (100 * (source - result) / source).toFixed(2) + '%)');
if (map) {
console.error('Source map:', fmt(map), 'bytes', '(' + (100 * map / (result + map)).toFixed(2) + '% of total)');
console.error('Total: ', fmt(map + result), 'bytes');
}
console.error('Time: ', time, 'ms');
console.error('Memory: ', (mem / (1024 * 1024)).toFixed(3), 'MB');
}
function showParseError(source, filename, details, message) {
function processLines(start, end) {
return lines.slice(start, end).map(function(line, idx) {
var num = String(start + idx + 1);
while (num.length < maxNumLength) {
num = ' ' + num;
}
return num + ' |' + line;
}).join('\n');
}
var lines = source.split(/\n|\r\n?|\f/);
var column = details.column;
var line = details.line;
var startLine = Math.max(1, line - 2);
var endLine = Math.min(line + 2, lines.length + 1);
var maxNumLength = Math.max(4, String(endLine).length) + 1;
console.error('\nParse error ' + filename + ': ' + message);
console.error(processLines(startLine - 1, line));
console.error(new Array(column + maxNumLength + 2).join('-') + '^');
console.error(processLines(line, endLine));
console.error();
}
function debugLevel(level) {
// level is undefined when no param -> 1
return isNaN(level) ? 1 : Math.max(Number(level), 0);
}
function resolveSourceMap(source, inputMap, outputMap, inputFile, outputFile) {
var inputMapContent = null;
var inputMapFile = null;
var outputMapFile = null;
switch (outputMap) {
case 'none':
// don't generate source map
outputMap = false;
inputMap = 'none';
break;
case 'inline':
// nothing to do
break;
case 'file':
if (!outputFile) {
console.error('Output filename should be specified when `--source-map file` is used');
process.exit(2);
}
outputMapFile = outputFile + '.map';
break;
default:
// process filename
if (outputMap) {
// check path is reachable
if (!fs.existsSync(path.dirname(outputMap))) {
console.error('Directory for map file should exists:', path.dirname(path.resolve(outputMap)));
process.exit(2);
}
// resolve to absolute path
outputMapFile = path.resolve(process.cwd(), outputMap);
}
}
switch (inputMap) {
case 'none':
// nothing to do
break;
case 'auto':
if (outputMap) {
// try fetch source map from source
var inputMapComment = source.match(/\/\*# sourceMappingURL=(\S+)\s*\*\/\s*$/);
if (inputFile === '<stdin>') {
inputFile = false;
}
if (inputMapComment) {
// if comment found value is filename or base64-encoded source map
inputMapComment = inputMapComment[1];
if (inputMapComment.substr(0, 5) === 'data:') {
// decode source map content from comment
inputMapContent = Buffer.from(inputMapComment.substr(inputMapComment.indexOf('base64,') + 7), 'base64').toString();
} else {
// value is filename resolve it as absolute path
if (inputFile) {
inputMapFile = path.resolve(path.dirname(inputFile), inputMapComment);
}
}
} else {
// comment doesn't found - look up file with `.map` extension nearby input file
if (inputFile && fs.existsSync(inputFile + '.map')) {
inputMapFile = inputFile + '.map';
}
}
}
break;
default:
if (inputMap) {
inputMapFile = inputMap;
}
}
// source map placed in external file
if (inputMapFile) {
inputMapContent = fs.readFileSync(inputMapFile, 'utf8');
}
return {
input: inputMapContent,
inputFile: inputMapFile || (inputMapContent ? '<inline>' : false),
output: outputMap,
outputFile: outputMapFile
};
}
function processCommentsOption(value) {
switch (value) {
case 'exclamation':
case 'first-exclamation':
case 'none':
return value;
}
console.error('Wrong value for `comments` option: %s', value);
process.exit(2);
}
function processOptions(options, args) {
var inputFile = options.input || args[0];
var outputFile = options.output;
var usageFile = options.usage;
var usageData = false;
var sourceMap = options.sourceMap;
var inputSourceMap = options.inputSourceMap;
var declarationList = options.declarationList;
var restructure = Boolean(options.restructure);
var forceMediaMerge = Boolean(options.forceMediaMerge);
var comments = processCommentsOption(options.comments);
var debug = options.debug;
var statistics = options.stat;
var watch = options.watch;
if (process.stdin.isTTY && !inputFile && !outputFile) {
return null;
}
if (!inputFile) {
inputFile = '<stdin>';
} else {
inputFile = path.resolve(process.cwd(), inputFile);
}
if (outputFile) {
outputFile = path.resolve(process.cwd(), outputFile);
}
if (usageFile) {
if (!fs.existsSync(usageFile)) {
console.error('Usage data file doesn\'t found (%s)', usageFile);
process.exit(2);
}
usageData = fs.readFileSync(usageFile, 'utf-8');
try {
usageData = JSON.parse(usageData);
} catch (e) {
console.error('Usage data parse error (%s)', usageFile);
process.exit(2);
}
}
return {
inputFile: inputFile,
outputFile: outputFile,
usageData: usageData,
sourceMap: sourceMap,
inputSourceMap: inputSourceMap,
declarationList: declarationList,
restructure: restructure,
forceMediaMerge: forceMediaMerge,
comments: comments,
statistics: statistics,
debug: debug,
watch: watch
};
}
function minifyStream(options) {
var inputStream = options.inputFile !== '<stdin>'
? fs.createReadStream(options.inputFile)
: process.stdin;
readFromStream(inputStream, function(source) {
var time = process.hrtime();
var mem = process.memoryUsage().heapUsed;
var relInputFilename = path.relative(process.cwd(), options.inputFile);
var sourceMap = resolveSourceMap(
source,
options.inputSourceMap,
options.sourceMap,
options.inputFile,
options.outputFile
);
var sourceMapAnnotation = '';
var result;
// main action
try {
var minifyFunc = options.declarationList ? csso.minifyBlock : csso.minify;
result = minifyFunc(source, {
filename: unixPathname(relInputFilename),
sourceMap: Boolean(sourceMap.output),
usage: options.usageData,
restructure: options.restructure,
forceMediaMerge: options.forceMediaMerge,
comments: options.comments,
debug: options.debug
});
// for backward capability minify returns a string
if (typeof result === 'string') {
result = {
css: result,
map: null
};
}
} catch (e) {
if (e.parseError) {
showParseError(source, options.inputFile, e.parseError, e.message);
if (!options.debug) {
process.exit(2);
}
}
throw e;
}
if (sourceMap.output && result.map) {
// apply input map
if (sourceMap.input) {
result.map.applySourceMap(
new SourceMapConsumer(sourceMap.input),
unixPathname(relInputFilename)
);
}
// add source map to result
if (sourceMap.outputFile) {
// write source map to file
fs.writeFileSync(sourceMap.outputFile, result.map.toString(), 'utf-8');
sourceMapAnnotation = '\n' +
'/*# sourceMappingURL=' +
unixPathname(path.relative(options.outputFile ? path.dirname(options.outputFile) : process.cwd(), sourceMap.outputFile)) +
' */';
} else {
// inline source map
sourceMapAnnotation = '\n' +
'/*# sourceMappingURL=data:application/json;base64,' +
Buffer.from(result.map.toString()).toString('base64') +
' */';
}
result.css += sourceMapAnnotation;
}
// output result
if (options.outputFile) {
fs.writeFileSync(options.outputFile, result.css, 'utf-8');
} else {
console.log(result.css);
}
// output statistics
if (options.statistics) {
var timeDiff = process.hrtime(time);
showStat(
relInputFilename,
source.length,
result.css.length,
sourceMap.inputFile,
sourceMapAnnotation.length,
parseInt(timeDiff[0] * 1e3 + timeDiff[1] / 1e6, 10),
process.memoryUsage().heapUsed - mem
);
}
});
}
var command = cli.create('csso', '[input]')
.version(require('csso/package.json').version)
.option('-i, --input <filename>', 'Input file')
.option('-o, --output <filename>', 'Output file (result outputs to stdout if not set)')
.option('-s, --source-map <destination>', 'Generate source map: none (default), inline, file or <filename>', 'none')
.option('-u, --usage <filename>', 'Usage data file')
.option('--input-source-map <source>', 'Input source map: none, auto (default) or <filename>', 'auto')
.option('-d, --declaration-list', 'Treat input as a declaration list')
.option('--no-restructure', 'Disable structural optimisations')
.option('--force-media-merge', 'Enable unsafe merge of @media rules')
.option('--comments <value>', 'Comments to keep: exclamation (default), first-exclamation or none', 'exclamation')
.option('--stat', 'Output statistics in stderr')
.option('--debug [level]', 'Output intermediate state of CSS during a compression', debugLevel, 0)
.option('--watch', 'Watch source file for changes')
.action(function(args) {
var options = processOptions(this.values, args);
if (options === null) {
this.showHelp();
return;
}
minifyStream(options);
// enable watch mode only when input is a file
if (options.watch && options.inputFile !== '<stdin>') {
// NOTE: require chokidar here to keep down start up time when --watch doesn't use
// (yep, chokidar adds a penalty ~0.2-0.3s on its init)
require('chokidar').watch(options.inputFile).on('change', function() {
minifyStream(options);
});
}
});
module.exports = {
run: command.run.bind(command),
isCliError: function(err) {
return err instanceof cli.Error;
}
};