From 84c6e9bc04fe3f4733533c155598528e53d54696 Mon Sep 17 00:00:00 2001 From: Tamas Besenyei Date: Thu, 19 Mar 2020 12:58:46 +0100 Subject: [PATCH 1/3] implement chain selector support --- ...enerate-descendant-pieces-from-selector.js | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/generate-descendant-pieces-from-selector.js b/lib/generate-descendant-pieces-from-selector.js index 42ddcdf..19a8b84 100644 --- a/lib/generate-descendant-pieces-from-selector.js +++ b/lib/generate-descendant-pieces-from-selector.js @@ -4,6 +4,21 @@ // We could almost use `/\b\s(?![><+~][\s]+?)/` to split the selector but this doesn't work with attribute selectors var RE_SELECTOR_DESCENDANT_SPLIT = (/(.*?(?:(?:\([^\)]+\)|\[[^\]]+\]|(?![><+~\s]).)+)(?:(?:(?:\s(?!>>))|(?:\t(?!>>))|(?:\s?>>\s?))(?!\s+))(?![><+~][\s]+?))/); +// Separate selector to classes and ids +var RE_SELECTOR_SEPARATOR = /(([\.#]?)[^\.#]+)/g; + +// Helper function to get all combination of a text array +var getCombinations = function(strings) { + var result = []; + var f = function(prefix, strings) { + for (var i = 0; i < strings.length; i++) { + result.push(prefix + strings[i]); + f(prefix + strings[i], strings.slice(i + 1)); + } + } + f('', strings); + return result; +} var generateDescendantPiecesFromSelector = function(selector) { return selector.split(RE_SELECTOR_DESCENDANT_SPLIT) @@ -17,7 +32,19 @@ var generateDescendantPiecesFromSelector = function(selector) { // Trim whitespace which would be a normal descendant selector // and trim off the CSS4 descendant `>>` into a normal descendant selector return piece.trim().replace(/\s*?>>\s*?/g, ''); - }); + }) + .reduce(function(result, piece) { + if (piece.indexOf(' ') !== -1) { + result.push(piece); + return result; + } + // a.b#c => [a, .b, #c] + var pieces = piece.match(RE_SELECTOR_SEPARATOR); + // [a, .b, #c] => [a, a.b, a.b#c, a#c, .b, .b#c, #c] + var combinations = getCombinations(pieces); + result = result.concat(combinations); + return result; + }, []); }; module.exports = generateDescendantPiecesFromSelector; From ac1db561081fbd0e75d68a7dc40e21d56e8ab7d2 Mon Sep 17 00:00:00 2001 From: Tamas Besenyei Date: Thu, 19 Mar 2020 12:59:43 +0100 Subject: [PATCH 2/3] add tests for chain selector support --- test/fixtures/chained-selector.css | 30 +++++++++++++++++++++ test/fixtures/chained-selector.expected.css | 19 +++++++++++++ test/test.js | 1 + 3 files changed, 50 insertions(+) create mode 100644 test/fixtures/chained-selector.css create mode 100644 test/fixtures/chained-selector.expected.css diff --git a/test/fixtures/chained-selector.css b/test/fixtures/chained-selector.css new file mode 100644 index 0000000..642cb15 --- /dev/null +++ b/test/fixtures/chained-selector.css @@ -0,0 +1,30 @@ +button { + --width-small: 50px; +} + +button.custom { + --width-small: 100px; + --width-medium: 200px; + --width-large: 300px; + --font-size: 12px; +} + +button.rounded.small { + width: var(--width-small); +} + +button.custom.large { + width: var(--width-large); +} + +button.rounded.custom.medium { + width: var(--width-medium); +} + +button#id.rounded.custom.medium { + width: var(--width-medium); +} + +button.small.pill.custom { + width: var(--width-small); +} diff --git a/test/fixtures/chained-selector.expected.css b/test/fixtures/chained-selector.expected.css new file mode 100644 index 0000000..f8d1021 --- /dev/null +++ b/test/fixtures/chained-selector.expected.css @@ -0,0 +1,19 @@ +button.rounded.small { + width: 50px; +} + +button.custom.large { + width: 300px; +} + +button.rounded.custom.medium { + width: 200px; +} + +button#id.rounded.custom.medium { + width: 200px; +} + +button.small.pill.custom { + width: 100px; +} diff --git a/test/test.js b/test/test.js index b0867b0..3d83d9b 100644 --- a/test/test.js +++ b/test/test.js @@ -129,6 +129,7 @@ describe('postcss-css-variables', function() { test('should work with `!important` variable declarations', 'important-variable-declaration'); + test('should work with chained selectors', 'chained-selector'); describe('with at-rules', function() { From 472a4052962396c9520023fd596c2876b59b28df Mon Sep 17 00:00:00 2001 From: Tamas Besenyei Date: Thu, 19 Mar 2020 13:02:19 +0100 Subject: [PATCH 3/3] fix indentation --- ...enerate-descendant-pieces-from-selector.js | 24 +++++++++---------- test/fixtures/chained-selector.css | 20 ++++++++-------- test/fixtures/chained-selector.expected.css | 10 ++++---- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/generate-descendant-pieces-from-selector.js b/lib/generate-descendant-pieces-from-selector.js index 19a8b84..1524578 100644 --- a/lib/generate-descendant-pieces-from-selector.js +++ b/lib/generate-descendant-pieces-from-selector.js @@ -9,15 +9,15 @@ var RE_SELECTOR_SEPARATOR = /(([\.#]?)[^\.#]+)/g; // Helper function to get all combination of a text array var getCombinations = function(strings) { - var result = []; - var f = function(prefix, strings) { - for (var i = 0; i < strings.length; i++) { - result.push(prefix + strings[i]); - f(prefix + strings[i], strings.slice(i + 1)); - } - } - f('', strings); - return result; + var result = []; + var f = function(prefix, strings) { + for (var i = 0; i < strings.length; i++) { + result.push(prefix + strings[i]); + f(prefix + strings[i], strings.slice(i + 1)); + } + } + f('', strings); + return result; } var generateDescendantPiecesFromSelector = function(selector) { @@ -41,10 +41,10 @@ var generateDescendantPiecesFromSelector = function(selector) { // a.b#c => [a, .b, #c] var pieces = piece.match(RE_SELECTOR_SEPARATOR); // [a, .b, #c] => [a, a.b, a.b#c, a#c, .b, .b#c, #c] - var combinations = getCombinations(pieces); + var combinations = getCombinations(pieces); result = result.concat(combinations); - return result; - }, []); + return result; + }, []); }; module.exports = generateDescendantPiecesFromSelector; diff --git a/test/fixtures/chained-selector.css b/test/fixtures/chained-selector.css index 642cb15..19b9748 100644 --- a/test/fixtures/chained-selector.css +++ b/test/fixtures/chained-selector.css @@ -1,30 +1,30 @@ button { - --width-small: 50px; + --width-small: 50px; } button.custom { - --width-small: 100px; - --width-medium: 200px; - --width-large: 300px; - --font-size: 12px; + --width-small: 100px; + --width-medium: 200px; + --width-large: 300px; + --font-size: 12px; } button.rounded.small { - width: var(--width-small); + width: var(--width-small); } button.custom.large { - width: var(--width-large); + width: var(--width-large); } button.rounded.custom.medium { - width: var(--width-medium); + width: var(--width-medium); } button#id.rounded.custom.medium { - width: var(--width-medium); + width: var(--width-medium); } button.small.pill.custom { - width: var(--width-small); + width: var(--width-small); } diff --git a/test/fixtures/chained-selector.expected.css b/test/fixtures/chained-selector.expected.css index f8d1021..7a53acf 100644 --- a/test/fixtures/chained-selector.expected.css +++ b/test/fixtures/chained-selector.expected.css @@ -1,19 +1,19 @@ button.rounded.small { - width: 50px; + width: 50px; } button.custom.large { - width: 300px; + width: 300px; } button.rounded.custom.medium { - width: 200px; + width: 200px; } button#id.rounded.custom.medium { - width: 200px; + width: 200px; } button.small.pill.custom { - width: 100px; + width: 100px; }