Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compiler-sfc): deep selector supports multiple values #8232

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
90 changes: 89 additions & 1 deletion packages/compiler-sfc/__tests__/compileStyle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,36 @@ color: red
"[data-v-test] .foo { color: red;
}"
`)
expect(compileScoped(`:deep(.foo) .bar { color: red; }`))
.toMatchInlineSnapshot(`
"[data-v-test] .foo .bar { color: red;
}"
`)
expect(compileScoped(`::v-deep(.foo,.baz) { color: red; }`))
.toMatchInlineSnapshot(`
"[data-v-test] .foo,[data-v-test] .baz { color: red;
}"
`)
expect(compileScoped(`::v-deep(.foo,.baz,.bar,.fcc) { color: red; }`))
.toMatchInlineSnapshot(`
"[data-v-test] .foo,[data-v-test] .baz,[data-v-test] .bar,[data-v-test] .fcc { color: red;
}"
`)
expect(compileScoped(`::v-deep(.foo,.baz) .fc { color: red; }`))
.toMatchInlineSnapshot(`
"[data-v-test] .foo .fc,[data-v-test] .baz .fc { color: red;
}"
`)
expect(compileScoped(`::v-deep(.foo,.baz .bar) .fc { color: red; }`))
.toMatchInlineSnapshot(`
"[data-v-test] .foo .fc,[data-v-test] .baz .bar .fc { color: red;
}"
`)
expect(compileScoped(`.bar :deep(.foo,.baz,.abc) { color: red; }`))
.toMatchInlineSnapshot(`
".bar[data-v-test] .foo,.bar[data-v-test] .baz,.bar[data-v-test] .abc { color: red;
}"
`)
expect(compileScoped(`::v-deep(.foo) { color: red; }`))
.toMatchInlineSnapshot(`
"[data-v-test] .foo { color: red;
Expand All @@ -123,6 +153,16 @@ color: red
".baz .qux[data-v-test] .foo .bar { color: red;
}"
`)
expect(compileScoped(`.baz .qux ::v-deep(.foo,.bar) { color: red; }`))
.toMatchInlineSnapshot(`
".baz .qux[data-v-test] .foo,.baz .qux[data-v-test] .bar { color: red;
}"
`)
expect(compileScoped(`.baz .qux ::v-deep(.foo,.bar) .m { color: red; }`))
.toMatchInlineSnapshot(`
".baz .qux[data-v-test] .foo .m,.baz .qux[data-v-test] .bar .m { color: red;
}"
`)
expect(compileScoped(`:is(.foo :deep(.bar)) { color: red; }`))
.toMatchInlineSnapshot(`
":is(.foo[data-v-test] .bar) { color: red;
Expand All @@ -147,7 +187,22 @@ color: red
.toMatchInlineSnapshot(`
".foo[data-v-test-s] { color: red;
}"
`)
`)
expect(compileScoped(`:slotted(.foo) .bar { color: red; }`))
.toMatchInlineSnapshot(`
".foo[data-v-test-s] .bar { color: red;
}"
`)
expect(compileScoped(`::v-slotted(.foo,.baz .bar) { color: red; }`))
.toMatchInlineSnapshot(`
".foo[data-v-test-s],.baz .bar[data-v-test-s] { color: red;
}"
`)
expect(compileScoped(`::v-slotted(.foo,.baz) { color: red; }`))
.toMatchInlineSnapshot(`
".foo[data-v-test-s],.baz[data-v-test-s] { color: red;
}"
`)
expect(compileScoped(`::v-slotted(.foo) { color: red; }`))
.toMatchInlineSnapshot(`
".foo[data-v-test-s] { color: red;
Expand All @@ -163,6 +218,16 @@ color: red
".baz .qux .foo .bar[data-v-test-s] { color: red;
}"
`)
expect(compileScoped(`.baz .qux ::v-slotted(.foo,.bar) { color: red; }`))
.toMatchInlineSnapshot(`
".baz .qux .foo[data-v-test-s],.baz .qux .bar[data-v-test-s] { color: red;
}"
`)
expect(compileScoped(`.baz .qux ::v-slotted(.foo,.bar) .m { color: red; }`))
.toMatchInlineSnapshot(`
".baz .qux .foo[data-v-test-s] .m,.baz .qux .bar[data-v-test-s] .m { color: red;
}"
`)
})

test('::v-global', () => {
Expand All @@ -171,6 +236,11 @@ color: red
".foo { color: red;
}"
`)
expect(compileScoped(`::v-global(.foo,.bar) { color: red; }`))
.toMatchInlineSnapshot(`
".foo,.bar { color: red;
}"
`)
expect(compileScoped(`::v-global(.foo) { color: red; }`))
.toMatchInlineSnapshot(`
".foo { color: red;
Expand All @@ -187,6 +257,24 @@ color: red
".foo .bar { color: red;
}"
`)
expect(
compileScoped(`.baz .qux ::v-global(.foo .bar, .bar) { color: red; }`),
).toMatchInlineSnapshot(`
".foo .bar, .bar { color: red;
}"
`)
// global ignores anything after it
expect(compileScoped(`::v-global(.foo .bar) .baz { color: red; }`))
.toMatchInlineSnapshot(`
".foo .bar { color: red;
}"
`)
expect(
compileScoped(`.baz ::v-global(.foo .bar,.faa) .qux { color: red; }`),
).toMatchInlineSnapshot(`
".foo .bar,.faa { color: red;
}"
`)
})

test(':is() and :where() with multiple selectors', () => {
Expand Down
120 changes: 96 additions & 24 deletions packages/compiler-sfc/src/style/pluginScoped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ function rewriteSelector(
slotted = false,
) {
let node: selectorParser.Node | null = null
const nodes: (selectorParser.Node | null)[] = []
const nextNodes = [] as typeof selector.nodes
let shouldInject = !deep
// find the last child node to insert attribute selector
selector.each(n => {
Expand Down Expand Up @@ -129,9 +131,55 @@ function rewriteSelector(
// .foo ::v-deep(.bar) -> .foo[xxxxxxx] .bar
// replace the current node with ::v-deep's inner selector
let last: selectorParser.Selector['nodes'][0] = n
n.nodes[0].each(ss => {
selector.insertAfter(last, ss)
last = ss
if (node) {
nodes.push(node)
}
n.nodes.forEach((_node, i) => {
const index = selector.index(n)
if (i > 0) {
const prevList = selector.nodes
.slice(0, index)
.concat(_node.nodes)
if (nextNodes.length) {
prevList.push(...nextNodes)
nextNodes.forEach(s => {
selector.removeChild(s)
selector.insertAfter(last, s)
last = s
})
}
const newList = prevList.map(s => {
const _newNode = s.clone(
{},
) as selectorParser.Selector['nodes'][0]
if (nodes.includes(s)) {
nodes.push(_newNode)
}
return _newNode
})
newList.unshift(selectorParser.combinator({ value: ',' }))
if (!nodes.length) {
nodes.push(null, newList[0])
} else {
// :deep(.a,.b,.c) -> [xxx] .a,[xxx] .b,[xxx] .c
if (nodes[0] === null) {
nodes.push(newList[0])
}
}
if (!node) {
newList.splice(1, 0, selectorParser.combinator({ value: ' ' }))
}
newList.forEach(s => {
selector.insertAfter(last, s)
last = s
})
} else {
nextNodes.push(...selector.nodes.slice(index + 1))
_node.each(ss => {
selector.insertAfter(last, ss)
last = ss
})
}
})
// insert a space combinator before if it doesn't already have one
const prev = selector.at(selector.index(n) - 1)
Expand Down Expand Up @@ -165,18 +213,37 @@ function rewriteSelector(
// instead.
// ::v-slotted(.foo) -> .foo[xxxxxxx-s]
if (value === ':slotted' || value === '::v-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)
last = ss
const nextNodes = [] as typeof selector.nodes
n.nodes.forEach((ss, i) => {
rewriteSelector(id, rule, ss, selectorRoot, deep, true /* slotted */)
const index = selector.index(n)
if (i > 0) {
const prevList = selector.nodes.slice(0, index).concat(ss.nodes)
if (nextNodes.length) {
prevList.push(...nextNodes)
nextNodes.forEach(s => {
selector.removeChild(s)
selector.insertAfter(last, s)
last = s
})
}
const newList = prevList.map(
s => s.clone({}) as selectorParser.Selector['nodes'][0],
)

newList.unshift(selectorParser.combinator({ value: ',' }))
newList.forEach(s => {
selector.insertAfter(last, s)
last = s
})
} else {
nextNodes.push(...selector.nodes.slice(index + 1))
ss.each(s => {
selector.insertAfter(last, s)
last = s
})
}
})
// selector.insertAfter(n, n.nodes[0])
selector.removeChild(n)
Expand All @@ -189,7 +256,7 @@ 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])
selector.replaceWith(...n.nodes)
return false
}
}
Expand Down Expand Up @@ -229,6 +296,9 @@ function rewriteSelector(
node = n
}
})
if (!nodes.length) {
nodes.push(node)
}

if (rule.nodes.some(node => node.type === 'rule')) {
const deep = (rule as any).__deep
Expand Down Expand Up @@ -263,17 +333,19 @@ function rewriteSelector(

if (shouldInject) {
const idToAdd = slotted ? id + '-s' : id
selector.insertAfter(
nodes.forEach(n => {
// If node is null it means we need to inject [id] at the start
// insertAfter can handle `null` here
node as any,
selectorParser.attribute({
attribute: idToAdd,
value: idToAdd,
raws: {},
quoteMark: `"`,
}),
)
selector.insertAfter(
n as any,
selectorParser.attribute({
attribute: idToAdd,
value: idToAdd,
raws: {},
quoteMark: `"`,
}),
)
})
}
}

Expand Down
Loading