Skip to content

Commit e714b89

Browse files
committed
feat: add hasUnscopedGlobalCss to compile metadata
1 parent 0abd7f2 commit e714b89

File tree

7 files changed

+77
-7
lines changed

7 files changed

+77
-7
lines changed

.changeset/plenty-hotels-mix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': minor
3+
---
4+
5+
feat: add `hasUnscopedGlobalCss` to `compile` metadata

packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import { is_keyframes_node } from '../../css.js';
77
import { is_global, is_unscoped_pseudo_class } from './utils.js';
88

99
/**
10+
* We need to use an object for `has_global_unscoped` since state is spread
1011
* @typedef {Visitors<
1112
* AST.CSS.Node,
1213
* {
1314
* keyframes: string[];
1415
* rule: AST.CSS.Rule | null;
16+
* has_global_unscoped: { value: boolean };
1517
* }
1618
* >} CssVisitors
1719
*/
@@ -28,6 +30,30 @@ function is_global_block_selector(simple_selector) {
2830
);
2931
}
3032

33+
/**
34+
* @param {import('../types.js').Context["path"]} path
35+
* @param {AST.CSS.Rule | null} [rule]
36+
* @returns
37+
*/
38+
function is_unscoped_global(path, rule) {
39+
// remove every at rule or stylesheet and the current rule in case is passed in from `ComplexSelector`
40+
const parents = path.filter(
41+
(parent) => parent.type !== 'Atrule' && parent.type !== 'StyleSheet' && parent !== rule
42+
);
43+
44+
let unscoped_global = true;
45+
46+
// no parents means we are on top
47+
if (parents.length > 0) {
48+
// let's check that everything in the path is not a global block
49+
unscoped_global = parents.every((parent) => {
50+
return parent.type !== 'Rule' || parent.metadata.is_global_block;
51+
});
52+
}
53+
54+
return unscoped_global;
55+
}
56+
3157
/**
3258
*
3359
* @param {Array<AST.CSS.Node>} path
@@ -64,6 +90,16 @@ const css_visitors = {
6490
}
6591
}
6692
}
93+
94+
if (idx === 0) {
95+
if (
96+
is_unscoped_global(context.path, context.state.rule) &&
97+
context.state.rule &&
98+
context.state.rule.block.children.length > 0
99+
) {
100+
context.state.has_global_unscoped.value = true;
101+
}
102+
}
67103
}
68104
}
69105

@@ -174,7 +210,8 @@ const css_visitors = {
174210
node.metadata.is_global_block = node.prelude.children.some((selector) => {
175211
let is_global_block = false;
176212

177-
for (const child of selector.children) {
213+
for (let i = 0; i < selector.children.length; i++) {
214+
const child = selector.children[i];
178215
const idx = child.selectors.findIndex(is_global_block_selector);
179216

180217
if (is_global_block) {
@@ -184,6 +221,11 @@ const css_visitors = {
184221

185222
if (idx !== -1) {
186223
is_global_block = true;
224+
if (i === 0) {
225+
if (is_unscoped_global(context.path) && node.block.children.length > 0) {
226+
context.state.has_global_unscoped.value = true;
227+
}
228+
}
187229
for (let i = idx + 1; i < child.selectors.length; i++) {
188230
walk(/** @type {AST.CSS.Node} */ (child.selectors[i]), null, {
189231
ComplexSelector(node) {
@@ -283,5 +325,12 @@ const css_visitors = {
283325
* @param {ComponentAnalysis} analysis
284326
*/
285327
export function analyze_css(stylesheet, analysis) {
286-
walk(stylesheet, { keyframes: analysis.css.keyframes, rule: null }, css_visitors);
328+
const css_state = {
329+
keyframes: analysis.css.keyframes,
330+
rule: null,
331+
// we need to use an object since state is spread
332+
has_global_unscoped: { value: false }
333+
};
334+
walk(stylesheet, css_state, css_visitors);
335+
analysis.css.has_global_unscoped = css_state.has_global_unscoped.value;
287336
}

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,8 @@ export function analyze_component(root, source, options) {
455455
hash
456456
})
457457
: '',
458-
keyframes: []
458+
keyframes: [],
459+
has_global_unscoped: false
459460
},
460461
source,
461462
undefined_exports: new Map(),

packages/svelte/src/compiler/phases/3-transform/index.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export function transform_component(analysis, source, options) {
2121
css: null,
2222
warnings: state.warnings, // set afterwards
2323
metadata: {
24-
runes: analysis.runes
24+
runes: analysis.runes,
25+
hasUnscopedGlobalCss: analysis.css.has_global_unscoped
2526
},
2627
ast: /** @type {any} */ (null) // set afterwards
2728
};
@@ -53,7 +54,8 @@ export function transform_component(analysis, source, options) {
5354
css,
5455
warnings: state.warnings, // set afterwards. TODO apply preprocessor sourcemap
5556
metadata: {
56-
runes: analysis.runes
57+
runes: analysis.runes,
58+
hasUnscopedGlobalCss: analysis.css.has_global_unscoped
5759
},
5860
ast: /** @type {any} */ (null) // set afterwards
5961
};
@@ -72,7 +74,8 @@ export function transform_module(analysis, source, options) {
7274
css: null,
7375
warnings: state.warnings, // set afterwards
7476
metadata: {
75-
runes: true
77+
runes: true,
78+
hasUnscopedGlobalCss: false
7679
},
7780
ast: /** @type {any} */ (null) // set afterwards
7881
};
@@ -102,7 +105,8 @@ export function transform_module(analysis, source, options) {
102105
}),
103106
css: null,
104107
metadata: {
105-
runes: true
108+
runes: true,
109+
hasUnscopedGlobalCss: false
106110
},
107111
warnings: state.warnings, // set afterwards
108112
ast: /** @type {any} */ (null) // set afterwards

packages/svelte/src/compiler/phases/types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export interface ComponentAnalysis extends Analysis {
7373
ast: AST.CSS.StyleSheet | null;
7474
hash: string;
7575
keyframes: string[];
76+
has_global_unscoped: boolean;
7677
};
7778
source: string;
7879
undefined_exports: Map<string, Node>;

packages/svelte/src/compiler/types/index.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ export interface CompileResult {
3535
* For `compileModule`, this is always `true`
3636
*/
3737
runes: boolean;
38+
/**
39+
* Whether the component contains a top level :global selector or not
40+
* For `compileModule`, this is always `true`
41+
*/
42+
hasUnscopedGlobalCss: boolean;
3843
};
3944
/** The AST */
4045
ast: any;

packages/svelte/types/index.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,11 @@ declare module 'svelte/compiler' {
769769
* For `compileModule`, this is always `true`
770770
*/
771771
runes: boolean;
772+
/**
773+
* Whether the component contains a top level :global selector or not
774+
* For `compileModule`, this is always `true`
775+
*/
776+
hasUnscopedGlobalCss: boolean;
772777
};
773778
/** The AST */
774779
ast: any;

0 commit comments

Comments
 (0)