var mergeAdjacent = require('./merge-adjacent');
var reduceNonAdjacent = require('./reduce-non-adjacent');
var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector');
+var mergeNonAdjacentByBody = require('./merge-non-adjacent-by-body');
function AdvancedOptimizer(options, context) {
this.options = options;
this.validator = context.validator;
}
-function unsafeSelector(value) {
- return /\.|\*| :/.test(value);
-}
-
function naturalSorter(a, b) {
return a > b;
}
return this.options.compatibility.selectors.special.test(selector);
};
-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;
-
- for (var i = tokens.length - 1; i >= 0; i--) {
- var token = tokens[i];
- if (token[0] != 'selector')
- continue;
-
- 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);
-
- oldToken[2] = [];
- candidates[stringifyBody(token[2])] = null;
- }
-
- candidates[stringifyBody(token[2])] = token;
- }
-};
-
AdvancedOptimizer.prototype.restructure = function (tokens) {
var movableTokens = {};
var movedProperties = [];
reduceNonAdjacent(tokens, self.options, self.validator);
mergeNonAdjacentBySelector(tokens, self.options, self.validator);
- self.mergeNonAdjacentByBody(tokens);
+ mergeNonAdjacentByBody(tokens, self.options);
if (self.options.restructuring && withRestructuring) {
self.restructure(tokens);
--- /dev/null
+var stringifyBody = require('../stringifier/one-time').body;
+var stringifySelectors = require('../stringifier/one-time').selectors;
+var cleanUpSelectors = require('./clean-up').selectors;
+var isSpecial = require('./is-special');
+
+function unsafeSelector(value) {
+ return /\.|\*| :/.test(value);
+}
+
+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];
+ }
+}
+
+function mergeNonAdjacentByBody(tokens, options) {
+ var candidates = {};
+ var adjacentSpace = options.compatibility.selectors.adjacentSpace;
+
+ for (var i = tokens.length - 1; i >= 0; i--) {
+ var token = tokens[i];
+ if (token[0] != 'selector')
+ continue;
+
+ if (token[2].length > 0 && (!options.semanticMerging && unsafeSelector(stringifySelectors(token[1]))))
+ candidates = {};
+
+ if (token[2].length > 0 && options.semanticMerging && isBemElement(token))
+ removeAnyUnsafeElements(token, candidates);
+
+ var oldToken = candidates[stringifyBody(token[2])];
+ if (oldToken && !isSpecial(options, stringifySelectors(token[1])) && !isSpecial(options, stringifySelectors(oldToken[1]))) {
+ token[1] = cleanUpSelectors(oldToken[1].concat(token[1]), false, adjacentSpace);
+
+ oldToken[2] = [];
+ candidates[stringifyBody(token[2])] = null;
+ }
+
+ candidates[stringifyBody(token[2])] = token;
+ }
+}
+
+module.exports = mergeNonAdjacentByBody;
]
})
)
- .addBatch(
- optimizerContext('same bodies', {
- 'of two non-adjacent selectors': [
- '.one{color:red}.two{color:#00f}.three{color:red}',
- '.one{color:red}.two{color:#00f}.three{color:red}'
- ],
- 'of two adjacent single selectors': [
- '.one{color:red}.two{color:red}',
- '.one,.two{color:red}'
- ],
- 'of three adjacent complex, multiple selectors': [
- '.one{color:red}#two.three{color:red}.four>.five{color:red}',
- '#two.three,.four>.five,.one{color:red}'
- ],
- 'with repeated selectors': [
- '#zero>p,.one,.two{color:red}.two,#zero>p,.three{color:red}',
- '#zero>p,.one,.three,.two{color:red}'
- ],
- 'of element selectors': [
- 'p{color:red}a{color:#000}div{color:red}',
- 'div,p{color:red}a{color:#000}'
- ],
- 'of element selectors inside @media': [
- '@media screen{p{color:red}a{color:#000}div{color:red}}',
- '@media screen{div,p{color:red}a{color:#000}}'
- ],
- 'of element selectors with a class selector in between': [
- 'p{color:red}.a{color:#000}div{color:red}',
- 'p{color:red}.a{color:#000}div{color:red}'
- ],
- 'of element selectors with an empty class selector in between': [
- 'p{color:red}.a{}div{color:red}',
- 'div,p{color:red}'
- ]
- })
- )
- .addBatch(
- optimizerContext('same bodies - IE8 compat', {
- 'of two supported selectors': [
- '.one:first-child{color:red}.two>.three{color:red}',
- '.one:first-child,.two>.three{color:red}'
- ],
- 'of supported and unsupported selector': [
- '.one:first-child{color:red}.two:last-child{color:red}',
- '.one:first-child{color:red}.two:last-child{color:red}'
- ],
- 'of two unsupported selectors': [
- '.one:nth-child(5){color:red}.two:last-child{color:red}',
- '.one:nth-child(5){color:red}.two:last-child{color:red}'
- ]
- }, { compatibility: 'ie8' })
- )
- .addBatch(
- optimizerContext('same bodies - IE7 compat', {
- 'of two supported selectors': [
- '.one{color:red}.two>.three{color:red}',
- '.one,.two>.three{color:red}'
- ],
- 'of supported and unsupported selector': [
- '.one{color:red}.two:last-child{color:red}',
- '.one{color:red}.two:last-child{color:red}'
- ],
- 'of two unsupported selectors': [
- '.one:before{color:red}.two:last-child{color:red}',
- '.one:before{color:red}.two:last-child{color:red}'
- ]
- }, { compatibility: 'ie7' })
- )
- .addBatch(
- optimizerContext('same bodies - +adjacentSpace', {
- 'of two supported selectors': [
- '.one{color:red}.two + nav{color:red}',
- '.one,.two+ nav{color:red}'
- ]
- }, { compatibility: { selectors: { adjacentSpace: true } } })
- )
.addBatch(
optimizerContext('units - IE8 compatibility', {
'rems': [
]
}, { 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('@media', {
'empty': [
'.one{color:red}.one{font-weight:700}.one{font-size:12px}',
'.one{color:red;font-weight:700;font-size:12px}'
],
+ 'of three adjacent complex, multiple selectors': [
+ '.one{color:red}#two.three{color:red}.four>.five{color:red}',
+ '#two.three,.four>.five,.one{color:red}'
+ ],
'of two adjacent single, complex selectors': [
'#box>.one{color:red}#box>.one{font-weight:700}',
'#box>.one{color:red;font-weight:700}'
--- /dev/null
+var vows = require('vows');
+var optimizerContext = require('../test-helper').optimizerContext;
+
+vows.describe('merge non djacent by body')
+ .addBatch(
+ optimizerContext('advanced on', {
+ 'of two non-adjacent selectors': [
+ '.one{color:red}.two{color:#00f}.three{color:red}',
+ '.one{color:red}.two{color:#00f}.three{color:red}'
+ ],
+ 'with repeated selectors': [
+ '#zero>p,.one,.two{color:red}.two,#zero>p,.three{color:red}',
+ '#zero>p,.one,.three,.two{color:red}'
+ ],
+ 'of element selectors': [
+ 'p{color:red}a{color:#000}div{color:red}',
+ 'div,p{color:red}a{color:#000}'
+ ],
+ 'of element selectors inside @media': [
+ '@media screen{p{color:red}a{color:#000}div{color:red}}',
+ '@media screen{div,p{color:red}a{color:#000}}'
+ ],
+ 'of element selectors with a class selector in between': [
+ 'p{color:red}.a{color:#000}div{color:red}',
+ 'p{color:red}.a{color:#000}div{color:red}'
+ ],
+ 'of element selectors with an empty class selector in between': [
+ 'p{color:red}.a{}div{color:red}',
+ 'div,p{color:red}'
+ ]
+ })
+ )
+ .addBatch(
+ optimizerContext('advanced off', {
+ 'with repeated selectors': [
+ '#zero>p,.one,.two{color:red}#zero>p,.three,.two{color:red}',
+ '#zero>p,.one,.two{color:red}#zero>p,.three,.two{color:red}'
+ ],
+ 'of element selectors': [
+ 'p{color:red}a{color:#000}div{color:red}',
+ 'p{color:red}a{color:#000}div{color:red}'
+ ],
+ 'of element selectors inside @media': [
+ '@media screen{p{color:red}a{color:#000}div{color:red}}',
+ '@media screen{p{color:red}a{color:#000}div{color:red}}'
+ ]
+ }, { advanced: false })
+ )
+ .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('IE8 compatibility', {
+ 'of two supported selectors': [
+ '.one:first-child{color:red}.two>.three{color:red}',
+ '.one:first-child,.two>.three{color:red}'
+ ],
+ 'of supported and unsupported selector': [
+ '.one:first-child{color:red}.two:last-child{color:red}',
+ '.one:first-child{color:red}.two:last-child{color:red}'
+ ],
+ 'of two unsupported selectors': [
+ '.one:nth-child(5){color:red}.two:last-child{color:red}',
+ '.one:nth-child(5){color:red}.two:last-child{color:red}'
+ ]
+ }, { compatibility: 'ie8' })
+ )
+ .addBatch(
+ optimizerContext('IE7 compatibility', {
+ 'of two supported selectors': [
+ '.one{color:red}.two>.three{color:red}',
+ '.one,.two>.three{color:red}'
+ ],
+ 'of supported and unsupported selector': [
+ '.one{color:red}.two:last-child{color:red}',
+ '.one{color:red}.two:last-child{color:red}'
+ ],
+ 'of two unsupported selectors': [
+ '.one:before{color:red}.two:last-child{color:red}',
+ '.one:before{color:red}.two:last-child{color:red}'
+ ]
+ }, { compatibility: 'ie7' })
+ )
+ .addBatch(
+ optimizerContext('+adjacentSpace', {
+ 'of two supported selectors': [
+ '.one{color:red}.two + nav{color:red}',
+ '.one,.two+ nav{color:red}'
+ ]
+ }, { compatibility: { selectors: { adjacentSpace: true } } })
+ )
+ .export(module);