diff --git a/packages/compiler-sfc/__tests__/compileStyle.spec.ts b/packages/compiler-sfc/__tests__/compileStyle.spec.ts index 78fd52425..70c6af557 100644 --- a/packages/compiler-sfc/__tests__/compileStyle.spec.ts +++ b/packages/compiler-sfc/__tests__/compileStyle.spec.ts @@ -493,7 +493,31 @@ describe('SFC style preprocessors', () => { }" `) expect(compileScoped(`.foo * { color: red; }`)).toMatchInlineSnapshot(` - ".foo[data-v-test] * { color: red; + ".foo[data-v-test] [data-v-test] { color: red; + }" + `) + expect(compileScoped(`.foo :active { color: red; }`)) + .toMatchInlineSnapshot(` + ".foo[data-v-test] :active { color: red; + }" + `) + expect(compileScoped(`.foo *:active { color: red; }`)) + .toMatchInlineSnapshot(` + ".foo[data-v-test] [data-v-test]:active { color: red; + }" + `) + expect(compileScoped(`.foo * .bar { color: red; }`)).toMatchInlineSnapshot(` + ".foo * .bar[data-v-test] { color: red; + }" + `) + expect(compileScoped(`:last-child * { color: red; }`)) + .toMatchInlineSnapshot(` + "[data-v-test]:last-child [data-v-test] { color: red; + }" + `) + expect(compileScoped(`:last-child *:active { color: red; }`)) + .toMatchInlineSnapshot(` + "[data-v-test]:last-child [data-v-test]:active { color: red; }" `) }) diff --git a/packages/compiler-sfc/src/style/pluginScoped.ts b/packages/compiler-sfc/src/style/pluginScoped.ts index d0aaddd76..4845d8eee 100644 --- a/packages/compiler-sfc/src/style/pluginScoped.ts +++ b/packages/compiler-sfc/src/style/pluginScoped.ts @@ -102,6 +102,7 @@ function rewriteSelector( slotted = false, ) { let node: selectorParser.Node | null = null + let starNode: selectorParser.Node | null = null let shouldInject = !deep // find the last child node to insert attribute selector selector.each(n => { @@ -216,17 +217,21 @@ function rewriteSelector( return false } } - // .foo * -> .foo[xxxxxxx] * - if (node) return + // store the universal selector so it can be rewritten later + // .foo * -> .foo[xxxxxxx] [xxxxxxx] + starNode = n } if ( - (n.type !== 'pseudo' && n.type !== 'combinator') || + (n.type !== 'pseudo' && + n.type !== 'combinator' && + n.type !== 'universal') || (n.type === 'pseudo' && (n.value === ':is' || n.value === ':where') && !node) ) { node = n + starNode = null } }) @@ -274,6 +279,20 @@ function rewriteSelector( quoteMark: `"`, }), ) + // Used for trailing universal selectors (#12906) + // `.foo * {}` -> `.foo[xxxxxxx] [xxxxxxx] {}` + if (starNode) { + selector.insertBefore( + starNode, + selectorParser.attribute({ + attribute: idToAdd, + value: idToAdd, + raws: {}, + quoteMark: `"`, + }), + ) + selector.removeChild(starNode) + } } }