/** * 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); }); }); } else { return minifyAll(input, options, maybeSourceMap, maybeCallback); } }; function minifyAll(input, options, maybeSourceMap, maybeCallback) { if (options.batch && Array.isArray(input)) { return minifyInBatchesFromArray(input, options, maybeSourceMap, maybeCallback); } else if (options.batch && (typeof input == 'object')) { return minifyInBatchesFromHash(input, options, maybeSourceMap, maybeCallback); } else { 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); 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; }