From e0e79b4fe47f64006718d547c898b9f67fe4d424 Mon Sep 17 00:00:00 2001 From: Stefanos Anagnostou Date: Thu, 9 Nov 2023 12:02:23 +0200 Subject: [PATCH] [SDK-805] Use the `errorThrower` shared util when throwing errors (#1999) * refactor(clerk-react): Use the errorThrower shared util when throwing errors * chore(clerk-react): Add changeset * chore(clerk-react): Update packages/react/src/utils/useMaxAllowedInstancesGuard.tsx Co-authored-by: panteliselef --------- Co-authored-by: panteliselef --- .changeset/tough-pots-grow.md | 6 ++++ packages/react/src/components/withClerk.tsx | 3 +- packages/react/src/components/withSession.tsx | 3 +- packages/react/src/components/withUser.tsx | 3 +- packages/react/src/contexts/assertHelpers.ts | 5 +-- packages/react/src/errors.ts | 31 +++++++++---------- packages/react/src/hooks/useAuth.ts | 3 +- packages/react/src/isomorphicClerk.ts | 6 ++-- packages/react/src/utils/childrenUtils.tsx | 3 +- packages/react/src/utils/errorThrower.ts | 2 +- .../src/utils/useMaxAllowedInstancesGuard.tsx | 4 ++- packages/shared/src/error.ts | 5 +++ .../shared/src/utils/logErrorInDevMode.ts | 2 +- 13 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 .changeset/tough-pots-grow.md diff --git a/.changeset/tough-pots-grow.md b/.changeset/tough-pots-grow.md new file mode 100644 index 0000000000..5f5c5b9ee9 --- /dev/null +++ b/.changeset/tough-pots-grow.md @@ -0,0 +1,6 @@ +--- +'@clerk/shared': patch +'@clerk/clerk-react': patch +--- + +Use the errorThrower shared utility when throwing errors diff --git a/packages/react/src/components/withClerk.tsx b/packages/react/src/components/withClerk.tsx index e7a4f6b1bb..e0065dc025 100644 --- a/packages/react/src/components/withClerk.tsx +++ b/packages/react/src/components/withClerk.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { useIsomorphicClerkContext } from '../contexts/IsomorphicClerkContext'; import { LoadedGuarantee } from '../contexts/StructureContext'; import { hocChildrenNotAFunctionError } from '../errors'; +import { errorThrower } from '../utils'; export const withClerk =

( Component: React.ComponentType

, @@ -37,7 +38,7 @@ export const WithClerk: React.FC<{ const clerk = useIsomorphicClerkContext(); if (typeof children !== 'function') { - throw new Error(hocChildrenNotAFunctionError); + errorThrower.throw(hocChildrenNotAFunctionError); } if (!clerk.loaded) { diff --git a/packages/react/src/components/withSession.tsx b/packages/react/src/components/withSession.tsx index a4b08e264f..276269beb5 100644 --- a/packages/react/src/components/withSession.tsx +++ b/packages/react/src/components/withSession.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { useSessionContext } from '../contexts/SessionContext'; import { hocChildrenNotAFunctionError } from '../errors'; +import { errorThrower } from '../utils'; export const withSession =

( Component: React.ComponentType

, @@ -35,7 +36,7 @@ export const WithSession: React.FC<{ const session = useSessionContext(); if (typeof children !== 'function') { - throw new Error(hocChildrenNotAFunctionError); + errorThrower.throw(hocChildrenNotAFunctionError); } if (!session) { diff --git a/packages/react/src/components/withUser.tsx b/packages/react/src/components/withUser.tsx index f49f9ebab4..87d3aeece7 100644 --- a/packages/react/src/components/withUser.tsx +++ b/packages/react/src/components/withUser.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { useUserContext } from '../contexts/UserContext'; import { hocChildrenNotAFunctionError } from '../errors'; +import { errorThrower } from '../utils'; export const withUser =

(Component: React.ComponentType

, displayName?: string) => { displayName = displayName || Component.displayName || Component.name || 'Component'; @@ -32,7 +33,7 @@ export const WithUser: React.FC<{ const user = useUserContext(); if (typeof children !== 'function') { - throw new Error(hocChildrenNotAFunctionError); + errorThrower.throw(hocChildrenNotAFunctionError); } if (!user) { diff --git a/packages/react/src/contexts/assertHelpers.ts b/packages/react/src/contexts/assertHelpers.ts index dec4654c53..3d5d4ae612 100644 --- a/packages/react/src/contexts/assertHelpers.ts +++ b/packages/react/src/contexts/assertHelpers.ts @@ -1,13 +1,14 @@ import { noClerkProviderError, noGuaranteedLoadedError } from '../errors'; +import { errorThrower } from '../utils'; export function assertWrappedByClerkProvider(contextVal: unknown): asserts contextVal { if (!contextVal) { - throw new Error(noClerkProviderError); + errorThrower.throw(noClerkProviderError); } } export function assertClerkLoadedGuarantee(guarantee: unknown, hookName: string): asserts guarantee { if (!guarantee) { - throw new Error(noGuaranteedLoadedError(hookName)); + errorThrower.throw(noGuaranteedLoadedError(hookName)); } } diff --git a/packages/react/src/errors.ts b/packages/react/src/errors.ts index b5e20bf6d1..9ab1690fe6 100644 --- a/packages/react/src/errors.ts +++ b/packages/react/src/errors.ts @@ -6,43 +6,40 @@ export { isMetamaskError, } from '@clerk/shared/error'; -export const noClerkProviderError = 'Clerk: You must wrap your application in a component.'; +export const noClerkProviderError = 'You must wrap your application in a component.'; export const noGuaranteedLoadedError = (hookName: string) => - `Clerk: You're calling ${hookName} before there's a guarantee the client has been loaded. Call ${hookName} from a child of , , or , or use the withClerk() HOC.`; - -export const noGuaranteedUserError = (hookName: string) => - `Clerk: You're calling ${hookName} before there's a guarantee there's an active user. Call ${hookName} from a child of or use the withUser() HOC.`; + `You're calling ${hookName} before there's a guarantee the client has been loaded. Call ${hookName} from a child of , , or , or use the withClerk() HOC.`; export const multipleClerkProvidersError = - "Clerk: You've added multiple components in your React component tree. Wrap your components in a single ."; + "You've added multiple components in your React component tree. Wrap your components in a single ."; -export const hocChildrenNotAFunctionError = 'Clerk: Child of WithClerk must be a function.'; +export const hocChildrenNotAFunctionError = 'Child of WithClerk must be a function.'; export const multipleChildrenInButtonComponent = (name: string) => - `Clerk: You've passed multiple children components to <${name}/>. You can only pass a single child component or text.`; + `You've passed multiple children components to <${name}/>. You can only pass a single child component or text.`; export const invalidStateError = - 'Clerk: Invalid state. Feel free to submit a bug or reach out to support here: https://clerk.com/support'; + 'Invalid state. Feel free to submit a bug or reach out to support here: https://clerk.com/support'; export const unsupportedNonBrowserDomainOrProxyUrlFunction = - 'Clerk: Unsupported usage of domain or proxyUrl. The usage of domain or proxyUrl as function is not supported in non-browser environments.'; + 'Unsupported usage of domain or proxyUrl. The usage of domain or proxyUrl as function is not supported in non-browser environments.'; export const userProfilePageRenderedError = - 'Clerk: component needs to be a direct child of `` or ``.'; + ' component needs to be a direct child of `` or ``.'; export const userProfileLinkRenderedError = - 'Clerk: component needs to be a direct child of `` or ``.'; + ' component needs to be a direct child of `` or ``.'; export const organizationProfilePageRenderedError = - 'Clerk: component needs to be a direct child of `` or ``.'; + ' component needs to be a direct child of `` or ``.'; export const organizationProfileLinkRenderedError = - 'Clerk: component needs to be a direct child of `` or ``.'; + ' component needs to be a direct child of `` or ``.'; export const customPagesIgnoredComponent = (componentName: string) => - `Clerk: <${componentName} /> can only accept <${componentName}.Page /> and <${componentName}.Link /> as its children. Any other provided component will be ignored.`; + `<${componentName} /> can only accept <${componentName}.Page /> and <${componentName}.Link /> as its children. Any other provided component will be ignored.`; export const customPageWrongProps = (componentName: string) => - `Clerk: Missing props. <${componentName}.Page /> component requires the following props: url, label, labelIcon, alongside with children to be rendered inside the page.`; + `Missing props. <${componentName}.Page /> component requires the following props: url, label, labelIcon, alongside with children to be rendered inside the page.`; export const customLinkWrongProps = (componentName: string) => - `Clerk: Missing props. <${componentName}.Link /> component requires the following props: url, label and labelIcon.`; + `Missing props. <${componentName}.Link /> component requires the following props: url, label and labelIcon.`; diff --git a/packages/react/src/hooks/useAuth.ts b/packages/react/src/hooks/useAuth.ts index 7cfb14f788..f5f6c531d3 100644 --- a/packages/react/src/hooks/useAuth.ts +++ b/packages/react/src/hooks/useAuth.ts @@ -11,6 +11,7 @@ import { useAuthContext } from '../contexts/AuthContext'; import { useIsomorphicClerkContext } from '../contexts/IsomorphicClerkContext'; import { invalidStateError } from '../errors'; import type IsomorphicClerk from '../isomorphicClerk'; +import { errorThrower } from '../utils'; import { createGetToken, createSignOut } from './utils'; type experimental__CheckAuthorizationSignedOut = ( @@ -213,5 +214,5 @@ export const useAuth: UseAuth = () => { }; } - throw new Error(invalidStateError); + return errorThrower.throw(invalidStateError); }; diff --git a/packages/react/src/isomorphicClerk.ts b/packages/react/src/isomorphicClerk.ts index 1580c5821e..53602c57d3 100644 --- a/packages/react/src/isomorphicClerk.ts +++ b/packages/react/src/isomorphicClerk.ts @@ -39,7 +39,7 @@ import type { HeadlessBrowserClerkConstrutor, IsomorphicClerkOptions, } from './types'; -import { isConstructor, loadClerkJsScript } from './utils'; +import { errorThrower, isConstructor, loadClerkJsScript } from './utils'; export interface Global { Clerk?: HeadlessBrowserClerk | BrowserClerk; @@ -105,7 +105,7 @@ export default class IsomorphicClerk { return handleValueOrFn(this.#domain, new URL(window.location.href), ''); } if (typeof this.#domain === 'function') { - throw new Error(unsupportedNonBrowserDomainOrProxyUrlFunction); + return errorThrower.throw(unsupportedNonBrowserDomainOrProxyUrlFunction); } return this.#domain || ''; } @@ -117,7 +117,7 @@ export default class IsomorphicClerk { return handleValueOrFn(this.#proxyUrl, new URL(window.location.href), ''); } if (typeof this.#proxyUrl === 'function') { - throw new Error(unsupportedNonBrowserDomainOrProxyUrlFunction); + return errorThrower.throw(unsupportedNonBrowserDomainOrProxyUrlFunction); } return this.#proxyUrl || ''; } diff --git a/packages/react/src/utils/childrenUtils.tsx b/packages/react/src/utils/childrenUtils.tsx index d568e76aa9..1afc87fc7e 100644 --- a/packages/react/src/utils/childrenUtils.tsx +++ b/packages/react/src/utils/childrenUtils.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { multipleChildrenInButtonComponent } from '../errors'; +import { errorThrower } from './errorThrower'; export const assertSingleChild = (children: React.ReactNode) => @@ -8,7 +9,7 @@ export const assertSingleChild = try { return React.Children.only(children); } catch (e) { - throw new Error(multipleChildrenInButtonComponent(name)); + return errorThrower.throw(multipleChildrenInButtonComponent(name)); } }; diff --git a/packages/react/src/utils/errorThrower.ts b/packages/react/src/utils/errorThrower.ts index 66f0290d9d..c3c328d1cb 100644 --- a/packages/react/src/utils/errorThrower.ts +++ b/packages/react/src/utils/errorThrower.ts @@ -1,7 +1,7 @@ import type { ErrorThrowerOptions } from '@clerk/shared/error'; import { buildErrorThrower } from '@clerk/shared/error'; -const errorThrower = buildErrorThrower({ packageName: '@clerk/react' }); +const errorThrower = buildErrorThrower({ packageName: '@clerk/clerk-react' }); function __internal__setErrorThrowerOptions(options: ErrorThrowerOptions) { errorThrower.setMessages(options).setPackageName(options); diff --git a/packages/react/src/utils/useMaxAllowedInstancesGuard.tsx b/packages/react/src/utils/useMaxAllowedInstancesGuard.tsx index cd2c223c12..5e3c716105 100644 --- a/packages/react/src/utils/useMaxAllowedInstancesGuard.tsx +++ b/packages/react/src/utils/useMaxAllowedInstancesGuard.tsx @@ -1,12 +1,14 @@ import React from 'react'; +import { errorThrower } from './errorThrower'; + const counts = new Map(); export function useMaxAllowedInstancesGuard(name: string, error: string, maxCount = 1): void { React.useEffect(() => { const count = counts.get(name) || 0; if (count == maxCount) { - throw new Error(error); + return errorThrower.throw(error); } counts.set(name, count + 1); diff --git a/packages/shared/src/error.ts b/packages/shared/src/error.ts index 9ac224afe7..60e73bc994 100644 --- a/packages/shared/src/error.ts +++ b/packages/shared/src/error.ts @@ -256,6 +256,7 @@ export interface ErrorThrower { throwInvalidProxyUrl(params: { url?: string }): never; throwMissingPublishableKeyError(): never; + throw(message: string): never; } export function buildErrorThrower({ packageName, customMessages }: ErrorThrowerOptions): ErrorThrower { @@ -310,5 +311,9 @@ export function buildErrorThrower({ packageName, customMessages }: ErrorThrowerO throwMissingPublishableKeyError(): never { throw new Error(buildMessage(messages.MissingPublishableKeyErrorMessage)); }, + + throw(message: string): never { + throw new Error(buildMessage(message)); + }, }; } diff --git a/packages/shared/src/utils/logErrorInDevMode.ts b/packages/shared/src/utils/logErrorInDevMode.ts index ea299ba832..20fd0495b3 100644 --- a/packages/shared/src/utils/logErrorInDevMode.ts +++ b/packages/shared/src/utils/logErrorInDevMode.ts @@ -2,6 +2,6 @@ import { isDevelopmentEnvironment } from './runtimeEnvironment'; export const logErrorInDevMode = (message: string) => { if (isDevelopmentEnvironment()) { - console.error(message); + console.error(`Clerk: ${message}`); } };