diff --git a/.changeset/sweet-islands-sell.md b/.changeset/sweet-islands-sell.md new file mode 100644 index 0000000000..b4fbbf182c --- /dev/null +++ b/.changeset/sweet-islands-sell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: avoid auto-parenthesis for special-keywords-only `MediaQuery` diff --git a/packages/svelte/src/reactivity/media-query.js b/packages/svelte/src/reactivity/media-query.js index 22310df18d..d286709719 100644 --- a/packages/svelte/src/reactivity/media-query.js +++ b/packages/svelte/src/reactivity/media-query.js @@ -3,6 +3,19 @@ import { ReactiveValue } from './reactive-value.js'; const parenthesis_regex = /\(.+\)/; +// these keywords are valid media queries but they need to be without parenthesis +// +// eg: new MediaQuery('screen') +// +// however because of the auto-parenthesis logic in the constructor since there's no parentehesis +// in the media query they'll be surrounded by parenthesis +// +// however we can check if the media query is only composed of these keywords +// and skip the auto-parenthesis +// +// https://github.com/sveltejs/svelte/issues/15930 +const non_parenthesized_keywords = new Set(['all', 'print', 'screen', 'and', 'or', 'not', 'only']); + /** * Creates a media query and provides a `current` property that reflects whether or not it matches. * @@ -27,7 +40,12 @@ export class MediaQuery extends ReactiveValue { * @param {boolean} [fallback] Fallback value for the server */ constructor(query, fallback) { - let final_query = parenthesis_regex.test(query) ? query : `(${query})`; + let final_query = + parenthesis_regex.test(query) || + // we need to use `some` here because technically this `window.matchMedia('random,screen')` still returns true + query.split(/[\s,]+/).some((keyword) => non_parenthesized_keywords.has(keyword.trim())) + ? query + : `(${query})`; const q = window.matchMedia(final_query); super( () => q.matches, diff --git a/packages/svelte/tests/runtime-runes/samples/media-query/_config.js b/packages/svelte/tests/runtime-runes/samples/media-query/_config.js index f7a4ca05f5..d8b202955a 100644 --- a/packages/svelte/tests/runtime-runes/samples/media-query/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/media-query/_config.js @@ -5,5 +5,10 @@ export default test({ async test({ window }) { expect(window.matchMedia).toHaveBeenCalledWith('(max-width: 599px), (min-width: 900px)'); expect(window.matchMedia).toHaveBeenCalledWith('(min-width: 900px)'); + expect(window.matchMedia).toHaveBeenCalledWith('screen'); + expect(window.matchMedia).toHaveBeenCalledWith('not print'); + expect(window.matchMedia).toHaveBeenCalledWith('screen,print'); + expect(window.matchMedia).toHaveBeenCalledWith('screen, print'); + expect(window.matchMedia).toHaveBeenCalledWith('screen, random'); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/media-query/main.svelte b/packages/svelte/tests/runtime-runes/samples/media-query/main.svelte index 446a9213dd..fe07ed8ab0 100644 --- a/packages/svelte/tests/runtime-runes/samples/media-query/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/media-query/main.svelte @@ -3,4 +3,9 @@ const mq = new MediaQuery("(max-width: 599px), (min-width: 900px)"); const mq2 = new MediaQuery("min-width: 900px"); + const mq3 = new MediaQuery("screen"); + const mq4 = new MediaQuery("not print"); + const mq5 = new MediaQuery("screen,print"); + const mq6 = new MediaQuery("screen, print"); + const mq7 = new MediaQuery("screen, random");