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', () => {
|
test('multiple selectors', () => {
|
||||||
expect(compileScoped(`h1 .foo, .bar, .baz { color: red; }`)).toMatch(
|
expect(compileScoped(`h1 .foo, .bar, .baz { color: red; }`)).toMatch(
|
||||||
`h1 .foo[data-v-test], .bar[data-v-test], .baz[data-v-test] { color: red;`,
|
`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;
|
":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', () => {
|
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 selectorParser from 'postcss-selector-parser'
|
||||||
import { warn } from '../warn'
|
import { warn } from '../warn'
|
||||||
|
|
||||||
|
@ -71,21 +77,32 @@ function processRule(id: string, rule: Rule) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
processedRules.add(rule)
|
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 => {
|
rule.selector = selectorParser(selectorRoot => {
|
||||||
selectorRoot.each(selector => {
|
selectorRoot.each(selector => {
|
||||||
rewriteSelector(id, selector, selectorRoot)
|
rewriteSelector(id, rule, selector, selectorRoot, deep)
|
||||||
})
|
})
|
||||||
}).processSync(rule.selector)
|
}).processSync(rule.selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
function rewriteSelector(
|
function rewriteSelector(
|
||||||
id: string,
|
id: string,
|
||||||
|
rule: Rule,
|
||||||
selector: selectorParser.Selector,
|
selector: selectorParser.Selector,
|
||||||
selectorRoot: selectorParser.Root,
|
selectorRoot: selectorParser.Root,
|
||||||
|
deep: boolean,
|
||||||
slotted = false,
|
slotted = false,
|
||||||
) {
|
) {
|
||||||
let node: selectorParser.Node | null = null
|
let node: selectorParser.Node | null = null
|
||||||
let shouldInject = true
|
let shouldInject = !deep
|
||||||
// find the last child node to insert attribute selector
|
// find the last child node to insert attribute selector
|
||||||
selector.each(n => {
|
selector.each(n => {
|
||||||
// DEPRECATED ">>>" and "/deep/" combinator
|
// DEPRECATED ">>>" and "/deep/" combinator
|
||||||
|
@ -107,6 +124,7 @@ function rewriteSelector(
|
||||||
// deep: inject [id] attribute at the node before the ::v-deep
|
// deep: inject [id] attribute at the node before the ::v-deep
|
||||||
// combinator.
|
// combinator.
|
||||||
if (value === ':deep' || value === '::v-deep') {
|
if (value === ':deep' || value === '::v-deep') {
|
||||||
|
;(rule as any).__deep = true
|
||||||
if (n.nodes.length) {
|
if (n.nodes.length) {
|
||||||
// .foo ::v-deep(.bar) -> .foo[xxxxxxx] .bar
|
// .foo ::v-deep(.bar) -> .foo[xxxxxxx] .bar
|
||||||
// replace the current node with ::v-deep's inner selector
|
// replace the current node with ::v-deep's inner selector
|
||||||
|
@ -147,7 +165,14 @@ function rewriteSelector(
|
||||||
// instead.
|
// instead.
|
||||||
// ::v-slotted(.foo) -> .foo[xxxxxxx-s]
|
// ::v-slotted(.foo) -> .foo[xxxxxxx-s]
|
||||||
if (value === ':slotted' || value === '::v-slotted') {
|
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
|
let last: selectorParser.Selector['nodes'][0] = n
|
||||||
n.nodes[0].each(ss => {
|
n.nodes[0].each(ss => {
|
||||||
selector.insertAfter(last, 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) {
|
if (node) {
|
||||||
const { type, value } = node as selectorParser.Node
|
const { type, value } = node as selectorParser.Node
|
||||||
if (type === 'pseudo' && (value === ':is' || value === ':where')) {
|
if (type === 'pseudo' && (value === ':is' || value === ':where')) {
|
||||||
;(node as selectorParser.Pseudo).nodes.forEach(value =>
|
;(node as selectorParser.Pseudo).nodes.forEach(value =>
|
||||||
rewriteSelector(id, value, selectorRoot, slotted),
|
rewriteSelector(id, rule, value, selectorRoot, deep, slotted),
|
||||||
)
|
)
|
||||||
shouldInject = false
|
shouldInject = false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue