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 b155091920..033e5bf71d 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/Selector.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/Selector.js @@ -118,23 +118,10 @@ export default class Selector { * @returns {void} */ apply(node) { - /** - * Create a map of blocks to their nodes to know whether they should be encapsulated - * @type {Map>} - * */ - const used_blocks = new Map(); - for (const complex_selector of this.local_selector_list) { - apply_selector(complex_selector.slice(), node, used_blocks); - } - - // Iterate over used_blocks - for (const [relative_selector, nodes] of used_blocks) { - relative_selector.should_encapsulate = true; - for (const node of nodes) { - this.stylesheet.nodes_with_css_class.add(node); + if (apply_selector(complex_selector.slice(), node, this.stylesheet)) { + this.used = true; } - this.used = true; } } @@ -301,25 +288,13 @@ export default class Selector { } } -/** - * @param {Map>} map - * @param {RelativeSelector} block - * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node - */ -function add_node(map, block, node) { - if (!map.has(block)) { - map.set(block, new Set()); - } - map.get(block)?.add(node); -} - /** * @param {RelativeSelector[]} blocks * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement | null} node - * @param {Map>} to_encapsulate + * @param {Stylesheet} stylesheet * @returns {boolean} */ -function apply_selector(blocks, node, to_encapsulate) { +function apply_selector(blocks, node, stylesheet) { const block = blocks.pop(); if (!block) return false; if (!node) { @@ -333,48 +308,64 @@ function apply_selector(blocks, node, to_encapsulate) { return false; } - if (applies === UNKNOWN_SELECTOR) { - add_node(to_encapsulate, block, node); + /** + * Mark both the compound selector and the node it selects as encapsulated, + * for transformation in a later step + * @param {RelativeSelector} block + * @param {import('#compiler').RegularElement | import('#compiler').SvelteElement} node + */ + function mark(block, node) { + block.should_encapsulate = true; + stylesheet.nodes_with_css_class.add(node); return true; } + if (applies === UNKNOWN_SELECTOR) { + return mark(block, node); + } + if (block.combinator) { if (block.combinator.type === 'Combinator' && block.combinator.name === ' ') { for (const ancestor_block of blocks) { if (ancestor_block.global) { continue; } + if (ancestor_block.host) { - add_node(to_encapsulate, block, node); - return true; + return mark(block, node); } + /** @type {import('#compiler').RegularElement | import('#compiler').SvelteElement | null} */ let parent = node; + let matched = false; while ((parent = get_element_parent(parent))) { if (block_might_apply_to_node(ancestor_block, parent) !== NO_MATCH) { - add_node(to_encapsulate, ancestor_block, parent); + mark(ancestor_block, parent); + matched = true; } } - if (to_encapsulate.size) { - add_node(to_encapsulate, block, node); - return true; + + if (matched) { + return mark(block, node); } } + if (blocks.every((block) => block.global)) { - add_node(to_encapsulate, block, node); - return true; + return mark(block, node); } + return false; } else if (block.combinator.name === '>') { const has_global_parent = blocks.every((block) => block.global); - if (has_global_parent || apply_selector(blocks, get_element_parent(node), to_encapsulate)) { - add_node(to_encapsulate, block, node); - return true; + if (has_global_parent || apply_selector(blocks, get_element_parent(node), stylesheet)) { + return mark(block, node); } + return false; } else if (block.combinator.name === '+' || block.combinator.name === '~') { const siblings = get_possible_element_siblings(node, block.combinator.name === '+'); let has_match = false; + // NOTE: if we have :global(), we couldn't figure out what is selected within `:global` due to the // css-tree limitation that does not parse the inner selector of :global // so unless we are sure there will be no sibling to match, we will consider it as matched @@ -383,23 +374,24 @@ function apply_selector(blocks, node, to_encapsulate) { if (siblings.size === 0 && get_element_parent(node) !== null) { return false; } - add_node(to_encapsulate, block, node); - return true; + return mark(block, node); } + for (const possible_sibling of siblings.keys()) { - if (apply_selector(blocks.slice(), possible_sibling, to_encapsulate)) { - add_node(to_encapsulate, block, node); + if (apply_selector(blocks.slice(), possible_sibling, stylesheet)) { + mark(block, node); has_match = true; } } + return has_match; } + // TODO other combinators - add_node(to_encapsulate, block, node); - return true; + return mark(block, node); } - add_node(to_encapsulate, block, node); - return true; + + return mark(block, node); } const regex_backslash_and_following_character = /\\(.)/g;