From dc030743d800116a078bd0d584b1b859ae351af6 Mon Sep 17 00:00:00 2001 From: Eric Mika Date: Sun, 4 Jun 2023 15:59:11 -0400 Subject: [PATCH 1/4] Filter out Svelte's unexpected class prop console warnings Astro's hydration code passes a `class` prop to Svelte components, inducing Svelte to log a warning about an unknown prop. Preempting this by exporting a `class` prop from the Svelte component isn't a viable workaround since `class` is a reserved identifier in JS. This PR implements the console-filtering workaround suggested by @HiDeoo in #5665, borrowing the `useConsoleFilter` approach from the [preact integration](https://github.com/withastro/astro/blob/a1c0cbe604c9f91cdc421b5606aab574999eba01/packages/integrations/preact/src/server.ts#L72-L94). It would probably be better to generalize console filtering so it could be shared across multiple integrations. Ideally there would be a way to handle this in Svelte, but as was pointed out in the issue thread even they resort to [similar cringe-inducing hackery](https://github.com/sveltejs/kit/blob/master/packages/kit/src/runtime/client/client.js#L1974-L1996) in sveltekit. --- packages/integrations/svelte/client.js | 84 ++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/packages/integrations/svelte/client.js b/packages/integrations/svelte/client.js index 658ddb02f3d7..bc889f42d413 100644 --- a/packages/integrations/svelte/client.js +++ b/packages/integrations/svelte/client.js @@ -1,5 +1,8 @@ const noop = () => {}; +let originalConsoleWarning; +let consoleFilterRefs = 0; + export default (target) => { return (Component, props, slotted, { client }) => { if (!target.hasAttribute('ssr')) return; @@ -7,18 +10,24 @@ export default (target) => { for (const [key, value] of Object.entries(slotted)) { slots[key] = createSlotDefinition(key, value); } + try { - new Component({ - target, - props: { - ...props, - $$slots: slots, - $$scope: { ctx: [] }, - }, - hydrate: client !== 'only', - $$inline: true, - }); - } catch (e) {} + useConsoleFilter(); + try { + new Component({ + target, + props: { + ...props, + $$slots: slots, + $$scope: { ctx: [] }, + }, + hydrate: client !== 'only', + $$inline: true, + }); + } catch (e) {} + } finally { + finishUsingConsoleFilter(); + } }; }; @@ -51,3 +60,56 @@ function createSlotDefinition(key, children) { noop, ]; } + +/** + * Reduces console noise by filtering known non-problematic warnings. + * + * Performs reference counting to allow parallel usage from async code. + * + * To stop filtering, please ensure that there always is a matching call + * to `finishUsingConsoleFilter` afterwards. + */ +function useConsoleFilter() { + consoleFilterRefs++; + + if (!originalConsoleWarning) { + // eslint-disable-next-line no-console + originalConsoleWarning = console.warn; + try { + // eslint-disable-next-line no-console + console.warn = filteredConsoleWarning; + } catch (error) { + // If we're unable to hook `console.warn`, just accept it + } + } +} + +/** + * Indicates that the filter installed by `useConsoleFilter` + * is no longer needed by the calling code. + */ +function finishUsingConsoleFilter() { + consoleFilterRefs--; + + // Note: Instead of reverting `console.warning` back to the original + // when the reference counter reaches 0, we leave our hook installed + // to prevent potential race conditions once `check` is made async +} + +/** + * Hook/wrapper function for the global `console.warning` function. + * + * Ignores known non-problematic errors while any code is using the console filter. + * Otherwise, simply forwards all arguments to the original function. + */ +function filteredConsoleWarning(msg, ...rest) { + if (consoleFilterRefs > 0 && typeof msg === 'string') { + // Astro passes a `class` prop to the Svelte component, which + // outputs the following warning, which we can safely filter out. + const isKnownSvelteError = msg.includes( + "was created with unknown prop 'class'" + ); + if (isKnownSvelteError) return; + } + originalConsoleWarning(msg, ...rest); +} From c4c375806324ab779a00f0df0bb07d445d0381cc Mon Sep 17 00:00:00 2001 From: Eric Mika Date: Sat, 10 Jun 2023 14:34:36 -0400 Subject: [PATCH 2/4] Only filter Svelte console warnings in dev builds --- packages/integrations/svelte/client.js | 32 ++++++++++++-------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/integrations/svelte/client.js b/packages/integrations/svelte/client.js index bc889f42d413..d3a7d5a98f52 100644 --- a/packages/integrations/svelte/client.js +++ b/packages/integrations/svelte/client.js @@ -12,21 +12,21 @@ export default (target) => { } try { - useConsoleFilter(); - try { - new Component({ - target, - props: { - ...props, - $$slots: slots, - $$scope: { ctx: [] }, - }, - hydrate: client !== 'only', - $$inline: true, - }); - } catch (e) {} + if (import.meta.env.DEV) useConsoleFilter(); + + new Component({ + target, + props: { + ...props, + $$slots: slots, + $$scope: { ctx: [] }, + }, + hydrate: client !== 'only', + $$inline: true, + }); + } catch (e) { } finally { - finishUsingConsoleFilter(); + if (import.meta.env.DEV) finishUsingConsoleFilter(); } }; }; @@ -106,9 +106,7 @@ function filteredConsoleWarning(msg, ...rest) { if (consoleFilterRefs > 0 && typeof msg === 'string') { // Astro passes a `class` prop to the Svelte component, which // outputs the following warning, which we can safely filter out. - const isKnownSvelteError = msg.includes( - "was created with unknown prop 'class'" - ); + const isKnownSvelteError = msg.endsWith("was created with unknown prop 'class'"); if (isKnownSvelteError) return; } originalConsoleWarning(msg, ...rest); From 6216ac4d937e55c54a9e8a23aa2e67740c3cefc4 Mon Sep 17 00:00:00 2001 From: bluwy Date: Wed, 5 Jul 2023 14:29:39 +0800 Subject: [PATCH 3/4] Add changeset --- .changeset/big-elephants-drive.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/big-elephants-drive.md diff --git a/.changeset/big-elephants-drive.md b/.changeset/big-elephants-drive.md new file mode 100644 index 000000000000..2d760f715ee2 --- /dev/null +++ b/.changeset/big-elephants-drive.md @@ -0,0 +1,5 @@ +--- +'@astrojs/svelte': patch +--- + +Filter unknown `class` prop warnings From 8df5a2faf1c839cae80d1e5faaffb44cd9f79fbc Mon Sep 17 00:00:00 2001 From: Eric Mika Date: Sun, 13 Aug 2023 23:09:15 -0400 Subject: [PATCH 4/4] Fix lint error. --- packages/integrations/svelte/client.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/integrations/svelte/client.js b/packages/integrations/svelte/client.js index d3a7d5a98f52..0d07ff2ba563 100644 --- a/packages/integrations/svelte/client.js +++ b/packages/integrations/svelte/client.js @@ -73,10 +73,8 @@ function useConsoleFilter() { consoleFilterRefs++; if (!originalConsoleWarning) { - // eslint-disable-next-line no-console originalConsoleWarning = console.warn; try { - // eslint-disable-next-line no-console console.warn = filteredConsoleWarning; } catch (error) { // If we're unable to hook `console.warn`, just accept it