diff --git a/packages/compiler-sfc/__tests__/compileStyle.spec.ts b/packages/compiler-sfc/__tests__/compileStyle.spec.ts index 9b7e7c53710..f4fecfaef68 100644 --- a/packages/compiler-sfc/__tests__/compileStyle.spec.ts +++ b/packages/compiler-sfc/__tests__/compileStyle.spec.ts @@ -158,6 +158,24 @@ color: red } }" `) + expect(compileScoped(`:deep(.foo) { .bar { .baz { color: red; } } }`)) + .toMatchInlineSnapshot(` + "[data-v-test] .foo { + .bar { + .baz { color: red; + } + } + }" + `) + expect(compileScoped(`.outer { :deep(.foo) { * .bar { color: red; } } }`)) + .toMatchInlineSnapshot(` + ".outer { + [data-v-test] .foo { + * .bar { color: red; + } + } + }" + `) }) test('::v-slotted', () => { @@ -397,6 +415,116 @@ color: red ).toHaveBeenWarned() }) }) + + test('should keep leading universal selector before non-space combinator', () => { + expect(compileScoped(`* > .section { color: red; }`)) + .toMatchInlineSnapshot(` + "* > .section[data-v-test] { color: red; + }" + `) + expect(compileScoped(`* + .section { color: red; }`)) + .toMatchInlineSnapshot(` + "* + .section[data-v-test] { color: red; + }" + `) + expect(compileScoped(`* ~ .section { color: red; }`)) + .toMatchInlineSnapshot(` + "* ~ .section[data-v-test] { color: red; + }" + `) + }) + + test('should keep leading universal selector in nested rules', () => { + expect(compileScoped(`* .section { color: red; }`)).toMatchInlineSnapshot(` + ".section[data-v-test] { color: red; + }" + `) + expect(compileScoped(`.outer { * .section { color: red; } }`)) + .toMatchInlineSnapshot(` + ".outer { + * .section[data-v-test] { color: red; + } + }" + `) + expect(compileScoped(`.outer { * > .section { color: red; } }`)) + .toMatchInlineSnapshot(` + ".outer { + * > .section[data-v-test] { color: red; + } + }" + `) + expect(compileScoped(`.outer { *.foo { color: red; } }`)) + .toMatchInlineSnapshot(` + ".outer { + .foo[data-v-test] { color: red; + } + }" + `) + expect(compileScoped(`.outer { * { color: red; } }`)) + .toMatchInlineSnapshot(` + ".outer { + [data-v-test] { color: red; + } + }" + `) + expect(compileScoped(`@media screen { * .section { color: red; } }`)) + .toMatchInlineSnapshot(` + "@media screen { + .section[data-v-test] { color: red; + } + }" + `) + expect( + compileScoped(`.outer { @media screen { * .section { color: red; } } }`), + ).toMatchInlineSnapshot(` + ".outer[data-v-test] { + @media screen { + * .section[data-v-test] { color: red; + } + } + }" + `) + }) + + test('should keep leading universal selector in :is() in nested rules', () => { + expect(compileScoped(`.outer { :is(* .foo) { color: red; } }`)) + .toMatchInlineSnapshot(` + ".outer { + :is(* .foo[data-v-test]) { color: red; + } + }" + `) + }) + + test('should keep leading universal selector in :where() in nested rules', () => { + expect(compileScoped(`.outer { :where(* .foo) { color: red; } }`)) + .toMatchInlineSnapshot(` + ".outer { + :where(* .foo[data-v-test]) { color: red; + } + }" + `) + }) + + test('should keep leading universal selector before non-space combinator in :is() in nested rules', () => { + expect(compileScoped(`.outer { :is(* > .foo) { color: red; } }`)) + .toMatchInlineSnapshot(` + ".outer { + :is(* > .foo[data-v-test]) { color: red; + } + }" + `) + }) + + test('should keep leading universal selector in ::v-slotted() in nested rules', () => { + expect(compileScoped(`.outer { ::v-slotted(* .foo) { color: red; } }`)) + .toMatchInlineSnapshot(` + ".outer { + * .foo[data-v-test-s] { color: red; + } + }" + `) + }) }) describe('SFC CSS modules', () => { diff --git a/packages/compiler-sfc/src/style/pluginScoped.ts b/packages/compiler-sfc/src/style/pluginScoped.ts index 81c735a6c3f..1506012eca9 100644 --- a/packages/compiler-sfc/src/style/pluginScoped.ts +++ b/packages/compiler-sfc/src/style/pluginScoped.ts @@ -76,17 +76,20 @@ function processRule(id: string, rule: Rule) { } processedRules.add(rule) let deep = false + let isNested = false let parent: Document | Container | undefined = rule.parent while (parent && parent.type !== 'root') { if ((parent as any).__deep) { deep = true - break + } + if (parent.type === 'rule') { + isNested = true } parent = parent.parent } rule.selector = selectorParser(selectorRoot => { selectorRoot.each(selector => { - rewriteSelector(id, rule, selector, selectorRoot, deep) + rewriteSelector(id, rule, selector, selectorRoot, deep, false, isNested) }) }).processSync(rule.selector) } @@ -98,6 +101,7 @@ function rewriteSelector( selectorRoot: selectorParser.Root, deep: boolean, slotted = false, + isNested = false, ) { let node: selectorParser.Node | null = null let shouldInject = !deep @@ -170,6 +174,7 @@ function rewriteSelector( selectorRoot, deep, true /* slotted */, + isNested, ) let last: selectorParser.Selector['nodes'][0] = n n.nodes[0].each(ss => { @@ -199,9 +204,16 @@ function rewriteSelector( if (!prev) { // * .foo {} -> .foo[xxxxxxx] {} if (next) { - if (next.type === 'combinator' && next.value === ' ') { - selector.removeChild(next) + if (next.type === 'combinator') { + if (next.value === ' ' && !isNested) { + // * .foo {} -> .foo[xxxxxxx] {} + selector.removeChild(next) + selector.removeChild(n) + } + // keep *: nested .outer { * .foo {} } or non-space combinator (* > .foo etc.) + return } + // *.foo {} -> .foo[xxxxxxx] {} selector.removeChild(n) return } else { @@ -244,7 +256,7 @@ function rewriteSelector( const { type, value } = node as selectorParser.Node if (type === 'pseudo' && (value === ':is' || value === ':where')) { ;(node as selectorParser.Pseudo).nodes.forEach(value => - rewriteSelector(id, rule, value, selectorRoot, deep, slotted), + rewriteSelector(id, rule, value, selectorRoot, deep, slotted, isNested), ) shouldInject = false }