1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | var level0Optimize = require('./optimizer/level-0/optimize');
|
7 | var level1Optimize = require('./optimizer/level-1/optimize');
|
8 | var level2Optimize = require('./optimizer/level-2/optimize');
|
9 | var validator = require('./optimizer/validator');
|
10 |
|
11 | var compatibilityFrom = require('./options/compatibility');
|
12 | var fetchFrom = require('./options/fetch');
|
13 | var formatFrom = require('./options/format').formatFrom;
|
14 | var inlineFrom = require('./options/inline');
|
15 | var inlineRequestFrom = require('./options/inline-request');
|
16 | var inlineTimeoutFrom = require('./options/inline-timeout');
|
17 | var OptimizationLevel = require('./options/optimization-level').OptimizationLevel;
|
18 | var optimizationLevelFrom = require('./options/optimization-level').optimizationLevelFrom;
|
19 | var pluginsFrom = require('./options/plugins');
|
20 | var rebaseFrom = require('./options/rebase');
|
21 | var rebaseToFrom = require('./options/rebase-to');
|
22 |
|
23 | var inputSourceMapTracker = require('./reader/input-source-map-tracker');
|
24 | var readSources = require('./reader/read-sources');
|
25 |
|
26 | var serializeStyles = require('./writer/simple');
|
27 | var serializeStylesAndSourceMap = require('./writer/source-maps');
|
28 |
|
29 | var 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 |
|
53 | CleanCSS.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 |
|
67 | CleanCSS.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 |
|
83 | function 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 |
|
93 | function 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 |
|
126 | function 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 |
|
147 | function 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 |
|
206 | function runner(localOnly) {
|
207 |
|
208 |
|
209 | return localOnly ?
|
210 | function (callback) { return callback(); } :
|
211 | process.nextTick;
|
212 | }
|
213 |
|
214 | function 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 |
|
227 | function 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 |
|
236 | function 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 | }
|