Skip to content

Commit 7e539df

Browse files
Moves in all CLI code from clean-css repo.
This is a copy with as little changes as possible. We'll take it from here and improve further.
1 parent 88d087a commit 7e539df

35 files changed

+1103
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/node_modules

.jshintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.git
2+
node_modules

.jshintrc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"camelcase": true,
3+
"curly": false,
4+
"eqeqeq": false,
5+
"immed": true,
6+
"indent": 2,
7+
"noarg": true,
8+
"node": true,
9+
"plusplus": false,
10+
"quotmark": "single",
11+
"strict": false,
12+
"undef": true,
13+
"unused": true
14+
}

bin/cleancss

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
#!/usr/bin/env node
2+
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(' %> # `optimizeFont controls `font` property optimizatons; defaults to `on` ');
70+
console.log(' %> # `optimizeFontWeight` controls `font-weight` property optimizatons; defaults to `on`');
71+
console.log(' %> # `optimizeOutline` controls `outline` property optimizatons; defaults to `on`');
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(' %> # `restructureRules` controls rule restructuring; defaults to `off`');
100+
101+
process.exit();
102+
});
103+
104+
commands.parse(process.argv);
105+
106+
if (commands.rawArgs.indexOf('-O0') > -1) {
107+
commands.O0 = true;
108+
}
109+
110+
if (commands.rawArgs.indexOf('-O1') > -1) {
111+
commands.O1 = findArgumentTo('-O1', commands.rawArgs, commands.args);
112+
}
113+
114+
if (commands.rawArgs.indexOf('-O2') > -1) {
115+
commands.O2 = findArgumentTo('-O2', commands.rawArgs, commands.args);
116+
}
117+
118+
// If no sensible data passed in just print help and exit
119+
var fromStdin = !process.env.__DIRECT__ && !process.stdin.isTTY;
120+
if (!fromStdin && commands.args.length === 0) {
121+
commands.outputHelp();
122+
return 0;
123+
}
124+
125+
// Now coerce commands into CleanCSS configuration...
126+
var debugMode = commands.debug;
127+
var options = {
128+
compatibility: commands.compatibility,
129+
format: commands.format,
130+
inline: commands.inline || 'local',
131+
inlineTimeout: commands.inlineTimeout * 1000,
132+
level: commands.O0 || commands.O1 || commands.O2 ?
133+
{ '0': commands.O0, '1': commands.O1, '2': commands.O2 } :
134+
undefined,
135+
output: commands.output,
136+
rebase: commands.skipRebase ? false : true,
137+
rebaseTo: ('output' in commands) && commands.output.length > 0 ? path.dirname(path.resolve(commands.output)) : process.cwd(),
138+
sourceMap: commands.sourceMap,
139+
sourceMapInlineSources: commands.sourceMapInlineSources
140+
};
141+
142+
if (options.sourceMap && !options.output) {
143+
outputFeedback(['Source maps will not be built because you have not specified an output file.'], true);
144+
options.sourceMap = false;
145+
}
146+
147+
// ... and do the magic!
148+
if (commands.args.length > 0) {
149+
minify(commands.args);
150+
} else {
151+
var stdin = process.openStdin();
152+
stdin.setEncoding('utf-8');
153+
var data = '';
154+
stdin.on('data', function (chunk) {
155+
data += chunk;
156+
});
157+
stdin.on('end', function () {
158+
minify(data);
159+
});
160+
}
161+
162+
function findArgumentTo(option, rawArgs, args) {
163+
var value = true;
164+
var optionAt = rawArgs.indexOf(option);
165+
var nextOption = rawArgs[optionAt + 1];
166+
var looksLikePath;
167+
var asArgumentAt;
168+
169+
if (!nextOption) {
170+
return value;
171+
}
172+
173+
looksLikePath = nextOption.indexOf('.css') > -1 ||
174+
/\//.test(nextOption) ||
175+
/\\[^\-]/.test(nextOption) ||
176+
/^https?:\/\//.test(nextOption);
177+
asArgumentAt = args.indexOf(nextOption);
178+
179+
if (!looksLikePath) {
180+
value = nextOption;
181+
}
182+
183+
if (!looksLikePath && asArgumentAt > -1) {
184+
args.splice(asArgumentAt, 1);
185+
}
186+
187+
return value;
188+
}
189+
190+
function minify(data) {
191+
new CleanCSS(options).minify(data, function (errors, minified) {
192+
if (debugMode) {
193+
console.error('Original: %d bytes', minified.stats.originalSize);
194+
console.error('Minified: %d bytes', minified.stats.minifiedSize);
195+
console.error('Efficiency: %d%', ~~(minified.stats.efficiency * 10000) / 100.0);
196+
console.error('Time spent: %dms', minified.stats.timeSpent);
197+
198+
if (minified.inlinedStylesheets.length > 0) {
199+
console.error('Inlined stylesheets:');
200+
minified.inlinedStylesheets.forEach(function (uri) {
201+
console.error('- %s', uri);
202+
});
203+
}
204+
}
205+
206+
outputFeedback(minified.errors, true);
207+
outputFeedback(minified.warnings);
208+
209+
if (minified.errors.length > 0) {
210+
process.exit(1);
211+
}
212+
213+
if (minified.sourceMap) {
214+
var mapFilename = path.basename(options.output) + '.map';
215+
output(minified.styles + '/*# sourceMappingURL=' + mapFilename + ' */');
216+
outputMap(minified.sourceMap, mapFilename);
217+
} else {
218+
output(minified.styles);
219+
}
220+
});
221+
}
222+
223+
function output(minified) {
224+
if (options.output) {
225+
fs.writeFileSync(options.output, minified, 'utf8');
226+
} else {
227+
process.stdout.write(minified);
228+
}
229+
}
230+
231+
function outputMap(sourceMap, mapFilename) {
232+
var mapPath = path.join(path.dirname(options.output), mapFilename);
233+
fs.writeFileSync(mapPath, sourceMap.toString(), 'utf-8');
234+
}
235+
236+
function outputFeedback(messages, isError) {
237+
var prefix = isError ? '\x1B[31mERROR\x1B[39m:' : 'WARNING:';
238+
239+
messages.forEach(function (message) {
240+
console.error('%s %s', prefix, message);
241+
});
242+
}

package.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "clean-css-cli",
3+
"version": "4.0.0-rc0",
4+
"description": "A command-line interface to clean-css CSS optimization library",
5+
"scripts": {
6+
"check": "jshint ./bin/cleancss .",
7+
"prepublish": "npm run check",
8+
"test": "vows"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/jakubpawlowicz/clean-css-cli.git"
13+
},
14+
"keywords": [
15+
"css",
16+
"optimizer",
17+
"minifier"
18+
],
19+
"bin": {
20+
"cleancss": "./bin/cleancss"
21+
},
22+
"author": "Jakub Pawlowicz <contact@jakubpawlowicz.com> (http://twitter.com/jakubpawlowicz)",
23+
"license": "MIT",
24+
"bugs": {
25+
"url": "https://github.com/jakubpawlowicz/clean-css-cli/issues"
26+
},
27+
"homepage": "https://github.com/jakubpawlowicz/clean-css-cli#readme",
28+
"dependencies": {
29+
"clean-css": "jakubpawlowicz/clean-css#fdf123b",
30+
"commander": "2.x"
31+
},
32+
"devDependencies": {
33+
"http-proxy": "1.x",
34+
"jshint": "2.x",
35+
"source-map": "0.5.x",
36+
"vows": "0.8.x"
37+
},
38+
"engines": {
39+
"node": ">= 4.0"
40+
}
41+
}

0 commit comments

Comments
 (0)