From ce53bd6fd56c887cfab5ab4543af4b714c87ba5e Mon Sep 17 00:00:00 2001 From: paoloricciuti Date: Thu, 24 Oct 2024 17:34:37 +0200 Subject: [PATCH] fix: migrate css with JS and not regex --- packages/svelte/src/compiler/migrate/index.js | 54 ++++++++++++++++--- .../samples/is-not-where-has/input.svelte | 2 +- .../samples/is-not-where-has/output.svelte | 2 +- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 18fdadeb90..fa6b427fc9 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -34,6 +34,51 @@ class MigrationError extends Error { } } +/** + * + * @param {State} state + */ +function migrate_css(state) { + if (!state.analysis.css.ast?.start) return; + let code = state.str + .snip(state.analysis.css.ast.start, /** @type {number} */ (state.analysis.css.ast?.end)) + .toString(); + let starting = 0; + + // since we already blank css we can't work directly on `state.str` so we will create a copy that we can update + const str = new MagicString(code); + while (!code.startsWith('') && !!code.trim()) { + if ( + code.startsWith(':has') || + code.startsWith(':not') || + code.startsWith(':is') || + code.startsWith(':where') + ) { + let parenthesis = 1; + let start = code.indexOf('(') + 1; + let end = start; + let char = code[end]; + // find the closing parenthesis + while (parenthesis !== 0 && char) { + if (char === '(') parenthesis++; + if (char === ')') parenthesis--; + end++; + char = code[end]; + } + if (start && end) { + str.prependLeft(starting + start, ':global('); + str.appendRight(starting + end - 1, ')'); + starting += end - 1; + code = code.substring(end - 1); + continue; + } + } + starting++; + code = code.substring(1); + } + state.str.update(state.analysis.css.ast?.start, state.analysis.css.ast?.end, str.toString()); +} + /** * Does a best-effort migration of Svelte code towards using runes, event attributes and render tags. * May throw an error if the code is too complex to migrate automatically. @@ -317,14 +362,9 @@ export function migrate(source, { filename } = {}) { if (!parsed.instance && need_script) { str.appendRight(insertion_point, '\n\n\n'); } + migrate_css(state); return { - code: str - .toString() - // for some reason replacing the magic string doesn't work - .replaceAll( - /(?<=]*>[\s\S]*?:(?:is|not|where|has)\()([\s\S]+)(?=\)[\s\S]*?<\/style>)/gm, - ':global($1)' - ) + code: str.toString() }; } catch (e) { if (!(e instanceof MigrationError)) { diff --git a/packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte b/packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte index 41656ab50f..f8c3929824 100644 --- a/packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte +++ b/packages/svelte/tests/migrate/samples/is-not-where-has/input.svelte @@ -39,7 +39,7 @@ what if i'm talking about `:has()` in my blog? div :not(.class:is(span:where(:focus-within))){} div :is(.class:is(span:is(:hover))){} div :where(.class:is(span:has(* > *))){} -div :is(.class:is(span:is(:hover)), .x){} + div :is(.class:is(span:is(:hover)), .x){} div{ p:has(&){ diff --git a/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte b/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte index c7fd02e012..aacd8edccc 100644 --- a/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte +++ b/packages/svelte/tests/migrate/samples/is-not-where-has/output.svelte @@ -39,7 +39,7 @@ what if i'm talking about `:has()` in my blog? div :not(:global(.class:is(span:where(:focus-within)))){} div :is(:global(.class:is(span:is(:hover)))){} div :where(:global(.class:is(span:has(* > *)))){} -div :is(:global(.class:is(span:is(:hover)), .x)){} + div :is(:global(.class:is(span:is(:hover)), .x)){} div{ p:has(:global(&)){