Skip to content

Commit

Permalink
fix(elements): Fix elements router usage (#4513)
Browse files Browse the repository at this point in the history
Co-authored-by: Bryce Kalow <bryce@clerk.dev>
  • Loading branch information
alexcarpenter and BRKalow authored Nov 8, 2024
1 parent f5f6c4d commit f7472e2
Show file tree
Hide file tree
Showing 18 changed files with 119 additions and 133 deletions.
9 changes: 9 additions & 0 deletions .changeset/empty-dots-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@clerk/elements': patch
'@clerk/nextjs': patch
'@clerk/shared': patch
'@clerk/types': patch
'@clerk/clerk-js': patch
---

Fixes issues in `ClerkRouter` that were causing inaccurate pathnames within Elements flows. Also fixes a dependency issue where `@clerk/elements` was pulling in the wrong version of `@clerk/shared`.
4 changes: 3 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/clerk-js/bundlewatch.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{ "path": "./dist/clerk.js", "maxSize": "707kB" },
{ "path": "./dist/clerk.browser.js", "maxSize": "75kB" },
{ "path": "./dist/clerk.headless.js", "maxSize": "48kB" },
{ "path": "./dist/ui-common*.js", "maxSize": "87KB" },
{ "path": "./dist/ui-common*.js", "maxSize": "88KB" },
{ "path": "./dist/vendors*.js", "maxSize": "70KB" },
{ "path": "./dist/coinbase*.js", "maxSize": "58KB" },
{ "path": "./dist/createorganization*.js", "maxSize": "5KB" },
Expand Down
1 change: 1 addition & 0 deletions packages/clerk-js/jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ if (typeof window !== 'undefined') {

global.__PKG_NAME__ = '';
global.__PKG_VERSION__ = '';
global.BUILD_ENABLE_NEW_COMPONENTS = '';

//@ts-expect-error
global.IntersectionObserver = class IntersectionObserver {
Expand Down
1 change: 1 addition & 0 deletions packages/clerk-js/rspack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const common = ({ mode }) => {
__DEV__: isDevelopment(mode),
__PKG_VERSION__: JSON.stringify(packageJSON.version),
__PKG_NAME__: JSON.stringify(packageJSON.name),
BUILD_ENABLE_NEW_COMPONENTS: JSON.stringify(process.env.BUILD_ENABLE_NEW_COMPONENTS),
}),
new rspack.EnvironmentPlugin({
CLERK_ENV: mode,
Expand Down
14 changes: 8 additions & 6 deletions packages/clerk-js/src/core/clerk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,12 +340,14 @@ export class Clerk implements ClerkInterface {
this.#loaded = await this.#loadInNonStandardBrowser();
}

if (clerkIsLoaded(this)) {
this.__experimental_ui = new UI({
router: this.#options.__experimental_router,
clerk: this,
options: this.#options,
});
if (BUILD_ENABLE_NEW_COMPONENTS) {
if (clerkIsLoaded(this)) {
this.__experimental_ui = new UI({
router: this.#options.__experimental_router,
clerk: this,
options: this.#options,
});
}
}
};

Expand Down
2 changes: 2 additions & 0 deletions packages/clerk-js/src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ declare module '@clerk/ui/styles.css' {
const content: string;
export default content;
}

declare const BUILD_ENABLE_NEW_COMPONENTS: string;
32 changes: 17 additions & 15 deletions packages/clerk-js/src/ui/new/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,23 @@ export class UI {
this.clerk = clerk;
this.options = options;

// register components
this.register('SignIn', {
type: 'component',
load: () =>
import(/* webpackChunkName: "rebuild--sign-in" */ '@clerk/ui/sign-in').then(({ SignIn }) => ({
default: SignIn,
})),
});
this.register('SignUp', {
type: 'component',
load: () =>
import(/* webpackChunkName: "rebuild--sign-up" */ '@clerk/ui/sign-up').then(({ SignUp }) => ({
default: SignUp,
})),
});
if (BUILD_ENABLE_NEW_COMPONENTS) {
// register components
this.register('SignIn', {
type: 'component',
load: () =>
import(/* webpackChunkName: "rebuild--sign-in" */ '@clerk/ui/sign-in').then(({ SignIn }) => ({
default: SignIn,
})),
});
this.register('SignUp', {
type: 'component',
load: () =>
import(/* webpackChunkName: "rebuild--sign-up" */ '@clerk/ui/sign-up').then(({ SignUp }) => ({
default: SignUp,
})),
});
}
}

// Mount a component from the registry
Expand Down
1 change: 1 addition & 0 deletions packages/clerk-js/turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"extends": ["//"],
"tasks": {
"build": {
"env": ["BUILD_ENABLE_NEW_COMPONENTS"],
"inputs": [
"*.d.ts",
"bundlewatch.config.json",
Expand Down
4 changes: 3 additions & 1 deletion packages/elements/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
"test:cache:clear": "jest --clearCache --useStderr"
},
"dependencies": {
"@clerk/shared": "2.11.5",
"@clerk/types": "^4.30.0",
"@radix-ui/react-form": "^0.1.0",
"@radix-ui/react-slot": "^1.1.0",
Expand All @@ -82,6 +81,7 @@
"devDependencies": {
"@clerk/clerk-react": "5.15.1",
"@clerk/eslint-config-custom": "*",
"@clerk/shared": "2.11.5",
"@statelyai/inspect": "^0.4.0",
"@types/node": "^18.19.33",
"@types/react": "*",
Expand All @@ -94,6 +94,8 @@
"typescript": "*"
},
"peerDependencies": {
"@clerk/shared": "2.x",
"next": "^13.5.4 || ^14.0.3 || ^15",
"react": "^18.0.0 || ^19.0.0-beta",
"react-dom": "^18.0.0 || ^19.0.0-beta"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ export const handleRedirectCallback = fromCallback<AnyEventObject, HandleRedirec
{
signInForceRedirectUrl: ClerkJSNavigationEvent.complete,
signInFallbackRedirectUrl: ClerkJSNavigationEvent.complete,
signUpForceRedirectUrl: ClerkJSNavigationEvent.signUp,
signUpFallbackRedirectUrl: ClerkJSNavigationEvent.signUp,
signUpForceRedirectUrl: ClerkJSNavigationEvent.complete,
signUpFallbackRedirectUrl: ClerkJSNavigationEvent.complete,
continueSignUpUrl: ClerkJSNavigationEvent.continue,
firstFactorUrl: ClerkJSNavigationEvent.signIn,
resetPasswordUrl: ClerkJSNavigationEvent.resetPassword,
Expand Down
3 changes: 2 additions & 1 deletion packages/elements/src/react/router/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { Route, Router, useClerkRouter } from '@clerk/shared/router';
export { Route, Router, useClerkRouter, ClerkHostRouterContext } from '@clerk/shared/router';
export { useVirtualRouter } from './virtual';
export { useNextRouter } from './next';
35 changes: 35 additions & 0 deletions packages/elements/src/react/router/next.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { ClerkHostRouter } from '@clerk/types';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';

import { NEXT_WINDOW_HISTORY_SUPPORT_VERSION } from '~/internals/constants';

import { usePathnameWithoutCatchAll } from '../utils/path-inference/next';

/**
* Clerk Elements router integration with Next.js's router.
*/
export const useNextRouter = (): ClerkHostRouter => {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const inferredBasePath = usePathnameWithoutCatchAll();

// The window.history APIs seem to prevent Next.js from triggering a full page re-render, allowing us to
// preserve internal state between steps.
const canUseWindowHistoryAPIs =
typeof window !== 'undefined' && window.next && window.next.version >= NEXT_WINDOW_HISTORY_SUPPORT_VERSION;

return {
mode: 'path',
name: 'NextRouter',
push: (path: string) => router.push(path),
replace: (path: string) =>
canUseWindowHistoryAPIs ? window.history.replaceState(null, '', path) : router.replace(path),
shallowPush(path: string) {
canUseWindowHistoryAPIs ? window.history.pushState(null, '', path) : router.push(path, {});
},
pathname: () => pathname,
searchParams: () => searchParams,
inferredBasePath: () => inferredBasePath,
};
};
35 changes: 16 additions & 19 deletions packages/elements/src/react/sign-in/root.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useClerk } from '@clerk/shared/react';
import { useClerkHostRouter } from '@clerk/shared/router';
import { eventComponentMounted } from '@clerk/shared/telemetry';
import { useSelector } from '@xstate/react';
import React, { useEffect } from 'react';
Expand All @@ -10,11 +9,10 @@ import { FormStoreProvider, useFormStore } from '~/internals/machines/form/form.
import type { SignInRouterInitEvent } from '~/internals/machines/sign-in';
import { SignInRouterMachine } from '~/internals/machines/sign-in';
import { inspect } from '~/internals/utils/inspector';
import { Router, useClerkRouter, useVirtualRouter } from '~/react/router';
import { ClerkHostRouterContext, Router, useClerkRouter, useNextRouter, useVirtualRouter } from '~/react/router';
import { SignInRouterCtx } from '~/react/sign-in/context';

import { Form } from '../common/form';
import { removeOptionalCatchAllSegment } from '../utils/path-inference/utils';

type SignInFlowProviderProps = {
children: React.ReactNode;
Expand Down Expand Up @@ -118,9 +116,9 @@ export function SignInRoot({
routing = ROUTING.path,
}: SignInRootProps): JSX.Element | null {
const clerk = useClerk();
const router = (routing === ROUTING.virtual ? useVirtualRouter : useClerkHostRouter)();
const router = (routing === ROUTING.virtual ? useVirtualRouter : useNextRouter)();
const pathname = router.pathname();
const inferredPath = removeOptionalCatchAllSegment(pathname);
const inferredPath = router.inferredBasePath?.();
const path = pathProp || inferredPath || SIGN_IN_DEFAULT_BASE_PATH;
const isRootPath = path === pathname;

Expand All @@ -134,19 +132,18 @@ export function SignInRoot({
);

return (
<Router
basePath={path}
router={router}
>
<FormStoreProvider>
<SignInFlowProvider
exampleMode={exampleMode}
fallback={fallback}
isRootPath={isRootPath}
>
{children}
</SignInFlowProvider>
</FormStoreProvider>
</Router>
<ClerkHostRouterContext.Provider value={router}>
<Router basePath={path}>
<FormStoreProvider>
<SignInFlowProvider
exampleMode={exampleMode}
fallback={fallback}
isRootPath={isRootPath}
>
{children}
</SignInFlowProvider>
</FormStoreProvider>
</Router>
</ClerkHostRouterContext.Provider>
);
}
35 changes: 16 additions & 19 deletions packages/elements/src/react/sign-up/root.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useClerk } from '@clerk/shared/react';
import { useClerkHostRouter } from '@clerk/shared/router';
import { eventComponentMounted } from '@clerk/shared/telemetry';
import { useSelector } from '@xstate/react';
import { useEffect } from 'react';
Expand All @@ -10,11 +9,10 @@ import { FormStoreProvider, useFormStore } from '~/internals/machines/form/form.
import type { SignUpRouterInitEvent } from '~/internals/machines/sign-up';
import { SignUpRouterMachine } from '~/internals/machines/sign-up';
import { inspect } from '~/internals/utils/inspector';
import { Router, useClerkRouter, useVirtualRouter } from '~/react/router';
import { ClerkHostRouterContext, Router, useClerkRouter, useNextRouter, useVirtualRouter } from '~/react/router';
import { SignUpRouterCtx } from '~/react/sign-up/context';

import { Form } from '../common/form';
import { removeOptionalCatchAllSegment } from '../utils/path-inference/utils';

type SignUpFlowProviderProps = {
children: React.ReactNode;
Expand Down Expand Up @@ -117,9 +115,9 @@ export function SignUpRoot({
routing = ROUTING.path,
}: SignUpRootProps): JSX.Element | null {
const clerk = useClerk();
const router = (routing === ROUTING.virtual ? useVirtualRouter : useClerkHostRouter)();
const router = (routing === ROUTING.virtual ? useVirtualRouter : useNextRouter)();
const pathname = router.pathname();
const inferredPath = removeOptionalCatchAllSegment(pathname);
const inferredPath = router.inferredBasePath?.();
const path = pathProp || inferredPath || SIGN_UP_DEFAULT_BASE_PATH;
const isRootPath = path === pathname;

Expand All @@ -133,19 +131,18 @@ export function SignUpRoot({
);

return (
<Router
basePath={path}
router={router}
>
<FormStoreProvider>
<SignUpFlowProvider
exampleMode={exampleMode}
fallback={fallback}
isRootPath={isRootPath}
>
{children}
</SignUpFlowProvider>
</FormStoreProvider>
</Router>
<ClerkHostRouterContext.Provider value={router}>
<Router basePath={path}>
<FormStoreProvider>
<SignUpFlowProvider
exampleMode={exampleMode}
fallback={fallback}
isRootPath={isRootPath}
>
{children}
</SignUpFlowProvider>
</FormStoreProvider>
</Router>
</ClerkHostRouterContext.Provider>
);
}
Loading

0 comments on commit f7472e2

Please sign in to comment.