Tim van der Lippe | 652ccb7 | 2021-05-27 17:07:12 +0100 | [diff] [blame^] | 1 | /** |
| 2 | * Clean-css - https://github.com/jakubpawlowicz/clean-css |
| 3 | * Released under the terms of MIT license |
| 4 | * |
| 5 | * Copyright (C) 2017 JakubPawlowicz.com |
| 6 | */ |
| 7 | |
| 8 | var level0Optimize = require('./optimizer/level-0/optimize'); |
| 9 | var level1Optimize = require('./optimizer/level-1/optimize'); |
| 10 | var level2Optimize = require('./optimizer/level-2/optimize'); |
| 11 | var validator = require('./optimizer/validator'); |
| 12 | |
| 13 | var compatibilityFrom = require('./options/compatibility'); |
| 14 | var fetchFrom = require('./options/fetch'); |
| 15 | var formatFrom = require('./options/format').formatFrom; |
| 16 | var inlineFrom = require('./options/inline'); |
| 17 | var inlineRequestFrom = require('./options/inline-request'); |
| 18 | var inlineTimeoutFrom = require('./options/inline-timeout'); |
| 19 | var OptimizationLevel = require('./options/optimization-level').OptimizationLevel; |
| 20 | var optimizationLevelFrom = require('./options/optimization-level').optimizationLevelFrom; |
| 21 | var rebaseFrom = require('./options/rebase'); |
| 22 | var rebaseToFrom = require('./options/rebase-to'); |
| 23 | |
| 24 | var inputSourceMapTracker = require('./reader/input-source-map-tracker'); |
| 25 | var readSources = require('./reader/read-sources'); |
| 26 | |
| 27 | var serializeStyles = require('./writer/simple'); |
| 28 | var serializeStylesAndSourceMap = require('./writer/source-maps'); |
| 29 | |
| 30 | var CleanCSS = module.exports = function CleanCSS(options) { |
| 31 | options = options || {}; |
| 32 | |
| 33 | this.options = { |
| 34 | compatibility: compatibilityFrom(options.compatibility), |
| 35 | fetch: fetchFrom(options.fetch), |
| 36 | format: formatFrom(options.format), |
| 37 | inline: inlineFrom(options.inline), |
| 38 | inlineRequest: inlineRequestFrom(options.inlineRequest), |
| 39 | inlineTimeout: inlineTimeoutFrom(options.inlineTimeout), |
| 40 | level: optimizationLevelFrom(options.level), |
| 41 | rebase: rebaseFrom(options.rebase), |
| 42 | rebaseTo: rebaseToFrom(options.rebaseTo), |
| 43 | returnPromise: !!options.returnPromise, |
| 44 | sourceMap: !!options.sourceMap, |
| 45 | sourceMapInlineSources: !!options.sourceMapInlineSources |
| 46 | }; |
| 47 | }; |
| 48 | |
| 49 | |
| 50 | // for compatibility with optimize-css-assets-webpack-plugin |
| 51 | CleanCSS.process = function (input, opts) { |
| 52 | var cleanCss; |
| 53 | var optsTo = opts.to; |
| 54 | |
| 55 | delete opts.to; |
| 56 | cleanCss = new CleanCSS(Object.assign({ returnPromise: true, rebaseTo: optsTo }, opts)); |
| 57 | |
| 58 | return cleanCss.minify(input) |
| 59 | .then(function(output) { |
| 60 | return { css: output.styles }; |
| 61 | }); |
| 62 | }; |
| 63 | |
| 64 | |
| 65 | CleanCSS.prototype.minify = function (input, maybeSourceMap, maybeCallback) { |
| 66 | var options = this.options; |
| 67 | |
| 68 | if (options.returnPromise) { |
| 69 | return new Promise(function (resolve, reject) { |
| 70 | minify(input, options, maybeSourceMap, function (errors, output) { |
| 71 | return errors ? |
| 72 | reject(errors) : |
| 73 | resolve(output); |
| 74 | }); |
| 75 | }); |
| 76 | } else { |
| 77 | return minify(input, options, maybeSourceMap, maybeCallback); |
| 78 | } |
| 79 | }; |
| 80 | |
| 81 | function minify(input, options, maybeSourceMap, maybeCallback) { |
| 82 | var sourceMap = typeof maybeSourceMap != 'function' ? |
| 83 | maybeSourceMap : |
| 84 | null; |
| 85 | var callback = typeof maybeCallback == 'function' ? |
| 86 | maybeCallback : |
| 87 | (typeof maybeSourceMap == 'function' ? maybeSourceMap : null); |
| 88 | var context = { |
| 89 | stats: { |
| 90 | efficiency: 0, |
| 91 | minifiedSize: 0, |
| 92 | originalSize: 0, |
| 93 | startedAt: Date.now(), |
| 94 | timeSpent: 0 |
| 95 | }, |
| 96 | cache: { |
| 97 | specificity: {} |
| 98 | }, |
| 99 | errors: [], |
| 100 | inlinedStylesheets: [], |
| 101 | inputSourceMapTracker: inputSourceMapTracker(), |
| 102 | localOnly: !callback, |
| 103 | options: options, |
| 104 | source: null, |
| 105 | sourcesContent: {}, |
| 106 | validator: validator(options.compatibility), |
| 107 | warnings: [] |
| 108 | }; |
| 109 | |
| 110 | if (sourceMap) { |
| 111 | context.inputSourceMapTracker.track(undefined, sourceMap); |
| 112 | } |
| 113 | |
| 114 | return runner(context.localOnly)(function () { |
| 115 | return readSources(input, context, function (tokens) { |
| 116 | var serialize = context.options.sourceMap ? |
| 117 | serializeStylesAndSourceMap : |
| 118 | serializeStyles; |
| 119 | |
| 120 | var optimizedTokens = optimize(tokens, context); |
| 121 | var optimizedStyles = serialize(optimizedTokens, context); |
| 122 | var output = withMetadata(optimizedStyles, context); |
| 123 | |
| 124 | return callback ? |
| 125 | callback(context.errors.length > 0 ? context.errors : null, output) : |
| 126 | output; |
| 127 | }); |
| 128 | }); |
| 129 | } |
| 130 | |
| 131 | function runner(localOnly) { |
| 132 | // to always execute code asynchronously when a callback is given |
| 133 | // more at blog.izs.me/post/59142742143/designing-apis-for-asynchrony |
| 134 | return localOnly ? |
| 135 | function (callback) { return callback(); } : |
| 136 | process.nextTick; |
| 137 | } |
| 138 | |
| 139 | function optimize(tokens, context) { |
| 140 | var optimized; |
| 141 | |
| 142 | optimized = level0Optimize(tokens, context); |
| 143 | optimized = OptimizationLevel.One in context.options.level ? |
| 144 | level1Optimize(tokens, context) : |
| 145 | tokens; |
| 146 | optimized = OptimizationLevel.Two in context.options.level ? |
| 147 | level2Optimize(tokens, context, true) : |
| 148 | optimized; |
| 149 | |
| 150 | return optimized; |
| 151 | } |
| 152 | |
| 153 | function withMetadata(output, context) { |
| 154 | output.stats = calculateStatsFrom(output.styles, context); |
| 155 | output.errors = context.errors; |
| 156 | output.inlinedStylesheets = context.inlinedStylesheets; |
| 157 | output.warnings = context.warnings; |
| 158 | |
| 159 | return output; |
| 160 | } |
| 161 | |
| 162 | function calculateStatsFrom(styles, context) { |
| 163 | var finishedAt = Date.now(); |
| 164 | var timeSpent = finishedAt - context.stats.startedAt; |
| 165 | |
| 166 | delete context.stats.startedAt; |
| 167 | context.stats.timeSpent = timeSpent; |
| 168 | context.stats.efficiency = 1 - styles.length / context.stats.originalSize; |
| 169 | context.stats.minifiedSize = styles.length; |
| 170 | |
| 171 | return context.stats; |
| 172 | } |