clean.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /**
  2. * Clean-css - https://github.com/jakubpawlowicz/clean-css
  3. * Released under the terms of MIT license
  4. *
  5. * Copyright (C) 2017 JakubPawlowicz.com
  6. */
  7. var level0Optimize = require('./optimizer/level-0/optimize');
  8. var level1Optimize = require('./optimizer/level-1/optimize');
  9. var level2Optimize = require('./optimizer/level-2/optimize');
  10. var validator = require('./optimizer/validator');
  11. var compatibilityFrom = require('./options/compatibility');
  12. var formatFrom = require('./options/format').formatFrom;
  13. var inlineFrom = require('./options/inline');
  14. var inlineRequestFrom = require('./options/inline-request');
  15. var inlineTimeoutFrom = require('./options/inline-timeout');
  16. var OptimizationLevel = require('./options/optimization-level').OptimizationLevel;
  17. var optimizationLevelFrom = require('./options/optimization-level').optimizationLevelFrom;
  18. var rebaseFrom = require('./options/rebase');
  19. var rebaseToFrom = require('./options/rebase-to');
  20. var inputSourceMapTracker = require('./reader/input-source-map-tracker');
  21. var readSources = require('./reader/read-sources');
  22. var serializeStyles = require('./writer/simple');
  23. var serializeStylesAndSourceMap = require('./writer/source-maps');
  24. var CleanCSS = module.exports = function CleanCSS(options) {
  25. options = options || {};
  26. this.options = {
  27. compatibility: compatibilityFrom(options.compatibility),
  28. format: formatFrom(options.format),
  29. inline: inlineFrom(options.inline),
  30. inlineRequest: inlineRequestFrom(options.inlineRequest),
  31. inlineTimeout: inlineTimeoutFrom(options.inlineTimeout),
  32. level: optimizationLevelFrom(options.level),
  33. rebase: rebaseFrom(options.rebase),
  34. rebaseTo: rebaseToFrom(options.rebaseTo),
  35. returnPromise: !!options.returnPromise,
  36. sourceMap: !!options.sourceMap,
  37. sourceMapInlineSources: !!options.sourceMapInlineSources
  38. };
  39. };
  40. CleanCSS.prototype.minify = function (input, maybeSourceMap, maybeCallback) {
  41. var options = this.options;
  42. if (options.returnPromise) {
  43. return new Promise(function (resolve, reject) {
  44. minify(input, options, maybeSourceMap, function (errors, output) {
  45. return errors ?
  46. reject(errors) :
  47. resolve(output);
  48. });
  49. });
  50. } else {
  51. return minify(input, options, maybeSourceMap, maybeCallback);
  52. }
  53. };
  54. function minify(input, options, maybeSourceMap, maybeCallback) {
  55. var sourceMap = typeof maybeSourceMap != 'function' ?
  56. maybeSourceMap :
  57. null;
  58. var callback = typeof maybeCallback == 'function' ?
  59. maybeCallback :
  60. (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
  61. var context = {
  62. stats: {
  63. efficiency: 0,
  64. minifiedSize: 0,
  65. originalSize: 0,
  66. startedAt: Date.now(),
  67. timeSpent: 0
  68. },
  69. cache: {
  70. specificity: {}
  71. },
  72. errors: [],
  73. inlinedStylesheets: [],
  74. inputSourceMapTracker: inputSourceMapTracker(),
  75. localOnly: !callback,
  76. options: options,
  77. source: null,
  78. sourcesContent: {},
  79. validator: validator(options.compatibility),
  80. warnings: []
  81. };
  82. if (sourceMap) {
  83. context.inputSourceMapTracker.track(undefined, sourceMap);
  84. }
  85. return runner(context.localOnly)(function () {
  86. return readSources(input, context, function (tokens) {
  87. var serialize = context.options.sourceMap ?
  88. serializeStylesAndSourceMap :
  89. serializeStyles;
  90. var optimizedTokens = optimize(tokens, context);
  91. var optimizedStyles = serialize(optimizedTokens, context);
  92. var output = withMetadata(optimizedStyles, context);
  93. return callback ?
  94. callback(context.errors.length > 0 ? context.errors : null, output) :
  95. output;
  96. });
  97. });
  98. }
  99. function runner(localOnly) {
  100. // to always execute code asynchronously when a callback is given
  101. // more at blog.izs.me/post/59142742143/designing-apis-for-asynchrony
  102. return localOnly ?
  103. function (callback) { return callback(); } :
  104. process.nextTick;
  105. }
  106. function optimize(tokens, context) {
  107. var optimized;
  108. optimized = level0Optimize(tokens, context);
  109. optimized = OptimizationLevel.One in context.options.level ?
  110. level1Optimize(tokens, context) :
  111. tokens;
  112. optimized = OptimizationLevel.Two in context.options.level ?
  113. level2Optimize(tokens, context, true) :
  114. optimized;
  115. return optimized;
  116. }
  117. function withMetadata(output, context) {
  118. output.stats = calculateStatsFrom(output.styles, context);
  119. output.errors = context.errors;
  120. output.inlinedStylesheets = context.inlinedStylesheets;
  121. output.warnings = context.warnings;
  122. return output;
  123. }
  124. function calculateStatsFrom(styles, context) {
  125. var finishedAt = Date.now();
  126. var timeSpent = finishedAt - context.stats.startedAt;
  127. delete context.stats.startedAt;
  128. context.stats.timeSpent = timeSpent;
  129. context.stats.efficiency = 1 - styles.length / context.stats.originalSize;
  130. context.stats.minifiedSize = styles.length;
  131. return context.stats;
  132. }