fix(nextjs): declare dist/esm as ESM and split ./server by react-server condition#8397
fix(nextjs): declare dist/esm as ESM and split ./server by react-server condition#8397jacekradko wants to merge 5 commits intomainfrom
Conversation
Add `"type": "module"` to `package.esm.json` so `dist/esm/package.json` declares the bundle as ESM. Node 22+ and tsx consumers under `"type": "module"` can now import named exports from `@clerk/nextjs/server` without hitting SyntaxError at instantiate time. Swap `lint:attw --ignore-rules unexpected-module-syntax` (no longer needed) for `--ignore-rules false-cjs`, which fires because types are still emitted as `.d.ts`. The `.d.mts` split is tracked separately. Closes #8396
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
🦋 Changeset detectedLatest commit: e9c5776 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds Changesets release notes and marks the ESM build as an ES module by adding "type": "module" to packages/nextjs/package.esm.json. Updates package.json exports for "./server" to add a Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
Webpack's next-flight-loader rejects 'export *' in client boundaries once a package declares itself as ESM. With the type:module sidecar now in place, the existing re-export chain through '@clerk/nextjs/experimental' broke Next.js 15 app builds that imported from it in client components. Spell out the named exports to satisfy the client-boundary rule while keeping the public API intact.
…statically
Fixes the Next 15 prerender regression that the dist/esm sidecar introduced.
- Split `./server` into a pages-safe `index.ts` and an RSC-layer `index.rsc.ts`.
The RSC variant re-exports the pages-safe surface plus `auth` and `currentUser`,
which are now only resolvable under the `react-server` export condition.
- Replace the lazy `require('server-only')` calls in `auth.ts` and `currentUser.ts`
with a top-of-file `import 'server-only'`. Safe because those modules only load
under the `react-server` condition; webpack can analyze the static import in
both CJS and ESM module classifications.
- Update `./server` exports condition map and extend the exports snapshot test to
cover both the default and react-server surfaces.
vitest has no react-server resolve condition, so importing the RSC surface transitively loads auth.ts and crashes on the client-facing server-only export. Stubbing the module keeps the test focused on verifying the re-export surface.
Vitest keys nested describes with ' > ' between every level including the final `it`. The initial snapshot file was missing the separator between the inner describe and the test name, so CI (which refuses to auto-create snapshots) reported the tests as mismatched.
Summary
Two changes, landed together to keep Next 15 working while fixing native-Node ESM resolution of
@clerk/nextjs/server:Declare
dist/esmas ESM via atype:modulesidecar.packages/nextjs/package.esm.jsonnow ships with"type": "module"so Node 22+ andtsxconsumers under"type": "module"can import named exports from@clerk/nextjs/serverwithout theSyntaxError: does not provide an export named 'clerkClient'reported in dist/esm lacks 'type: module' metadata — breaks @clerk/nextjs on Node 22+ under type:module consumers #8396.Split
./serverby thereact-servercondition and importserver-onlystatically. Under ESM classification webpack preservesrequire('server-only')as a raw Node runtime call, which resolves without thereact-servercondition and trips the client-only guard — that was the Next 15 prerender regression the sidecar alone introduced. The fix:packages/nextjs/src/server/index.rsc.tsselected by thereact-serverexport condition. It re-exports the pages-safe surface plusauthandcurrentUser.packages/nextjs/src/server/index.tskeeps the pages-safe surface (getAuth,buildClerkProps,clerkMiddleware, etc.). Pages-router consumers never transitively loadauth.ts.auth.tsandcurrentUser.tsnowimport 'server-only'at the top of the file instead of callingrequire('server-only')lazily inside the function body. Analyzable by webpack in both CJS and ESM module classifications.lint:attwswapsunexpected-module-syntaxfor--ignore-rules false-cjs: withtype: moduleset,unexpected-module-syntaxstops firing, but attw now flagsfalse-cjson every subpath because type declarations are still emitted as.d.ts. Splitting types to.d.mtsis tracked as a separate follow-up.Closes #8396
Test plan
pnpm --filter @clerk/nextjs buildsucceeds —dist/esm/package.jsoncontains"type": "module",dist/esm/server/index.rsc.jsis emitted.pnpm --filter @clerk/nextjs lint:attwpasses with the swapped ignore rule.pnpm --filter @clerk/nextjs lint:publintpasses (pre-existing warnings unchanged).pnpm --filter @clerk/nextjs test— no new regressions. Snapshot test extended to cover both default andreact-serversurfaces of./server.import { clerkClient } from '@clerk/nextjs/server'under"type": "module") now printsclerkClient typeof: function.await auth()builds cleanly — previously failed withModule "server-only" cannot be imported from a Client Component module.