UNPKG

5.35 kBJavaScriptView Raw
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
8var level0Optimize = require('./optimizer/level-0/optimize');
9var level1Optimize = require('./optimizer/level-1/optimize');
10var level2Optimize = require('./optimizer/level-2/optimize');
11var validator = require('./optimizer/validator');
12
13var compatibilityFrom = require('./options/compatibility');
14var fetchFrom = require('./options/fetch');
15var formatFrom = require('./options/format').formatFrom;
16var inlineFrom = require('./options/inline');
17var inlineRequestFrom = require('./options/inline-request');
18var inlineTimeoutFrom = require('./options/inline-timeout');
19var OptimizationLevel = require('./options/optimization-level').OptimizationLevel;
20var optimizationLevelFrom = require('./options/optimization-level').optimizationLevelFrom;
21var rebaseFrom = require('./options/rebase');
22var rebaseToFrom = require('./options/rebase-to');
23
24var inputSourceMapTracker = require('./reader/input-source-map-tracker');
25var readSources = require('./reader/read-sources');
26
27var serializeStyles = require('./writer/simple');
28var serializeStylesAndSourceMap = require('./writer/source-maps');
29
30var 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
51CleanCSS.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
65CleanCSS.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
81function 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
131function 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
139function 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
153function 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
162function 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}