-c, --compatibility [ie7|ie8] Force compatibility mode (see Readme for advanced examples)
--source-map Enables building input's source map
--source-map-inline-sources Enables inlining sources inside source map's `sourcesContent` field
+--semantic-merging Enables semantic merging mode by assuming BEM-like content (warning, this may break your styling!)
-d, --debug Shows debug information (minification time & compression efficiency)
```
* `restructuring` - set to false to disable restructuring in advanced optimizations
* `root` - path to **resolve** absolute `@import` rules and **rebase** relative URLs
* `roundingPrecision` - rounding precision; defaults to `2`; `-1` disables rounding
+* `semanticMerging` - set to true to enable semantic merging mode which assumes BEM-like content (default is false as it's highly likely this will break your stylesheets - **use with caution**!)
* `shorthandCompacting` - set to false to skip shorthand compacting (default is true unless sourceMap is set when it's false)
* `sourceMap` - exposes source map under `sourceMap` property, e.g. `new CleanCSS().minify(source).sourceMap` (default is false)
If input styles are a product of CSS preprocessor (LESS, SASS) an input source map can be passed as a string.
.option('-c, --compatibility [ie7|ie8]', 'Force compatibility mode (see Readme for advanced examples)')
.option('--source-map', 'Enables building input\'s source map')
.option('--source-map-inline-sources', 'Enables inlining sources inside source maps')
+ .option('--semantic-merging', 'Enables unsafe mode by assuming BEM-like semantic stylesheets (warning, this may break your styling!)')
.option('-t, --timeout [seconds]', 'Per connection timeout when fetching remote @imports (defaults to 5 seconds)')
.option('-d, --debug', 'Shows debug information (minification time & compression efficiency)');
restructuring: commands.skipRestructuring ? false : true,
root: commands.root,
roundingPrecision: commands.roundingPrecision,
+ semanticMerging: commands.semanticMerging ? true : false,
shorthandCompacting: commands.skipShorthandCompacting ? false : true,
sourceMap: commands.sourceMap,
sourceMapInlineSources: commands.sourceMapInlineSources,
restructuring: undefined === options.restructuring ? true : !!options.restructuring,
root: options.root || process.cwd(),
roundingPrecision: options.roundingPrecision,
+ semanticMerging: undefined === options.semanticMerging ? false : !!options.semanticMerging,
shorthandCompacting: undefined === options.shorthandCompacting ? true : !!options.shorthandCompacting,
sourceMap: options.sourceMap,
sourceMapInlineSources: !!options.sourceMapInlineSources,
}
};
+function isBemElement(token) {
+ var asString = stringifySelectors(token[1]);
+ return asString.indexOf('__') > -1 || asString.indexOf('--') > -1;
+}
+
+function withoutModifier(selector) {
+ return selector.replace(/--[^ ,>\+~:]+/g, '');
+}
+
+function removeAnyUnsafeElements(left, candidates) {
+ var leftSelector = withoutModifier(stringifySelectors(left[1]));
+
+ for (var body in candidates) {
+ var right = candidates[body];
+ var rightSelector = withoutModifier(stringifySelectors(right[1]));
+
+ if (rightSelector.indexOf(leftSelector) > -1 || leftSelector.indexOf(rightSelector) > -1)
+ delete candidates[body];
+ }
+}
+
AdvancedOptimizer.prototype.mergeNonAdjacentByBody = function (tokens) {
var candidates = {};
var adjacentSpace = this.options.compatibility.selectors.adjacentSpace;
if (token[0] != 'selector')
continue;
- if (token[2].length > 0 && unsafeSelector(stringifySelectors(token[1])))
+ if (token[2].length > 0 && (!this.options.semanticMerging && unsafeSelector(stringifySelectors(token[1]))))
candidates = {};
+ if (token[2].length > 0 && this.options.semanticMerging && isBemElement(token))
+ removeAnyUnsafeElements(token, candidates);
+
var oldToken = candidates[stringifyBody(token[2])];
if (oldToken && !this.isSpecial(stringifySelectors(token[1])) && !this.isSpecial(stringifySelectors(oldToken[1]))) {
token[1] = CleanUp.selectors(oldToken[1].concat(token[1]), false, adjacentSpace);
deleteFile('import-inline.min.css.map');
}
})
+ },
+ 'semantic merging': {
+ 'disabled': pipedContext('.a{margin:0}.b{margin:10px;padding:0}.c{margin:0}', '', {
+ 'should output right data': function(error, stdout) {
+ assert.equal(stdout, '.a{margin:0}.b{margin:10px;padding:0}.c{margin:0}');
+ }
+ }),
+ 'enabled': pipedContext('.a{margin:0}.b{margin:10px;padding:0}.c{margin:0}', '--semantic-merging', {
+ 'should output right data': function(error, stdout) {
+ assert.equal(stdout, '.a,.c{margin:0}.b{margin:10px;padding:0}');
+ }
+ })
}
});
'gets right output': function (minified) {
assert.equal(minified.styles, 'div{margin-top:0}.one{margin:0}.two{display:block;margin-top:0}');
}
+ },
+ 'semantic merging - off': {
+ 'topic': function () {
+ return new CleanCSS().minify('.a{margin:0}.b{margin:10px;padding:0}.c{margin:0}');
+ },
+ 'gets right output': function (minified) {
+ assert.equal(minified.styles, '.a{margin:0}.b{margin:10px;padding:0}.c{margin:0}');
+ }
+ },
+ 'semantic merging - on': {
+ 'topic': function () {
+ return new CleanCSS({ semanticMerging: true }).minify('.a{margin:0}.b{margin:10px;padding:0}.c{margin:0}');
+ },
+ 'gets right output': function (minified) {
+ assert.equal(minified.styles, '.a,.c{margin:0}.b{margin:10px;padding:0}');
+ }
}
},
'source map': {
]
}, { advanced: true })
)
+ .addBatch(
+ optimizerContext('selectors - semantic merging mode', {
+ 'simple': [
+ '.a{color:red}.b{color:#000}.c{color:red}',
+ '.a,.c{color:red}.b{color:#000}'
+ ],
+ 'BEM - modifiers #1': [
+ '.block{color:red}.block__element{color:#000}.block__element--modifier{color:red}',
+ '.block{color:red}.block__element{color:#000}.block__element--modifier{color:red}'
+ ],
+ 'BEM - modifiers #2': [
+ '.block1{color:red}.block1__element,.block2{color:#000}.block1__element--modifier{color:red}',
+ '.block1{color:red}.block1__element,.block2{color:#000}.block1__element--modifier{color:red}'
+ ],
+ 'BEM - modifiers #3': [
+ '.block1{color:red}.block1--modifier,.block2{color:#000}.block1--another-modifier{color:red}',
+ '.block1{color:red}.block1--modifier,.block2{color:#000}.block1--another-modifier{color:red}'
+ ],
+ 'BEM - tail merging': [
+ '.block1{color:red}.block1__element{color:#000}.block1__element--modifier{color:red}a{color:red}.block2__element--modifier{color:red}',
+ '.block1{color:red}.block1__element{color:#000}.block1__element--modifier,.block2__element--modifier,a{color:red}'
+ ],
+ 'BEM - two blocks #1': [
+ '.block1__element{color:#000}.block2{color:red}.block2__element{color:#000}.block2__element--modifier{color:red}',
+ '.block1__element,.block2__element{color:#000}.block2,.block2__element--modifier{color:red}'
+ ],
+ 'BEM - two blocks #2': [
+ '.block1__element{color:#000}.block1__element--modifier{color:red}.block2{color:red}.block2__element{color:#000}.block2__element--modifier{color:red}',
+ '.block1__element,.block2__element{color:#000}.block1__element--modifier,.block2,.block2__element--modifier{color:red}'
+ ],
+ 'BEM - complex traversing #1': [
+ '.block1__element{color:#000}.block1__element--modifier{color:red}.block2{color:#000;display:block;width:100%}',
+ '.block1__element{color:#000}.block1__element--modifier{color:red}.block2{color:#000;display:block;width:100%}'
+ // '.block1__element,.block2{color:#000}.block1__element--modifier{color:red}.block2{display:block;width:100%}' - pending #588
+ ]
+ }, { advanced: true, semanticMerging: true })
+ )
.addBatch(
optimizerContext('properties', {
'empty body': [