Skip to content

Commit aff94d0

Browse files
See #7 - moves CLI logic into a module.
Why: * It may become reusable this way.
1 parent 7dbbf65 commit aff94d0

File tree

3 files changed

+257
-243
lines changed

3 files changed

+257
-243
lines changed

bin/cleancss

+2-243
Original file line numberDiff line numberDiff line change
@@ -1,245 +1,4 @@
11
#!/usr/bin/env node
22

3-
var fs = require('fs');
4-
var path = require('path');
5-
6-
var CleanCSS = require('clean-css');
7-
var commands = require('commander');
8-
9-
var packageConfig = fs.readFileSync(path.join(path.dirname(fs.realpathSync(process.argv[1])), '../package.json'));
10-
var buildVersion = JSON.parse(packageConfig).version;
11-
12-
// Specify commander options to parse command line params correctly
13-
commands
14-
.version(buildVersion, '-v, --version')
15-
.usage('[options] <source-file ...>')
16-
.option('-c, --compatibility [ie7|ie8]', 'Force compatibility mode (see Readme for advanced examples)')
17-
.option('-d, --debug', 'Shows debug information (minification time & compression efficiency)')
18-
.option('-f, --format <options>', 'Controls output formatting, see examples below')
19-
.option('-o, --output [output-file]', 'Use [output-file] as output instead of STDOUT')
20-
.option('-O <n> [optimizations]', 'Turn on level <n> optimizations; optionally accepts a list of fine-grained options, defaults to `1`, see examples below', function (val) { return Math.abs(parseInt(val)); })
21-
.option('--inline [rules]', 'Enables inlining for listed sources (defaults to `local`)')
22-
.option('--inline-timeout [seconds]', 'Per connection timeout when fetching remote stylesheets (defaults to 5 seconds)', parseFloat)
23-
.option('--skip-rebase', 'Disable URLs rebasing')
24-
.option('--source-map', 'Enables building input\'s source map')
25-
.option('--source-map-inline-sources', 'Enables inlining sources inside source maps');
26-
27-
commands.on('--help', function () {
28-
console.log(' Examples:\n');
29-
console.log(' %> cleancss one.css');
30-
console.log(' %> cleancss -o one-min.css one.css');
31-
console.log(' %> cleancss -o merged-and-minified.css one.css two.css three.css');
32-
console.log(' %> cleancss one.css two.css three.css | gzip -9 -c > merged-minified-and-gzipped.css.gz');
33-
console.log('');
34-
console.log(' Formatting options:');
35-
console.log(' %> cleancss --format beautify one.css');
36-
console.log(' %> cleancss --format keep-breaks one.css');
37-
console.log(' %> cleancss --format \'indentBy:1;indentWith:tab\' one.css');
38-
console.log(' %> cleancss --format \'breaks:afterBlockBegins=on;spaces:aroundSelectorRelation=on\' one.css');
39-
console.log(' %> # `breaks` controls where to insert breaks');
40-
console.log(' %> # `afterAtRule` controls if a line break comes after an at-rule; e.g. `@charset`; defaults to `off` (alias to `false`)');
41-
console.log(' %> # `afterBlockBegins` controls if a line break comes after a block begins; e.g. `@media`; defaults to `off`');
42-
console.log(' %> # `afterBlockEnds` controls if a line break comes after a block ends, defaults to `off`');
43-
console.log(' %> # `afterComment` controls if a line break comes after a comment; defaults to `off`');
44-
console.log(' %> # `afterProperty` controls if a line break comes after a property; defaults to `off`');
45-
console.log(' %> # `afterRuleBegins` controls if a line break comes after a rule begins; defaults to `off`');
46-
console.log(' %> # `afterRuleEnds` controls if a line break comes after a rule ends; defaults to `off`');
47-
console.log(' %> # `beforeBlockEnds` controls if a line break comes before a block ends; defaults to `off`');
48-
console.log(' %> # `betweenSelectors` controls if a line break comes between selectors; defaults to `off`');
49-
console.log(' %> # `indentBy` controls number of characters to indent with; defaults to `0`');
50-
console.log(' %> # `indentWith` controls a character to indent with, can be `space` or `tab`; defaults to `space`');
51-
console.log(' %> # `spaces` controls where to insert spaces');
52-
console.log(' %> # `aroundSelectorRelation` controls if spaces come around selector relations; e.g. `div > a`; defaults to `off`');
53-
console.log(' %> # `beforeBlockBegins` controls if a space comes before a block begins; e.g. `.block {`; defaults to `off`');
54-
console.log(' %> # `beforeValue` controls if a space comes before a value; e.g. `width: 1rem`; defaults to `off`');
55-
console.log(' %> # `wrapAt` controls maximum line length; defaults to `off`');
56-
console.log('');
57-
console.log(' Level 0 optimizations:');
58-
console.log(' %> cleancss -O0 one.css');
59-
console.log('');
60-
console.log(' Level 1 optimizations:');
61-
console.log(' %> cleancss -O1 one.css');
62-
console.log(' %> cleancss -O1 removeQuotes:off;roundingPrecision:4;specialComments:1 one.css');
63-
console.log(' %> cleancss -O1 all:off;specialComments:1 one.css');
64-
console.log(' %> # `cleanupCharsets` controls `@charset` moving to the front of a stylesheet; defaults to `on`');
65-
console.log(' %> # `normalizeUrls` controls URL normalzation; default to `on`');
66-
console.log(' %> # `optimizeBackground` controls `background` property optimizatons; defaults to `on`');
67-
console.log(' %> # `optimizeBorderRadius` controls `border-radius` property optimizatons; defaults to `on`');
68-
console.log(' %> # `optimizeFilter` controls `filter` property optimizatons; defaults to `on`');
69-
console.log(' %> # `optimizeFontWeight` controls `font-weight` property optimizatons; defaults to `on`');
70-
console.log(' %> # `optimizeOutline` controls `outline` property optimizatons; defaults to `on`');
71-
console.log(' %> # `removeEmpty` controls removing empty rules and nested blocks; defaults to `on` (since 4.1.0)');
72-
console.log(' %> # `removeNegativePaddings` controls removing negative paddings; defaults to `on`');
73-
console.log(' %> # `removeQuotes` controls removing quotes when unnecessary; defaults to `on`');
74-
console.log(' %> # `removeWhitespace` controls removing unused whitespace; defaults to `on`');
75-
console.log(' %> # `replaceMultipleZeros` contols removing redundant zeros; defaults to `on`');
76-
console.log(' %> # `replaceTimeUnits` controls replacing time units with shorter values; defaults to `on');
77-
console.log(' %> # `replaceZeroUnits` controls replacing zero values with units; defaults to `on`');
78-
console.log(' %> # `roundingPrecision` rounds pixel values to `N` decimal places; `off` disables rounding; defaults to `off`');
79-
console.log(' %> # `selectorsSortingMethod` denotes selector sorting method; can be `natural` or `standard`; defaults to `standard`');
80-
console.log(' %> # `specialComments` denotes a number of /*! ... */ comments preserved; defaults to `all`');
81-
console.log(' %> # `tidyAtRules` controls at-rules (e.g. `@charset`, `@import`) optimizing; defaults to `on`');
82-
console.log(' %> # `tidyBlockScopes` controls block scopes (e.g. `@media`) optimizing; defaults to `on`');
83-
console.log(' %> # `tidySelectors` controls selectors optimizing; defaults to `on`');
84-
console.log('');
85-
console.log(' Level 2 optimizations:');
86-
console.log(' %> cleancss -O2 one.css');
87-
console.log(' %> cleancss -O2 mergeMedia:off;restructureRules:off;mergeSemantically:on;mergeIntoShorthands:off one.css');
88-
console.log(' %> cleancss -O2 all:off;removeDuplicateRules:on one.css');
89-
console.log(' %> # `mergeAdjacentRules` controls adjacent rules merging; defaults to `on`');
90-
console.log(' %> # `mergeIntoShorthands` controls merging properties into shorthands; defaults to `on`');
91-
console.log(' %> # `mergeMedia` controls `@media` merging; defaults to `on`');
92-
console.log(' %> # `mergeNonAdjacentRules` controls non-adjacent rule merging; defaults to `on`');
93-
console.log(' %> # `mergeSemantically` controls semantic merging; defaults to `off`');
94-
console.log(' %> # `overrideProperties` controls property overriding based on understandability; defaults to `on`');
95-
console.log(' %> # `reduceNonAdjacentRules` controls non-adjacent rule reducing; defaults to `on`');
96-
console.log(' %> # `removeDuplicateFontRules` controls duplicate `@font-face` removing; defaults to `on`');
97-
console.log(' %> # `removeDuplicateMediaBlocks` controls duplicate `@media` removing; defaults to `on`');
98-
console.log(' %> # `removeDuplicateRules` controls duplicate rules removing; defaults to `on`');
99-
console.log(' %> # `removeEmpty` controls removing empty rules and nested blocks; defaults to `on` (since 4.1.0)');
100-
console.log(' %> # `removeUnusedAtRules` controls unused at rule removing; defaults to `off` (since 4.1.0)');
101-
console.log(' %> # `restructureRules` controls rule restructuring; defaults to `off`');
102-
console.log(' %> # `skipProperties` controls which properties won\'t be optimized, defaults to empty list which means all will be optimized (since 4.1.0)');
103-
104-
process.exit();
105-
});
106-
107-
commands.parse(process.argv);
108-
109-
if (commands.rawArgs.indexOf('-O0') > -1) {
110-
commands.O0 = true;
111-
}
112-
113-
if (commands.rawArgs.indexOf('-O1') > -1) {
114-
commands.O1 = findArgumentTo('-O1', commands.rawArgs, commands.args);
115-
}
116-
117-
if (commands.rawArgs.indexOf('-O2') > -1) {
118-
commands.O2 = findArgumentTo('-O2', commands.rawArgs, commands.args);
119-
}
120-
121-
// If no sensible data passed in just print help and exit
122-
var fromStdin = !process.env.__DIRECT__ && !process.stdin.isTTY;
123-
if (!fromStdin && commands.args.length === 0) {
124-
commands.outputHelp();
125-
return 0;
126-
}
127-
128-
// Now coerce commands into CleanCSS configuration...
129-
var debugMode = commands.debug;
130-
var options = {
131-
compatibility: commands.compatibility,
132-
format: commands.format,
133-
inline: commands.inline || 'local',
134-
inlineTimeout: commands.inlineTimeout * 1000,
135-
level: commands.O0 || commands.O1 || commands.O2 ?
136-
{ '0': commands.O0, '1': commands.O1, '2': commands.O2 } :
137-
undefined,
138-
output: commands.output,
139-
rebase: commands.skipRebase ? false : true,
140-
rebaseTo: ('output' in commands) && commands.output.length > 0 ? path.dirname(path.resolve(commands.output)) : process.cwd(),
141-
sourceMap: commands.sourceMap,
142-
sourceMapInlineSources: commands.sourceMapInlineSources
143-
};
144-
145-
if (options.sourceMap && !options.output) {
146-
outputFeedback(['Source maps will not be built because you have not specified an output file.'], true);
147-
options.sourceMap = false;
148-
}
149-
150-
// ... and do the magic!
151-
if (commands.args.length > 0) {
152-
minify(commands.args);
153-
} else {
154-
var stdin = process.openStdin();
155-
stdin.setEncoding('utf-8');
156-
var data = '';
157-
stdin.on('data', function (chunk) {
158-
data += chunk;
159-
});
160-
stdin.on('end', function () {
161-
minify(data);
162-
});
163-
}
164-
165-
function findArgumentTo(option, rawArgs, args) {
166-
var value = true;
167-
var optionAt = rawArgs.indexOf(option);
168-
var nextOption = rawArgs[optionAt + 1];
169-
var looksLikePath;
170-
var asArgumentAt;
171-
172-
if (!nextOption) {
173-
return value;
174-
}
175-
176-
looksLikePath = nextOption.indexOf('.css') > -1 ||
177-
/\//.test(nextOption) ||
178-
/\\[^\-]/.test(nextOption) ||
179-
/^https?:\/\//.test(nextOption);
180-
asArgumentAt = args.indexOf(nextOption);
181-
182-
if (!looksLikePath) {
183-
value = nextOption;
184-
}
185-
186-
if (!looksLikePath && asArgumentAt > -1) {
187-
args.splice(asArgumentAt, 1);
188-
}
189-
190-
return value;
191-
}
192-
193-
function minify(data) {
194-
new CleanCSS(options).minify(data, function (errors, minified) {
195-
if (debugMode) {
196-
console.error('Original: %d bytes', minified.stats.originalSize);
197-
console.error('Minified: %d bytes', minified.stats.minifiedSize);
198-
console.error('Efficiency: %d%', ~~(minified.stats.efficiency * 10000) / 100.0);
199-
console.error('Time spent: %dms', minified.stats.timeSpent);
200-
201-
if (minified.inlinedStylesheets.length > 0) {
202-
console.error('Inlined stylesheets:');
203-
minified.inlinedStylesheets.forEach(function (uri) {
204-
console.error('- %s', uri);
205-
});
206-
}
207-
}
208-
209-
outputFeedback(minified.errors, true);
210-
outputFeedback(minified.warnings);
211-
212-
if (minified.errors.length > 0) {
213-
process.exit(1);
214-
}
215-
216-
if (minified.sourceMap) {
217-
var mapFilename = path.basename(options.output) + '.map';
218-
output(minified.styles + '/*# sourceMappingURL=' + mapFilename + ' */');
219-
outputMap(minified.sourceMap, mapFilename);
220-
} else {
221-
output(minified.styles);
222-
}
223-
});
224-
}
225-
226-
function output(minified) {
227-
if (options.output) {
228-
fs.writeFileSync(options.output, minified, 'utf8');
229-
} else {
230-
process.stdout.write(minified);
231-
}
232-
}
233-
234-
function outputMap(sourceMap, mapFilename) {
235-
var mapPath = path.join(path.dirname(options.output), mapFilename);
236-
fs.writeFileSync(mapPath, sourceMap.toString(), 'utf-8');
237-
}
238-
239-
function outputFeedback(messages, isError) {
240-
var prefix = isError ? '\x1B[31mERROR\x1B[39m:' : 'WARNING:';
241-
242-
messages.forEach(function (message) {
243-
console.error('%s %s', prefix, message);
244-
});
245-
}
3+
var cleanCssCli = require('../index');
4+
return cleanCssCli(process);

0 commit comments

Comments
 (0)