Skip to content

Commit e869d4a

Browse files
committed
Supports ::v-deep
1 parent ade4a68 commit e869d4a

File tree

6 files changed

+236
-8
lines changed

6 files changed

+236
-8
lines changed

lib/styles/parser/selector/css-selector-parser.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,14 +618,19 @@ function removeInvalidDescendantCombinator(
618618
const results = []
619619

620620
let prev = null
621-
for (const node of nodes) {
621+
for (let index = 0; index < nodes.length; index++) {
622+
const node = nodes[index]
622623
if (isDescendantCombinator(node)) {
623624
if (results.length === 0) {
624625
continue
625626
}
626627
if (isSelectorCombinator(prev)) {
627628
continue
628629
}
630+
const next = nodes[index + 1]
631+
if (isSelectorCombinator(next)) {
632+
continue
633+
}
629634
}
630635
results.push(node)
631636
prev = node

lib/styles/utils/selectors.ts

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
VCSSNode,
1010
VCSSTypeSelector,
1111
VCSSSelectorContainerNode,
12+
VCSSSelectorPseudo,
1213
} from "../ast"
1314
import { isVCSSAtRule } from "./css-nodes"
1415

@@ -25,6 +26,22 @@ export function hasNodesSelector(
2526
)
2627
}
2728

29+
type VDeepPseudo = VCSSSelectorPseudo & { value: "::v-deep" }
30+
31+
/**
32+
* Checks whether the given node is ::v-deep pseudo
33+
* @param node node to check
34+
*/
35+
export function isVDeepPseudo(
36+
node: VCSSSelectorNode | null,
37+
): node is VDeepPseudo {
38+
if (isPseudo(node)) {
39+
const val = node.value.trim()
40+
return val === "::v-deep"
41+
}
42+
return false
43+
}
44+
2845
/**
2946
* Checks whether the given node is VCSSTypeSelector
3047
* @param node node to check
@@ -75,14 +92,23 @@ export function isNestingSelector(
7592
return node?.type === "VCSSNestingSelector"
7693
}
7794

95+
/**
96+
* Checks whether the given node is VCSSNestingSelector
97+
* @param node node to check
98+
*/
99+
export function isPseudo(
100+
node: VCSSSelectorNode | null,
101+
): node is VCSSSelectorPseudo {
102+
return node?.type === "VCSSSelectorPseudo"
103+
}
78104
/**
79105
* Checks whether the given node is VCSSSelectorCombinator
80106
* @param node node to check
81107
*/
82108
export function isSelectorCombinator(
83109
node: VCSSSelectorNode | null,
84-
): node is VCSSSelectorCombinator {
85-
return node?.type === "VCSSSelectorCombinator"
110+
): node is VCSSSelectorCombinator | VDeepPseudo {
111+
return node?.type === "VCSSSelectorCombinator" || isVDeepPseudo(node)
86112
}
87113

88114
/**
@@ -131,12 +157,14 @@ export function isGeneralSiblingCombinator(
131157
*/
132158
export function isDeepCombinator(
133159
node: VCSSSelectorNode | null,
134-
): node is VCSSSelectorCombinator & { value: ">>>" | "/deep/" } {
135-
if (!isSelectorCombinator(node)) {
136-
return false
160+
): node is
161+
| (VCSSSelectorCombinator & { value: ">>>" | "/deep/" })
162+
| VDeepPseudo {
163+
if (isSelectorCombinator(node)) {
164+
const val = node.value.trim()
165+
return val === ">>>" || val === "/deep/" || isVDeepPseudo(node)
137166
}
138-
const val = node.value.trim()
139-
return val === ">>>" || val === "/deep/"
167+
return false
140168
}
141169

142170
/**

tests/lib/rules/no-unused-selector.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,76 @@ tester.run("no-unused-selector", rule, {
470470
},
471471
],
472472
},
473+
{
474+
code: `
475+
<template>
476+
<div>
477+
<ul class="list">
478+
<slot/>
479+
</ul>
480+
</div>
481+
</template>
482+
<style scoped>
483+
div > ul /deep/ .a {}
484+
div > li /deep/ .a {}
485+
div > .list /deep/ .a {}
486+
div > .item /deep/ .a {}
487+
</style>
488+
`,
489+
errors: [
490+
{
491+
messageId: "unused",
492+
data: { selector: "div>li" },
493+
line: 11,
494+
column: 13,
495+
endLine: 11,
496+
endColumn: 21,
497+
},
498+
{
499+
messageId: "unused",
500+
data: { selector: ".item" },
501+
line: 13,
502+
column: 19,
503+
endLine: 13,
504+
endColumn: 24,
505+
},
506+
],
507+
},
508+
{
509+
code: `
510+
<template>
511+
<div>
512+
<ul class="list">
513+
<slot/>
514+
</ul>
515+
</div>
516+
</template>
517+
<style scoped>
518+
div > ul ::v-deep .a {}
519+
div > li ::v-deep .a {}
520+
div > .list ::v-deep .a {}
521+
div > .item ::v-deep .a {}
522+
</style>
523+
`,
524+
errors: [
525+
{
526+
messageId: "unused",
527+
data: { selector: "div>li" },
528+
line: 11,
529+
column: 13,
530+
endLine: 11,
531+
endColumn: 21,
532+
},
533+
{
534+
messageId: "unused",
535+
data: { selector: ".item" },
536+
line: 13,
537+
column: 19,
538+
endLine: 13,
539+
endColumn: 24,
540+
},
541+
],
542+
},
473543

474544
// multiple children parents
475545
{

tests/lib/rules/require-selector-used-inside.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,76 @@ tester.run("require-selector-used-inside", rule, {
471471
},
472472
],
473473
},
474+
{
475+
code: `
476+
<template>
477+
<div>
478+
<ul class="list">
479+
<slot/>
480+
</ul>
481+
</div>
482+
</template>
483+
<style scoped>
484+
div > ul /deep/ .a {}
485+
div > li /deep/ .a {}
486+
div > .list /deep/ .a {}
487+
div > .item /deep/ .a {}
488+
</style>
489+
`,
490+
errors: [
491+
{
492+
messageId: "unused",
493+
data: { selector: "div>li" },
494+
line: 11,
495+
column: 13,
496+
endLine: 11,
497+
endColumn: 21,
498+
},
499+
{
500+
messageId: "unused",
501+
data: { selector: "div>.item" },
502+
line: 13,
503+
column: 13,
504+
endLine: 13,
505+
endColumn: 24,
506+
},
507+
],
508+
},
509+
{
510+
code: `
511+
<template>
512+
<div>
513+
<ul class="list">
514+
<slot/>
515+
</ul>
516+
</div>
517+
</template>
518+
<style scoped>
519+
div > ul ::v-deep .a {}
520+
div > li ::v-deep .a {}
521+
div > .list ::v-deep .a {}
522+
div > .item ::v-deep .a {}
523+
</style>
524+
`,
525+
errors: [
526+
{
527+
messageId: "unused",
528+
data: { selector: "div>li" },
529+
line: 11,
530+
column: 13,
531+
endLine: 11,
532+
endColumn: 21,
533+
},
534+
{
535+
messageId: "unused",
536+
data: { selector: "div>.item" },
537+
line: 13,
538+
column: 13,
539+
endLine: 13,
540+
endColumn: 24,
541+
},
542+
],
543+
},
474544

475545
// multiple parents children
476546
{
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"#g>>>.bar": [
3+
"ul.foo>li#g>ul>li#h.bar",
4+
"ul.foo>li#g>ul>li#k.bar",
5+
"ul.foo>li#g>div#n.bar",
6+
"ul.foo>li#g>div#q.bar"
7+
],
8+
"#g/deep/.bar": [
9+
"ul.foo>li#g>ul>li#h.bar",
10+
"ul.foo>li#g>ul>li#k.bar",
11+
"ul.foo>li#g>div#n.bar",
12+
"ul.foo>li#g>div#q.bar"
13+
],
14+
"#g::v-deep.bar": [
15+
"ul.foo>li#g>ul>li#h.bar",
16+
"ul.foo>li#g>ul>li#k.bar",
17+
"ul.foo>li#g>div#n.bar",
18+
"ul.foo>li#g>div#q.bar"
19+
]
20+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<template>
2+
<ul class="foo">
3+
<li id="a" class="bar" />
4+
<li id="b" class="baz" />
5+
<li id="c" class="qux" />
6+
<li id="d" class="bar" />
7+
<li id="e" class="baz" />
8+
<li id="f" class="qux" />
9+
<li id="g">
10+
<ul>
11+
<li id="h" class="bar" />
12+
<li id="i" class="baz" />
13+
<li id="j" class="qux" />
14+
<li id="k" class="bar" />
15+
<li id="l" class="baz" />
16+
<li id="m" class="qux" />
17+
</ul>
18+
<div id="n" class="bar" />
19+
<div id="o" class="baz" />
20+
<div id="p" class="qux" />
21+
<div id="q" class="bar" />
22+
<div id="r" class="baz" />
23+
<div id="s" class="qux" />
24+
</li>
25+
</ul>
26+
</template>
27+
<style scoped>
28+
#g >>> .bar {
29+
}
30+
#g /deep/ .bar {
31+
}
32+
#g ::v-deep .bar {
33+
}
34+
</style>
35+

0 commit comments

Comments
 (0)