feat: add `hasUnscopedGlobalCss` to `compile` metadata

pull/15450/head
paoloricciuti 1 year ago
parent 0abd7f2a7f
commit e714b89545

@ -0,0 +1,5 @@
---
'svelte': minor
---
feat: add `hasUnscopedGlobalCss` to `compile` metadata

@ -7,11 +7,13 @@ import { is_keyframes_node } from '../../css.js';
import { is_global, is_unscoped_pseudo_class } from './utils.js';
/**
* We need to use an object for `has_global_unscoped` since state is spread
* @typedef {Visitors<
* AST.CSS.Node,
* {
* keyframes: string[];
* rule: AST.CSS.Rule | null;
* has_global_unscoped: { value: boolean };
* }
* >} CssVisitors
*/
@ -28,6 +30,30 @@ function is_global_block_selector(simple_selector) {
);
}
/**
* @param {import('../types.js').Context["path"]} path
* @param {AST.CSS.Rule | null} [rule]
* @returns
*/
function is_unscoped_global(path, rule) {
// remove every at rule or stylesheet and the current rule in case is passed in from `ComplexSelector`
const parents = path.filter(
(parent) => parent.type !== 'Atrule' && parent.type !== 'StyleSheet' && parent !== rule
);
let unscoped_global = true;
// no parents means we are on top
if (parents.length > 0) {
// let's check that everything in the path is not a global block
unscoped_global = parents.every((parent) => {
return parent.type !== 'Rule' || parent.metadata.is_global_block;
});
}
return unscoped_global;
}
/**
*
* @param {Array<AST.CSS.Node>} path
@ -64,6 +90,16 @@ const css_visitors = {
}
}
}
if (idx === 0) {
if (
is_unscoped_global(context.path, context.state.rule) &&
context.state.rule &&
context.state.rule.block.children.length > 0
) {
context.state.has_global_unscoped.value = true;
}
}
}
}
@ -174,7 +210,8 @@ const css_visitors = {
node.metadata.is_global_block = node.prelude.children.some((selector) => {
let is_global_block = false;
for (const child of selector.children) {
for (let i = 0; i < selector.children.length; i++) {
const child = selector.children[i];
const idx = child.selectors.findIndex(is_global_block_selector);
if (is_global_block) {
@ -184,6 +221,11 @@ const css_visitors = {
if (idx !== -1) {
is_global_block = true;
if (i === 0) {
if (is_unscoped_global(context.path) && node.block.children.length > 0) {
context.state.has_global_unscoped.value = true;
}
}
for (let i = idx + 1; i < child.selectors.length; i++) {
walk(/** @type {AST.CSS.Node} */ (child.selectors[i]), null, {
ComplexSelector(node) {
@ -283,5 +325,12 @@ const css_visitors = {
* @param {ComponentAnalysis} analysis
*/
export function analyze_css(stylesheet, analysis) {
walk(stylesheet, { keyframes: analysis.css.keyframes, rule: null }, css_visitors);
const css_state = {
keyframes: analysis.css.keyframes,
rule: null,
// we need to use an object since state is spread
has_global_unscoped: { value: false }
};
walk(stylesheet, css_state, css_visitors);
analysis.css.has_global_unscoped = css_state.has_global_unscoped.value;
}

@ -455,7 +455,8 @@ export function analyze_component(root, source, options) {
hash
})
: '',
keyframes: []
keyframes: [],
has_global_unscoped: false
},
source,
undefined_exports: new Map(),

@ -21,7 +21,8 @@ export function transform_component(analysis, source, options) {
css: null,
warnings: state.warnings, // set afterwards
metadata: {
runes: analysis.runes
runes: analysis.runes,
hasUnscopedGlobalCss: analysis.css.has_global_unscoped
},
ast: /** @type {any} */ (null) // set afterwards
};
@ -53,7 +54,8 @@ export function transform_component(analysis, source, options) {
css,
warnings: state.warnings, // set afterwards. TODO apply preprocessor sourcemap
metadata: {
runes: analysis.runes
runes: analysis.runes,
hasUnscopedGlobalCss: analysis.css.has_global_unscoped
},
ast: /** @type {any} */ (null) // set afterwards
};
@ -72,7 +74,8 @@ export function transform_module(analysis, source, options) {
css: null,
warnings: state.warnings, // set afterwards
metadata: {
runes: true
runes: true,
hasUnscopedGlobalCss: false
},
ast: /** @type {any} */ (null) // set afterwards
};
@ -102,7 +105,8 @@ export function transform_module(analysis, source, options) {
}),
css: null,
metadata: {
runes: true
runes: true,
hasUnscopedGlobalCss: false
},
warnings: state.warnings, // set afterwards
ast: /** @type {any} */ (null) // set afterwards

@ -73,6 +73,7 @@ export interface ComponentAnalysis extends Analysis {
ast: AST.CSS.StyleSheet | null;
hash: string;
keyframes: string[];
has_global_unscoped: boolean;
};
source: string;
undefined_exports: Map<string, Node>;

@ -35,6 +35,11 @@ export interface CompileResult {
* For `compileModule`, this is always `true`
*/
runes: boolean;
/**
* Whether the component contains a top level :global selector or not
* For `compileModule`, this is always `true`
*/
hasUnscopedGlobalCss: boolean;
};
/** The AST */
ast: any;

@ -769,6 +769,11 @@ declare module 'svelte/compiler' {
* For `compileModule`, this is always `true`
*/
runes: boolean;
/**
* Whether the component contains a top level :global selector or not
* For `compileModule`, this is always `true`
*/
hasUnscopedGlobalCss: boolean;
};
/** The AST */
ast: any;

Loading…
Cancel
Save