Skip to content

Commit

Permalink
refactor: Use swc AST to determine use client and server directives (#…
Browse files Browse the repository at this point in the history
…54358)

Follow up for #54202, use swc AST to determine the directives. Also add server actions directives for extendibility here in case we might need it in the future

Closes NEXT-1538
  • Loading branch information
huozhi authored Aug 23, 2023
1 parent ad556ae commit db72cb1
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 9 deletions.
36 changes: 31 additions & 5 deletions packages/next/src/build/analysis/get-page-static-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,17 @@ export interface PageStaticInfo {

const CLIENT_MODULE_LABEL =
/\/\* __next_internal_client_entry_do_not_use__ ([^ ]*) (cjs|auto) \*\//

const ACTION_MODULE_LABEL =
/\/\* __next_internal_action_entry_do_not_use__ ([^ ]+) \*\//

const CLIENT_DIRECTIVE = 'use client'
const SERVER_ACTION_DIRECTIVE = 'use server'

export type RSCModuleType = 'server' | 'client'
export function getRSCModuleInformation(
source: string,
isServerLayer = true
isServerLayer: boolean
): RSCMeta {
const actions = source.match(ACTION_MODULE_LABEL)?.[1]?.split(',')
const clientInfoMatch = source.match(CLIENT_MODULE_LABEL)
Expand All @@ -75,14 +79,13 @@ export function getRSCModuleInformation(
const clientEntryType = clientInfoMatch?.[2] as 'cjs' | 'auto'

const type = clientRefs ? RSC_MODULE_TYPES.client : RSC_MODULE_TYPES.server
const hasUseClientDirective = /^\s*['"]use client['"]/.test(source)

return {
type,
actions,
clientRefs,
clientEntryType,
isClientRef,
hasUseClientDirective,
}
}

Expand Down Expand Up @@ -124,6 +127,7 @@ function checkExports(
generateSitemaps?: boolean
generateStaticParams: boolean
extraProperties?: Set<string>
directives?: Set<string>
} {
const exportsSet = new Set<string>([
'getStaticProps',
Expand All @@ -142,8 +146,27 @@ function checkExports(
let generateSitemaps: boolean = false
let generateStaticParams = false
let extraProperties = new Set<string>()
let directives = new Set<string>()
let hasLeadingNonDirectiveNode = false

for (const node of swcAST.body) {
// There should be no non-string literals nodes before directives
if (
node.type === 'ExpressionStatement' &&
node.expression.type === 'StringLiteral'
) {
if (!hasLeadingNonDirectiveNode) {
const directive = node.expression.value
if (CLIENT_DIRECTIVE === directive) {
directives.add('client')
}
if (SERVER_ACTION_DIRECTIVE === directive) {
directives.add('server')
}
}
} else {
hasLeadingNonDirectiveNode = true
}
if (
node.type === 'ExportDeclaration' &&
node.declaration?.type === 'VariableDeclaration'
Expand Down Expand Up @@ -240,6 +263,7 @@ function checkExports(
generateSitemaps,
generateStaticParams,
extraProperties,
directives,
}
} catch (err) {}
}
Expand All @@ -253,6 +277,7 @@ function checkExports(
generateSitemaps: false,
generateStaticParams: false,
extraProperties: undefined,
directives: undefined,
}
}

Expand Down Expand Up @@ -467,8 +492,9 @@ export async function getPageStaticInfo(params: {
preferredRegion,
generateStaticParams,
extraProperties,
directives,
} = checkExports(swcAST, pageFilePath)
const rscInfo = getRSCModuleInformation(fileContent)
const rscInfo = getRSCModuleInformation(fileContent, true)
const rsc = rscInfo.type

// default / failsafe value for config
Expand Down Expand Up @@ -575,7 +601,7 @@ export async function getPageStaticInfo(params: {

if (
pageType === 'app' &&
rscInfo.hasUseClientDirective &&
directives?.has('client') &&
generateStaticParams
) {
throw new Error(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export interface RSCMeta {
clientRefs?: string[]
clientEntryType?: 'cjs' | 'auto'
isClientRef?: boolean
hasUseClientDirective?: boolean
requests?: string[] // client requests in flight client entry
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function transformSource(
// Assign the RSC meta information to buildInfo.
// Exclude next internal files which are not marked as client files
const buildInfo = getModuleBuildInfo(this._module)
buildInfo.rsc = getRSCModuleInformation(source)
buildInfo.rsc = getRSCModuleInformation(source, true)

// A client boundary.
if (buildInfo.rsc?.type === RSC_MODULE_TYPES.client) {
Expand Down
6 changes: 6 additions & 0 deletions test/e2e/app-dir/rsc-basic/app/css-in-js/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default function Layout({ children }) {
return children
}

// make children routes dynamic
export const dynamic = 'force-dynamic'
2 changes: 2 additions & 0 deletions test/e2e/app-dir/rsc-basic/app/css-in-js/suspense/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ export default function page() {
</div>
)
}

export const dynamic = 'force-dynamic'
2 changes: 0 additions & 2 deletions test/e2e/app-dir/rsc-basic/app/layout.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React from 'react'
import RootStyleRegistry from './root-style-registry'

export const revalidate = 0

export default function AppLayout({ children }) {
return (
<html>
Expand Down
2 changes: 2 additions & 0 deletions test/e2e/app-dir/rsc-basic/app/partial-hydration/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ export default function () {
</>
)
}

export const dynamic = 'force-dynamic'

0 comments on commit db72cb1

Please sign in to comment.