From 362831d8abfda56107c31d0236b7cbd264ff7526 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 19 Dec 2019 17:54:52 -0500 Subject: [PATCH] fix(sfc): fix v-slotted attribute injection --- ...ginScoped.spec.ts => compileStyle.spec.ts} | 117 +++++++++++------- .../compiler-sfc/src/stylePluginScoped.ts | 31 +++-- 2 files changed, 93 insertions(+), 55 deletions(-) rename packages/compiler-sfc/__tests__/{stylePluginScoped.spec.ts => compileStyle.spec.ts} (67%) diff --git a/packages/compiler-sfc/__tests__/stylePluginScoped.spec.ts b/packages/compiler-sfc/__tests__/compileStyle.spec.ts similarity index 67% rename from packages/compiler-sfc/__tests__/stylePluginScoped.spec.ts rename to packages/compiler-sfc/__tests__/compileStyle.spec.ts index ed24199a..5198e7a7 100644 --- a/packages/compiler-sfc/__tests__/stylePluginScoped.spec.ts +++ b/packages/compiler-sfc/__tests__/compileStyle.spec.ts @@ -55,52 +55,73 @@ describe('SFC scoped CSS', () => { }) test('::v-deep', () => { - expect(compile(`::v-deep(.foo) { color: red; }`)).toMatch( - `[test] .foo { color: red;` - ) - expect(compile(`::v-deep(.foo .bar) { color: red; }`)).toMatch( - `[test] .foo .bar { color: red;` - ) - expect(compile(`.baz .qux ::v-deep(.foo .bar) { color: red; }`)).toMatch( - `.baz .qux[test] .foo .bar { color: red;` - ) + expect(compile(`::v-deep(.foo) { color: red; }`)).toMatchInlineSnapshot(` + "[test] .foo { color: red; + }" + `) + expect(compile(`::v-deep(.foo .bar) { color: red; }`)) + .toMatchInlineSnapshot(` + "[test] .foo .bar { color: red; + }" + `) + expect(compile(`.baz .qux ::v-deep(.foo .bar) { color: red; }`)) + .toMatchInlineSnapshot(` + ".baz .qux[test] .foo .bar { color: red; + }" + `) }) test('::v-slotted', () => { - expect(compile(`::v-slotted(.foo) { color: red; }`)).toMatch( - `.foo[test-s] { color: red;` - ) - expect(compile(`::v-slotted(.foo .bar) { color: red; }`)).toMatch( - `.foo .bar[test-s] { color: red;` - ) - expect(compile(`.baz .qux ::v-slotted(.foo .bar) { color: red; }`)).toMatch( - `.baz .qux[test] .foo .bar[test-s] { color: red;` - ) + expect(compile(`::v-slotted(.foo) { color: red; }`)).toMatchInlineSnapshot(` + ".foo[test-s] { color: red; + }" + `) + expect(compile(`::v-slotted(.foo .bar) { color: red; }`)) + .toMatchInlineSnapshot(` + ".foo .bar[test-s] { color: red; + }" + `) + expect(compile(`.baz .qux ::v-slotted(.foo .bar) { color: red; }`)) + .toMatchInlineSnapshot(` + ".baz .qux .foo .bar[test-s] { color: red; + }" + `) }) test('::v-global', () => { - expect(compile(`::v-global(.foo) { color: red; }`)).toMatch( - `.foo { color: red;` - ) - expect(compile(`::v-global(.foo .bar) { color: red; }`)).toMatch( - `.foo .bar { color: red;` - ) + expect(compile(`::v-global(.foo) { color: red; }`)).toMatchInlineSnapshot(` + ".foo { color: red; + }" + `) + expect(compile(`::v-global(.foo .bar) { color: red; }`)) + .toMatchInlineSnapshot(` + ".foo .bar { color: red; + }" + `) // global ignores anything before it - expect(compile(`.baz .qux ::v-global(.foo .bar) { color: red; }`)).toMatch( - `.foo .bar { color: red;` - ) + expect(compile(`.baz .qux ::v-global(.foo .bar) { color: red; }`)) + .toMatchInlineSnapshot(` + ".foo .bar { color: red; + }" + `) }) test('media query', () => { - expect(compile(`@media print { .foo { color: red }}`)).toMatch( - /@media print {\s+\.foo\[test\] \{ color: red/ - ) + expect(compile(`@media print { .foo { color: red }}`)) + .toMatchInlineSnapshot(` + "@media print { + .foo[test] { color: red + }}" + `) }) test('supports query', () => { - expect( - compile(`@supports(display: grid) { .foo { display: grid }}`) - ).toMatch(/@supports\(display: grid\) {\s+\.foo\[test\] \{ display: grid/) + expect(compile(`@supports(display: grid) { .foo { display: grid }}`)) + .toMatchInlineSnapshot(` + "@supports(display: grid) { + .foo[test] { display: grid + }}" + `) }) test('scoped keyframes', () => { @@ -169,17 +190,23 @@ describe('SFC scoped CSS', () => { id: 'test' }) - expect(code).toMatch(`.foo[test], .bar[test] { color: red;`) + expect(code).toMatchInlineSnapshot(` + ".foo[test], .bar[test] { color: red; + }" + `) }) describe('deprecated syntax', () => { test('::v-deep as combinator', () => { - expect(compile(`::v-deep .foo { color: red; }`)).toMatch( - `[test] .foo { color: red;` - ) - expect(compile(`.bar ::v-deep .foo { color: red; }`)).toMatch( - `.bar[test] .foo { color: red;` - ) + expect(compile(`::v-deep .foo { color: red; }`)).toMatchInlineSnapshot(` + "[test] .foo { color: red; + }" + `) + expect(compile(`.bar ::v-deep .foo { color: red; }`)) + .toMatchInlineSnapshot(` + ".bar[test] .foo { color: red; + }" + `) expect( `::v-deep usage as a combinator has been deprecated.` ).toHaveBeenWarned() @@ -187,7 +214,10 @@ describe('SFC scoped CSS', () => { test('>>> (deprecated syntax)', () => { const code = compile(`>>> .foo { color: red; }`) - expect(code).toMatch(`[test] .foo { color: red;`) + expect(code).toMatchInlineSnapshot(` + "[test] .foo { color: red; + }" + `) expect( `the >>> and /deep/ combinators have been deprecated.` ).toHaveBeenWarned() @@ -195,7 +225,10 @@ describe('SFC scoped CSS', () => { test('/deep/ (deprecated syntax)', () => { const code = compile(`/deep/ .foo { color: red; }`) - expect(code).toMatch(`[test] .foo { color: red;`) + expect(code).toMatchInlineSnapshot(` + "[test] .foo { color: red; + }" + `) expect( `the >>> and /deep/ combinators have been deprecated.` ).toHaveBeenWarned() diff --git a/packages/compiler-sfc/src/stylePluginScoped.ts b/packages/compiler-sfc/src/stylePluginScoped.ts index 112e7944..5f9abfa5 100644 --- a/packages/compiler-sfc/src/stylePluginScoped.ts +++ b/packages/compiler-sfc/src/stylePluginScoped.ts @@ -22,7 +22,7 @@ export default postcss.plugin('vue-scoped', (options: any) => (root: Root) => { node.selector = selectorParser(selectors => { function rewriteSelector(selector: Selector, slotted?: boolean) { let node: Node | null = null - + let shouldInject = true // find the last child node to insert attribute selector selector.each(n => { // DEPRECATED ">>>" and "/deep/" combinator @@ -81,6 +81,9 @@ export default postcss.plugin('vue-scoped', (options: any) => (root: Root) => { rewriteSelector(n.nodes[0] as Selector, true /* slotted */) selector.insertAfter(n, n.nodes[0]) selector.removeChild(n) + // since slotted attribute already scopes the selector there's no + // need for the non-slot attribute. + shouldInject = false return false } @@ -107,18 +110,20 @@ export default postcss.plugin('vue-scoped', (options: any) => (root: Root) => { selector.first.spaces.before = '' } - const idToAdd = slotted ? id + '-s' : id - selector.insertAfter( - // 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: `"` - }) - ) + if (shouldInject) { + const idToAdd = slotted ? id + '-s' : id + selector.insertAfter( + // 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: `"` + }) + ) + } } selectors.each(selector => rewriteSelector(selector as Selector)) }).processSync(node.selector)