diff --git a/packages/compiler-sfc/__tests__/compileStyle.spec.ts b/packages/compiler-sfc/__tests__/compileStyle.spec.ts index b76414364dc..27a966d6426 100644 --- a/packages/compiler-sfc/__tests__/compileStyle.spec.ts +++ b/packages/compiler-sfc/__tests__/compileStyle.spec.ts @@ -181,12 +181,40 @@ color: red ".foo .bar { color: red; }" `) - // global ignores anything before it + expect(compileScoped(`.baz .qux ::v-global(.foo .bar) { color: red; }`)) .toMatchInlineSnapshot(` - ".foo .bar { color: red; + ".baz .qux[data-v-test] .foo .bar { color: red; + }" + `) + + expect(compileScoped(`::v-global(body) h1 { color: red; }`)) + .toMatchInlineSnapshot(` + "body h1[data-v-test] { color: red; + }" + `) + + expect(compileScoped(`::v-global(body h1) { color: red; }`)) + .toMatchInlineSnapshot(` + "body h1 { color: red; + }" + `) + + expect(compileScoped(`html ::v-global(body) h1 { color: red; }`)) + .toMatchInlineSnapshot(` + "html body h1[data-v-test] { color: red; }" `) + + expect(compileScoped(`::v-global(body){ color: red; h1 { color: blue; } }`)) + .toMatchInlineSnapshot(` + "body{ +&{ color: red; +} +h1[data-v-test] { color: blue; +} +}" + `) }) test(':is() and :where() with multiple selectors', () => { diff --git a/packages/compiler-sfc/src/style/pluginScoped.ts b/packages/compiler-sfc/src/style/pluginScoped.ts index d0aaddd7676..901656ed87a 100644 --- a/packages/compiler-sfc/src/style/pluginScoped.ts +++ b/packages/compiler-sfc/src/style/pluginScoped.ts @@ -103,8 +103,14 @@ function rewriteSelector( ) { let node: selectorParser.Node | null = null let shouldInject = !deep + let wrappedGlobal = false // find the last child node to insert attribute selector selector.each(n => { + if ((rule as any).__global) { + shouldInject = false + return false + } + // DEPRECATED ">>>" and "/deep/" combinator if ( n.type === 'combinator' && @@ -189,8 +195,12 @@ function rewriteSelector( // global: replace with inner selector and do not inject [id]. // ::v-global(.foo) -> .foo if (value === ':global' || value === '::v-global') { - selector.replaceWith(n.nodes[0]) - return false + n.replaceWith(...n.nodes) + if (selector.nodes.length === 1) { + shouldInject = false + wrappedGlobal = true + return false + } } } @@ -233,7 +243,7 @@ function rewriteSelector( if (rule.nodes.some(node => node.type === 'rule')) { const deep = (rule as any).__deep if (!deep) { - extractAndWrapNodes(rule) + extractAndWrapNodes(rule, wrappedGlobal) const atruleNodes = rule.nodes.filter(node => node.type === 'atrule') for (const atnode of atruleNodes) { extractAndWrapNodes(atnode) @@ -281,7 +291,7 @@ function isSpaceCombinator(node: selectorParser.Node) { return node.type === 'combinator' && /^\s+$/.test(node.value) } -function extractAndWrapNodes(parentNode: Rule | AtRule) { +function extractAndWrapNodes(parentNode: Rule | AtRule, wrappedGlobal = false) { if (!parentNode.nodes) return const nodes = parentNode.nodes.filter( node => node.type === 'decl' || node.type === 'comment', @@ -294,6 +304,7 @@ function extractAndWrapNodes(parentNode: Rule | AtRule) { nodes: nodes, selector: '&', }) + ;(wrappedRule as any).__global = wrappedGlobal parentNode.prepend(wrappedRule) } }