blob: 8cdb4b793297791465019e954c04221743213503 [file] [log] [blame]
/**
* Clean-css - https://github.com/jakubpawlowicz/clean-css
* Released under the terms of MIT license
*
* Copyright (C) 2017 JakubPawlowicz.com
*/
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 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 = {
compatibility: compatibilityFrom(options.compatibility),
fetch: fetchFrom(options.fetch),
format: formatFrom(options.format),
inline: inlineFrom(options.inline),
inlineRequest: inlineRequestFrom(options.inlineRequest),
inlineTimeout: inlineTimeoutFrom(options.inlineTimeout),
level: optimizationLevelFrom(options.level),
rebase: rebaseFrom(options.rebase),
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) {
minify(input, options, maybeSourceMap, function (errors, output) {
return errors ?
reject(errors) :
resolve(output);
});
});
} else {
return minify(input, options, maybeSourceMap, maybeCallback);
}
};
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: []
};
if (sourceMap) {
context.inputSourceMapTracker.track(undefined, sourceMap);
}
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;
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;
}