From 9f890cf75b6f673a736281b5653f3c7a3c6f23db Mon Sep 17 00:00:00 2001 From: Dimitris Klouvas Date: Fri, 22 Sep 2023 13:28:01 +0300 Subject: [PATCH 1/9] fix(shared): Use `use client` directive to support shared in RSC This change will resolve the following issue we had when importing @clerk/shared in @clerk/nextjs: ``` Your're importing a component that needs useState. It only works in a Client Component none of its parents are marked with "use client", so they're Server Components by default. [javascript/packages/shared/src/hooks/usePagesOrInfinite.js:1:1] import { useCallback, useMemo, useRef, useState } from "react"; ``` --- packages/shared/src/hooks/clerk-swr.ts | 1 + packages/shared/src/hooks/usePagesOrInfinite.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/shared/src/hooks/clerk-swr.ts b/packages/shared/src/hooks/clerk-swr.ts index c54d7d48495..060ecc68653 100644 --- a/packages/shared/src/hooks/clerk-swr.ts +++ b/packages/shared/src/hooks/clerk-swr.ts @@ -1,3 +1,4 @@ +'use client'; export * from 'swr'; export { default as useSWR } from 'swr'; export { default as useSWRInfinite } from 'swr/infinite'; diff --git a/packages/shared/src/hooks/usePagesOrInfinite.ts b/packages/shared/src/hooks/usePagesOrInfinite.ts index 3f7d64c6d16..1af2afa0233 100644 --- a/packages/shared/src/hooks/usePagesOrInfinite.ts +++ b/packages/shared/src/hooks/usePagesOrInfinite.ts @@ -1,3 +1,5 @@ +'use client'; + import { useCallback, useMemo, useRef, useState } from 'react'; import { useSWR, useSWRInfinite } from './clerk-swr'; From e6174a5e674901bff2893b91c2461275444b493b Mon Sep 17 00:00:00 2001 From: Dimitris Klouvas Date: Tue, 19 Sep 2023 02:33:56 +0300 Subject: [PATCH 2/9] chore(nextjs): Introduce shared module to contain @clerk/shared related code --- packages/nextjs/src/server/utils.ts | 27 +-------------------------- packages/nextjs/src/shared/index.ts | 1 + packages/nextjs/src/shared/url.ts | 25 +++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 26 deletions(-) create mode 100644 packages/nextjs/src/shared/index.ts create mode 100644 packages/nextjs/src/shared/url.ts diff --git a/packages/nextjs/src/server/utils.ts b/packages/nextjs/src/server/utils.ts index 6f239d007ca..ec0a0c938d3 100644 --- a/packages/nextjs/src/server/utils.ts +++ b/packages/nextjs/src/server/utils.ts @@ -4,6 +4,7 @@ import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; import { constants as nextConstants } from '../constants'; +import { handleValueOrFn, isHttpOrHttps } from '../shared'; import { API_KEY, DOMAIN, IS_SATELLITE, PROXY_URL, SECRET_KEY, SIGN_IN_URL } from './clerkClient'; import { missingDomainAndProxy, missingSignInUrlInDev } from './errors'; import type { NextMiddlewareResult, RequestLike } from './types'; @@ -147,32 +148,6 @@ export const injectSSRStateIntoObject = (obj: O, authObject: T) => { return { ...obj, __clerk_ssr_state }; }; -// TODO: Use the same function defined in @clerk/shared once the package is tree shakeable -type VOrFnReturnsV = T | undefined | ((v: URL) => T); - -export function handleValueOrFn(value: VOrFnReturnsV, url: URL): T | undefined; -export function handleValueOrFn(value: VOrFnReturnsV, url: URL, defaultValue: T): T; -export function handleValueOrFn(value: VOrFnReturnsV, url: URL, defaultValue?: unknown): unknown { - if (typeof value === 'function') { - return (value as (v: URL) => T)(url); - } - - if (typeof value !== 'undefined') { - return value; - } - - if (typeof defaultValue !== 'undefined') { - return defaultValue; - } - - return undefined; -} - -// TODO: use @clerk/shared once it is tree-shakeable -export function isHttpOrHttps(key: string | undefined) { - return /^http(s)?:\/\//.test(key || ''); -} - export function isDevelopmentFromApiKey(apiKey: string): boolean { return apiKey.startsWith('test_') || apiKey.startsWith('sk_test_'); } diff --git a/packages/nextjs/src/shared/index.ts b/packages/nextjs/src/shared/index.ts new file mode 100644 index 00000000000..5d91d074b97 --- /dev/null +++ b/packages/nextjs/src/shared/index.ts @@ -0,0 +1 @@ +export { isHttpOrHttps, handleValueOrFn } from './url'; diff --git a/packages/nextjs/src/shared/url.ts b/packages/nextjs/src/shared/url.ts new file mode 100644 index 00000000000..c99aac60d29 --- /dev/null +++ b/packages/nextjs/src/shared/url.ts @@ -0,0 +1,25 @@ +// TODO: Use the same function defined in @clerk/shared once the package is tree shakeable + +type VOrFnReturnsV = T | undefined | ((v: URL) => T); + +export function handleValueOrFn(value: VOrFnReturnsV, url: URL): T | undefined; +export function handleValueOrFn(value: VOrFnReturnsV, url: URL, defaultValue: T): T; +export function handleValueOrFn(value: VOrFnReturnsV, url: URL, defaultValue?: unknown): unknown { + if (typeof value === 'function') { + return (value as (v: URL) => T)(url); + } + + if (typeof value !== 'undefined') { + return value; + } + + if (typeof defaultValue !== 'undefined') { + return defaultValue; + } + + return undefined; +} + +export function isHttpOrHttps(key: string | undefined) { + return /^http(s)?:\/\//.test(key || ''); +} From 5864bc7d2884178ce907713b6ce13973030522f3 Mon Sep 17 00:00:00 2001 From: Dimitris Klouvas Date: Fri, 22 Sep 2023 13:43:21 +0300 Subject: [PATCH 3/9] chore(nextjs): Use utilities from @clerk/shared --- .changeset/unlucky-emus-greet.md | 7 +++++++ packages/nextjs/src/server/utils.ts | 2 +- packages/nextjs/src/shared/index.ts | 1 - packages/nextjs/src/shared/url.ts | 25 ------------------------- 4 files changed, 8 insertions(+), 27 deletions(-) create mode 100644 .changeset/unlucky-emus-greet.md delete mode 100644 packages/nextjs/src/shared/index.ts delete mode 100644 packages/nextjs/src/shared/url.ts diff --git a/.changeset/unlucky-emus-greet.md b/.changeset/unlucky-emus-greet.md new file mode 100644 index 00000000000..19c0b0b439f --- /dev/null +++ b/.changeset/unlucky-emus-greet.md @@ -0,0 +1,7 @@ +--- +'@clerk/nextjs': minor +'@clerk/shared': minor +--- + +Add the `use client` directive in `@clerk/shared` to make the package compatible with an RSC environment. +Remove several helpers from `@clerk/nextjs` and import them from `@clerk/shared` instead. diff --git a/packages/nextjs/src/server/utils.ts b/packages/nextjs/src/server/utils.ts index ec0a0c938d3..14df666c6b6 100644 --- a/packages/nextjs/src/server/utils.ts +++ b/packages/nextjs/src/server/utils.ts @@ -1,10 +1,10 @@ import type { RequestState } from '@clerk/backend'; import { buildRequestUrl, constants } from '@clerk/backend'; +import { handleValueOrFn, isHttpOrHttps } from '@clerk/shared'; import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; import { constants as nextConstants } from '../constants'; -import { handleValueOrFn, isHttpOrHttps } from '../shared'; import { API_KEY, DOMAIN, IS_SATELLITE, PROXY_URL, SECRET_KEY, SIGN_IN_URL } from './clerkClient'; import { missingDomainAndProxy, missingSignInUrlInDev } from './errors'; import type { NextMiddlewareResult, RequestLike } from './types'; diff --git a/packages/nextjs/src/shared/index.ts b/packages/nextjs/src/shared/index.ts deleted file mode 100644 index 5d91d074b97..00000000000 --- a/packages/nextjs/src/shared/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { isHttpOrHttps, handleValueOrFn } from './url'; diff --git a/packages/nextjs/src/shared/url.ts b/packages/nextjs/src/shared/url.ts deleted file mode 100644 index c99aac60d29..00000000000 --- a/packages/nextjs/src/shared/url.ts +++ /dev/null @@ -1,25 +0,0 @@ -// TODO: Use the same function defined in @clerk/shared once the package is tree shakeable - -type VOrFnReturnsV = T | undefined | ((v: URL) => T); - -export function handleValueOrFn(value: VOrFnReturnsV, url: URL): T | undefined; -export function handleValueOrFn(value: VOrFnReturnsV, url: URL, defaultValue: T): T; -export function handleValueOrFn(value: VOrFnReturnsV, url: URL, defaultValue?: unknown): unknown { - if (typeof value === 'function') { - return (value as (v: URL) => T)(url); - } - - if (typeof value !== 'undefined') { - return value; - } - - if (typeof defaultValue !== 'undefined') { - return defaultValue; - } - - return undefined; -} - -export function isHttpOrHttps(key: string | undefined) { - return /^http(s)?:\/\//.test(key || ''); -} From 514d743d4532d0ff54ba37b4d01a9bc366c4b274 Mon Sep 17 00:00:00 2001 From: Dimitris Klouvas Date: Tue, 19 Sep 2023 02:35:41 +0300 Subject: [PATCH 4/9] chore(nextjs): Warn about withClerkMiddleware deprecation --- packages/nextjs/src/server/withClerkMiddleware.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/nextjs/src/server/withClerkMiddleware.ts b/packages/nextjs/src/server/withClerkMiddleware.ts index 656f7901302..0beca065c5b 100644 --- a/packages/nextjs/src/server/withClerkMiddleware.ts +++ b/packages/nextjs/src/server/withClerkMiddleware.ts @@ -1,5 +1,6 @@ import type { RequestState } from '@clerk/backend'; import { constants, debugRequestState } from '@clerk/backend'; +import { deprecated } from '@clerk/shared'; import type { NextFetchEvent, NextMiddleware, NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; @@ -36,6 +37,11 @@ export const withClerkMiddleware: WithClerkMiddleware = (...args: unknown[]) => const noop = () => undefined; const [handler = noop, opts = {}] = args as [NextMiddleware, WithAuthOptions] | []; + deprecated( + 'withClerkMiddleware', + 'Use `authMiddleware` instead.\nFor more details, consult the middleware documentation: https://clerk.com/docs/nextjs/middleware', + ); + return async (req: NextRequest, event: NextFetchEvent) => { const { isSatellite, domain, signInUrl, proxyUrl } = handleMultiDomainAndProxy(req, opts); From 89d05f4cc9b6241edd42a6f593922e75b461486a Mon Sep 17 00:00:00 2001 From: Dimitris Klouvas Date: Tue, 19 Sep 2023 02:41:55 +0300 Subject: [PATCH 5/9] chore(nextjs): Add deprecation JSDoc for server constants to be marked as internal --- packages/nextjs/src/server/clerkClient.ts | 23 ++++++----------------- packages/nextjs/src/server/constants.ts | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 packages/nextjs/src/server/constants.ts diff --git a/packages/nextjs/src/server/clerkClient.ts b/packages/nextjs/src/server/clerkClient.ts index 2891ec5e132..5ec9ca27118 100644 --- a/packages/nextjs/src/server/clerkClient.ts +++ b/packages/nextjs/src/server/clerkClient.ts @@ -1,23 +1,7 @@ /* eslint-disable turbo/no-undeclared-env-vars */ import { Clerk } from '@clerk/backend'; -/** - * @deprecated - */ -export const JS_VERSION = process.env.CLERK_JS_VERSION || ''; -export const CLERK_JS_VERSION = process.env.NEXT_PUBLIC_CLERK_JS_VERSION || ''; -export const CLERK_JS_URL = process.env.NEXT_PUBLIC_CLERK_JS || ''; -export const API_URL = process.env.CLERK_API_URL || 'https://api.clerk.dev'; -export const API_VERSION = process.env.CLERK_API_VERSION || 'v1'; -export const API_KEY = process.env.CLERK_API_KEY || ''; -export const SECRET_KEY = process.env.CLERK_SECRET_KEY || ''; -export const FRONTEND_API = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API || ''; -export const PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY || ''; -export const DOMAIN = process.env.NEXT_PUBLIC_CLERK_DOMAIN || ''; -export const PROXY_URL = process.env.NEXT_PUBLIC_CLERK_PROXY_URL || ''; -export const IS_SATELLITE = process.env.NEXT_PUBLIC_CLERK_IS_SATELLITE === 'true' || false; -export const SIGN_IN_URL = process.env.NEXT_PUBLIC_CLERK_SIGN_IN_URL || ''; -export const SIGN_UP_URL = process.env.NEXT_PUBLIC_CLERK_SIGN_UP_URL || ''; +import { API_KEY, API_URL, API_VERSION, DOMAIN, IS_SATELLITE, PROXY_URL, SECRET_KEY } from './constants'; const clerkClient = Clerk({ apiKey: API_KEY, @@ -36,3 +20,8 @@ const createClerkClient = Clerk; export { clerkClient, createClerkClient, Clerk }; export * from '@clerk/backend'; + +/** + * @deprecated Don't export the constants. Should be marked as internal + */ +export * from './constants'; diff --git a/packages/nextjs/src/server/constants.ts b/packages/nextjs/src/server/constants.ts new file mode 100644 index 00000000000..ede3e99b723 --- /dev/null +++ b/packages/nextjs/src/server/constants.ts @@ -0,0 +1,17 @@ +/** + * @deprecated + */ +export const JS_VERSION = process.env.CLERK_JS_VERSION || ''; +export const CLERK_JS_VERSION = process.env.NEXT_PUBLIC_CLERK_JS_VERSION || ''; +export const CLERK_JS_URL = process.env.NEXT_PUBLIC_CLERK_JS || ''; +export const API_URL = process.env.CLERK_API_URL || 'https://api.clerk.dev'; +export const API_VERSION = process.env.CLERK_API_VERSION || 'v1'; +export const API_KEY = process.env.CLERK_API_KEY || ''; +export const SECRET_KEY = process.env.CLERK_SECRET_KEY || ''; +export const FRONTEND_API = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API || ''; +export const PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY || ''; +export const DOMAIN = process.env.NEXT_PUBLIC_CLERK_DOMAIN || ''; +export const PROXY_URL = process.env.NEXT_PUBLIC_CLERK_PROXY_URL || ''; +export const IS_SATELLITE = process.env.NEXT_PUBLIC_CLERK_IS_SATELLITE === 'true' || false; +export const SIGN_IN_URL = process.env.NEXT_PUBLIC_CLERK_SIGN_IN_URL || ''; +export const SIGN_UP_URL = process.env.NEXT_PUBLIC_CLERK_SIGN_UP_URL || ''; From 45d74831c9d70f326d53d9f2624bf279e27de302 Mon Sep 17 00:00:00 2001 From: Dimitris Klouvas Date: Tue, 19 Sep 2023 02:43:13 +0300 Subject: [PATCH 6/9] chore(nextjs): Warn about CLERK_JS_VERSION environment variable deprecation --- packages/nextjs/src/server/constants.ts | 7 ++++++- turbo.json | 21 ++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/nextjs/src/server/constants.ts b/packages/nextjs/src/server/constants.ts index ede3e99b723..52228459335 100644 --- a/packages/nextjs/src/server/constants.ts +++ b/packages/nextjs/src/server/constants.ts @@ -1,7 +1,12 @@ +import { deprecated } from '@clerk/shared'; + /** - * @deprecated + * @deprecated Use `CLERK_JS_VERSION` instead. */ export const JS_VERSION = process.env.CLERK_JS_VERSION || ''; +if (JS_VERSION) { + deprecated('CLERK_JS_VERSION', 'Use `NEXT_PUBLIC_CLERK_JS_VERSION` environment variable instead.'); +} export const CLERK_JS_VERSION = process.env.NEXT_PUBLIC_CLERK_JS_VERSION || ''; export const CLERK_JS_URL = process.env.NEXT_PUBLIC_CLERK_JS || ''; export const API_URL = process.env.CLERK_API_URL || 'https://api.clerk.dev'; diff --git a/turbo.json b/turbo.json index 2ea2227a0c0..db1a2977d24 100644 --- a/turbo.json +++ b/turbo.json @@ -11,7 +11,26 @@ "tsconfig.json", "tsconfig.*.json" ], - "globalEnv": ["NODE_VERSION", "NPM_VERSION", "NODE_ENV", "VERCEL"], + "globalEnv": [ + "NODE_VERSION", + "NPM_VERSION", + "NODE_ENV", + "VERCEL", + "CLERK_JS_VERSION", + "CLERK_API_URL", + "CLERK_API_VERSION", + "CLERK_API_KEY", + "CLERK_SECRET_KEY", + "NEXT_PUBLIC_CLERK_JS_VERSION", + "NEXT_PUBLIC_CLERK_JS", + "NEXT_PUBLIC_CLERK_FRONTEND_API", + "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY", + "NEXT_PUBLIC_CLERK_DOMAIN", + "NEXT_PUBLIC_CLERK_PROXY_URL", + "NEXT_PUBLIC_CLERK_IS_SATELLITE", + "NEXT_PUBLIC_CLERK_SIGN_IN_URL", + "NEXT_PUBLIC_CLERK_SIGN_UP_URL" + ], "pipeline": { "build": { "dependsOn": ["^build"], From c32924b0f258da8acc09bdbc9d76f49dde77d2da Mon Sep 17 00:00:00 2001 From: Dimitris Klouvas Date: Tue, 19 Sep 2023 02:49:56 +0300 Subject: [PATCH 7/9] chore(nextjs): Warn about CLERK_API_KEY environment variable deprecation --- packages/nextjs/src/server/constants.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/nextjs/src/server/constants.ts b/packages/nextjs/src/server/constants.ts index 52228459335..39630eebd6e 100644 --- a/packages/nextjs/src/server/constants.ts +++ b/packages/nextjs/src/server/constants.ts @@ -11,7 +11,13 @@ export const CLERK_JS_VERSION = process.env.NEXT_PUBLIC_CLERK_JS_VERSION || ''; export const CLERK_JS_URL = process.env.NEXT_PUBLIC_CLERK_JS || ''; export const API_URL = process.env.CLERK_API_URL || 'https://api.clerk.dev'; export const API_VERSION = process.env.CLERK_API_VERSION || 'v1'; +/** + * @deprecated Use `CLERK_SECRET_KEY` instead. + */ export const API_KEY = process.env.CLERK_API_KEY || ''; +if (API_KEY) { + deprecated('CLERK_API_KEY', 'Use `CLERK_SECRET_KEY` environment variable instead.'); +} export const SECRET_KEY = process.env.CLERK_SECRET_KEY || ''; export const FRONTEND_API = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API || ''; export const PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY || ''; From 17ad6da738cd4917484f6ea8abc1c6a502113260 Mon Sep 17 00:00:00 2001 From: Dimitris Klouvas Date: Tue, 19 Sep 2023 02:50:27 +0300 Subject: [PATCH 8/9] chore(nextjs): Warn about NEXT_PUBLIC_CLERK_FRONTEND_API environment variable deprecation --- packages/nextjs/src/server/constants.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/nextjs/src/server/constants.ts b/packages/nextjs/src/server/constants.ts index 39630eebd6e..c5c3eb71159 100644 --- a/packages/nextjs/src/server/constants.ts +++ b/packages/nextjs/src/server/constants.ts @@ -19,7 +19,13 @@ if (API_KEY) { deprecated('CLERK_API_KEY', 'Use `CLERK_SECRET_KEY` environment variable instead.'); } export const SECRET_KEY = process.env.CLERK_SECRET_KEY || ''; +/** + * @deprecated Use `PUBLISHABLE_KEY` instead. + */ export const FRONTEND_API = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API || ''; +if (FRONTEND_API) { + deprecated('NEXT_PUBLIC_CLERK_FRONTEND_API', 'Use `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` environment variable instead.'); +} export const PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY || ''; export const DOMAIN = process.env.NEXT_PUBLIC_CLERK_DOMAIN || ''; export const PROXY_URL = process.env.NEXT_PUBLIC_CLERK_PROXY_URL || ''; From 921892fffa05d6ab70a8a4afe03472d61421345c Mon Sep 17 00:00:00 2001 From: Dimitris Klouvas Date: Tue, 19 Sep 2023 03:06:51 +0300 Subject: [PATCH 9/9] chore(nextjs): Warn about withServerSideAuth deprecation --- .changeset/spicy-toys-change.md | 5 +++++ packages/nextjs/src/ssr/withServerSideAuth.ts | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changeset/spicy-toys-change.md diff --git a/.changeset/spicy-toys-change.md b/.changeset/spicy-toys-change.md new file mode 100644 index 00000000000..e79bd7a4cb4 --- /dev/null +++ b/.changeset/spicy-toys-change.md @@ -0,0 +1,5 @@ +--- +'@clerk/nextjs': patch +--- + +Warn about deprecations that will be dropped in next major version diff --git a/packages/nextjs/src/ssr/withServerSideAuth.ts b/packages/nextjs/src/ssr/withServerSideAuth.ts index e6d710314fd..409e47ba497 100644 --- a/packages/nextjs/src/ssr/withServerSideAuth.ts +++ b/packages/nextjs/src/ssr/withServerSideAuth.ts @@ -1,5 +1,6 @@ import type { RequestState } from '@clerk/backend'; import { constants, debugRequestState } from '@clerk/backend'; +import { deprecated } from '@clerk/shared'; import type { ServerResponse } from 'http'; import type { GetServerSidePropsContext, GetServerSidePropsResult } from 'next'; @@ -43,6 +44,10 @@ const decorateResponseWithObservabilityHeaders = (res: ServerResponse, requestSt export const withServerSideAuth: WithServerSideAuth = (cbOrOptions: any, options?: any): any => { const cb = typeof cbOrOptions === 'function' ? cbOrOptions : undefined; const opts = (options ? options : typeof cbOrOptions !== 'function' ? cbOrOptions : {}) || {}; + deprecated( + 'withServerSideAuth', + 'Use `authMiddleware` instead.\nFor more details, consult the middleware documentation: https://clerk.com/docs/nextjs/middleware', + ); // Support both loadOrganization and the older loadOrg option without breaking changes // TODO: Remove pre v5