Skip to content

Commit 450d747

Browse files
committed
change rsc detection
1 parent 90a3618 commit 450d747

File tree

12 files changed

+86
-94
lines changed

12 files changed

+86
-94
lines changed

packages/next/build/analysis/get-page-static-info.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import * as Log from '../output/log'
1212
import { SERVER_RUNTIME } from '../../lib/constants'
1313
import { ServerRuntime } from 'next/types'
1414
import { checkCustomRoutes } from '../../lib/load-custom-routes'
15+
import { RSC_MODULE_TYPES } from '../../shared/lib/constants'
1516

1617
export interface MiddlewareConfig {
1718
matchers: MiddlewareMatcher[]
@@ -27,9 +28,34 @@ export interface PageStaticInfo {
2728
runtime?: ServerRuntime
2829
ssg?: boolean
2930
ssr?: boolean
31+
rsc?: RSCModuleType
3032
middleware?: Partial<MiddlewareConfig>
3133
}
3234

35+
export type RSCModuleType = 'server' | 'client'
36+
export function getRSCModuleType(swcAST: any): RSCModuleType {
37+
const { body } = swcAST
38+
// TODO-APP: optimize the directive detection
39+
// Assume there're only "use strict" and "client" directives at top,
40+
// so pick the 2 nodes
41+
const firstTwoNodes = body.slice(0, 2)
42+
43+
let rscType: RSCModuleType = 'server'
44+
for (const node of firstTwoNodes) {
45+
if (
46+
node.type === 'ExpressionStatement' &&
47+
node.expression.type === 'StringLiteral'
48+
) {
49+
if (node.expression.value === RSC_MODULE_TYPES.client) {
50+
// Detect client entry
51+
rscType = 'client'
52+
break
53+
}
54+
}
55+
}
56+
return rscType
57+
}
58+
3359
/**
3460
* Receives a parsed AST from SWC and checks if it belongs to a module that
3561
* requires a runtime to be specified. Those are:
@@ -226,6 +252,7 @@ export async function getPageStaticInfo(params: {
226252
if (/runtime|getStaticProps|getServerSideProps|matcher/.test(fileContent)) {
227253
const swcAST = await parseModule(pageFilePath, fileContent)
228254
const { ssg, ssr } = checkExports(swcAST)
255+
const rsc = getRSCModuleType(swcAST)
229256

230257
// default / failsafe value for config
231258
let config: any = {}
@@ -273,10 +300,16 @@ export async function getPageStaticInfo(params: {
273300
return {
274301
ssr,
275302
ssg,
303+
rsc,
276304
...(middlewareConfig && { middleware: middlewareConfig }),
277305
...(runtime && { runtime }),
278306
}
279307
}
280308

281-
return { ssr: false, ssg: false, runtime: nextConfig.experimental?.runtime }
309+
return {
310+
ssr: false,
311+
ssg: false,
312+
rsc: RSC_MODULE_TYPES.server,
313+
runtime: nextConfig.experimental?.runtime,
314+
}
282315
}

packages/next/build/entries.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,12 @@ import { warn } from './output/log'
3737
import {
3838
isMiddlewareFile,
3939
isMiddlewareFilename,
40-
isServerComponentPage,
4140
NestedMiddlewareError,
4241
MiddlewareInServerlessTargetError,
4342
} from './utils'
4443
import { getPageStaticInfo } from './analysis/get-page-static-info'
4544
import { normalizePathSep } from '../shared/lib/page-path/normalize-path-sep'
4645
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
47-
import { serverComponentRegex } from './webpack/loaders/utils'
4846
import { ServerRuntime } from '../types'
4947
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'
5048
import { encodeMatchers } from './webpack/loaders/next-middleware-loader'
@@ -204,10 +202,7 @@ export function getEdgeServerEntry(opts: {
204202
absolutePagePath: opts.absolutePagePath,
205203
buildId: opts.buildId,
206204
dev: opts.isDev,
207-
isServerComponent: isServerComponentPage(
208-
opts.config,
209-
opts.absolutePagePath
210-
),
205+
isServerComponent: opts.isServerComponent,
211206
page: opts.page,
212207
stringifiedConfig: JSON.stringify(opts.config),
213208
pagesType: opts.pagesType,
@@ -414,16 +409,19 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
414409
nestedMiddleware.push(page)
415410
}
416411

417-
const isServerComponent = serverComponentRegex.test(absolutePagePath)
418-
const isInsideAppDir = appDir && absolutePagePath.startsWith(appDir)
419-
412+
const isInsideAppDir =
413+
!!appDir &&
414+
(absolutePagePath.startsWith(APP_DIR_ALIAS) ||
415+
absolutePagePath.startsWith(appDir))
420416
const staticInfo = await getPageStaticInfo({
421417
nextConfig: config,
422418
pageFilePath,
423419
isDev,
424420
page,
425421
})
426422

423+
const isServerComponent = isInsideAppDir && staticInfo.rsc === 'server'
424+
427425
if (isMiddlewareFile(page)) {
428426
middlewareMatchers = staticInfo.middleware?.matchers ?? [
429427
{ regexp: '.*' },

packages/next/build/index.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ import {
9696
printTreeView,
9797
copyTracedFiles,
9898
isReservedPage,
99-
isServerComponentPage,
10099
} from './utils'
101100
import getBaseWebpackConfig from './webpack-config'
102101
import { PagesManifest } from './webpack/plugins/pages-manifest-plugin'
@@ -1267,21 +1266,16 @@ export default async function build(
12671266
)
12681267
: appPaths?.find((p) => p.startsWith(actualPage + '/page.'))
12691268

1270-
const pageRuntime =
1269+
const staticInfo =
12711270
pagesDir && pageType === 'pages' && pagePath
1272-
? (
1273-
await getPageStaticInfo({
1274-
pageFilePath: join(pagesDir, pagePath),
1275-
nextConfig: config,
1276-
})
1277-
).runtime
1278-
: undefined
1279-
1280-
if (hasServerComponents && pagePath) {
1281-
if (isServerComponentPage(config, pagePath)) {
1282-
isServerComponent = true
1283-
}
1284-
}
1271+
? await getPageStaticInfo({
1272+
pageFilePath: join(pagesDir, pagePath),
1273+
nextConfig: config,
1274+
})
1275+
: {}
1276+
const pageRuntime = staticInfo.runtime
1277+
isServerComponent =
1278+
pageType === 'app' && staticInfo.rsc === 'server'
12851279

12861280
if (
12871281
// Only calculate page static information if the page is not an

packages/next/build/utils.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,6 +1327,7 @@ export function detectConflictingPaths(
13271327
}
13281328
}
13291329

1330+
// TODO-APP: drop withoutRSCExtensions to use original page extensions
13301331
/**
13311332
* With RSC we automatically add .server and .client to page extensions. This
13321333
* function allows to remove them for cases where we just need to strip out
@@ -1338,22 +1339,6 @@ export function withoutRSCExtensions(pageExtensions: string[]): string[] {
13381339
)
13391340
}
13401341

1341-
export function isServerComponentPage(
1342-
nextConfig: NextConfigComplete,
1343-
filePath: string
1344-
): boolean {
1345-
if (!nextConfig.experimental.serverComponents) {
1346-
return false
1347-
}
1348-
1349-
const rawPageExtensions = withoutRSCExtensions(
1350-
nextConfig.pageExtensions || []
1351-
)
1352-
return rawPageExtensions.some((ext) => {
1353-
return filePath.endsWith(`.server.${ext}`)
1354-
})
1355-
}
1356-
13571342
export async function copyTracedFiles(
13581343
dir: string,
13591344
distDir: string,

packages/next/build/webpack-config.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -685,10 +685,6 @@ export default async function getBaseWebpackConfig(
685685
? withoutRSCExtensions(config.pageExtensions)
686686
: config.pageExtensions
687687

688-
const serverComponentsRegex = new RegExp(
689-
`\\.server\\.(${rawPageExtensions.join('|')})$`
690-
)
691-
692688
const babelIncludeRegexes: RegExp[] = [
693689
/next[\\/]dist[\\/]shared[\\/]lib/,
694690
/next[\\/]dist[\\/]client/,
Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import path from 'path'
2-
import { RSC_CLIENT_ENTRY } from '../../../../shared/lib/constants'
3-
import { checkExports } from '../../../analysis/get-page-static-info'
2+
import { RSC_MODULE_TYPES } from '../../../../shared/lib/constants'
3+
import {
4+
checkExports,
5+
getRSCModuleType,
6+
} from '../../../analysis/get-page-static-info'
47
import { parse } from '../../../swc'
58

69
function transformClient(resourcePath: string): string {
@@ -31,50 +34,35 @@ const isPageOrLayoutFile = (filePath: string) => {
3134

3235
export default async function transformSource(
3336
this: any,
34-
source: string
35-
): Promise<string> {
37+
source: string,
38+
sourceMap: any
39+
) {
3640
if (typeof source !== 'string') {
3741
throw new Error('Expected source to have been transformed to a string.')
3842
}
3943

4044
const { resourcePath } = this
45+
const callback = this.async()
4146
const buildInfo = (this as any)._module.buildInfo
4247

4348
const swcAST = await parse(source, {
4449
filename: resourcePath,
4550
isModule: 'unknown',
4651
})
47-
const isModule = swcAST.type === 'Module'
48-
const { body } = swcAST
49-
// TODO-APP: optimize the directive detection
50-
// Assume there're only "use strict" and "<type>-entry" directives at top,
51-
// so pick the 2 nodes
52-
const firstTwoNodes = body.slice(0, 2)
53-
const appDir = path.join(this.rootContext, 'app')
54-
const isUnderAppDir = containsPath(appDir, this.resourcePath)
5552

53+
const rscType = getRSCModuleType(swcAST)
54+
55+
// Assign the RSC meta information to buildInfo.
56+
buildInfo.rsc = { type: rscType }
57+
58+
const isModule = swcAST.type === 'Module'
5659
const createError = (name: string) =>
5760
new Error(
5861
`${name} is not supported in client components.\nFrom: ${this.resourcePath}`
5962
)
60-
63+
const appDir = path.join(this.rootContext, 'app')
64+
const isUnderAppDir = containsPath(appDir, this.resourcePath)
6165
const isResourcePageOrLayoutFile = isPageOrLayoutFile(this.resourcePath)
62-
63-
// Assign the RSC meta information to buildInfo.
64-
buildInfo.rsc = {}
65-
for (const node of firstTwoNodes) {
66-
if (
67-
node.type === 'ExpressionStatement' &&
68-
node.expression.type === 'StringLiteral'
69-
) {
70-
if (node.expression.value === RSC_CLIENT_ENTRY) {
71-
// Detect client entry
72-
buildInfo.rsc.type = RSC_CLIENT_ENTRY
73-
break
74-
}
75-
}
76-
}
77-
7866
// If client entry has any gSSP/gSP data fetching methods, error
7967
function errorForInvalidDataFetching(onError: (error: any) => void) {
8068
if (isUnderAppDir && isResourcePageOrLayoutFile) {
@@ -88,12 +76,12 @@ export default async function transformSource(
8876
}
8977
}
9078

91-
if (buildInfo.rsc.type === RSC_CLIENT_ENTRY) {
79+
if (buildInfo.rsc.type === RSC_MODULE_TYPES.client) {
9280
errorForInvalidDataFetching(this.emitError)
9381
const code = transformClient(this.resourcePath)
94-
return code
82+
return callback(null, code, sourceMap)
9583
}
9684

9785
const code = transformServer(source, isModule)
98-
return code
86+
return callback(null, code, sourceMap)
9987
}

packages/next/build/webpack/plugins/flight-client-entry-plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { APP_DIR_ALIAS } from '../../../lib/constants'
1515
import {
1616
COMPILER_NAMES,
1717
FLIGHT_SERVER_CSS_MANIFEST,
18+
RSC_MODULE_TYPES,
1819
} from '../../../shared/lib/constants'
1920
import { FlightCSSManifest } from './flight-manifest-plugin'
2021

@@ -240,7 +241,7 @@ export class FlightClientEntryPlugin {
240241

241242
const isCSS = regexCSS.test(modRequest)
242243
const rscModuleType = mod.buildInfo.rsc?.type
243-
const isClientComponent = rscModuleType === 'client-entry'
244+
const isClientComponent = rscModuleType === RSC_MODULE_TYPES.client
244245

245246
if (isCSS) {
246247
serverCSSImports[layoutOrPageRequest] =

packages/next/build/webpack/plugins/flight-manifest-plugin.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import { webpack, sources } from 'next/dist/compiled/webpack/webpack'
99
import {
1010
FLIGHT_MANIFEST,
11-
RSC_CLIENT_ENTRY,
11+
RSC_MODULE_TYPES,
1212
} from '../../../shared/lib/constants'
1313
import { relative } from 'path'
1414

@@ -179,7 +179,7 @@ export class FlightManifestPlugin {
179179
// That way we know by the type of dep whether to include.
180180
// It also resolves conflicts when the same module is in multiple chunks.
181181
const rscType = mod.buildInfo.rsc?.type
182-
if (rscType !== RSC_CLIENT_ENTRY) return
182+
if (rscType !== RSC_MODULE_TYPES.client) return
183183

184184
if (/\/(page|layout)\.(ts|js)x?$/.test(resource)) {
185185
entryFilepath = resource

packages/next/server/dev/hot-reloader.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ import { getProperError } from '../../lib/is-error'
4646
import ws from 'next/dist/compiled/ws'
4747
import { promises as fs } from 'fs'
4848
import { getPageStaticInfo } from '../../build/analysis/get-page-static-info'
49-
import { serverComponentRegex } from '../../build/webpack/loaders/utils'
5049
import { UnwrapPromise } from '../../lib/coalesced-function'
5150

5251
function diff(a: Set<any>, b: Set<any>) {
@@ -598,27 +597,24 @@ export default class HotReloader {
598597
}
599598
}
600599

601-
const isServerComponent = isEntry
602-
? serverComponentRegex.test(entryData.absolutePagePath)
603-
: false
604-
600+
const isAppPath = !!this.appDir && bundlePath.startsWith('app/')
605601
const staticInfo = isEntry
606602
? await getPageStaticInfo({
607603
pageFilePath: entryData.absolutePagePath,
608604
nextConfig: this.config,
609605
isDev: true,
610606
})
611607
: {}
608+
const isServerComponent = isAppPath && staticInfo.rsc === 'server'
612609

613610
await runDependingOnPageType({
614611
page,
615612
pageRuntime: staticInfo.runtime,
616613
onEdgeServer: () => {
617614
// TODO-APP: verify if child entry should support.
618615
if (!isEdgeServerCompilation || !isEntry) return
619-
const isApp = this.appDir && bundlePath.startsWith('app/')
620616
const appDirLoader =
621-
isApp && this.appDir
617+
isAppPath && this.appDir
622618
? getAppEntry({
623619
name: bundlePath,
624620
appPaths: entryData.appPaths,
@@ -648,7 +644,7 @@ export default class HotReloader {
648644
pages: this.pagesMapping,
649645
isServerComponent,
650646
appDirLoader,
651-
pagesType: isApp ? 'app' : undefined,
647+
pagesType: isAppPath ? 'app' : undefined,
652648
}),
653649
appDir: this.config.experimental.appDir,
654650
})

0 commit comments

Comments
 (0)