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