Skip to content

Commit

Permalink
Polish the error message when using "use client" from a client action (
Browse files Browse the repository at this point in the history
…vercel#57164)

Fixes NEXT-1605 - explained in the error message.
  • Loading branch information
shuding authored Oct 22, 2023
1 parent e5c0824 commit 7445c35
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { RSC_MOD_REF_PROXY_ALIAS } from '../../../../lib/constants'
import {
RSC_MOD_REF_PROXY_ALIAS,
WEBPACK_LAYERS,
} from '../../../../lib/constants'
import { RSC_MODULE_TYPES } from '../../../../shared/lib/constants'
import { warnOnce } from '../../../../shared/lib/utils/warn-once'
import { getRSCModuleInformation } from '../../../analysis/get-page-static-info'
Expand Down Expand Up @@ -26,10 +29,31 @@ export default function transformSource(

// A client boundary.
if (buildInfo.rsc?.type === RSC_MODULE_TYPES.client) {
const issuerLayer = this._module.layer
const sourceType = this._module?.parser?.sourceType
const detectedClientEntryType = buildInfo.rsc.clientEntryType
const clientRefs = buildInfo.rsc.clientRefs!

if (issuerLayer === WEBPACK_LAYERS.actionBrowser) {
// You're importing a Server Action module ("use server") from a client module
// (hence you're on the actionBrowser layer), and you're trying to import a
// client module again from it. This is not allowed because of cyclic module
// graph.

// We need to only error for user code, not for node_modules/Next.js internals.
// Things like `next/navigation` exports both `cookies()` and `useRouter()`
// and we shouldn't error for that. In the future we might want to find a way
// to only throw when it's used.
if (!this.resourcePath.includes('node_modules')) {
this.callback(
new Error(
`You're importing a Client Component ("use client") from another Client Component imported Server Action file ("use server"). This is not allowed due to cyclic module graph between Server and Client.\nYou can work around it by defining and passing this Server Action from a Server Component into the Client Component via props.`
)
)
return
}
}

// It's tricky to detect the type of a client boundary, but we should always
// use the `module` type when we can, to support `export *` and `export from`
// syntax in other modules that import this client boundary.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { suspense } from '../../shared/lib/lazy-dynamic/dynamic-no-ssr'
import { throwWithNoSSR } from '../../shared/lib/lazy-dynamic/no-ssr-error'
import { staticGenerationAsyncStorage } from './static-generation-async-storage.external'

export function bailoutToClientRendering(): boolean | never {
Expand All @@ -9,7 +9,7 @@ export function bailoutToClientRendering(): boolean | never {
}

if (staticGenerationStore?.isStaticGeneration) {
suspense()
throwWithNoSSR()
}

return false
Expand Down
10 changes: 2 additions & 8 deletions packages/next/src/shared/lib/lazy-dynamic/dynamic-no-ssr.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
'use client'

import type React from 'react'
import { NEXT_DYNAMIC_NO_SSR_CODE } from './no-ssr-error'

export function suspense() {
const error = new Error(NEXT_DYNAMIC_NO_SSR_CODE)
;(error as any).digest = NEXT_DYNAMIC_NO_SSR_CODE
throw error
}
import { throwWithNoSSR } from './no-ssr-error'

type Child = React.ReactElement<any, any>

export function NoSSR({ children }: { children: Child }): Child {
if (typeof window === 'undefined') {
suspense()
throwWithNoSSR()
}

return children
Expand Down
6 changes: 6 additions & 0 deletions packages/next/src/shared/lib/lazy-dynamic/no-ssr-error.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// This has to be a shared module which is shared between client component error boundary and dynamic component

export const NEXT_DYNAMIC_NO_SSR_CODE = 'NEXT_DYNAMIC_NO_SSR_CODE'

export function throwWithNoSSR() {
const error = new Error(NEXT_DYNAMIC_NO_SSR_CODE)
;(error as any).digest = NEXT_DYNAMIC_NO_SSR_CODE
throw error
}

0 comments on commit 7445c35

Please sign in to comment.