From 8138b72c221d7e2e482bb98eee84369c76d234d4 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowicz Date: Sun, 21 Jun 2015 12:26:50 +0100 Subject: [PATCH] Extracts 'merge non adjacent by selector' optimization into a module. --- lib/selectors/advanced.js | 73 +----------------- .../merge-non-adjacent-by-selector.js | 76 +++++++++++++++++++ .../merge-non-adjacent-by-selector-test.js | 33 ++++++++ 3 files changed, 111 insertions(+), 71 deletions(-) create mode 100644 lib/selectors/merge-non-adjacent-by-selector.js create mode 100644 test/selectors/merge-non-adjacent-by-selector-test.js diff --git a/lib/selectors/advanced.js b/lib/selectors/advanced.js index cdfa961f..7c806f37 100644 --- a/lib/selectors/advanced.js +++ b/lib/selectors/advanced.js @@ -11,6 +11,7 @@ var stringifySelectors = require('../stringifier/one-time').selectors; var removeDuplicates = require('./remove-duplicates'); var mergeAdjacent = require('./merge-adjacent'); var reduceNonAdjacent = require('./reduce-non-adjacent'); +var mergeNonAdjacentBySelector = require('./merge-non-adjacent-by-selector'); function AdvancedOptimizer(options, context) { this.options = options; @@ -29,76 +30,6 @@ AdvancedOptimizer.prototype.isSpecial = function (selector) { return this.options.compatibility.selectors.special.test(selector); }; -AdvancedOptimizer.prototype.mergeNonAdjacentBySelector = function (tokens) { - var allSelectors = {}; - var repeatedSelectors = []; - var i; - - for (i = tokens.length - 1; i >= 0; i--) { - if (tokens[i][0] != 'selector') - continue; - if (tokens[i][2].length === 0) - continue; - - var selector = stringifySelectors(tokens[i][1]); - allSelectors[selector] = [i].concat(allSelectors[selector] || []); - - if (allSelectors[selector].length == 2) - repeatedSelectors.push(selector); - } - - for (i = repeatedSelectors.length - 1; i >= 0; i--) { - var positions = allSelectors[repeatedSelectors[i]]; - - selectorIterator: - for (var j = positions.length - 1; j > 0; j--) { - var positionOne = positions[j - 1]; - var tokenOne = tokens[positionOne]; - var positionTwo = positions[j]; - var tokenTwo = tokens[positionTwo]; - - directionIterator: - for (var direction = 1; direction >= -1; direction -= 2) { - var topToBottom = direction == 1; - var from = topToBottom ? positionOne + 1 : positionTwo - 1; - var to = topToBottom ? positionTwo : positionOne; - var delta = topToBottom ? 1 : -1; - var moved = topToBottom ? tokenOne : tokenTwo; - var target = topToBottom ? tokenTwo : tokenOne; - var movedProperties = extractProperties(moved); - var joinAt; - - while (from != to) { - var traversedProperties = extractProperties(tokens[from]); - from += delta; - - // traversed then moved as we move selectors towards the start - var reorderable = topToBottom ? - canReorder(movedProperties, traversedProperties) : - canReorder(traversedProperties, movedProperties); - - if (!reorderable && !topToBottom) - continue selectorIterator; - if (!reorderable && topToBottom) - continue directionIterator; - } - - if (topToBottom) { - joinAt = [moved[2].length]; - Array.prototype.push.apply(moved[2], target[2]); - target[2] = moved[2]; - } else { - joinAt = [target[2].length]; - Array.prototype.push.apply(target[2], moved[2]); - } - - optimizeProperties(target[1], target[2], joinAt, true, this.options, this.validator); - moved[2] = []; - } - } - } -}; - function isBemElement(token) { var asString = stringifySelectors(token[1]); return asString.indexOf('__') > -1 || asString.indexOf('--') > -1; @@ -604,7 +535,7 @@ AdvancedOptimizer.prototype.optimize = function (tokens) { mergeAdjacent(tokens, self.options, self.validator); reduceNonAdjacent(tokens, self.options, self.validator); - self.mergeNonAdjacentBySelector(tokens); + mergeNonAdjacentBySelector(tokens, self.options, self.validator); self.mergeNonAdjacentByBody(tokens); if (self.options.restructuring && withRestructuring) { diff --git a/lib/selectors/merge-non-adjacent-by-selector.js b/lib/selectors/merge-non-adjacent-by-selector.js new file mode 100644 index 00000000..28924b94 --- /dev/null +++ b/lib/selectors/merge-non-adjacent-by-selector.js @@ -0,0 +1,76 @@ +var optimizeProperties = require('../properties/optimizer'); +var stringifySelectors = require('../stringifier/one-time').selectors; +var extractProperties = require('./extractor'); +var canReorder = require('./reorderable').canReorder; + +function mergeNonAdjacentBySelector(tokens, options, validator) { + var allSelectors = {}; + var repeatedSelectors = []; + var i; + + for (i = tokens.length - 1; i >= 0; i--) { + if (tokens[i][0] != 'selector') + continue; + if (tokens[i][2].length === 0) + continue; + + var selector = stringifySelectors(tokens[i][1]); + allSelectors[selector] = [i].concat(allSelectors[selector] || []); + + if (allSelectors[selector].length == 2) + repeatedSelectors.push(selector); + } + + for (i = repeatedSelectors.length - 1; i >= 0; i--) { + var positions = allSelectors[repeatedSelectors[i]]; + + selectorIterator: + for (var j = positions.length - 1; j > 0; j--) { + var positionOne = positions[j - 1]; + var tokenOne = tokens[positionOne]; + var positionTwo = positions[j]; + var tokenTwo = tokens[positionTwo]; + + directionIterator: + for (var direction = 1; direction >= -1; direction -= 2) { + var topToBottom = direction == 1; + var from = topToBottom ? positionOne + 1 : positionTwo - 1; + var to = topToBottom ? positionTwo : positionOne; + var delta = topToBottom ? 1 : -1; + var moved = topToBottom ? tokenOne : tokenTwo; + var target = topToBottom ? tokenTwo : tokenOne; + var movedProperties = extractProperties(moved); + var joinAt; + + while (from != to) { + var traversedProperties = extractProperties(tokens[from]); + from += delta; + + // traversed then moved as we move selectors towards the start + var reorderable = topToBottom ? + canReorder(movedProperties, traversedProperties) : + canReorder(traversedProperties, movedProperties); + + if (!reorderable && !topToBottom) + continue selectorIterator; + if (!reorderable && topToBottom) + continue directionIterator; + } + + if (topToBottom) { + joinAt = [moved[2].length]; + Array.prototype.push.apply(moved[2], target[2]); + target[2] = moved[2]; + } else { + joinAt = [target[2].length]; + Array.prototype.push.apply(target[2], moved[2]); + } + + optimizeProperties(target[1], target[2], joinAt, true, options, validator); + moved[2] = []; + } + } + } +} + +module.exports = mergeNonAdjacentBySelector; diff --git a/test/selectors/merge-non-adjacent-by-selector-test.js b/test/selectors/merge-non-adjacent-by-selector-test.js new file mode 100644 index 00000000..bc13298b --- /dev/null +++ b/test/selectors/merge-non-adjacent-by-selector-test.js @@ -0,0 +1,33 @@ +var vows = require('vows'); +var optimizerContext = require('../test-helper').optimizerContext; + +vows.describe('merge non djacent by selector') + .addBatch( + optimizerContext('advanced on', { + 'up': [ + '.one{color:red}.two{color:#fff}.one{font-weight:400}', + '.one{color:red;font-weight:400}.two{color:#fff}' + ], + 'down': [ + '.one{color:red}.two{font-weight:700}.one{font-weight:400}', + '.two{font-weight:700}.one{color:red;font-weight:400}' + ] + // 'up - blocked': [ + // '.one{color:red;with:100%}.two{display:inline-block;width:10px}.one{font-weight:400;display:block}', + // '.one{color:red;with:100%}.two{display:inline-block;width:10px}.one{font-weight:400;display:block}' + // ] + }) + ) + .addBatch( + optimizerContext('advanced off', { + 'up': [ + '.one{color:red}.two{color:#fff}.one{font-weight:400}', + '.one{color:red}.two{color:#fff}.one{font-weight:400}' + ], + 'down': [ + '.one{color:red}.two{font-weight:700}.one{font-weight:400}', + '.one{color:red}.two{font-weight:700}.one{font-weight:400}' + ] + }, { advanced: false }) + ) + .export(module); -- 2.34.1