Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/nextjs-esm-type-module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/nextjs': patch
---

Fix ESM resolution under consumers with `"type": "module"`. The package now emits `dist/esm/package.json` with `"type": "module"`, so Node 22+ and tsx no longer treat bundled ESM output as CJS and named imports from `@clerk/nextjs/server` resolve correctly. Closes #8396.
5 changes: 5 additions & 0 deletions .changeset/nextjs-rsc-server-only.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/nextjs': patch
---

Restore Next 15 prerender compatibility for pages that call `auth()` / `currentUser()`. The `./server` subpath now splits by the `react-server` export condition: app-router-only helpers live under the RSC condition, while the pages-router-safe surface (`getAuth`, `buildClerkProps`, `clerkMiddleware`, etc.) remains in the default condition. `server-only` is now imported statically in `auth.ts` / `currentUser.ts` — cleaner than the previous lazy-`require` and analyzable by webpack in both CJS and ESM module classifications.
1 change: 1 addition & 0 deletions packages/nextjs/package.esm.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"type": "module",
"sideEffects": false,
"imports": {
"#components": {
Expand Down
8 changes: 6 additions & 2 deletions packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@
"require": "./dist/cjs/index.js"
},
"./server": {
"types": "./dist/types/server/index.d.ts",
"types": "./dist/types/server/index.rsc.d.ts",
"react-server": {
"import": "./dist/esm/server/index.rsc.js",
"require": "./dist/cjs/server/index.rsc.js"
},
"import": "./dist/esm/server/index.js",
"require": "./dist/cjs/server/index.js"
},
Expand Down Expand Up @@ -78,7 +82,7 @@
"format": "node ../../scripts/format-package.mjs",
"format:check": "node ../../scripts/format-package.mjs --check",
"lint": "eslint src",
"lint:attw": "attw --pack . --profile node16 --ignore-rules unexpected-module-syntax",
"lint:attw": "attw --pack . --profile node16 --ignore-rules false-cjs",
"lint:publint": "publint",
"test": "vitest run",
"test:watch": "vitest watch"
Expand Down
8 changes: 2 additions & 6 deletions packages/nextjs/src/app-router/server/auth.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'server-only';

import type { SessionAuthObject } from '@clerk/backend';
import type { AuthOptions, GetAuthFnNoRequest, RedirectFun } from '@clerk/backend/internal';
import { constants, createClerkRequest, createRedirect, TokenType } from '@clerk/backend/internal';
Expand Down Expand Up @@ -75,9 +77,6 @@ export type AuthFn = GetAuthFnNoRequest<SessionAuthWithRedirect, true> & {
* - Requires [`clerkMiddleware()`](https://clerk.com/docs/reference/nextjs/clerk-middleware) to be configured.
*/
export const auth: AuthFn = (async (options?: AuthOptions) => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
require('server-only');

try {
const request = await buildRequestLike();

Expand Down Expand Up @@ -158,9 +157,6 @@ export const auth: AuthFn = (async (options?: AuthOptions) => {
}) as AuthFn;

auth.protect = async (...args: any[]) => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
require('server-only');

const request = await buildRequestLike();
const requestedToken = args?.[0]?.token || args?.[1]?.token || TokenType.SessionToken;
const authObject = await auth({ acceptsToken: requestedToken });
Expand Down
5 changes: 2 additions & 3 deletions packages/nextjs/src/app-router/server/currentUser.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'server-only';

import type { User } from '@clerk/backend';
import type { PendingSessionOptions } from '@clerk/shared/types';

Expand Down Expand Up @@ -29,9 +31,6 @@ type CurrentUserOptions = PendingSessionOptions;
* ```
*/
export async function currentUser(opts?: CurrentUserOptions): Promise<User | null> {
// eslint-disable-next-line @typescript-eslint/no-require-imports
require('server-only');

try {
const { userId } = await auth({ treatPendingAsSignedOut: opts?.treatPendingAsSignedOut });
if (!userId) {
Expand Down
22 changes: 21 additions & 1 deletion packages/nextjs/src/experimental.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
'use client';

export * from '@clerk/react/experimental';
export {
CheckoutButton,
PlanDetailsButton,
SubscriptionDetailsButton,
PaymentElementProvider,
usePaymentElement,
PaymentElement,
usePaymentAttempts,
useStatements,
usePaymentMethods,
usePlans,
useSubscription,
CheckoutProvider,
useCheckout,
} from '@clerk/react/experimental';

export type {
CheckoutButtonProps,
SubscriptionDetailsButtonProps,
PlanDetailsButtonProps,
} from '@clerk/react/experimental';
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`/server public exports > should not include a breaking change 1`] = `
exports[`/server public exports > default condition (pages-safe) > should not include a breaking change 1`] = `
[
"buildClerkProps",
"clerkClient",
"clerkFrontendApiProxy",
"clerkMiddleware",
"createClerkClient",
"createFrontendApiProxyHandlers",
"createRouteMatcher",
"getAuth",
"reverificationError",
"reverificationErrorResponse",
"verifyToken",
]
`;

exports[`/server public exports > react-server condition > should not include a breaking change 1`] = `
[
"auth",
"buildClerkProps",
Expand Down
17 changes: 14 additions & 3 deletions packages/nextjs/src/server/__tests__/exports.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { describe, expect, it } from 'vitest';
import { describe, expect, it, vi } from 'vitest';

vi.mock('server-only', () => ({}));

import * as publicExports from '../index';
import * as rscExports from '../index.rsc';

describe('/server public exports', () => {
it('should not include a breaking change', () => {
expect(Object.keys(publicExports).sort()).toMatchSnapshot();
describe('default condition (pages-safe)', () => {
it('should not include a breaking change', () => {
expect(Object.keys(publicExports).sort()).toMatchSnapshot();
});
});

describe('react-server condition', () => {
it('should not include a breaking change', () => {
expect(Object.keys(rscExports).sort()).toMatchSnapshot();
});
});
});
14 changes: 14 additions & 0 deletions packages/nextjs/src/server/index.rsc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* RSC-layer entrypoint for `@clerk/nextjs/server`.
*
* Selected by the `react-server` export condition in `package.json`. Adds the
* app-router-only helpers (`auth`, `currentUser`) on top of the pages-safe
* surface exported from `./index`. Pulling those helpers here — rather than
* from `./index` directly — keeps pages-router consumers from transitively
* importing `server-only`, which throws under a non-RSC condition.
*/

export * from './index';

export { auth } from '../app-router/server/auth';
export { currentUser } from '../app-router/server/currentUser';
7 changes: 5 additions & 2 deletions packages/nextjs/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@ export type {

/**
* NextJS-specific exports
*
* NOTE: `auth` and `currentUser` are re-exported from `./index.rsc.ts` only,
* which is selected via the `react-server` export condition. Loading them
* outside the RSC layer (e.g. from pages-router code) would transitively
* import `server-only` and crash at module load.
*/
export { getAuth } from './createGetAuth';
export { buildClerkProps } from './buildClerkProps';
export { auth } from '../app-router/server/auth';
export { currentUser } from '../app-router/server/currentUser';
export { clerkMiddleware } from './clerkMiddleware';
export type { ClerkMiddlewareAuth, ClerkMiddlewareSessionAuthObject, ClerkMiddlewareOptions } from './clerkMiddleware';

Expand Down
Loading