You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

242 lines
7.8 KiB

2 years ago
/**
* Clean-css - https://github.com/clean-css/clean-css
* Released under the terms of MIT license
*/
var level0Optimize = require('./optimizer/level-0/optimize');
var level1Optimize = require('./optimizer/level-1/optimize');
var level2Optimize = require('./optimizer/level-2/optimize');
var validator = require('./optimizer/validator');
var compatibilityFrom = require('./options/compatibility');
var fetchFrom = require('./options/fetch');
var formatFrom = require('./options/format').formatFrom;
var inlineFrom = require('./options/inline');
var inlineRequestFrom = require('./options/inline-request');
var inlineTimeoutFrom = require('./options/inline-timeout');
var OptimizationLevel = require('./options/optimization-level').OptimizationLevel;
var optimizationLevelFrom = require('./options/optimization-level').optimizationLevelFrom;
var pluginsFrom = require('./options/plugins');
var rebaseFrom = require('./options/rebase');
var rebaseToFrom = require('./options/rebase-to');
var inputSourceMapTracker = require('./reader/input-source-map-tracker');
var readSources = require('./reader/read-sources');
var serializeStyles = require('./writer/simple');
var serializeStylesAndSourceMap = require('./writer/source-maps');
var CleanCSS = module.exports = function CleanCSS(options) {
options = options || {};
this.options = {
batch: !!options.batch,
compatibility: compatibilityFrom(options.compatibility),
explicitRebaseTo: 'rebaseTo' in options,
fetch: fetchFrom(options.fetch),
format: formatFrom(options.format),
inline: inlineFrom(options.inline),
inlineRequest: inlineRequestFrom(options.inlineRequest),
inlineTimeout: inlineTimeoutFrom(options.inlineTimeout),
level: optimizationLevelFrom(options.level),
plugins: pluginsFrom(options.plugins),
rebase: rebaseFrom(options.rebase, options.rebaseTo),
rebaseTo: rebaseToFrom(options.rebaseTo),
returnPromise: !!options.returnPromise,
sourceMap: !!options.sourceMap,
sourceMapInlineSources: !!options.sourceMapInlineSources
};
};
// for compatibility with optimize-css-assets-webpack-plugin
CleanCSS.process = function(input, opts) {
var cleanCss;
var optsTo = opts.to;
delete opts.to;
cleanCss = new CleanCSS(Object.assign({
returnPromise: true, rebaseTo: optsTo
}, opts));
return cleanCss.minify(input)
.then(function(output) {
return { css: output.styles };
});
};
CleanCSS.prototype.minify = function(input, maybeSourceMap, maybeCallback) {
var options = this.options;
if (options.returnPromise) {
return new Promise(function(resolve, reject) {
minifyAll(input, options, maybeSourceMap, function(errors, output) {
return errors
? reject(errors)
: resolve(output);
});
});
}
return minifyAll(input, options, maybeSourceMap, maybeCallback);
};
function minifyAll(input, options, maybeSourceMap, maybeCallback) {
if (options.batch && Array.isArray(input)) {
return minifyInBatchesFromArray(input, options, maybeSourceMap, maybeCallback);
} if (options.batch && (typeof input == 'object')) {
return minifyInBatchesFromHash(input, options, maybeSourceMap, maybeCallback);
}
return minify(input, options, maybeSourceMap, maybeCallback);
}
function minifyInBatchesFromArray(input, options, maybeSourceMap, maybeCallback) {
var callback = typeof maybeCallback == 'function'
? maybeCallback
: (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
var errors = [];
var outputAsHash = {};
var inputValue;
var i, l;
function whenHashBatchDone(innerErrors, output) {
outputAsHash = Object.assign(outputAsHash, output);
if (innerErrors !== null) {
errors = errors.concat(innerErrors);
}
}
for (i = 0, l = input.length; i < l; i++) {
if (typeof input[i] == 'object') {
minifyInBatchesFromHash(input[i], options, whenHashBatchDone);
} else {
inputValue = input[i];
outputAsHash[inputValue] = minify([inputValue], options);
errors = errors.concat(outputAsHash[inputValue].errors);
}
}
return callback
? callback(errors.length > 0 ? errors : null, outputAsHash)
: outputAsHash;
}
function minifyInBatchesFromHash(input, options, maybeSourceMap, maybeCallback) {
var callback = typeof maybeCallback == 'function'
? maybeCallback
: (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
var errors = [];
var outputAsHash = {};
var inputKey;
var inputValue;
for (inputKey in input) {
inputValue = input[inputKey];
outputAsHash[inputKey] = minify(inputValue.styles, options, inputValue.sourceMap);
errors = errors.concat(outputAsHash[inputKey].errors);
}
return callback
? callback(errors.length > 0 ? errors : null, outputAsHash)
: outputAsHash;
}
function minify(input, options, maybeSourceMap, maybeCallback) {
var sourceMap = typeof maybeSourceMap != 'function'
? maybeSourceMap
: null;
var callback = typeof maybeCallback == 'function'
? maybeCallback
: (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
var context = {
stats: {
efficiency: 0,
minifiedSize: 0,
originalSize: 0,
startedAt: Date.now(),
timeSpent: 0
},
cache: { specificity: {} },
errors: [],
inlinedStylesheets: [],
inputSourceMapTracker: inputSourceMapTracker(),
localOnly: !callback,
options: options,
source: null,
sourcesContent: {},
validator: validator(options.compatibility),
warnings: []
};
var implicitRebaseToWarning;
if (sourceMap) {
context.inputSourceMapTracker.track(undefined, sourceMap);
}
if (options.rebase && !options.explicitRebaseTo) {
implicitRebaseToWarning = 'You have set `rebase: true` without giving `rebaseTo` option, which, in this case, defaults to the current working directory. '
+ 'You are then warned this can lead to unexpected URL rebasing (aka here be dragons)! '
+ '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.';
context.warnings.push(implicitRebaseToWarning);
}
return runner(context.localOnly)(function() {
return readSources(input, context, function(tokens) {
var serialize = context.options.sourceMap
? serializeStylesAndSourceMap
: serializeStyles;
var optimizedTokens = optimize(tokens, context);
var optimizedStyles = serialize(optimizedTokens, context);
var output = withMetadata(optimizedStyles, context);
return callback
? callback(context.errors.length > 0 ? context.errors : null, output)
: output;
});
});
}
function runner(localOnly) {
// to always execute code asynchronously when a callback is given
// more at blog.izs.me/post/59142742143/designing-apis-for-asynchrony
return localOnly
? function(callback) { return callback(); }
: process.nextTick;
}
function optimize(tokens, context) {
var optimized = level0Optimize(tokens, context);
optimized = OptimizationLevel.One in context.options.level
? level1Optimize(tokens, context)
: tokens;
optimized = OptimizationLevel.Two in context.options.level
? level2Optimize(tokens, context, true)
: optimized;
return optimized;
}
function withMetadata(output, context) {
output.stats = calculateStatsFrom(output.styles, context);
output.errors = context.errors;
output.inlinedStylesheets = context.inlinedStylesheets;
output.warnings = context.warnings;
return output;
}
function calculateStatsFrom(styles, context) {
var finishedAt = Date.now();
var timeSpent = finishedAt - context.stats.startedAt;
delete context.stats.startedAt;
context.stats.timeSpent = timeSpent;
context.stats.efficiency = 1 - styles.length / context.stats.originalSize;
context.stats.minifiedSize = styles.length;
return context.stats;
}