lepu-test-platform-web/node_modules/csso/lib/cli.js

339 lines
12 KiB
JavaScript
Raw Permalink 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 SourceMapConsumer = require('source-map').SourceMapConsumer;
var csso = require('./index.js');
function readFromStream(stream, minify) {
var buffer = [];
// FIXME: don't chain until node.js 0.10 drop, since setEncoding isn't chainable in 0.10
stream.setEncoding('utf8');
stream
.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, map, inputFile, outputFile) {
var inputMapContent = null;
var inputMapFile = null;
var outputMapFile = null;
switch (map) {
case 'none':
// don't generate source map
map = false;
inputMap = 'none';
break;
case 'inline':
// nothing to do
break;
case 'file':
if (!outputFile) {
console.error('Output filename should be specified when `--map file` is used');
process.exit(2);
}
outputMapFile = outputFile + '.map';
break;
default:
// process filename
if (map) {
// check path is reachable
if (!fs.existsSync(path.dirname(map))) {
console.error('Directory for map file should exists:', path.dirname(path.resolve(map)));
process.exit(2);
}
// resolve to absolute path
outputMapFile = path.resolve(process.cwd(), map);
}
}
switch (inputMap) {
case 'none':
// nothing to do
break;
case 'auto':
if (map) {
// 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 = new Buffer(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: map,
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);
}
var command = cli.create('csso', '[input] [output]')
.version(require('../package.json').version)
.option('-i, --input <filename>', 'Input file')
.option('-o, --output <filename>', 'Output file (result outputs to stdout if not set)')
.option('-m, --map <destination>', 'Generate source map: none (default), inline, file or <filename>', 'none')
.option('-u, --usage <filenane>', 'Usage data file')
.option('--input-map <source>', 'Input source map: none, auto (default) or <filename>', 'auto')
.option('--restructure-off', 'Turns structure minimization off')
.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 compression', debugLevel, 0)
.action(function(args) {
var options = this.values;
var inputFile = options.input || args[0];
var outputFile = options.output || args[1];
var usageFile = options.usage;
var usageData = false;
var map = options.map;
var inputMap = options.inputMap;
var structureOptimisationOff = options.restructureOff;
var comments = processCommentsOption(options.comments);
var debug = options.debug;
var statistics = options.stat;
var inputStream;
if (process.stdin.isTTY && !inputFile && !outputFile) {
this.showHelp();
return;
}
if (!inputFile) {
inputFile = '<stdin>';
inputStream = process.stdin;
} else {
inputFile = path.resolve(process.cwd(), inputFile);
inputStream = fs.createReadStream(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);
}
}
readFromStream(inputStream, function(source) {
var time = process.hrtime();
var mem = process.memoryUsage().heapUsed;
var sourceMap = resolveSourceMap(source, inputMap, map, inputFile, outputFile);
var sourceMapAnnotation = '';
var result;
// main action
try {
result = csso.minify(source, {
filename: inputFile,
sourceMap: sourceMap.output,
usage: usageData,
restructure: !structureOptimisationOff,
comments: comments,
debug: debug
});
// for backward capability minify returns a string
if (typeof result === 'string') {
result = {
css: result,
map: null
};
}
} catch (e) {
if (e.parseError) {
showParseError(source, inputFile, e.parseError, e.message);
if (!debug) {
process.exit(2);
}
}
throw e;
}
if (sourceMap.output && result.map) {
// apply input map
if (sourceMap.input) {
result.map.applySourceMap(
new SourceMapConsumer(sourceMap.input),
inputFile
);
}
// 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=' +
path.relative(outputFile ? path.dirname(outputFile) : process.cwd(), sourceMap.outputFile) +
' */';
} else {
// inline source map
sourceMapAnnotation = '\n' +
'/*# sourceMappingURL=data:application/json;base64,' +
new Buffer(result.map.toString()).toString('base64') +
' */';
}
result.css += sourceMapAnnotation;
}
// output result
if (outputFile) {
fs.writeFileSync(outputFile, result.css, 'utf-8');
} else {
console.log(result.css);
}
// output statistics
if (statistics) {
var timeDiff = process.hrtime(time);
showStat(
path.relative(process.cwd(), inputFile),
source.length,
result.css.length,
sourceMap.inputFile,
sourceMapAnnotation.length,
parseInt(timeDiff[0] * 1e3 + timeDiff[1] / 1e6),
process.memoryUsage().heapUsed - mem
);
}
});
});
module.exports = {
run: command.run.bind(command),
isCliError: function(err) {
return err instanceof cli.Error;
}
};