Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDK-805] Use the errorThrower shared util when throwing errors #1999

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/tough-pots-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@clerk/shared': patch
'@clerk/clerk-react': patch
---

Use the errorThrower shared utility when throwing errors
3 changes: 2 additions & 1 deletion packages/react/src/components/withClerk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <P extends { clerk: LoadedClerk }>(
Component: React.ComponentType<P>,
Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/components/withSession.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';

import { useSessionContext } from '../contexts/SessionContext';
import { hocChildrenNotAFunctionError } from '../errors';
import { errorThrower } from '../utils';

export const withSession = <P extends { session: SessionResource }>(
Component: React.ComponentType<P>,
Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/components/withUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';

import { useUserContext } from '../contexts/UserContext';
import { hocChildrenNotAFunctionError } from '../errors';
import { errorThrower } from '../utils';

export const withUser = <P extends { user: UserResource }>(Component: React.ComponentType<P>, displayName?: string) => {
displayName = displayName || Component.displayName || Component.name || 'Component';
Expand Down Expand Up @@ -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) {
Expand Down
5 changes: 3 additions & 2 deletions packages/react/src/contexts/assertHelpers.ts
Original file line number Diff line number Diff line change
@@ -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));
}
}
31 changes: 14 additions & 17 deletions packages/react/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,40 @@ export {
isMetamaskError,
} from '@clerk/shared/error';

export const noClerkProviderError = 'Clerk: You must wrap your application in a <ClerkProvider> component.';
export const noClerkProviderError = 'You must wrap your application in a <ClerkProvider> 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 <SignedIn>, <SignedOut>, or <ClerkLoaded>, 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 <SignedIn> 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 <SignedIn>, <SignedOut>, or <ClerkLoaded>, or use the withClerk() HOC.`;

export const multipleClerkProvidersError =
"Clerk: You've added multiple <ClerkProvider> components in your React component tree. Wrap your components in a single <ClerkProvider>.";
"You've added multiple <ClerkProvider> components in your React component tree. Wrap your components in a single <ClerkProvider>.";

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: <UserProfile.Page /> component needs to be a direct child of `<UserProfile />` or `<UserButton />`.';
'<UserProfile.Page /> component needs to be a direct child of `<UserProfile />` or `<UserButton />`.';
export const userProfileLinkRenderedError =
'Clerk: <UserProfile.Link /> component needs to be a direct child of `<UserProfile />` or `<UserButton />`.';
'<UserProfile.Link /> component needs to be a direct child of `<UserProfile />` or `<UserButton />`.';

export const organizationProfilePageRenderedError =
'Clerk: <OrganizationProfile.Page /> component needs to be a direct child of `<OrganizationProfile />` or `<OrganizationSwitcher />`.';
'<OrganizationProfile.Page /> component needs to be a direct child of `<OrganizationProfile />` or `<OrganizationSwitcher />`.';
export const organizationProfileLinkRenderedError =
'Clerk: <OrganizationProfile.Link /> component needs to be a direct child of `<OrganizationProfile />` or `<OrganizationSwitcher />`.';
'<OrganizationProfile.Link /> component needs to be a direct child of `<OrganizationProfile />` or `<OrganizationSwitcher />`.';

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.`;
3 changes: 2 additions & 1 deletion packages/react/src/hooks/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
Expand Down Expand Up @@ -213,5 +214,5 @@ export const useAuth: UseAuth = () => {
};
}

throw new Error(invalidStateError);
return errorThrower.throw(invalidStateError);
};
6 changes: 3 additions & 3 deletions packages/react/src/isomorphicClerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 || '';
}
Expand All @@ -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 || '';
}
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/utils/childrenUtils.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import React from 'react';

import { multipleChildrenInButtonComponent } from '../errors';
import { errorThrower } from './errorThrower';

export const assertSingleChild =
(children: React.ReactNode) =>
(name: 'SignInButton' | 'SignUpButton' | 'SignOutButton' | 'SignInWithMetamaskButton') => {
try {
return React.Children.only(children);
} catch (e) {
throw new Error(multipleChildrenInButtonComponent(name));
return errorThrower.throw(multipleChildrenInButtonComponent(name));
}
};

Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/utils/errorThrower.ts
Original file line number Diff line number Diff line change
@@ -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' });
anagstef marked this conversation as resolved.
Show resolved Hide resolved

function __internal__setErrorThrowerOptions(options: ErrorThrowerOptions) {
errorThrower.setMessages(options).setPackageName(options);
Expand Down
4 changes: 3 additions & 1 deletion packages/react/src/utils/useMaxAllowedInstancesGuard.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React from 'react';

import { errorThrower } from './errorThrower';

const counts = new Map<string, number>();

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);

Expand Down
5 changes: 5 additions & 0 deletions packages/shared/src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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));
},
};
}
2 changes: 1 addition & 1 deletion packages/shared/src/utils/logErrorInDevMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { isDevelopmentEnvironment } from './runtimeEnvironment';

export const logErrorInDevMode = (message: string) => {
if (isDevelopmentEnvironment()) {
console.error(message);
console.error(`Clerk: ${message}`);
}
};
Loading