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 1 year 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 > 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 ## css_global_invalid_placement
> :global(...) can be at the start or end of a selector sequence, but not in the middle > :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"); 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 * :global(...) can be at the start or end of a selector sequence, but not in the middle
* @param {null | number | NodeLike} node * @param {null | number | NodeLike} node

@ -16,7 +16,11 @@ import { merge } from '../../visitors.js';
* >} CssVisitors * >} 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) { function is_global(relative_selector) {
const first = relative_selector.selectors[0]; const first = relative_selector.selectors[0];
@ -135,16 +139,26 @@ const validation_visitors = {
context.next(); context.next();
}, },
ComplexSelector(node, context) { ComplexSelector(node) {
// ensure `:global(...)` is not used in the middle of a selector
{ {
const a = node.children.findIndex((child) => !is_global(child)); const global = node.children.find(is_global);
const b = node.children.findLastIndex((child) => !is_global(child));
if (global) {
if (a !== b) { const idx = node.children.indexOf(global);
for (let i = a; i <= b; i += 1) {
if (is_global(node.children[i])) { if (global.selectors[0].args === null && idx !== node.children.length - 1) {
e.css_global_invalid_placement(node.children[i].selectors[0]); // 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