Skip to content

Commit ce53bd6

Browse files
committed
fix: migrate css with JS and not regex
1 parent 24fafc3 commit ce53bd6

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

packages/svelte/src/compiler/migrate/index.js

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,51 @@ class MigrationError extends Error {
3434
}
3535
}
3636

37+
/**
38+
*
39+
* @param {State} state
40+
*/
41+
function migrate_css(state) {
42+
if (!state.analysis.css.ast?.start) return;
43+
let code = state.str
44+
.snip(state.analysis.css.ast.start, /** @type {number} */ (state.analysis.css.ast?.end))
45+
.toString();
46+
let starting = 0;
47+
48+
// since we already blank css we can't work directly on `state.str` so we will create a copy that we can update
49+
const str = new MagicString(code);
50+
while (!code.startsWith('</style>') && !!code.trim()) {
51+
if (
52+
code.startsWith(':has') ||
53+
code.startsWith(':not') ||
54+
code.startsWith(':is') ||
55+
code.startsWith(':where')
56+
) {
57+
let parenthesis = 1;
58+
let start = code.indexOf('(') + 1;
59+
let end = start;
60+
let char = code[end];
61+
// find the closing parenthesis
62+
while (parenthesis !== 0 && char) {
63+
if (char === '(') parenthesis++;
64+
if (char === ')') parenthesis--;
65+
end++;
66+
char = code[end];
67+
}
68+
if (start && end) {
69+
str.prependLeft(starting + start, ':global(');
70+
str.appendRight(starting + end - 1, ')');
71+
starting += end - 1;
72+
code = code.substring(end - 1);
73+
continue;
74+
}
75+
}
76+
starting++;
77+
code = code.substring(1);
78+
}
79+
state.str.update(state.analysis.css.ast?.start, state.analysis.css.ast?.end, str.toString());
80+
}
81+
3782
/**
3883
* Does a best-effort migration of Svelte code towards using runes, event attributes and render tags.
3984
* May throw an error if the code is too complex to migrate automatically.
@@ -317,14 +362,9 @@ export function migrate(source, { filename } = {}) {
317362
if (!parsed.instance && need_script) {
318363
str.appendRight(insertion_point, '\n</script>\n\n');
319364
}
365+
migrate_css(state);
320366
return {
321-
code: str
322-
.toString()
323-
// for some reason replacing the magic string doesn't work
324-
.replaceAll(
325-
/(?<=<style[^>]*>[\s\S]*?:(?:is|not|where|has)\()([\s\S]+)(?=\)[\s\S]*?<\/style>)/gm,
326-
':global($1)'
327-
)
367+
code: str.toString()
328368
};
329369
} catch (e) {
330370
if (!(e instanceof MigrationError)) {

packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ what if i'm talking about `:has()` in my blog?
3939
div :not(.class:is(span:where(:focus-within))){}
4040
div :is(.class:is(span:is(:hover))){}
4141
div :where(.class:is(span:has(* > *))){}
42-
div :is(.class:is(span:is(:hover)), .x){}
42+
div :is(.class:is(span:is(:hover)), .x){}
4343
4444
div{
4545
p:has(&){

packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ what if i'm talking about `:has()` in my blog?
3939
div :not(:global(.class:is(span:where(:focus-within)))){}
4040
div :is(:global(.class:is(span:is(:hover)))){}
4141
div :where(:global(.class:is(span:has(* > *)))){}
42-
div :is(:global(.class:is(span:is(:hover)), .x)){}
42+
div :is(:global(.class:is(span:is(:hover)), .x)){}
4343
4444
div{
4545
p:has(:global(&)){

0 commit comments

Comments
 (0)