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.
246 lines
7.8 KiB
246 lines
7.8 KiB
/** |
|
* 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); |
|
|
|
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; |
|
}
|
|
|