cleancss 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. #!/usr/bin/env node
  2. var util = require('util');
  3. var fs = require('fs');
  4. var path = require('path');
  5. var CleanCSS = require('../index');
  6. var commands = require('commander');
  7. var packageConfig = fs.readFileSync(path.join(path.dirname(fs.realpathSync(process.argv[1])), '../package.json'));
  8. var buildVersion = JSON.parse(packageConfig).version;
  9. var isWindows = process.platform == 'win32';
  10. // Specify commander options to parse command line params correctly
  11. commands
  12. .version(buildVersion, '-v, --version')
  13. .usage('[options] source-file, [source-file, ...]')
  14. .option('-b, --keep-line-breaks', 'Keep line breaks')
  15. .option('--s0', 'Remove all special comments, i.e. /*! comment */')
  16. .option('--s1', 'Remove all special comments but the first one')
  17. .option('-r, --root [root-path]', 'Set a root path to which resolve absolute @import rules')
  18. .option('-o, --output [output-file]', 'Use [output-file] as output instead of STDOUT')
  19. .option('-s, --skip-import', 'Disable @import processing')
  20. .option('--skip-rebase', 'Disable URLs rebasing')
  21. .option('--skip-advanced', 'Disable advanced optimizations - selector & property merging, reduction, etc.')
  22. .option('--skip-aggressive-merging', 'Disable properties merging based on their order')
  23. .option('--rounding-precision [value]', 'Rounding precision, defaults to 2', parseInt)
  24. .option('-c, --compatibility [ie7|ie8]', 'Force compatibility mode')
  25. .option('-t, --timeout [seconds]', 'Per connection timeout when fetching remote @imports (defaults to 5 seconds)')
  26. .option('-d, --debug', 'Shows debug information (minification time & compression efficiency)');
  27. commands.on('--help', function() {
  28. util.puts(' Examples:\n');
  29. util.puts(' %> cleancss one.css');
  30. util.puts(' %> cleancss -o one-min.css one.css');
  31. if (isWindows) {
  32. util.puts(' %> type one.css two.css three.css | cleancss -o merged-and-minified.css');
  33. } else {
  34. util.puts(' %> cat one.css two.css three.css | cleancss -o merged-and-minified.css');
  35. util.puts(' %> cat one.css two.css three.css | cleancss | gzip -9 -c > merged-minified-and-gzipped.css.gz');
  36. }
  37. util.puts('');
  38. process.exit();
  39. });
  40. commands.parse(process.argv);
  41. var options = {
  42. sources: null,
  43. target: null
  44. };
  45. var cleanOptions = {};
  46. var fromStdin = !process.env.__DIRECT__ && !process.stdin.isTTY;
  47. // If no sensible data passed in just print help and exit
  48. if (!fromStdin && commands.args.length === 0) {
  49. commands.outputHelp();
  50. return 0;
  51. }
  52. // Now coerce commands into CleanCSS configuration...
  53. if (commands.output)
  54. cleanOptions.target = options.target = commands.output;
  55. if (commands.keepLineBreaks)
  56. cleanOptions.keepBreaks = true;
  57. if (commands.s1)
  58. cleanOptions.keepSpecialComments = 1;
  59. if (commands.s0)
  60. cleanOptions.keepSpecialComments = 0;
  61. if (commands.root)
  62. cleanOptions.root = commands.root;
  63. if (commands.skipImport)
  64. cleanOptions.processImport = false;
  65. if (commands.skipRebase)
  66. cleanOptions.noRebase = true;
  67. if (commands.skipAdvanced)
  68. cleanOptions.noAdvanced = true;
  69. if (commands.skipAggressiveMerging)
  70. cleanOptions.noAggressiveMerging = true;
  71. if (commands.compatibility)
  72. cleanOptions.compatibility = commands.compatibility;
  73. if (commands.roundingPrecision !== undefined)
  74. cleanOptions.roundingPrecision = commands.roundingPrecision;
  75. if (commands.debug)
  76. cleanOptions.debug = true;
  77. if (commands.timeout)
  78. cleanOptions.inliner = { timeout: parseFloat(commands.timeout) * 1000 };
  79. if (commands.args.length > 0) {
  80. var relativeTo = (cleanOptions.noRebase ? false : cleanOptions.root) || commands.args[0];
  81. cleanOptions.relativeTo = path.dirname(path.resolve(relativeTo));
  82. options.sources = commands.args.map(function(source) {
  83. var isRemote = /^https?:\/\//.test(source);
  84. if (cleanOptions.processImport === false)
  85. source += '@shallow';
  86. return isRemote ?
  87. source :
  88. path.relative(cleanOptions.relativeTo, path.resolve(source));
  89. });
  90. }
  91. // ... and do the magic!
  92. if (options.sources) {
  93. var data = options.sources
  94. .map(function(source) {
  95. return '@import url(' + source + ');';
  96. })
  97. .join('');
  98. minify(data);
  99. } else {
  100. var stdin = process.openStdin();
  101. stdin.setEncoding('utf-8');
  102. var data = '';
  103. stdin.on('data', function(chunk) {
  104. data += chunk;
  105. });
  106. stdin.on('end', function() {
  107. minify(data);
  108. });
  109. }
  110. function minify(data) {
  111. new CleanCSS(cleanOptions).minify(data, function(errors, minified) {
  112. if (cleanOptions.debug) {
  113. console.error('Original: %d bytes', this.stats.originalSize);
  114. console.error('Minified: %d bytes', this.stats.minifiedSize);
  115. console.error('Efficiency: %d%', ~~(this.stats.efficiency * 10000) / 100.0);
  116. console.error('Time spent: %dms', this.stats.timeSpent);
  117. }
  118. outputFeedback(this.errors, true);
  119. outputFeedback(this.warnings);
  120. if (this.errors.length > 0)
  121. process.exit(1);
  122. output(minified);
  123. });
  124. }
  125. function output(minified) {
  126. if (options.target)
  127. fs.writeFileSync(options.target, minified, 'utf8');
  128. else
  129. process.stdout.write(minified);
  130. }
  131. function outputFeedback(messages, isError) {
  132. var prefix = isError ? '\x1B[31mERROR\x1B[39m:' : 'WARNING:';
  133. messages.forEach(function(message) {
  134. console.error('%s %s', prefix, message);
  135. });
  136. }