Skip to content

Commit 362831d

Browse files
committed
fix(sfc): fix v-slotted attribute injection
1 parent 3a3a24d commit 362831d

File tree

2 files changed

+93
-55
lines changed

2 files changed

+93
-55
lines changed

packages/compiler-sfc/__tests__/stylePluginScoped.spec.ts renamed to packages/compiler-sfc/__tests__/compileStyle.spec.ts

Lines changed: 75 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -55,52 +55,73 @@ describe('SFC scoped CSS', () => {
5555
})
5656

5757
test('::v-deep', () => {
58-
expect(compile(`::v-deep(.foo) { color: red; }`)).toMatch(
59-
`[test] .foo { color: red;`
60-
)
61-
expect(compile(`::v-deep(.foo .bar) { color: red; }`)).toMatch(
62-
`[test] .foo .bar { color: red;`
63-
)
64-
expect(compile(`.baz .qux ::v-deep(.foo .bar) { color: red; }`)).toMatch(
65-
`.baz .qux[test] .foo .bar { color: red;`
66-
)
58+
expect(compile(`::v-deep(.foo) { color: red; }`)).toMatchInlineSnapshot(`
59+
"[test] .foo { color: red;
60+
}"
61+
`)
62+
expect(compile(`::v-deep(.foo .bar) { color: red; }`))
63+
.toMatchInlineSnapshot(`
64+
"[test] .foo .bar { color: red;
65+
}"
66+
`)
67+
expect(compile(`.baz .qux ::v-deep(.foo .bar) { color: red; }`))
68+
.toMatchInlineSnapshot(`
69+
".baz .qux[test] .foo .bar { color: red;
70+
}"
71+
`)
6772
})
6873

6974
test('::v-slotted', () => {
70-
expect(compile(`::v-slotted(.foo) { color: red; }`)).toMatch(
71-
`.foo[test-s] { color: red;`
72-
)
73-
expect(compile(`::v-slotted(.foo .bar) { color: red; }`)).toMatch(
74-
`.foo .bar[test-s] { color: red;`
75-
)
76-
expect(compile(`.baz .qux ::v-slotted(.foo .bar) { color: red; }`)).toMatch(
77-
`.baz .qux[test] .foo .bar[test-s] { color: red;`
78-
)
75+
expect(compile(`::v-slotted(.foo) { color: red; }`)).toMatchInlineSnapshot(`
76+
".foo[test-s] { color: red;
77+
}"
78+
`)
79+
expect(compile(`::v-slotted(.foo .bar) { color: red; }`))
80+
.toMatchInlineSnapshot(`
81+
".foo .bar[test-s] { color: red;
82+
}"
83+
`)
84+
expect(compile(`.baz .qux ::v-slotted(.foo .bar) { color: red; }`))
85+
.toMatchInlineSnapshot(`
86+
".baz .qux .foo .bar[test-s] { color: red;
87+
}"
88+
`)
7989
})
8090

8191
test('::v-global', () => {
82-
expect(compile(`::v-global(.foo) { color: red; }`)).toMatch(
83-
`.foo { color: red;`
84-
)
85-
expect(compile(`::v-global(.foo .bar) { color: red; }`)).toMatch(
86-
`.foo .bar { color: red;`
87-
)
92+
expect(compile(`::v-global(.foo) { color: red; }`)).toMatchInlineSnapshot(`
93+
".foo { color: red;
94+
}"
95+
`)
96+
expect(compile(`::v-global(.foo .bar) { color: red; }`))
97+
.toMatchInlineSnapshot(`
98+
".foo .bar { color: red;
99+
}"
100+
`)
88101
// global ignores anything before it
89-
expect(compile(`.baz .qux ::v-global(.foo .bar) { color: red; }`)).toMatch(
90-
`.foo .bar { color: red;`
91-
)
102+
expect(compile(`.baz .qux ::v-global(.foo .bar) { color: red; }`))
103+
.toMatchInlineSnapshot(`
104+
".foo .bar { color: red;
105+
}"
106+
`)
92107
})
93108

94109
test('media query', () => {
95-
expect(compile(`@media print { .foo { color: red }}`)).toMatch(
96-
/@media print {\s+\.foo\[test\] \{ color: red/
97-
)
110+
expect(compile(`@media print { .foo { color: red }}`))
111+
.toMatchInlineSnapshot(`
112+
"@media print {
113+
.foo[test] { color: red
114+
}}"
115+
`)
98116
})
99117

100118
test('supports query', () => {
101-
expect(
102-
compile(`@supports(display: grid) { .foo { display: grid }}`)
103-
).toMatch(/@supports\(display: grid\) {\s+\.foo\[test\] \{ display: grid/)
119+
expect(compile(`@supports(display: grid) { .foo { display: grid }}`))
120+
.toMatchInlineSnapshot(`
121+
"@supports(display: grid) {
122+
.foo[test] { display: grid
123+
}}"
124+
`)
104125
})
105126

106127
test('scoped keyframes', () => {
@@ -169,33 +190,45 @@ describe('SFC scoped CSS', () => {
169190
id: 'test'
170191
})
171192

172-
expect(code).toMatch(`.foo[test], .bar[test] { color: red;`)
193+
expect(code).toMatchInlineSnapshot(`
194+
".foo[test], .bar[test] { color: red;
195+
}"
196+
`)
173197
})
174198

175199
describe('deprecated syntax', () => {
176200
test('::v-deep as combinator', () => {
177-
expect(compile(`::v-deep .foo { color: red; }`)).toMatch(
178-
`[test] .foo { color: red;`
179-
)
180-
expect(compile(`.bar ::v-deep .foo { color: red; }`)).toMatch(
181-
`.bar[test] .foo { color: red;`
182-
)
201+
expect(compile(`::v-deep .foo { color: red; }`)).toMatchInlineSnapshot(`
202+
"[test] .foo { color: red;
203+
}"
204+
`)
205+
expect(compile(`.bar ::v-deep .foo { color: red; }`))
206+
.toMatchInlineSnapshot(`
207+
".bar[test] .foo { color: red;
208+
}"
209+
`)
183210
expect(
184211
`::v-deep usage as a combinator has been deprecated.`
185212
).toHaveBeenWarned()
186213
})
187214

188215
test('>>> (deprecated syntax)', () => {
189216
const code = compile(`>>> .foo { color: red; }`)
190-
expect(code).toMatch(`[test] .foo { color: red;`)
217+
expect(code).toMatchInlineSnapshot(`
218+
"[test] .foo { color: red;
219+
}"
220+
`)
191221
expect(
192222
`the >>> and /deep/ combinators have been deprecated.`
193223
).toHaveBeenWarned()
194224
})
195225

196226
test('/deep/ (deprecated syntax)', () => {
197227
const code = compile(`/deep/ .foo { color: red; }`)
198-
expect(code).toMatch(`[test] .foo { color: red;`)
228+
expect(code).toMatchInlineSnapshot(`
229+
"[test] .foo { color: red;
230+
}"
231+
`)
199232
expect(
200233
`the >>> and /deep/ combinators have been deprecated.`
201234
).toHaveBeenWarned()

packages/compiler-sfc/src/stylePluginScoped.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default postcss.plugin('vue-scoped', (options: any) => (root: Root) => {
2222
node.selector = selectorParser(selectors => {
2323
function rewriteSelector(selector: Selector, slotted?: boolean) {
2424
let node: Node | null = null
25-
25+
let shouldInject = true
2626
// find the last child node to insert attribute selector
2727
selector.each(n => {
2828
// DEPRECATED ">>>" and "/deep/" combinator
@@ -81,6 +81,9 @@ export default postcss.plugin('vue-scoped', (options: any) => (root: Root) => {
8181
rewriteSelector(n.nodes[0] as Selector, true /* slotted */)
8282
selector.insertAfter(n, n.nodes[0])
8383
selector.removeChild(n)
84+
// since slotted attribute already scopes the selector there's no
85+
// need for the non-slot attribute.
86+
shouldInject = false
8487
return false
8588
}
8689

@@ -107,18 +110,20 @@ export default postcss.plugin('vue-scoped', (options: any) => (root: Root) => {
107110
selector.first.spaces.before = ''
108111
}
109112

110-
const idToAdd = slotted ? id + '-s' : id
111-
selector.insertAfter(
112-
// If node is null it means we need to inject [id] at the start
113-
// insertAfter can handle `null` here
114-
node as any,
115-
selectorParser.attribute({
116-
attribute: idToAdd,
117-
value: idToAdd,
118-
raws: {},
119-
quoteMark: `"`
120-
})
121-
)
113+
if (shouldInject) {
114+
const idToAdd = slotted ? id + '-s' : id
115+
selector.insertAfter(
116+
// If node is null it means we need to inject [id] at the start
117+
// insertAfter can handle `null` here
118+
node as any,
119+
selectorParser.attribute({
120+
attribute: idToAdd,
121+
value: idToAdd,
122+
raws: {},
123+
quoteMark: `"`
124+
})
125+
)
126+
}
122127
}
123128
selectors.each(selector => rewriteSelector(selector as Selector))
124129
}).processSync(node.selector)

0 commit comments

Comments
 (0)