* Re-enables `background-(clip|origin|size)` merging with `background` shorthand.
* Simplifies URL rebasing with a single `rebaseTo` option in API or inferred from `--output` in CLI.
* Splits `inliner` option into `inlineRequest` and `inlineTimeout`.
-* Fixed issue [#209](https://github.com/jakubpawlowicz/clean-css/issues/209) - adds output formatting via `beautify` flag.
+* Fixed issue [#209](https://github.com/jakubpawlowicz/clean-css/issues/209) - adds output formatting via `format` flag.
* Fixed issue [#432](https://github.com/jakubpawlowicz/clean-css/issues/432) - adds URLs normalization.
* Fixed issue [#460](https://github.com/jakubpawlowicz/clean-css/issues/460) - unescaped semicolon in selector.
* Fixed issue [#657](https://github.com/jakubpawlowicz/clean-css/issues/657) - adds property name validation.
* moves `roundingPrecision` and `specialComments` to level 1 optimizations options, see examples below;
* moves `mediaMerging`, `restructuring`, `semanticMerging`, and `shorthandCompacting` to level 2 optimizations options, see examples below;
* level 1 optimizations are the new default, up to 3.x it was level 2;
-* `--keep-line-breaks` / `keepBreaks` option is replaced with `--beautify keep-breaks` / `{ beautify: 'keep-breaks' }` to ease transition.
+* `--keep-line-breaks` / `keepBreaks` option is replaced with `--format keep-breaks` / `{ format: 'keep-breaks' }` to ease transition.
Please note this list is not final. You are more than welcome to comment these changes in [4.0 release discussion](https://github.com/jakubpawlowicz/clean-css/issues/842) thread.
-h, --help output usage information
-v, --version output the version number
- -b, --beautify [options] Controls output CSS formatting, see examples below
-c, --compatibility [ie7|ie8] Force compatibility mode (see Readme for advanced examples)
-d, --debug Shows debug information (minification time & compression efficiency)
+ -f, --format [options] Controls output formatting, see examples below
-o, --output [output-file] Use [output-file] as output instead of STDOUT
-O <n> [optimizations] Turn on level <n> optimizations; optionally accepts a list of fine-grained options, defaults to `1`, see examples below
--inline [rules] Enables inlining for listed sources (defaults to `local`)
Please note there is a difference between passing in a concatenated string and letting clean-css do the job. The former will discard `@import` statements appearing [not at the beginning](https://developer.mozilla.org/en-US/docs/Web/CSS/@import) of the string, while the latter will discard only those appearing not at the beginning of any of the files. Because of this behavior, the latter way (see examples above) is recommended.
-Beautify options:
+Formatting options:
```bash
-cleancss --beautify one.css
-cleancss --beautify 'indentBy:1;indentWith:tab' one.css
-cleancss --beautify 'breaks:afterBlockBegins=off;spaces:aroundSelectorRelation=off' one.css
+cleancss --format one.css
+cleancss --format 'indentBy:1;indentWith:tab' one.css
+cleancss --format 'breaks:afterBlockBegins=off;spaces:aroundSelectorRelation=off' one.css
# `breaks` controls where to insert breaks
# `afterAtRule` controls if a line break comes after an at-rule; e.g. `@charset`; defaults to `on` (alias to `true`)
# `afterBlockBegins` controls if a line break comes after a block begins; e.g. `@media`; defaults to `on`
`new CleanCSS(options)` with the following options available:
* `aggressiveMerging` - set to false to disable aggressive merging of properties.
-* `beautify` - formats output CSS by using indentation and one rule or property per line.
* `compatibility` - enables compatibility mode, see [below for more examples](#how-to-set-a-compatibility-mode)
+* `format` - formats output CSS by using indentation and one rule or property per line.
* `inline` - whether to inline `@import` rules, can be `['all']`, `['local']` (default), `['remote']`, or a blacklisted domain/path e.g. `['!fonts.googleapis.com']`
* `inlineRequest` - an object with [HTTP(S) request options](https://nodejs.org/api/http.html#http_http_request_options_callback) for inlining remote `@import` rules
* `inlineTimeout` - an integer denoting a number of milliseconds after which inlining a remote `@import` fails (defaults to 5000 ms)
* `timeSpent` - time spent on optimizations
* `efficiency` - a ratio of output size to input size (e.g. 25% if content was reduced from 100 bytes to 75 bytes)
-#### How to specify beautify formatting
+#### How to specify formatting
-The `beautify` option can also accept the following options
+The `format` option can accept the following options:
```js
new CleanCSS({
- beautify: {
+ format: {
breaks: { // controls where to insert breaks
afterAtRule: true, // controls if a line break comes after an at-rule; e.g. `@charset`; defaults to `true`
afterBlockBegins: true, // controls if a line break comes after a block begins; e.g. `@media`; defaults to `true`
commands
.version(buildVersion, '-v, --version')
.usage('[options] <source-file ...>')
- .option('-b, --beautify [options]', 'Controls output CSS formatting, see examples below')
.option('-c, --compatibility [ie7|ie8]', 'Force compatibility mode (see Readme for advanced examples)')
.option('-d, --debug', 'Shows debug information (minification time & compression efficiency)')
+ .option('-f, --format [options]', 'Controls output formatting, see examples below')
.option('-o, --output [output-file]', 'Use [output-file] as output instead of STDOUT')
.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)); })
.option('--inline [rules]', 'Enables inlining for listed sources (defaults to `local`)')
console.log(' %> cleancss -o merged-and-minified.css one.css two.css three.css');
console.log(' %> cleancss one.css two.css three.css | gzip -9 -c > merged-minified-and-gzipped.css.gz');
console.log('');
- console.log(' Beautify options:');
- console.log(' %> cleancss --beautify one.css');
- console.log(' %> cleancss --beautify \'indentBy:1;indentWith:tab\' one.css');
- console.log(' %> cleancss --beautify \'breaks:afterBlockBegins=off;spaces:aroundSelectorRelation=off\' one.css');
+ console.log(' Formatting options:');
+ console.log(' %> cleancss --format one.css');
+ console.log(' %> cleancss --format \'indentBy:1;indentWith:tab\' one.css');
+ console.log(' %> cleancss --format \'breaks:afterBlockBegins=off;spaces:aroundSelectorRelation=off\' one.css');
console.log(' %> # `breaks` controls where to insert breaks');
console.log(' %> # `afterAtRule` controls if a line break comes after an at-rule; e.g. `@charset`; defaults to `on` (alias to `true`)');
console.log(' %> # `afterBlockBegins` controls if a line break comes after a block begins; e.g. `@media`; defaults to `on`');
var debugMode = commands.debug;
var options = {
aggressiveMerging: commands.skipAggressiveMerging ? false : true,
- beautify: commands.beautify,
compatibility: commands.compatibility,
+ format: commands.format,
inline: commands.inline || 'local',
inlineTimeout: commands.inlineTimeout * 1000,
level: commands.O0 || commands.O1 || commands.O2 ?
var path = require('path');
var url = require('url');
-var beautifyFrom = require('./options/beautify').beautifyFrom;
+var formatFrom = require('./options/format').formatFrom;
var OptimizationLevel = require('./options/optimization-level').OptimizationLevel;
var optimizationLevelFrom = require('./options/optimization-level').optimizationLevelFrom;
this.options = {
aggressiveMerging: undefined === options.aggressiveMerging ? true : !!options.aggressiveMerging,
- beautify: beautifyFrom(options.beautify),
compatibility: compatibility(options.compatibility),
+ format: formatFrom(options.format),
inline: inlineOptionsFrom(options.inline),
inlineRequest: options.inlineRequest || {},
inlineTimeout: options.inlineTimeout || DEFAULT_TIMEOUT,
var ie7Hack = options.compatibility.selectors.ie7Hack;
var adjacentSpace = options.compatibility.selectors.adjacentSpace;
var spaceAfterClosingBrace = options.compatibility.properties.spaceAfterClosingBrace;
- var beautify = options.beautify;
+ var format = options.format;
var mayHaveCharset = false;
var afterRules = false;
optimizeComment(token, options);
break;
case Token.RULE:
- token[1] = levelOptions.tidySelectors ? tidyRules(token[1], !ie7Hack, adjacentSpace, beautify, context.warnings) : token[1];
+ token[1] = levelOptions.tidySelectors ? tidyRules(token[1], !ie7Hack, adjacentSpace, format, context.warnings) : token[1];
token[1] = token[1].length > 1 ? sortSelectors(token[1], levelOptions.selectorsSortingMethod) : token[1];
optimizeBody(token[2], context);
afterRules = true;
-var Spaces = require('../../options/beautify').Spaces;
+var Spaces = require('../../options/format').Spaces;
var Marker = require('../../tokenizer/marker');
var formatPosition = require('../../utils/format-position');
return isInvalid;
}
-function removeWhitespace(value, beautify) {
+function removeWhitespace(value, format) {
var stripped = [];
var character;
var isNewLineNix;
var roundBracketLevel = 0;
var wasRelation = false;
var wasWhitespace = false;
- var spaceAroundRelation = beautify && beautify.spaces[Spaces.AroundSelectorRelation];
+ var spaceAroundRelation = format && format.spaces[Spaces.AroundSelectorRelation];
var i, l;
for (i = 0, l = value.length; i < l; i++) {
.replace(/="([a-zA-Z][a-zA-Z\d\-_]+)"/g, '=$1');
}
-function tidyRules(rules, removeUnsupported, adjacentSpace, beautify, warnings) {
+function tidyRules(rules, removeUnsupported, adjacentSpace, format, warnings) {
var list = [];
var repeated = [];
continue;
}
- reduced = removeWhitespace(reduced, beautify);
+ reduced = removeWhitespace(reduced, format);
reduced = removeQuotes(reduced);
if (adjacentSpace && reduced.indexOf('nav') > 0) {
return spaceOptions;
}
-function beautifyFrom(source) {
+function formatFrom(source) {
if (source === undefined || source === false) {
return false;
}
module.exports = {
Breaks: Breaks,
Spaces: Spaces,
- beautifyFrom: beautifyFrom
+ formatFrom: formatFrom
};
var lineBreak = require('os').EOL;
var emptyCharacter = '';
-var Breaks = require('../options/beautify').Breaks;
-var Spaces = require('../options/beautify').Spaces;
+var Breaks = require('../options/format').Breaks;
+var Spaces = require('../options/format').Spaces;
var Marker = require('../tokenizer/marker');
var Token = require('../tokenizer/token');
}
function allowsBreak(context, where) {
- return context.beautify && context.beautify.breaks[where];
+ return context.format && context.format.breaks[where];
}
function allowsSpace(context, where) {
- return context.beautify && context.beautify.spaces[where];
+ return context.format && context.format.spaces[where];
}
function openBrace(context, where, needsPrefixSpace) {
- if (context.beautify) {
- context.indentBy += context.beautify.indentBy;
- context.indentWith = context.beautify.indentWith.repeat(context.indentBy);
+ if (context.format) {
+ context.indentBy += context.format.indentBy;
+ context.indentWith = context.format.indentWith.repeat(context.indentBy);
return (needsPrefixSpace && allowsSpace(context, Spaces.BeforeBlockBegins) ? Marker.SPACE : emptyCharacter) +
Marker.OPEN_BRACE +
(allowsBreak(context, where) ? lineBreak : emptyCharacter) +
}
function closeBrace(context, where, beforeBlockEnd, isLast) {
- if (context.beautify) {
- context.indentBy -= context.beautify.indentBy;
- context.indentWith = context.beautify.indentWith.repeat(context.indentBy);
+ if (context.format) {
+ context.indentBy -= context.format.indentBy;
+ context.indentWith = context.format.indentWith.repeat(context.indentBy);
return (allowsBreak(context, Breaks.AfterProperty) || beforeBlockEnd && allowsBreak(context, Breaks.BeforeBlockEnds) ? lineBreak : emptyCharacter) +
context.indentWith +
Marker.CLOSE_BRACE +
}
function colon(context) {
- return context.beautify ?
+ return context.format ?
Marker.COLON + (allowsSpace(context, Spaces.BeforeValue) ? Marker.SPACE : emptyCharacter) :
Marker.COLON;
}
function semicolon(context, where, isLast) {
- return context.beautify ?
+ return context.format ?
Marker.SEMICOLON + (isLast || !allowsBreak(context, where) ? emptyCharacter : lineBreak + context.indentWith) :
Marker.SEMICOLON;
}
function comma(context) {
- return context.beautify ?
+ return context.format ?
Marker.COMMA + (allowsBreak(context, Breaks.BetweenSelectors) ? lineBreak : emptyCharacter) + context.indentWith :
Marker.COMMA;
}
function serializeStyles(tokens, context) {
var serializeContext = {
- beautify: context.options.beautify,
+ format: context.options.format,
indentBy: 0,
indentWith: '',
output: [],
function serializeStylesAndSourceMap(tokens, context) {
var serializeContext = {
- beautify: context.options.beautify,
column: 0,
+ format: context.options.format,
indentBy: 0,
indentWith: '',
inlineSources: context.options.sourceMapInlineSources,
topic: function (data) {
new CleanCSS({
compatibility: isIE7Mode ? 'ie7' : '*',
- beautify: 'keep-breaks',
+ format: 'keep-breaks',
level: {
2: {
restructureRules: true
topic: function (data) {
new CleanCSS({
compatibility: isIE7Mode ? 'ie7' : '*',
- beautify: 'keep-breaks',
+ format: 'keep-breaks',
level: {
2: {
restructureRules: true
'minifying via CLI': {
'topic': function (data) {
exec(
- '__DIRECT__=1 ./bin/cleancss -b keep-breaks -O2 restructureRules:on ' + (isIE7Mode ? '-c ie7 ' : '') + path.join(dir, filename),
+ '__DIRECT__=1 ./bin/cleancss --format keep-breaks -O2 restructureRules:on ' + (isIE7Mode ? '-c ie7 ' : '') + path.join(dir, filename),
{ maxBuffer: 500 * 1024 },
this.callback.bind(null, data)
);
})
})
.addBatch({
- 'beautify': pipedContext('a{color: #f00}', '--beautify', {
+ 'format': pipedContext('a{color: #f00}', '--format', {
'outputs right styles': function (error, stdout) {
assert.equal(stdout, 'a {\n color: red\n}');
}
'@chArSET \'utf-8\';h1{color:red}',
'h1{color:red}'
]
- }, { beautify: 'keep-breaks' })
+ }, { format: 'keep-breaks' })
)
.addBatch(
optimizerContext('line breaks and important comments', {
'/*! some comment */' + lineBreak + lineBreak + '@charset \'utf-8\';' + lineBreak + lineBreak + 'a{display:block}',
'@charset \'utf-8\';' + lineBreak + 'a{display:block}'
]
- }, { beautify: 'keep-breaks', level: { 1: { specialComments: 0 } } })
+ }, { format: 'keep-breaks', level: { 1: { specialComments: 0 } } })
)
.addBatch(
optimizerContext('selectors', {
'a{color:red}p{display:block}',
'a{color:red}' + lineBreak + 'p{display:block}'
]
- }, { beautify: 'keep-breaks', level: 1 })
+ }, { format: 'keep-breaks', level: 1 })
)
.addBatch(
optimizerContext('invalid data tokenization', {
'a{display:block;@apply(--rule1);color:red}',
'a {' + lineBreak + ' display: block;' + lineBreak + ' @apply(--rule1);' + lineBreak + ' color: red' + lineBreak + '}'
]
- }, { beautify: true })
+ }, { format: true })
)
.addBatch(
optimizerContext('custom formatting', {
'@media screen{a{color:red}div{color:#000}}',
'@media screen {' + lineBreak + '\ta {' + lineBreak + '\t\tcolor: red' + lineBreak + '\t}\tdiv {' + lineBreak + '\t\tcolor: #000' + lineBreak + '\t}' + lineBreak + '}'
]
- }, { beautify: { breaks: { afterRuleEnds: false }, indentWith: 'tab', indentBy: 1 } })
+ }, { format: { breaks: { afterRuleEnds: false }, indentWith: 'tab', indentBy: 1 } })
)
.export(module);
var vows = require('vows');
-var beautifyFrom = require('../../lib/options/beautify').beautifyFrom;
+var formatFrom = require('../../lib/options/format').formatFrom;
-vows.describe(beautifyFrom)
+vows.describe(formatFrom)
.addBatch({
'undefined': {
'topic': function () {
- return beautifyFrom(undefined);
+ return formatFrom(undefined);
},
- 'is false': function (beautifyOptions) {
- assert.deepEqual(beautifyOptions, false);
+ 'is false': function (formatOptions) {
+ assert.deepEqual(formatOptions, false);
}
},
'false': {
'topic': function () {
- return beautifyFrom(false);
+ return formatFrom(false);
},
- 'is false': function (beautifyOptions) {
- assert.deepEqual(beautifyOptions, false);
+ 'is false': function (formatOptions) {
+ assert.deepEqual(formatOptions, false);
}
},
'true': {
'topic': function () {
- return beautifyFrom(true);
+ return formatFrom(true);
},
- 'is default': function (beautifyOptions) {
- assert.deepEqual(beautifyOptions, {
+ 'is default': function (formatOptions) {
+ assert.deepEqual(formatOptions, {
breaks: {
afterAtRule: true,
afterBlockBegins: true,
},
'hash': {
'topic': function () {
- return beautifyFrom({ breaks: { afterProperty: false }, indentBy: 1 });
+ return formatFrom({ breaks: { afterProperty: false }, indentBy: 1 });
},
- 'is merged with default': function (beautifyOptions) {
- assert.deepEqual(beautifyOptions, {
+ 'is merged with default': function (formatOptions) {
+ assert.deepEqual(formatOptions, {
breaks: {
afterAtRule: true,
afterBlockBegins: true,
},
'hash with indentBy as string': {
'topic': function () {
- return beautifyFrom({ indentBy: '2' });
+ return formatFrom({ indentBy: '2' });
},
- 'is merged with default': function (beautifyOptions) {
- assert.deepEqual(beautifyOptions, {
+ 'is merged with default': function (formatOptions) {
+ assert.deepEqual(formatOptions, {
breaks: {
afterAtRule: true,
afterBlockBegins: true,
},
'hash with explicit indentWith': {
'topic': function () {
- return beautifyFrom({ indentWith: '\t' });
+ return formatFrom({ indentWith: '\t' });
},
- 'is merged with default': function (beautifyOptions) {
- assert.deepEqual(beautifyOptions, {
+ 'is merged with default': function (formatOptions) {
+ assert.deepEqual(formatOptions, {
breaks: {
afterAtRule: true,
afterBlockBegins: true,
},
'hash with implicit indentWith': {
'topic': function () {
- return beautifyFrom({ indentWith: 'tab' });
+ return formatFrom({ indentWith: 'tab' });
},
- 'is merged with default': function (beautifyOptions) {
- assert.deepEqual(beautifyOptions, {
+ 'is merged with default': function (formatOptions) {
+ assert.deepEqual(formatOptions, {
breaks: {
afterAtRule: true,
afterBlockBegins: true,
},
'string': {
'topic': function () {
- return beautifyFrom('breaks:afterProperty=off;indentBy:3');
+ return formatFrom('breaks:afterProperty=off;indentBy:3');
},
- 'is merged with default': function (beautifyOptions) {
- assert.deepEqual(beautifyOptions, {
+ 'is merged with default': function (formatOptions) {
+ assert.deepEqual(formatOptions, {
breaks: {
afterAtRule: true,
afterBlockBegins: true,
},
'string with indentWith': {
'topic': function () {
- return beautifyFrom('indentWith:tab');
+ return formatFrom('indentWith:tab');
},
- 'is merged with default': function (beautifyOptions) {
- assert.deepEqual(beautifyOptions, {
+ 'is merged with default': function (formatOptions) {
+ assert.deepEqual(formatOptions, {
breaks: {
afterAtRule: true,
afterBlockBegins: true,
},
'string keyword': {
'topic': function () {
- return beautifyFrom('keep-breaks');
+ return formatFrom('keep-breaks');
},
- 'resolves correctly': function (beautifyOptions) {
- assert.deepEqual(beautifyOptions, {
+ 'resolves correctly': function (formatOptions) {
+ assert.deepEqual(formatOptions, {
breaks: {
afterAtRule: true,
afterBlockBegins: true,
},
'with formatting': {
'topic': function () {
- return new CleanCSS({ beautify: 'keep-breaks', level: 2, sourceMap: true }).minify('@media screen { a{color:red} p {color:blue} }div{color:pink}');
+ return new CleanCSS({ format: 'keep-breaks', level: 2, sourceMap: true }).minify('@media screen { a{color:red} p {color:blue} }div{color:pink}');
},
'has 10 mappings': function (minified) {
assert.lengthOf(minified.sourceMap._mappings._array, 10);