clean.js 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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. CleanCSS.prototype.minify = function (input, maybeSourceMap, maybeCallback) {
  43. var options = this.options;
  44. if (options.returnPromise) {
  45. return new Promise(function (resolve, reject) {
  46. minify(input, options, maybeSourceMap, function (errors, output) {
  47. return errors ?
  48. reject(errors) :
  49. resolve(output);
  50. });
  51. });
  52. } else {
  53. return minify(input, options, maybeSourceMap, maybeCallback);
  54. }
  55. };
  56. function minify(input, options, maybeSourceMap, maybeCallback) {
  57. var sourceMap = typeof maybeSourceMap != 'function' ?
  58. maybeSourceMap :
  59. null;
  60. var callback = typeof maybeCallback == 'function' ?
  61. maybeCallback :
  62. (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
  63. var context = {
  64. stats: {
  65. efficiency: 0,
  66. minifiedSize: 0,
  67. originalSize: 0,
  68. startedAt: Date.now(),
  69. timeSpent: 0
  70. },
  71. cache: {
  72. specificity: {}
  73. },
  74. errors: [],
  75. inlinedStylesheets: [],
  76. inputSourceMapTracker: inputSourceMapTracker(),
  77. localOnly: !callback,
  78. options: options,
  79. source: null,
  80. sourcesContent: {},
  81. validator: validator(options.compatibility),
  82. warnings: []
  83. };
  84. if (sourceMap) {
  85. context.inputSourceMapTracker.track(undefined, sourceMap);
  86. }
  87. return runner(context.localOnly)(function () {
  88. return readSources(input, context, function (tokens) {
  89. var serialize = context.options.sourceMap ?
  90. serializeStylesAndSourceMap :
  91. serializeStyles;
  92. var optimizedTokens = optimize(tokens, context);
  93. var optimizedStyles = serialize(optimizedTokens, context);
  94. var output = withMetadata(optimizedStyles, context);
  95. return callback ?
  96. callback(context.errors.length > 0 ? context.errors : null, output) :
  97. output;
  98. });
  99. });
  100. }
  101. function runner(localOnly) {
  102. // to always execute code asynchronously when a callback is given
  103. // more at blog.izs.me/post/59142742143/designing-apis-for-asynchrony
  104. return localOnly ?
  105. function (callback) { return callback(); } :
  106. process.nextTick;
  107. }
  108. function optimize(tokens, context) {
  109. var optimized;
  110. optimized = level0Optimize(tokens, context);
  111. optimized = OptimizationLevel.One in context.options.level ?
  112. level1Optimize(tokens, context) :
  113. tokens;
  114. optimized = OptimizationLevel.Two in context.options.level ?
  115. level2Optimize(tokens, context, true) :
  116. optimized;
  117. return optimized;
  118. }
  119. function withMetadata(output, context) {
  120. output.stats = calculateStatsFrom(output.styles, context);
  121. output.errors = context.errors;
  122. output.inlinedStylesheets = context.inlinedStylesheets;
  123. output.warnings = context.warnings;
  124. return output;
  125. }
  126. function calculateStatsFrom(styles, context) {
  127. var finishedAt = Date.now();
  128. var timeSpent = finishedAt - context.stats.startedAt;
  129. delete context.stats.startedAt;
  130. context.stats.timeSpent = timeSpent;
  131. context.stats.efficiency = 1 - styles.length / context.stats.originalSize;
  132. context.stats.minifiedSize = styles.length;
  133. return context.stats;
  134. }