UNPKG

4.98 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
49CleanCSS.prototype.minify = function (input, maybeSourceMap, maybeCallback) {
50 var options = this.options;
51
52 if (options.returnPromise) {
53 return new Promise(function (resolve, reject) {
54 minify(input, options, maybeSourceMap, function (errors, output) {
55 return errors ?
56 reject(errors) :
57 resolve(output);
58 });
59 });
60 } else {
61 return minify(input, options, maybeSourceMap, maybeCallback);
62 }
63};
64
65function minify(input, options, maybeSourceMap, maybeCallback) {
66 var sourceMap = typeof maybeSourceMap != 'function' ?
67 maybeSourceMap :
68 null;
69 var callback = typeof maybeCallback == 'function' ?
70 maybeCallback :
71 (typeof maybeSourceMap == 'function' ? maybeSourceMap : null);
72 var context = {
73 stats: {
74 efficiency: 0,
75 minifiedSize: 0,
76 originalSize: 0,
77 startedAt: Date.now(),
78 timeSpent: 0
79 },
80 cache: {
81 specificity: {}
82 },
83 errors: [],
84 inlinedStylesheets: [],
85 inputSourceMapTracker: inputSourceMapTracker(),
86 localOnly: !callback,
87 options: options,
88 source: null,
89 sourcesContent: {},
90 validator: validator(options.compatibility),
91 warnings: []
92 };
93
94 if (sourceMap) {
95 context.inputSourceMapTracker.track(undefined, sourceMap);
96 }
97
98 return runner(context.localOnly)(function () {
99 return readSources(input, context, function (tokens) {
100 var serialize = context.options.sourceMap ?
101 serializeStylesAndSourceMap :
102 serializeStyles;
103
104 var optimizedTokens = optimize(tokens, context);
105 var optimizedStyles = serialize(optimizedTokens, context);
106 var output = withMetadata(optimizedStyles, context);
107
108 return callback ?
109 callback(context.errors.length > 0 ? context.errors : null, output) :
110 output;
111 });
112 });
113}
114
115function runner(localOnly) {
116 // to always execute code asynchronously when a callback is given
117 // more at blog.izs.me/post/59142742143/designing-apis-for-asynchrony
118 return localOnly ?
119 function (callback) { return callback(); } :
120 process.nextTick;
121}
122
123function optimize(tokens, context) {
124 var optimized;
125
126 optimized = level0Optimize(tokens, context);
127 optimized = OptimizationLevel.One in context.options.level ?
128 level1Optimize(tokens, context) :
129 tokens;
130 optimized = OptimizationLevel.Two in context.options.level ?
131 level2Optimize(tokens, context, true) :
132 optimized;
133
134 return optimized;
135}
136
137function withMetadata(output, context) {
138 output.stats = calculateStatsFrom(output.styles, context);
139 output.errors = context.errors;
140 output.inlinedStylesheets = context.inlinedStylesheets;
141 output.warnings = context.warnings;
142
143 return output;
144}
145
146function calculateStatsFrom(styles, context) {
147 var finishedAt = Date.now();
148 var timeSpent = finishedAt - context.stats.startedAt;
149
150 delete context.stats.startedAt;
151 context.stats.timeSpent = timeSpent;
152 context.stats.efficiency = 1 - styles.length / context.stats.originalSize;
153 context.stats.minifiedSize = styles.length;
154
155 return context.stats;
156}