mirror of https://github.com/vuejs/core.git
fix(compiler-sfc): correct scoped injection for nesting selector (#11854)
close #10567
This commit is contained in:
parent
fe2ab1bbac
commit
b1de75ed04
|
@ -41,6 +41,12 @@ describe('SFC scoped CSS', () => {
|
|||
)
|
||||
})
|
||||
|
||||
test('nesting selector', () => {
|
||||
expect(compileScoped(`h1 { color: red; .foo { color: red; } }`)).toMatch(
|
||||
`h1 {\n&[data-v-test] { color: red;\n}\n.foo[data-v-test] { color: red;`,
|
||||
)
|
||||
})
|
||||
|
||||
test('multiple selectors', () => {
|
||||
expect(compileScoped(`h1 .foo, .bar, .baz { color: red; }`)).toMatch(
|
||||
`h1 .foo[data-v-test], .bar[data-v-test], .baz[data-v-test] { color: red;`,
|
||||
|
@ -95,6 +101,13 @@ describe('SFC scoped CSS', () => {
|
|||
":where(.foo[data-v-test] .bar) { color: red;
|
||||
}"
|
||||
`)
|
||||
expect(compileScoped(`:deep(.foo) { color: red; .bar { color: red; } }`))
|
||||
.toMatchInlineSnapshot(`
|
||||
"[data-v-test] .foo { color: red;
|
||||
.bar { color: red;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
||||
test('::v-slotted', () => {
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import type { AtRule, PluginCreator, Rule } from 'postcss'
|
||||
import {
|
||||
type AtRule,
|
||||
type Container,
|
||||
type Document,
|
||||
type PluginCreator,
|
||||
Rule,
|
||||
} from 'postcss'
|
||||
import selectorParser from 'postcss-selector-parser'
|
||||
import { warn } from '../warn'
|
||||
|
||||
|
@ -71,21 +77,32 @@ function processRule(id: string, rule: Rule) {
|
|||
return
|
||||
}
|
||||
processedRules.add(rule)
|
||||
let deep = false
|
||||
let parent: Document | Container | undefined = rule.parent
|
||||
while (parent && parent.type !== 'root') {
|
||||
if ((parent as any).__deep) {
|
||||
deep = true
|
||||
break
|
||||
}
|
||||
parent = parent.parent
|
||||
}
|
||||
rule.selector = selectorParser(selectorRoot => {
|
||||
selectorRoot.each(selector => {
|
||||
rewriteSelector(id, selector, selectorRoot)
|
||||
rewriteSelector(id, rule, selector, selectorRoot, deep)
|
||||
})
|
||||
}).processSync(rule.selector)
|
||||
}
|
||||
|
||||
function rewriteSelector(
|
||||
id: string,
|
||||
rule: Rule,
|
||||
selector: selectorParser.Selector,
|
||||
selectorRoot: selectorParser.Root,
|
||||
deep: boolean,
|
||||
slotted = false,
|
||||
) {
|
||||
let node: selectorParser.Node | null = null
|
||||
let shouldInject = true
|
||||
let shouldInject = !deep
|
||||
// find the last child node to insert attribute selector
|
||||
selector.each(n => {
|
||||
// DEPRECATED ">>>" and "/deep/" combinator
|
||||
|
@ -107,6 +124,7 @@ function rewriteSelector(
|
|||
// deep: inject [id] attribute at the node before the ::v-deep
|
||||
// combinator.
|
||||
if (value === ':deep' || value === '::v-deep') {
|
||||
;(rule as any).__deep = true
|
||||
if (n.nodes.length) {
|
||||
// .foo ::v-deep(.bar) -> .foo[xxxxxxx] .bar
|
||||
// replace the current node with ::v-deep's inner selector
|
||||
|
@ -147,7 +165,14 @@ function rewriteSelector(
|
|||
// instead.
|
||||
// ::v-slotted(.foo) -> .foo[xxxxxxx-s]
|
||||
if (value === ':slotted' || value === '::v-slotted') {
|
||||
rewriteSelector(id, n.nodes[0], selectorRoot, true /* slotted */)
|
||||
rewriteSelector(
|
||||
id,
|
||||
rule,
|
||||
n.nodes[0],
|
||||
selectorRoot,
|
||||
deep,
|
||||
true /* slotted */,
|
||||
)
|
||||
let last: selectorParser.Selector['nodes'][0] = n
|
||||
n.nodes[0].each(ss => {
|
||||
selector.insertAfter(last, ss)
|
||||
|
@ -206,11 +231,27 @@ function rewriteSelector(
|
|||
}
|
||||
})
|
||||
|
||||
if (rule.nodes.some(node => node.type === 'rule')) {
|
||||
const deep = (rule as any).__deep
|
||||
const decls = rule.nodes.filter(node => node.type === 'decl')
|
||||
if (!deep && decls.length) {
|
||||
for (const decl of decls) {
|
||||
rule.removeChild(decl)
|
||||
}
|
||||
const hostRule = new Rule({
|
||||
nodes: decls,
|
||||
selector: '&',
|
||||
})
|
||||
rule.prepend(hostRule)
|
||||
}
|
||||
shouldInject = deep
|
||||
}
|
||||
|
||||
if (node) {
|
||||
const { type, value } = node as selectorParser.Node
|
||||
if (type === 'pseudo' && (value === ':is' || value === ':where')) {
|
||||
;(node as selectorParser.Pseudo).nodes.forEach(value =>
|
||||
rewriteSelector(id, value, selectorRoot, slotted),
|
||||
rewriteSelector(id, rule, value, selectorRoot, deep, slotted),
|
||||
)
|
||||
shouldInject = false
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue