UNPKG

8 kBJavaScriptView Raw
1/**
2 * Clean-css - https://github.com/clean-css/clean-css
3 * Released under the terms of MIT license
4 */
5
6var level0Optimize = require('./optimizer/level-0/optimize');
7var level1Optimize = require('./optimizer/level-1/optimize');
8var level2Optimize = require('./optimizer/level-2/optimize');
9var validator = require('./optimizer/validator');
10
11var compatibilityFrom = require('./options/compatibility');
12var fetchFrom = require('./options/fetch');
13var formatFrom = require('./options/format').formatFrom;
14var inlineFrom = require('./options/inline');
15var inlineRequestFrom = require('./options/inline-request');
16var inlineTimeoutFrom = require('./options/inline-timeout');
17var OptimizationLevel = require('./options/optimization-level').OptimizationLevel;
18var optimizationLevelFrom = require('./options/optimization-level').optimizationLevelFrom;
19var pluginsFrom = require('./options/plugins');
20var rebaseFrom = require('./options/rebase');
21var rebaseToFrom = require('./options/rebase-to');
22
23var inputSourceMapTracker = require('./reader/input-source-map-tracker');
24var readSources = require('./reader/read-sources');
25
26var serializeStyles = require('./writer/simple');
27var serializeStylesAndSourceMap = require('./writer/source-maps');
28
29var CleanCSS = module.exports = function CleanCSS(options) {
30 options = options || {};
31
32 this.options = {
33 batch: !!options.batch,
34 compatibility: compatibilityFrom(options.compatibility),
35 explicitRebaseTo: 'rebaseTo' in options,
36 fetch: fetchFrom(options.fetch),
37 format: formatFrom(options.format),
38 inline: inlineFrom(options.inline),
39 inlineRequest: inlineRequestFrom(options.inlineRequest),
40 inlineTimeout: inlineTimeoutFrom(options.inlineTimeout),
41 level: optimizationLevelFrom(options.level),
42 plugins: pluginsFrom(options.plugins),
43 rebase: rebaseFrom(options.rebase, options.rebaseTo),
44 rebaseTo: rebaseToFrom(options.rebaseTo),
45 returnPromise: !!options.returnPromise,
46 sourceMap: !!options.sourceMap,
47 sourceMapInlineSources: !!options.sourceMapInlineSources
48 };
49};
50
51
52// for compatibility with optimize-css-assets-webpack-plugin
53CleanCSS.process = function (input, opts) {
54 var cleanCss;
55 var optsTo = opts.to;
56
57 delete opts.to;
58 cleanCss = new CleanCSS(Object.assign({ returnPromise: true, rebaseTo: optsTo }, opts));
59
60 return cleanCss.minify(input)
61 .then(function(output) {
62 return { css: output.styles };
63 });
64};
65
66
67CleanCSS.prototype.minify = function (input, maybeSourceMap, maybeCallback) {
68 var options = this.options;
69
70 if (options.returnPromise) {
71 return new Promise(function (resolve, reject) {
72 minifyAll(input, options, maybeSourceMap, function (errors, output) {
73 return errors ?
74 reject(errors) :
75 resolve(output);
76 });
77 });
78 } else {
79 return minifyAll(input, options, maybeSourceMap, maybeCallback);
80 }
81};
82
83function minifyAll(input, options, maybeSourceMap, maybeCallback) {
84 if (options.batch && Array.isArray(input)) {
85 return minifyInBatchesFromArray(input, options, maybeSourceMap, maybeCallback);
86 } else if (options.batch && (typeof input == 'object')) {
87 return minifyInBatchesFromHash(input, options, maybeSourceMap, maybeCallback);
88 } else {
89 return minify(input, options, maybeSourceMap, maybeCallback);
90 }
91}
92
93function minifyInBatchesFromArray(input, options, maybeSourceMap, maybeCallback) {
94 var callback = typeof maybeCallback == 'function' ?
95 maybeCallback :
96 (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
97 var errors = [];
98 var outputAsHash = {};
99 var inputValue;
100 var i, l;
101
102 function whenHashBatchDone(innerErrors, output) {
103 outputAsHash = Object.assign(outputAsHash, output);
104
105 if (innerErrors !== null) {
106 errors = errors.concat(innerErrors);
107 }
108 }
109
110 for (i = 0, l = input.length; i < l; i++) {
111 if (typeof input[i] == 'object') {
112 minifyInBatchesFromHash(input[i], options, whenHashBatchDone);
113 } else {
114 inputValue = input[i];
115
116 outputAsHash[inputValue] = minify([inputValue], options);
117 errors = errors.concat(outputAsHash[inputValue].errors);
118 }
119 }
120
121 return callback ?
122 callback(errors.length > 0 ? errors : null, outputAsHash) :
123 outputAsHash;
124}
125
126function minifyInBatchesFromHash(input, options, maybeSourceMap, maybeCallback) {
127 var callback = typeof maybeCallback == 'function' ?
128 maybeCallback :
129 (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
130 var errors = [];
131 var outputAsHash = {};
132 var inputKey;
133 var inputValue;
134
135 for (inputKey in input) {
136 inputValue = input[inputKey];
137
138 outputAsHash[inputKey] = minify(inputValue.styles, options, inputValue.sourceMap);
139 errors = errors.concat(outputAsHash[inputKey].errors);
140 }
141
142 return callback ?
143 callback(errors.length > 0 ? errors : null, outputAsHash) :
144 outputAsHash;
145}
146
147function minify(input, options, maybeSourceMap, maybeCallback) {
148 var sourceMap = typeof maybeSourceMap != 'function' ?
149 maybeSourceMap :
150 null;
151 var callback = typeof maybeCallback == 'function' ?
152 maybeCallback :
153 (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
154 var context = {
155 stats: {
156 efficiency: 0,
157 minifiedSize: 0,
158 originalSize: 0,
159 startedAt: Date.now(),
160 timeSpent: 0
161 },
162 cache: {
163 specificity: {}
164 },
165 errors: [],
166 inlinedStylesheets: [],
167 inputSourceMapTracker: inputSourceMapTracker(),
168 localOnly: !callback,
169 options: options,
170 source: null,
171 sourcesContent: {},
172 validator: validator(options.compatibility),
173 warnings: []
174 };
175 var implicitRebaseToWarning;
176
177 if (sourceMap) {
178 context.inputSourceMapTracker.track(undefined, sourceMap);
179 }
180
181 if (options.rebase && !options.explicitRebaseTo) {
182 implicitRebaseToWarning =
183 'You have set `rebase: true` without giving `rebaseTo` option, which, in this case, defaults to the current working directory. ' +
184 'You are then warned this can lead to unexpected URL rebasing (aka here be dragons)! ' +
185 '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.';
186 context.warnings.push(implicitRebaseToWarning);
187 }
188
189 return runner(context.localOnly)(function () {
190 return readSources(input, context, function (tokens) {
191 var serialize = context.options.sourceMap ?
192 serializeStylesAndSourceMap :
193 serializeStyles;
194
195 var optimizedTokens = optimize(tokens, context);
196 var optimizedStyles = serialize(optimizedTokens, context);
197 var output = withMetadata(optimizedStyles, context);
198
199 return callback ?
200 callback(context.errors.length > 0 ? context.errors : null, output) :
201 output;
202 });
203 });
204}
205
206function runner(localOnly) {
207 // to always execute code asynchronously when a callback is given
208 // more at blog.izs.me/post/59142742143/designing-apis-for-asynchrony
209 return localOnly ?
210 function (callback) { return callback(); } :
211 process.nextTick;
212}
213
214function optimize(tokens, context) {
215 var optimized = level0Optimize(tokens, context);
216
217 optimized = OptimizationLevel.One in context.options.level ?
218 level1Optimize(tokens, context) :
219 tokens;
220 optimized = OptimizationLevel.Two in context.options.level ?
221 level2Optimize(tokens, context, true) :
222 optimized;
223
224 return optimized;
225}
226
227function withMetadata(output, context) {
228 output.stats = calculateStatsFrom(output.styles, context);
229 output.errors = context.errors;
230 output.inlinedStylesheets = context.inlinedStylesheets;
231 output.warnings = context.warnings;
232
233 return output;
234}
235
236function calculateStatsFrom(styles, context) {
237 var finishedAt = Date.now();
238 var timeSpent = finishedAt - context.stats.startedAt;
239
240 delete context.stats.startedAt;
241 context.stats.timeSpent = timeSpent;
242 context.stats.efficiency = 1 - styles.length / context.stats.originalSize;
243 context.stats.minifiedSize = styles.length;
244
245 return context.stats;
246}