Skip to content

Commit

Permalink
feat(clerk-js): Throw errors for invalid mounts/modal opens in develo…
Browse files Browse the repository at this point in the history
…pment
  • Loading branch information
desiprisg committed Mar 13, 2024
1 parent a50f856 commit 5112bde
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 73 deletions.
5 changes: 5 additions & 0 deletions .changeset/great-turtles-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': minor
---

Throw an error in development when there is an invalid mount or modal open. This includes mounting a component when the resource is not available (i.e. `mountUserProfile()` when the user does not exist) as well as mounting a component without the feature being enabled via the clerk dashboard (i.e. `mountOrganizationProfile()` without having organizations enabled).
87 changes: 69 additions & 18 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
addClerkPrefix,
ClerkRuntimeError,
handleValueOrFn,
inBrowser as inClientSide,
is4xxError,
Expand Down Expand Up @@ -331,8 +332,12 @@ export class Clerk implements ClerkInterface {

public openSignIn = (props?: SignInProps): void => {
this.assertComponentsReady(this.#componentControls);
if (sessionExistsAndSingleSessionModeEnabled(this, this.#environment) && this.#instanceType === 'development') {
console.info(warnings.cannotOpenSignUpOrSignUp);
if (sessionExistsAndSingleSessionModeEnabled(this, this.#environment)) {
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotOpenSignInOrSignUp, {
code: 'cannot_open_single_session_enabled',
});
}
return;
}
void this.#componentControls
Expand All @@ -347,8 +352,13 @@ export class Clerk implements ClerkInterface {

public openSignUp = (props?: SignUpProps): void => {
this.assertComponentsReady(this.#componentControls);
if (sessionExistsAndSingleSessionModeEnabled(this, this.#environment) && this.#instanceType === 'development') {
return console.info(warnings.cannotOpenSignUpOrSignUp);
if (sessionExistsAndSingleSessionModeEnabled(this, this.#environment)) {
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotOpenSignInOrSignUp, {
code: 'cannot_open_single_session_enabled',
});
}
return;
}
void this.#componentControls
.ensureMounted({ preloadHint: 'SignUp' })
Expand All @@ -362,8 +372,13 @@ export class Clerk implements ClerkInterface {

public openUserProfile = (props?: UserProfileProps): void => {
this.assertComponentsReady(this.#componentControls);
if (noUserExists(this) && this.#instanceType === 'development') {
return console.info(warnings.cannotOpenUserProfile);
if (noUserExists(this)) {
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotOpenUserProfile, {
code: 'cannot_open_no_user',
});
}
return;
}
void this.#componentControls
.ensureMounted({ preloadHint: 'UserProfile' })
Expand All @@ -378,11 +393,19 @@ export class Clerk implements ClerkInterface {
public openOrganizationProfile = (props?: OrganizationProfileProps): void => {
this.assertComponentsReady(this.#componentControls);
if (disabledOrganizationsFeature(this, this.#environment)) {
console.info(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'));
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'), {
code: 'cannot_open_organizations_disabled',
});
}
return;
}
if (noOrganizationExists(this) && this.#instanceType === 'development') {
console.info(warnings.cannotRenderComponentWhenOrgDoesNotExist);
if (noOrganizationExists(this)) {
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotRenderComponentWhenOrgDoesNotExist, {
code: 'cannot_open_no_organization',
});
}
return;
}
void this.#componentControls
Expand All @@ -398,7 +421,11 @@ export class Clerk implements ClerkInterface {
public openCreateOrganization = (props?: CreateOrganizationProps): void => {
this.assertComponentsReady(this.#componentControls);
if (disabledOrganizationsFeature(this, this.#environment)) {
console.info(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'));
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'), {
code: 'cannot_open_organizations_disabled',
});
}
return;
}
void this.#componentControls
Expand Down Expand Up @@ -457,8 +484,12 @@ export class Clerk implements ClerkInterface {

public mountUserProfile = (node: HTMLDivElement, props?: UserProfileProps): void => {
this.assertComponentsReady(this.#componentControls);
if (noUserExists(this) && this.#instanceType === 'development') {
console.info(warnings.cannotRenderComponentWhenUserDoesNotExist);
if (noUserExists(this)) {
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotRenderComponentWhenUserDoesNotExist, {
code: 'cannot_open_no_user',
});
}
return;
}
void this.#componentControls.ensureMounted({ preloadHint: 'UserProfile' }).then(controls =>
Expand All @@ -485,11 +516,19 @@ export class Clerk implements ClerkInterface {
public mountOrganizationProfile = (node: HTMLDivElement, props?: OrganizationProfileProps) => {
this.assertComponentsReady(this.#componentControls);
if (disabledOrganizationsFeature(this, this.#environment)) {
console.info(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'));
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationProfile'), {
code: 'cannot_open_organizations_disabled',
});
}
return;
}
if (noOrganizationExists(this) && this.#instanceType === 'development') {
console.info(warnings.cannotRenderComponentWhenOrgDoesNotExist);
if (noOrganizationExists(this)) {
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotRenderComponentWhenOrgDoesNotExist, {
code: 'cannot_open_no_organization',
});
}
return;
}
void this.#componentControls.ensureMounted({ preloadHint: 'OrganizationProfile' }).then(controls =>
Expand All @@ -516,7 +555,11 @@ export class Clerk implements ClerkInterface {
public mountCreateOrganization = (node: HTMLDivElement, props?: CreateOrganizationProps) => {
this.assertComponentsReady(this.#componentControls);
if (disabledOrganizationsFeature(this, this.#environment)) {
console.info(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'));
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('CreateOrganization'), {
code: 'cannot_open_organizations_disabled',
});
}
return;
}
void this.#componentControls?.ensureMounted({ preloadHint: 'CreateOrganization' }).then(controls =>
Expand All @@ -543,7 +586,11 @@ export class Clerk implements ClerkInterface {
public mountOrganizationSwitcher = (node: HTMLDivElement, props?: OrganizationSwitcherProps) => {
this.assertComponentsReady(this.#componentControls);
if (disabledOrganizationsFeature(this, this.#environment)) {
console.info(warnings.cannotRenderAnyOrganizationComponent('OrganizationSwitcher'));
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationSwitcher'), {
code: 'cannot_open_organizations_disabled',
});
}
return;
}
void this.#componentControls?.ensureMounted({ preloadHint: 'OrganizationSwitcher' }).then(controls =>
Expand All @@ -566,7 +613,11 @@ export class Clerk implements ClerkInterface {
public mountOrganizationList = (node: HTMLDivElement, props?: OrganizationListProps) => {
this.assertComponentsReady(this.#componentControls);
if (disabledOrganizationsFeature(this, this.#environment)) {
console.info(warnings.cannotRenderAnyOrganizationComponent('OrganizationList'));
if (this.#instanceType === 'development') {
throw new ClerkRuntimeError(warnings.cannotRenderAnyOrganizationComponent('OrganizationList'), {
code: 'cannot_open_organizations_disabled',
});
}
return;
}
void this.#componentControls?.ensureMounted({ preloadHint: 'OrganizationList' }).then(controls =>
Expand Down
2 changes: 1 addition & 1 deletion packages/clerk-js/src/core/warnings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const warnings = {
cannotRenderAnyOrganizationComponent: createMessageForDisabledOrganizations,
cannotOpenUserProfile:
'The UserProfile modal cannot render unless a user is signed in. Since no user is signed in, this is no-op.',
cannotOpenSignUpOrSignUp:
cannotOpenSignInOrSignUp:
'The SignIn or SignUp modals do not render when a user is already signed in, unless the application allows multiple sessions. Since a user is signed in and this application only allows a single session, this is no-op.',
};

Expand Down
1 change: 0 additions & 1 deletion packages/clerk-js/src/ui/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,4 @@ export * from './RemoveResourceForm';
export * from './PrintableComponent';
export * from './NotificationCountBadge';
export * from './RemoveResourceForm';
export * from './withOrganizationsEnabledGuard';
export * from './QRCode';
29 changes: 0 additions & 29 deletions packages/clerk-js/src/ui/common/withOrganizationsEnabledGuard.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { CreateOrganizationModalProps } from '@clerk/types';

import { withOrganizationsEnabledGuard } from '../../common';
import { ComponentContext, withCoreUserGuard } from '../../contexts';
import { Flow } from '../../customizables';
import { withCardStateProvider } from '../../elements';
Expand All @@ -26,11 +25,7 @@ const AuthenticatedRoutes = withCoreUserGuard(() => {
return <CreateOrganizationPage />;
});

export const CreateOrganization = withOrganizationsEnabledGuard(
withCardStateProvider(_CreateOrganization),
'CreateOrganization',
{ mode: 'redirect' },
);
export const CreateOrganization = withCardStateProvider(_CreateOrganization);

export const CreateOrganizationModal = (props: CreateOrganizationModalProps): JSX.Element => {
const createOrganizationProps: CreateOrganizationCtx = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { withOrganizationsEnabledGuard } from '../../common';
import { withCoreUserGuard } from '../../contexts';
import { Flow } from '../../customizables';
import { Route, Switch } from '../../router';
Expand All @@ -20,6 +19,4 @@ const _OrganizationList = () => {

const AuthenticatedRoutes = withCoreUserGuard(OrganizationListPage);

export const OrganizationList = withOrganizationsEnabledGuard(_OrganizationList, 'OrganizationList', {
mode: 'redirect',
});
export const OrganizationList = _OrganizationList;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { useOrganization } from '@clerk/shared/react';
import type { OrganizationProfileModalProps, OrganizationProfileProps } from '@clerk/types';
import React from 'react';

import { withOrganizationsEnabledGuard } from '../../common';
import { ComponentContext, withCoreUserGuard } from '../../contexts';
import { Flow, localizationKeys } from '../../customizables';
import { NavbarMenuButtonRow, ProfileCard, withCardStateProvider } from '../../elements';
Expand Down Expand Up @@ -47,13 +46,7 @@ const AuthenticatedRoutes = withCoreUserGuard(() => {
);
});

export const OrganizationProfile = withOrganizationsEnabledGuard(
withCardStateProvider(_OrganizationProfile),
'OrganizationProfile',
{
mode: 'redirect',
},
);
export const OrganizationProfile = withCardStateProvider(_OrganizationProfile);

export const OrganizationProfileModal = (props: OrganizationProfileModalProps): JSX.Element => {
const organizationProfileProps: OrganizationProfileCtx = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useId } from 'react';

import { withOrganizationsEnabledGuard } from '../../common';
import { AcceptedInvitationsProvider, withCoreUserGuard } from '../../contexts';
import { Flow } from '../../customizables';
import { Popover, withCardStateProvider, withFloatingTree } from '../../elements';
Expand Down Expand Up @@ -46,8 +45,4 @@ const _OrganizationSwitcher = withFloatingTree(() => {
);
});

export const OrganizationSwitcher = withOrganizationsEnabledGuard(
withCoreUserGuard(withCardStateProvider(_OrganizationSwitcher)),
'OrganizationSwitcher',
{ mode: 'hide' },
);
export const OrganizationSwitcher = withCoreUserGuard(withCardStateProvider(_OrganizationSwitcher));

0 comments on commit 5112bde

Please sign in to comment.