)
})
+ 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;`,
":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', () => {
-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'
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
// 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
// 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)
}
})
+ 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
}