From 20629caf9120f2adfc854d2d23326fdc04935134 Mon Sep 17 00:00:00 2001 From: Albert Date: Wed, 7 Feb 2024 22:23:23 +1030 Subject: [PATCH] Getting closer --- .../compiler/phases/2-analyze/css/Selector.js | 95 ++++++++++++++----- 1 file changed, 70 insertions(+), 25 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/Selector.js b/packages/svelte/src/compiler/phases/2-analyze/css/Selector.js index 87ada13aa8..75dbdbc5bd 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/Selector.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/Selector.js @@ -149,11 +149,20 @@ export default class Selector { remove_global_pseudo_class(selector); } } + let i = relative_selector.compound.selectors.length; while (i--) { const selector = relative_selector.compound.selectors[i]; + if (!selector.visible) continue + if (selector.use_wrapper.used) continue; + + + console.log(selector, attr) + + selector.use_wrapper.used = true; + if (selector.type === 'PseudoElementSelector' || selector.type === 'PseudoClassSelector') { if (!relative_selector.root && !relative_selector.host) { if (i === 0) code.prependRight(selector.start, attr); @@ -170,9 +179,12 @@ export default class Selector { }; for (const complex_selector of this.selector_list) { - const amount_class_specificity_to_increase = - max_amount_class_specificity_increased - - complex_selector.filter(selector => selector.should_encapsulate).length; + let amount_class_specificity_to_increase = + max_amount_class_specificity_increased - complex_selector + .filter(selector => selector.should_encapsulate) + // .filter(selector => !selector.contains_invisible_selectors) + // .filter(selector => !selector.compound.selectors[0].use_wrapper.used) + .length; complex_selector.map((relative_selector, index) => { if (relative_selector.global) { @@ -183,7 +195,10 @@ export default class Selector { if(relative_selector.should_encapsulate) { encapsulate_block( relative_selector, - index === complex_selector.length - 1 + index === complex_selector + .filter(selector => !selector.contains_invisible_selectors) + // .filter(selector => selector.compound.selectors.some(selector => selector.use_wrapper.used)) + .length - 1 ? attr.repeat(amount_class_specificity_to_increase + 1) : attr ); @@ -880,22 +895,17 @@ class RelativeSelector { /** @type {boolean} */ should_encapsulate; - /** @type {boolean} */ - visible; - /** * @param {import('#compiler').Css.Combinator | null} combinator * @param {CompoundSelector} compound - * @param {boolean} visible - Whether this complex selector is visible, used in nested rules * */ - constructor(combinator, compound, visible) { + constructor(combinator, compound) { this.combinator = combinator; this.compound = compound; this.should_encapsulate = false; - this.visible = visible; } - /** @param {import('#compiler').Css.SimpleSelector} selector */ + /** @param {import('#compiler').Css.SimpleSelector & { use_wrapper: { used: boolean }, visible: boolean }} selector */ add(selector) { this.compound.add(selector); } @@ -908,6 +918,10 @@ class RelativeSelector { if (this.combinator) return this.combinator.start; return this.compound.start; } + + get contains_invisible_selectors() { + return this.compound.selectors.some(selector => !selector.visible); + } } /** @type {import('#compiler').Css.Combinator} */ @@ -923,7 +937,7 @@ const FakeCombinator = { * not encapsulated multiple times **/ class CompoundSelector { - /** @type {Array} */ + /** @type {Array} */ selectors; /** @type {number} */ @@ -938,19 +952,15 @@ class CompoundSelector { /** @type {boolean} */ root; - /** @type {boolean} */ - has_been_encapsulated; - constructor() { this.selectors = []; this.start = -1; this.end = -1; this.host = false this.root = false - this.has_been_encapsulated = false; } - /** @param {import('#compiler').Css.SimpleSelector} selector */ + /** @param {import('#compiler').Css.SimpleSelector & { use_wrapper: { used: boolean }, visible: boolean }} selector */ add(selector) { if (this.selectors.length === 0) { this.start = selector.start; @@ -981,17 +991,15 @@ class CompoundSelector { function group_selectors(selector, parent_selector_list) { // If it isn't a nested rule, then we add an empty block group if (parent_selector_list === null) { - return [selector_to_blocks(selector.children, null)]; + return [selector_to_blocks([...selector.children], null)]; } return parent_selector_list.map(parent_complex_selector => { const block_group = selector_to_blocks( - selector.children, + [...selector.children], [...parent_complex_selector] // Clone the parent's blocks to avoid modifying the original array ); - // console.log(JSON.stringify(blocks, null, 2)) - return block_group; }) } @@ -1002,20 +1010,57 @@ function group_selectors(selector, parent_selector_list) { * @param {ComplexSelector | null} parent_complex_selector - The parent blocks to insert into the nesting selector positions. */ function selector_to_blocks(children, parent_complex_selector) { - let block = new RelativeSelector(null, new CompoundSelector, true); + let block = new RelativeSelector(null, new CompoundSelector); const blocks = [block]; + + // If this is a nested rule + if (parent_complex_selector) { + let nested_selector_index = children.findIndex(child => child.type === 'NestingSelector'); + + if (nested_selector_index === -1) { + nested_selector_index = 0; // insert the parent selectors at the beginning of the children array + } else { + children.splice(nested_selector_index, 1); // remove the nesting selector, so we can insert there + } + + let parent_selectors = [] + for (const relative_selector of parent_complex_selector) { + if (relative_selector.combinator) parent_selectors.push(relative_selector.combinator); + parent_selectors.push(...relative_selector.compound.selectors.map(selector => ({ ...selector, visible: false }))); + } + + /** + * Some cases + * b { c { color: red }} -> need to insert ' ' before c, so output needs to look like b c + * b { & c { color: red }} -> already has a child combinator before c, so output needs to look like b c + * b { & > c { color: red }} -> next combinator is '>' so output needs to look like b > c + * b { c & { color: red }} -> so we need to insert ' ' after c so output needs to look like c b + */ + + children.unshift(FakeCombinator); // insert a fake combinator at the beginning + + + // Finally, insert the parent selectors into the children array + children.splice(nested_selector_index, 0, ...parent_selectors); + } + + for (const child of children) { if (child.type === 'Combinator') { - block = new RelativeSelector(child, new CompoundSelector, true); + block = new RelativeSelector(child, new CompoundSelector); blocks.push(block); } else if (child.type === 'NestingSelector') { if (!parent_complex_selector) { error(child, 'nesting-selector-not-allowed'); } else { - throw new Error('TODO'); + // We've already handled these above + throw new Error('unexpected nesting selector'); } } else { - block.add(child); + let new_child = /** @type {typeof child & { use_wrapper: { used: boolean }, visible: boolean}} */ (child); + new_child.visible = new_child.visible ?? true; + new_child.use_wrapper = new_child.use_wrapper ?? { used: false }; + block.add(new_child); } }