[79a0317] | 1 | /**
|
---|
| 2 | * Clean-css - https://github.com/clean-css/clean-css
|
---|
| 3 | * Released under the terms of MIT license
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | var level0Optimize = require('./optimizer/level-0/optimize');
|
---|
| 7 | var level1Optimize = require('./optimizer/level-1/optimize');
|
---|
| 8 | var level2Optimize = require('./optimizer/level-2/optimize');
|
---|
| 9 | var validator = require('./optimizer/validator');
|
---|
| 10 |
|
---|
| 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 pluginsFrom = require('./options/plugins');
|
---|
| 20 | var rebaseFrom = require('./options/rebase');
|
---|
| 21 | var rebaseToFrom = require('./options/rebase-to');
|
---|
| 22 |
|
---|
| 23 | var inputSourceMapTracker = require('./reader/input-source-map-tracker');
|
---|
| 24 | var readSources = require('./reader/read-sources');
|
---|
| 25 |
|
---|
| 26 | var serializeStyles = require('./writer/simple');
|
---|
| 27 | var serializeStylesAndSourceMap = require('./writer/source-maps');
|
---|
| 28 |
|
---|
| 29 | var CleanCSS = module.exports = function CleanCSS(options) {
|
---|
| 30 | options = options || {};
|
---|
| 31 |
|
---|
| 32 | this.options = {
|
---|
| 33 | batch: !!options.batch,
|
---|
| 34 | compatibility: compatibilityFrom(options.compatibility),
|
---|
| 35 | explicitRebaseTo: 'rebaseTo' in options,
|
---|
| 36 | fetch: fetchFrom(options.fetch),
|
---|
| 37 | format: formatFrom(options.format),
|
---|
| 38 | inline: inlineFrom(options.inline),
|
---|
| 39 | inlineRequest: inlineRequestFrom(options.inlineRequest),
|
---|
| 40 | inlineTimeout: inlineTimeoutFrom(options.inlineTimeout),
|
---|
| 41 | level: optimizationLevelFrom(options.level),
|
---|
| 42 | plugins: pluginsFrom(options.plugins),
|
---|
| 43 | rebase: rebaseFrom(options.rebase, options.rebaseTo),
|
---|
| 44 | rebaseTo: rebaseToFrom(options.rebaseTo),
|
---|
| 45 | returnPromise: !!options.returnPromise,
|
---|
| 46 | sourceMap: !!options.sourceMap,
|
---|
| 47 | sourceMapInlineSources: !!options.sourceMapInlineSources
|
---|
| 48 | };
|
---|
| 49 | };
|
---|
| 50 |
|
---|
| 51 | // for compatibility with optimize-css-assets-webpack-plugin
|
---|
| 52 | CleanCSS.process = function(input, opts) {
|
---|
| 53 | var cleanCss;
|
---|
| 54 | var optsTo = opts.to;
|
---|
| 55 |
|
---|
| 56 | delete opts.to;
|
---|
| 57 | cleanCss = new CleanCSS(Object.assign({
|
---|
| 58 | returnPromise: true, rebaseTo: optsTo
|
---|
| 59 | }, opts));
|
---|
| 60 |
|
---|
| 61 | return cleanCss.minify(input)
|
---|
| 62 | .then(function(output) {
|
---|
| 63 | return { css: output.styles };
|
---|
| 64 | });
|
---|
| 65 | };
|
---|
| 66 |
|
---|
| 67 | CleanCSS.prototype.minify = function(input, maybeSourceMap, maybeCallback) {
|
---|
| 68 | var options = this.options;
|
---|
| 69 |
|
---|
| 70 | if (options.returnPromise) {
|
---|
| 71 | return new Promise(function(resolve, reject) {
|
---|
| 72 | minifyAll(input, options, maybeSourceMap, function(errors, output) {
|
---|
| 73 | return errors
|
---|
| 74 | ? reject(errors)
|
---|
| 75 | : resolve(output);
|
---|
| 76 | });
|
---|
| 77 | });
|
---|
| 78 | }
|
---|
| 79 | return minifyAll(input, options, maybeSourceMap, maybeCallback);
|
---|
| 80 | };
|
---|
| 81 |
|
---|
| 82 | function minifyAll(input, options, maybeSourceMap, maybeCallback) {
|
---|
| 83 | if (options.batch && Array.isArray(input)) {
|
---|
| 84 | return minifyInBatchesFromArray(input, options, maybeSourceMap, maybeCallback);
|
---|
| 85 | } if (options.batch && (typeof input == 'object')) {
|
---|
| 86 | return minifyInBatchesFromHash(input, options, maybeSourceMap, maybeCallback);
|
---|
| 87 | }
|
---|
| 88 | return minify(input, options, maybeSourceMap, maybeCallback);
|
---|
| 89 | }
|
---|
| 90 |
|
---|
| 91 | function minifyInBatchesFromArray(input, options, maybeSourceMap, maybeCallback) {
|
---|
| 92 | var callback = typeof maybeCallback == 'function'
|
---|
| 93 | ? maybeCallback
|
---|
| 94 | : (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
|
---|
| 95 | var errors = [];
|
---|
| 96 | var outputAsHash = {};
|
---|
| 97 | var inputValue;
|
---|
| 98 | var i, l;
|
---|
| 99 |
|
---|
| 100 | function whenHashBatchDone(innerErrors, output) {
|
---|
| 101 | outputAsHash = Object.assign(outputAsHash, output);
|
---|
| 102 |
|
---|
| 103 | if (innerErrors !== null) {
|
---|
| 104 | errors = errors.concat(innerErrors);
|
---|
| 105 | }
|
---|
| 106 | }
|
---|
| 107 |
|
---|
| 108 | for (i = 0, l = input.length; i < l; i++) {
|
---|
| 109 | if (typeof input[i] == 'object') {
|
---|
| 110 | minifyInBatchesFromHash(input[i], options, whenHashBatchDone);
|
---|
| 111 | } else {
|
---|
| 112 | inputValue = input[i];
|
---|
| 113 |
|
---|
| 114 | outputAsHash[inputValue] = minify([inputValue], options);
|
---|
| 115 | errors = errors.concat(outputAsHash[inputValue].errors);
|
---|
| 116 | }
|
---|
| 117 | }
|
---|
| 118 |
|
---|
| 119 | return callback
|
---|
| 120 | ? callback(errors.length > 0 ? errors : null, outputAsHash)
|
---|
| 121 | : outputAsHash;
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | function minifyInBatchesFromHash(input, options, maybeSourceMap, maybeCallback) {
|
---|
| 125 | var callback = typeof maybeCallback == 'function'
|
---|
| 126 | ? maybeCallback
|
---|
| 127 | : (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
|
---|
| 128 | var errors = [];
|
---|
| 129 | var outputAsHash = {};
|
---|
| 130 | var inputKey;
|
---|
| 131 | var inputValue;
|
---|
| 132 |
|
---|
| 133 | for (inputKey in input) {
|
---|
| 134 | inputValue = input[inputKey];
|
---|
| 135 |
|
---|
| 136 | outputAsHash[inputKey] = minify(inputValue.styles, options, inputValue.sourceMap);
|
---|
| 137 | errors = errors.concat(outputAsHash[inputKey].errors);
|
---|
| 138 | }
|
---|
| 139 |
|
---|
| 140 | return callback
|
---|
| 141 | ? callback(errors.length > 0 ? errors : null, outputAsHash)
|
---|
| 142 | : outputAsHash;
|
---|
| 143 | }
|
---|
| 144 |
|
---|
| 145 | function minify(input, options, maybeSourceMap, maybeCallback) {
|
---|
| 146 | var sourceMap = typeof maybeSourceMap != 'function'
|
---|
| 147 | ? maybeSourceMap
|
---|
| 148 | : null;
|
---|
| 149 | var callback = typeof maybeCallback == 'function'
|
---|
| 150 | ? maybeCallback
|
---|
| 151 | : (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
|
---|
| 152 | var context = {
|
---|
| 153 | stats: {
|
---|
| 154 | efficiency: 0,
|
---|
| 155 | minifiedSize: 0,
|
---|
| 156 | originalSize: 0,
|
---|
| 157 | startedAt: Date.now(),
|
---|
| 158 | timeSpent: 0
|
---|
| 159 | },
|
---|
| 160 | cache: { specificity: {} },
|
---|
| 161 | errors: [],
|
---|
| 162 | inlinedStylesheets: [],
|
---|
| 163 | inputSourceMapTracker: inputSourceMapTracker(),
|
---|
| 164 | localOnly: !callback,
|
---|
| 165 | options: options,
|
---|
| 166 | source: null,
|
---|
| 167 | sourcesContent: {},
|
---|
| 168 | validator: validator(options.compatibility),
|
---|
| 169 | warnings: []
|
---|
| 170 | };
|
---|
| 171 | var implicitRebaseToWarning;
|
---|
| 172 |
|
---|
| 173 | if (sourceMap) {
|
---|
| 174 | context.inputSourceMapTracker.track(undefined, sourceMap);
|
---|
| 175 | }
|
---|
| 176 |
|
---|
| 177 | if (options.rebase && !options.explicitRebaseTo) {
|
---|
| 178 | implicitRebaseToWarning = 'You have set `rebase: true` without giving `rebaseTo` option, which, in this case, defaults to the current working directory. '
|
---|
| 179 | + 'You are then warned this can lead to unexpected URL rebasing (aka here be dragons)! '
|
---|
| 180 | + 'If you are OK with the clean-css output, then you can get rid of this warning by giving clean-css a `rebaseTo: process.cwd()` option.';
|
---|
| 181 | context.warnings.push(implicitRebaseToWarning);
|
---|
| 182 | }
|
---|
| 183 |
|
---|
| 184 | return runner(context.localOnly)(function() {
|
---|
| 185 | return readSources(input, context, function(tokens) {
|
---|
| 186 | var serialize = context.options.sourceMap
|
---|
| 187 | ? serializeStylesAndSourceMap
|
---|
| 188 | : serializeStyles;
|
---|
| 189 |
|
---|
| 190 | var optimizedTokens = optimize(tokens, context);
|
---|
| 191 | var optimizedStyles = serialize(optimizedTokens, context);
|
---|
| 192 | var output = withMetadata(optimizedStyles, context);
|
---|
| 193 |
|
---|
| 194 | return callback
|
---|
| 195 | ? callback(context.errors.length > 0 ? context.errors : null, output)
|
---|
| 196 | : output;
|
---|
| 197 | });
|
---|
| 198 | });
|
---|
| 199 | }
|
---|
| 200 |
|
---|
| 201 | function runner(localOnly) {
|
---|
| 202 | // to always execute code asynchronously when a callback is given
|
---|
| 203 | // more at blog.izs.me/post/59142742143/designing-apis-for-asynchrony
|
---|
| 204 | return localOnly
|
---|
| 205 | ? function(callback) { return callback(); }
|
---|
| 206 | : process.nextTick;
|
---|
| 207 | }
|
---|
| 208 |
|
---|
| 209 | function optimize(tokens, context) {
|
---|
| 210 | var optimized = level0Optimize(tokens, context);
|
---|
| 211 |
|
---|
| 212 | optimized = OptimizationLevel.One in context.options.level
|
---|
| 213 | ? level1Optimize(tokens, context)
|
---|
| 214 | : tokens;
|
---|
| 215 | optimized = OptimizationLevel.Two in context.options.level
|
---|
| 216 | ? level2Optimize(tokens, context, true)
|
---|
| 217 | : optimized;
|
---|
| 218 |
|
---|
| 219 | return optimized;
|
---|
| 220 | }
|
---|
| 221 |
|
---|
| 222 | function withMetadata(output, context) {
|
---|
| 223 | output.stats = calculateStatsFrom(output.styles, context);
|
---|
| 224 | output.errors = context.errors;
|
---|
| 225 | output.inlinedStylesheets = context.inlinedStylesheets;
|
---|
| 226 | output.warnings = context.warnings;
|
---|
| 227 |
|
---|
| 228 | return output;
|
---|
| 229 | }
|
---|
| 230 |
|
---|
| 231 | function calculateStatsFrom(styles, context) {
|
---|
| 232 | var finishedAt = Date.now();
|
---|
| 233 | var timeSpent = finishedAt - context.stats.startedAt;
|
---|
| 234 |
|
---|
| 235 | delete context.stats.startedAt;
|
---|
| 236 | context.stats.timeSpent = timeSpent;
|
---|
| 237 | context.stats.efficiency = 1 - styles.length / context.stats.originalSize;
|
---|
| 238 | context.stats.minifiedSize = styles.length;
|
---|
| 239 |
|
---|
| 240 | return context.stats;
|
---|
| 241 | }
|
---|