fix: preserve namespaces in CSS type selectors (#18678)

Fixes #18677.

- preserve namespace prefixes on CSS `TypeSelector` AST nodes
- accept wildcard local names in `svg|*` and `*|*`
- retain namespaces when printing modern ASTs
- keep namespaced universal selectors intact while adding scoped CSS
selectors
- add compile and print regressions covering all four namespace forms

---------

Co-authored-by: svelte-triage-bot <team@svelte.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
pull/18537/merge
svelte-triage-bot[bot] 5 days ago committed by GitHub
parent 19aa51d443
commit 9c2494b134
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: preserve namespaces in CSS type selectors

@ -204,15 +204,18 @@ function read_selector(parser, inside_pseudo_class = false) {
});
} else if (parser.eat('*')) {
let name = '*';
/** @type {string | undefined} */
let namespace;
if (parser.eat('|')) {
// * is the namespace (which we ignore)
name = read_identifier(parser);
namespace = name;
name = parser.eat('*') ? '*' : read_identifier(parser);
}
relative_selector.selectors.push({
type: 'TypeSelector',
name,
...(namespace !== undefined && { namespace }),
start,
end: parser.index
});
@ -314,15 +317,18 @@ function read_selector(parser, inside_pseudo_class = false) {
});
} else if (!parser.match_regex(REGEX_COMBINATOR)) {
let name = read_identifier(parser);
/** @type {string | undefined} */
let namespace;
if (parser.eat('|')) {
// we ignore the namespace when trying to find matching element classes
name = read_identifier(parser);
namespace = name;
name = parser.eat('*') ? '*' : read_identifier(parser);
}
relative_selector.selectors.push({
type: 'TypeSelector',
name,
...(namespace !== undefined && { namespace }),
start,
end: parser.index
});

@ -355,7 +355,11 @@ const visitors = {
continue;
}
if (selector.type === 'TypeSelector' && selector.name === '*') {
if (
selector.type === 'TypeSelector' &&
selector.name === '*' &&
selector.namespace === undefined
) {
context.state.code.update(selector.start, selector.end, modifier);
} else {
context.state.code.appendLeft(selector.end, modifier);

@ -520,6 +520,10 @@ function css_visitors(comments, js_comments) {
},
TypeSelector(node, context) {
if (node.namespace !== undefined) {
context.write(node.namespace === '*' ? '*' : escape_identifier(node.namespace));
context.write('|');
}
context.write(node.name === '*' ? node.name : escape_identifier(node.name));
}
};

@ -121,6 +121,7 @@ export namespace _CSS {
export interface TypeSelector extends BaseNode {
type: 'TypeSelector';
name: string;
namespace?: string;
}
export interface IdSelector extends BaseNode {

@ -0,0 +1,17 @@
@namespace svg url(http://www.w3.org/2000/svg);
svg|circle.svelte-xyz {
fill: red;
}
*|circle.svelte-xyz {
stroke: blue;
}
svg|*.svelte-xyz {
color: green;
}
*|*.svelte-xyz {
opacity: 0.5;
}

@ -0,0 +1,21 @@
<svg><circle /></svg>
<style>
@namespace svg url(http://www.w3.org/2000/svg);
svg|circle {
fill: red;
}
*|circle {
stroke: blue;
}
svg|* {
color: green;
}
*|* {
opacity: 0.5;
}
</style>

After

Width:  |  Height:  |  Size: 208 B

@ -0,0 +1,10 @@
<style>
@namespace svg url(http://www.w3.org/2000/svg);
svg|circle,
*|circle,
svg|*,
*|* {
fill: red;
}
</style>

@ -0,0 +1,12 @@
<style>
@namespace svg url(http://www.w3.org/2000/svg);
svg|circle,
*|circle,
svg|*,
*|* {
fill: red;
}
</style>

@ -1774,6 +1774,7 @@ declare module 'svelte/compiler' {
export interface TypeSelector extends BaseNode {
type: 'TypeSelector';
name: string;
namespace?: string;
}
export interface IdSelector extends BaseNode {

Loading…
Cancel
Save