You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			185 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Plaintext
		
	
		
		
			
		
	
	
			185 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Plaintext
		
	
| 
								 
											6 years ago
										 
									 | 
							
								#!/usr/bin/env node
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var fs = require('fs');
							 | 
						||
| 
								 | 
							
								var path = require('path');
							 | 
						||
| 
								 | 
							
								var CleanCSS = require('../index');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var commands = require('commander');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var packageConfig = fs.readFileSync(path.join(path.dirname(fs.realpathSync(process.argv[1])), '../package.json'));
							 | 
						||
| 
								 | 
							
								var buildVersion = JSON.parse(packageConfig).version;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var isWindows = process.platform == 'win32';
							 | 
						||
| 
								 | 
							
								var lineBreak = require('os').EOL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Specify commander options to parse command line params correctly
							 | 
						||
| 
								 | 
							
								commands
							 | 
						||
| 
								 | 
							
								  .version(buildVersion, '-v, --version')
							 | 
						||
| 
								 | 
							
								  .usage('[options] source-file, [source-file, ...]')
							 | 
						||
| 
								 | 
							
								  .option('-b, --keep-line-breaks', 'Keep line breaks')
							 | 
						||
| 
								 | 
							
								  .option('-c, --compatibility [ie7|ie8]', 'Force compatibility mode (see Readme for advanced examples)')
							 | 
						||
| 
								 | 
							
								  .option('-d, --debug', 'Shows debug information (minification time & compression efficiency)')
							 | 
						||
| 
								 | 
							
								  .option('-o, --output [output-file]', 'Use [output-file] as output instead of STDOUT')
							 | 
						||
| 
								 | 
							
								  .option('-r, --root [root-path]', 'Set a root path to which resolve absolute @import rules')
							 | 
						||
| 
								 | 
							
								  .option('-s, --skip-import', 'Disable @import processing')
							 | 
						||
| 
								 | 
							
								  .option('-t, --timeout [seconds]', 'Per connection timeout when fetching remote @imports (defaults to 5 seconds)')
							 | 
						||
| 
								 | 
							
								  .option('--rounding-precision [n]', 'Rounds to `N` decimal places. Defaults to 2. -1 disables rounding', parseInt)
							 | 
						||
| 
								 | 
							
								  .option('--s0', 'Remove all special comments, i.e. /*! comment */')
							 | 
						||
| 
								 | 
							
								  .option('--s1', 'Remove all special comments but the first one')
							 | 
						||
| 
								 | 
							
								  .option('--semantic-merging', 'Enables unsafe mode by assuming BEM-like semantic stylesheets (warning, this may break your styling!)')
							 | 
						||
| 
								 | 
							
								  .option('--skip-advanced', 'Disable advanced optimizations - ruleset reordering & merging')
							 | 
						||
| 
								 | 
							
								  .option('--skip-aggressive-merging', 'Disable properties merging based on their order')
							 | 
						||
| 
								 | 
							
								  .option('--skip-import-from [rules]', 'Disable @import processing for specified rules', function (val) { return val.split(','); }, [])
							 | 
						||
| 
								 | 
							
								  .option('--skip-media-merging', 'Disable @media merging')
							 | 
						||
| 
								 | 
							
								  .option('--skip-rebase', 'Disable URLs rebasing')
							 | 
						||
| 
								 | 
							
								  .option('--skip-restructuring', 'Disable restructuring optimizations')
							 | 
						||
| 
								 | 
							
								  .option('--skip-shorthand-compacting', 'Disable shorthand compacting')
							 | 
						||
| 
								 | 
							
								  .option('--source-map', 'Enables building input\'s source map')
							 | 
						||
| 
								 | 
							
								  .option('--source-map-inline-sources', 'Enables inlining sources inside source maps');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								commands.on('--help', function () {
							 | 
						||
| 
								 | 
							
								  console.log('  Examples:\n');
							 | 
						||
| 
								 | 
							
								  console.log('    %> cleancss one.css');
							 | 
						||
| 
								 | 
							
								  console.log('    %> cleancss -o one-min.css one.css');
							 | 
						||
| 
								 | 
							
								  if (isWindows) {
							 | 
						||
| 
								 | 
							
								    console.log('    %> type one.css two.css three.css | cleancss -o merged-and-minified.css');
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    console.log('    %> cat one.css two.css three.css | cleancss -o merged-and-minified.css');
							 | 
						||
| 
								 | 
							
								    console.log('    %> cat one.css two.css three.css | cleancss | gzip -9 -c > merged-minified-and-gzipped.css.gz');
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  console.log('');
							 | 
						||
| 
								 | 
							
								  process.exit();
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								commands.parse(process.argv);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// If no sensible data passed in just print help and exit
							 | 
						||
| 
								 | 
							
								var fromStdin = !process.env.__DIRECT__ && !process.stdin.isTTY;
							 | 
						||
| 
								 | 
							
								if (!fromStdin && commands.args.length === 0) {
							 | 
						||
| 
								 | 
							
								  commands.outputHelp();
							 | 
						||
| 
								 | 
							
								  return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Now coerce commands into CleanCSS configuration...
							 | 
						||
| 
								 | 
							
								var options = {
							 | 
						||
| 
								 | 
							
								  advanced: commands.skipAdvanced ? false : true,
							 | 
						||
| 
								 | 
							
								  aggressiveMerging: commands.skipAggressiveMerging ? false : true,
							 | 
						||
| 
								 | 
							
								  compatibility: commands.compatibility,
							 | 
						||
| 
								 | 
							
								  debug: commands.debug,
							 | 
						||
| 
								 | 
							
								  inliner: commands.timeout ? { timeout: parseFloat(commands.timeout) * 1000 } : undefined,
							 | 
						||
| 
								 | 
							
								  keepBreaks: !!commands.keepLineBreaks,
							 | 
						||
| 
								 | 
							
								  keepSpecialComments: commands.s0 ? 0 : (commands.s1 ? 1 : '*'),
							 | 
						||
| 
								 | 
							
								  mediaMerging: commands.skipMediaMerging ? false : true,
							 | 
						||
| 
								 | 
							
								  processImport: commands.skipImport ? false : true,
							 | 
						||
| 
								 | 
							
								  processImportFrom: processImportFrom(commands.skipImportFrom),
							 | 
						||
| 
								 | 
							
								  rebase: commands.skipRebase ? false : true,
							 | 
						||
| 
								 | 
							
								  restructuring: commands.skipRestructuring ? false : true,
							 | 
						||
| 
								 | 
							
								  root: commands.root,
							 | 
						||
| 
								 | 
							
								  roundingPrecision: commands.roundingPrecision,
							 | 
						||
| 
								 | 
							
								  semanticMerging: commands.semanticMerging ? true : false,
							 | 
						||
| 
								 | 
							
								  shorthandCompacting: commands.skipShorthandCompacting ? false : true,
							 | 
						||
| 
								 | 
							
								  sourceMap: commands.sourceMap,
							 | 
						||
| 
								 | 
							
								  sourceMapInlineSources: commands.sourceMapInlineSources,
							 | 
						||
| 
								 | 
							
								  target: commands.output
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if (options.root || commands.args.length > 0) {
							 | 
						||
| 
								 | 
							
								  var relativeTo = options.root || commands.args[0];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (isRemote(relativeTo)) {
							 | 
						||
| 
								 | 
							
								    options.relativeTo = relativeTo;
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    var resolvedRelativeTo = path.resolve(relativeTo);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    options.relativeTo = fs.statSync(resolvedRelativeTo).isFile() ?
							 | 
						||
| 
								 | 
							
								      path.dirname(resolvedRelativeTo) :
							 | 
						||
| 
								 | 
							
								      resolvedRelativeTo;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if (options.sourceMap && !options.target) {
							 | 
						||
| 
								 | 
							
								  outputFeedback(['Source maps will not be built because you have not specified an output file.'], true);
							 | 
						||
| 
								 | 
							
								  options.sourceMap = false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ... and do the magic!
							 | 
						||
| 
								 | 
							
								if (commands.args.length > 0) {
							 | 
						||
| 
								 | 
							
								  minify(commands.args);
							 | 
						||
| 
								 | 
							
								} else {
							 | 
						||
| 
								 | 
							
								  var stdin = process.openStdin();
							 | 
						||
| 
								 | 
							
								  stdin.setEncoding('utf-8');
							 | 
						||
| 
								 | 
							
								  var data = '';
							 | 
						||
| 
								 | 
							
								  stdin.on('data', function (chunk) {
							 | 
						||
| 
								 | 
							
								    data += chunk;
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								  stdin.on('end', function () {
							 | 
						||
| 
								 | 
							
								    minify(data);
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function isRemote(path) {
							 | 
						||
| 
								 | 
							
								  return /^https?:\/\//.test(path) || /^\/\//.test(path);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function processImportFrom(rules) {
							 | 
						||
| 
								 | 
							
								  if (rules.length === 0) {
							 | 
						||
| 
								 | 
							
								    return ['all'];
							 | 
						||
| 
								 | 
							
								  } else if (rules.length == 1 && rules[0] == 'all') {
							 | 
						||
| 
								 | 
							
								    return [];
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    return rules.map(function (rule) {
							 | 
						||
| 
								 | 
							
								      if (rule == 'local')
							 | 
						||
| 
								 | 
							
								        return 'remote';
							 | 
						||
| 
								 | 
							
								      else if (rule == 'remote')
							 | 
						||
| 
								 | 
							
								        return 'local';
							 | 
						||
| 
								 | 
							
								      else
							 | 
						||
| 
								 | 
							
								        return '!' + rule;
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function minify(data) {
							 | 
						||
| 
								 | 
							
								  new CleanCSS(options).minify(data, function (errors, minified) {
							 | 
						||
| 
								 | 
							
								    if (options.debug) {
							 | 
						||
| 
								 | 
							
								      console.error('Original: %d bytes', minified.stats.originalSize);
							 | 
						||
| 
								 | 
							
								      console.error('Minified: %d bytes', minified.stats.minifiedSize);
							 | 
						||
| 
								 | 
							
								      console.error('Efficiency: %d%', ~~(minified.stats.efficiency * 10000) / 100.0);
							 | 
						||
| 
								 | 
							
								      console.error('Time spent: %dms', minified.stats.timeSpent);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    outputFeedback(minified.errors, true);
							 | 
						||
| 
								 | 
							
								    outputFeedback(minified.warnings);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (minified.errors.length > 0)
							 | 
						||
| 
								 | 
							
								      process.exit(1);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (minified.sourceMap) {
							 | 
						||
| 
								 | 
							
								      var mapFilename = path.basename(options.target) + '.map';
							 | 
						||
| 
								 | 
							
								      output(minified.styles + lineBreak + '/*# sourceMappingURL=' + mapFilename + ' */');
							 | 
						||
| 
								 | 
							
								      outputMap(minified.sourceMap, mapFilename);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      output(minified.styles);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function output(minified) {
							 | 
						||
| 
								 | 
							
								  if (options.target)
							 | 
						||
| 
								 | 
							
								    fs.writeFileSync(options.target, minified, 'utf8');
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    process.stdout.write(minified);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function outputMap(sourceMap, mapFilename) {
							 | 
						||
| 
								 | 
							
								  var mapPath = path.join(path.dirname(options.target), mapFilename);
							 | 
						||
| 
								 | 
							
								  fs.writeFileSync(mapPath, sourceMap.toString(), 'utf-8');
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function outputFeedback(messages, isError) {
							 | 
						||
| 
								 | 
							
								  var prefix = isError ? '\x1B[31mERROR\x1B[39m:' : 'WARNING:';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  messages.forEach(function (message) {
							 | 
						||
| 
								 | 
							
								    console.error('%s %s', prefix, message);
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								}
							 |