@ -27,10 +27,25 @@ function is_global(relative_selector) {
return (
first . type === 'PseudoClassSelector' &&
first . name === 'global' &&
relative _selector . selectors . every (
( selector ) =>
selector . type === 'PseudoClassSelector' || selector . type === 'PseudoElementSelector'
)
( first . args === null ||
// Only these two selector types keep the whole selector global, because e.g.
// :global(button).x means that the selector is still scoped because of the .x
relative _selector . selectors . every (
( selector ) =>
selector . type === 'PseudoClassSelector' || selector . type === 'PseudoElementSelector'
) )
) ;
}
/ * *
* True if is ` :global `
* @ param { Css . SimpleSelector } simple _selector
* /
function is _global _block _selector ( simple _selector ) {
return (
simple _selector . type === 'PseudoClassSelector' &&
simple _selector . name === 'global' &&
simple _selector . args === null
) ;
}
@ -53,16 +68,7 @@ const analysis_visitors = {
) ;
} ,
RelativeSelector ( node , context ) {
node . metadata . is _global =
node . selectors . length >= 1 &&
node . selectors [ 0 ] . type === 'PseudoClassSelector' &&
node . selectors [ 0 ] . name === 'global' &&
// we want :global(...) and :global or :global.x, but not div:global
( node . selectors [ 0 ] . args === null ||
node . selectors . every (
( selector ) =>
selector . type === 'PseudoClassSelector' || selector . type === 'PseudoElementSelector'
) ) ;
node . metadata . is _global = node . selectors . length >= 1 && is _global ( node ) ;
if ( node . selectors . length === 1 ) {
const first = node . selectors [ 0 ] ;
@ -87,18 +93,14 @@ const analysis_visitors = {
Rule ( node , context ) {
node . metadata . parent _rule = context . state . rule ;
// `:global {...}` or `div :global {...}` or `:global div`
node . metadata . is _global _block = node . prelude . children . some ( ( selector ) => {
let is _global _block = false ;
for ( const child of selector . children ) {
const idx = child . selectors . findIndex (
( s ) => s . type === 'PseudoClassSelector' && s . name === 'global' && s . args === null
) ;
const idx = child . selectors . findIndex ( is _global _block _selector ) ;
if ( is _global _block ) {
// Do this here, not after setting it to true:
// All selectors after :global are unscoped, but in e.g. div:global, div is still scoped
// All selectors after :global are unscoped
child . metadata . is _global _like = true ;
}
@ -139,19 +141,44 @@ const validation_visitors = {
}
const complex _selector = node . prelude . children [ 0 ] ;
const relative _selector = complex _selector . children [ complex _selector . children . length - 1 ] ;
const global _selector = complex _selector . children . find ( ( r ) => {
return r . selectors . some ( is _global _block _selector ) ;
} ) ;
if ( ! global _selector ) {
throw new Error ( 'Internal error: global block without :global selector' ) ;
}
const global _block _selector _idx =
global _selector . selectors . findIndex ( is _global _block _selector ) ;
const starts _with _nesting _selector =
complex _selector . children [ 0 ] . selectors . length === 1 &&
complex _selector . children [ 0 ] . selectors [ 0 ] . type === 'NestingSelector' ;
if (
( ! global _selector . combinator &&
global _selector . selectors . length > 1 &&
( global _block _selector _idx === 0 ||
( global _block _selector _idx === 1 && starts _with _nesting _selector ) ) ) ||
( global _selector . combinator ? . name === ' ' && starts _with _nesting _selector )
) {
// div { :global.x { ... } } desugars to div :global.x { ... } which results in div.svelte-hash.x { ... }
// but it would be very hard to code-mod that and certainly doesn't look like it to the user,
// therefore we make this an error
e . css _global _block _invalid _placement ( global _selector ) ;
}
if (
relative _selector . combinator &&
relative _selector . combinator . name !== ' ' &&
relative _selector . selectors . length === 1
global _selector . combinator &&
// p :global {...} or p > :global.x {...} is valid
global _selector . combinator . name !== ' ' &&
global _selector . selectors . length === 1
) {
const s = relative _selector . selectors [ 0 ] ;
if ( s . type === 'PseudoClassSelector' && s . name === 'global' && s . args === null ) {
e . css _global _block _invalid _combinator (
relative _selector ,
relative _selector . combinator . name
) ;
const next =
complex _selector . children [ complex _selector . children . indexOf ( global _selector ) + 1 ] ;
// p > :global div {...} is valid, but p > :global > div {...} or p > :global {...} is not
if ( ! next || next . combinator ? . name !== ' ' ) {
e . css _global _block _invalid _combinator ( global _selector , global _selector . combinator . name ) ;
}
}
@ -159,7 +186,7 @@ const validation_visitors = {
if (
declaration &&
// :global { color: red; } is invalid, but foo :global { color: red; } or foo:global { color: red; } is valid
// :global { color: red; } is invalid, but foo :global { color: red; } is valid
node . prelude . children . length === 1 &&
node . prelude . children [ 0 ] . children . length === 1 &&
node . prelude . children [ 0 ] . children [ 0 ] . selectors . length === 1