clean.js 5.2 KB

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