From 0bf2cca5d7fe1fb025feae00ab3c12ed80f9ea9a Mon Sep 17 00:00:00 2001 From: Eric Mika Date: Tue, 15 Aug 2023 18:31:01 -0400 Subject: [PATCH] Filter out Svelte's unknown class prop console warnings (#7291) * 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. * Only filter Svelte console warnings in dev builds * Add changeset * Fix lint error. --------- Co-authored-by: bluwy Co-authored-by: Nate Moore --- .changeset/big-elephants-drive.md | 5 +++ packages/integrations/svelte/client.js | 60 +++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) 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 diff --git a/packages/integrations/svelte/client.js b/packages/integrations/svelte/client.js index 658ddb02f3d7..0d07ff2ba563 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,7 +10,10 @@ export default (target) => { for (const [key, value] of Object.entries(slotted)) { slots[key] = createSlotDefinition(key, value); } + try { + if (import.meta.env.DEV) useConsoleFilter(); + new Component({ target, props: { @@ -18,7 +24,10 @@ export default (target) => { hydrate: client !== 'only', $$inline: true, }); - } catch (e) {} + } catch (e) { + } finally { + if (import.meta.env.DEV) finishUsingConsoleFilter(); + } }; }; @@ -51,3 +60,52 @@ 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) { + originalConsoleWarning = console.warn; + try { + 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.endsWith("was created with unknown prop 'class'"); + if (isKnownSvelteError) return; + } + originalConsoleWarning(msg, ...rest); +}