fix: prevent invalid `:global` usage (#12474)

* fix: prevent invalid `:global` usage

Error when it's not at the end of a selector

closes #12437

* fix validation

* fix

* fix types

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/12478/head
Simon H 4 months ago committed by GitHub
parent bc9907aa1c
commit 04da87b599
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -22,6 +22,10 @@
> A :global {...} block cannot modify an existing selector
## css_global_block_invalid_placement
> A :global {...} block can only appear at the end of a selector sequence (did you mean to use :global(...) instead?)
## css_global_invalid_placement
> :global(...) can be at the start or end of a selector sequence, but not in the middle

@ -470,6 +470,15 @@ export function css_global_block_invalid_modifier(node) {
e(node, "css_global_block_invalid_modifier", "A :global {...} block cannot modify an existing selector");
}
/**
* A :global {...} block can only appear at the end of a selector sequence (did you mean to use :global(...) instead?)
* @param {null | number | NodeLike} node
* @returns {never}
*/
export function css_global_block_invalid_placement(node) {
e(node, "css_global_block_invalid_placement", "A :global {...} block can only appear at the end of a selector sequence (did you mean to use :global(...) instead?)");
}
/**
* :global(...) can be at the start or end of a selector sequence, but not in the middle
* @param {null | number | NodeLike} node

@ -16,7 +16,11 @@ import { merge } from '../../visitors.js';
* >} CssVisitors
*/
/** @param {Css.RelativeSelector} relative_selector */
/**
* True if is `:global(...)` or `:global`
* @param {Css.RelativeSelector} relative_selector
* @returns {relative_selector is Css.RelativeSelector & { selectors: [Css.PseudoClassSelector, ...Array<Css.PseudoClassSelector | Css.PseudoElementSelector>] }}
*/
function is_global(relative_selector) {
const first = relative_selector.selectors[0];
@ -135,16 +139,26 @@ const validation_visitors = {
context.next();
},
ComplexSelector(node, context) {
// ensure `:global(...)` is not used in the middle of a selector
ComplexSelector(node) {
{
const a = node.children.findIndex((child) => !is_global(child));
const b = node.children.findLastIndex((child) => !is_global(child));
if (a !== b) {
for (let i = a; i <= b; i += 1) {
if (is_global(node.children[i])) {
e.css_global_invalid_placement(node.children[i].selectors[0]);
const global = node.children.find(is_global);
if (global) {
const idx = node.children.indexOf(global);
if (global.selectors[0].args === null && idx !== node.children.length - 1) {
// ensure `:global` is only at the end of a selector
e.css_global_block_invalid_placement(global.selectors[0]);
} else if (
global.selectors[0].args !== null &&
idx !== 0 &&
idx !== node.children.length - 1
) {
// ensure `:global(...)` is not used in the middle of a selector (but multiple `global(...)` in sequence are ok)
for (let i = idx + 1; i < node.children.length; i++) {
if (!is_global(node.children[i])) {
e.css_global_invalid_placement(global.selectors[0]);
}
}
}
}

@ -0,0 +1,10 @@
import { test } from '../../test';
export default test({
error: {
code: 'css_global_block_invalid_placement',
message:
'A :global {...} block can only appear at the end of a selector sequence (did you mean to use :global(...) instead?)',
position: [50, 57]
}
});

@ -0,0 +1,8 @@
<style>
/* ok */
.x :global {
}
/* not ok */
:global .x {
}
</style>
Loading…
Cancel
Save