Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client directive #40415

Merged
merged 28 commits into from Sep 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 18 additions & 1 deletion packages/next/build/analysis/get-page-static-info.ts
Expand Up @@ -13,6 +13,7 @@ import { SERVER_RUNTIME } from '../../lib/constants'
import { ServerRuntime } from 'next/types'
import { checkCustomRoutes } from '../../lib/load-custom-routes'
import { matcher } from 'next/dist/compiled/micromatch'
import { RSC_MODULE_TYPES } from '../../shared/lib/constants'

export interface MiddlewareConfig {
matchers: MiddlewareMatcher[]
Expand All @@ -29,9 +30,18 @@ export interface PageStaticInfo {
runtime?: ServerRuntime
ssg?: boolean
ssr?: boolean
rsc?: RSCModuleType
middleware?: Partial<MiddlewareConfig>
}

const CLIENT_MODULE_LABEL = `/* __next_internal_client_entry_do_not_use__ */`
export type RSCModuleType = 'server' | 'client'
export function getRSCModuleType(source: string): RSCModuleType {
return source.includes(CLIENT_MODULE_LABEL)
? RSC_MODULE_TYPES.client
: RSC_MODULE_TYPES.server
}

/**
* Receives a parsed AST from SWC and checks if it belongs to a module that
* requires a runtime to be specified. Those are:
Expand Down Expand Up @@ -252,6 +262,7 @@ export async function getPageStaticInfo(params: {
) {
const swcAST = await parseModule(pageFilePath, fileContent)
const { ssg, ssr } = checkExports(swcAST)
const rsc = getRSCModuleType(fileContent)

// default / failsafe value for config
let config: any = {}
Expand Down Expand Up @@ -303,10 +314,16 @@ export async function getPageStaticInfo(params: {
return {
ssr,
ssg,
rsc,
...(middlewareConfig && { middleware: middlewareConfig }),
...(runtime && { runtime }),
}
}

return { ssr: false, ssg: false, runtime: nextConfig.experimental?.runtime }
return {
ssr: false,
ssg: false,
rsc: RSC_MODULE_TYPES.server,
runtime: nextConfig.experimental?.runtime,
}
}
26 changes: 9 additions & 17 deletions packages/next/build/entries.ts
Expand Up @@ -21,6 +21,7 @@ import {
SERVER_RUNTIME,
WEBPACK_LAYERS,
} from '../lib/constants'
import { RSC_MODULE_TYPES } from '../shared/lib/constants'
import {
CLIENT_STATIC_FILES_RUNTIME_AMP,
CLIENT_STATIC_FILES_RUNTIME_MAIN,
Expand All @@ -37,14 +38,12 @@ import { warn } from './output/log'
import {
isMiddlewareFile,
isMiddlewareFilename,
isServerComponentPage,
NestedMiddlewareError,
MiddlewareInServerlessTargetError,
} from './utils'
import { getPageStaticInfo } from './analysis/get-page-static-info'
import { normalizePathSep } from '../shared/lib/page-path/normalize-path-sep'
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
import { serverComponentRegex } from './webpack/loaders/utils'
import { ServerRuntime } from '../types'
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'
import { encodeMatchers } from './webpack/loaders/next-middleware-loader'
Expand All @@ -66,14 +65,12 @@ export function getPageFromPath(pagePath: string, pageExtensions: string[]) {
}

export function createPagesMapping({
hasServerComponents,
isDev,
pageExtensions,
pagePaths,
pagesType,
pagesDir,
}: {
hasServerComponents: boolean
isDev: boolean
pageExtensions: string[]
pagePaths: string[]
Expand All @@ -90,13 +87,6 @@ export function createPagesMapping({

const pageKey = getPageFromPath(pagePath, pageExtensions)

// Assume that if there's a Client Component, that there is
// a matching Server Component that will map to the page.
// so we will not process it
if (hasServerComponents && /\.client$/.test(pageKey)) {
return result
}

if (pageKey in result) {
warn(
`Duplicate page detected. ${chalk.cyan(
Expand Down Expand Up @@ -208,10 +198,7 @@ export function getEdgeServerEntry(opts: {
absolutePagePath: opts.absolutePagePath,
buildId: opts.buildId,
dev: opts.isDev,
isServerComponent: isServerComponentPage(
opts.config,
opts.absolutePagePath
),
isServerComponent: opts.isServerComponent,
page: opts.page,
stringifiedConfig: JSON.stringify(opts.config),
pagesType: opts.pagesType,
Expand Down Expand Up @@ -418,8 +405,10 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
nestedMiddleware.push(page)
}

const isServerComponent = serverComponentRegex.test(absolutePagePath)
const isInsideAppDir = appDir && absolutePagePath.startsWith(appDir)
const isInsideAppDir =
!!appDir &&
(absolutePagePath.startsWith(APP_DIR_ALIAS) ||
absolutePagePath.startsWith(appDir))

const staticInfo = await getPageStaticInfo({
nextConfig: config,
Expand All @@ -428,6 +417,9 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
page,
})

const isServerComponent =
isInsideAppDir && staticInfo.rsc !== RSC_MODULE_TYPES.client

if (isMiddlewareFile(page)) {
middlewareMatchers = staticInfo.middleware?.matchers ?? [
{ regexp: '.*' },
Expand Down
29 changes: 11 additions & 18 deletions packages/next/build/index.ts
Expand Up @@ -58,6 +58,7 @@ import {
COMPILER_NAMES,
APP_BUILD_MANIFEST,
FLIGHT_SERVER_CSS_MANIFEST,
RSC_MODULE_TYPES,
} from '../shared/lib/constants'
import { getSortedRoutes, isDynamicRoute } from '../shared/lib/router/utils'
import { __ApiPreviewProps } from '../server/api-utils'
Expand Down Expand Up @@ -96,7 +97,6 @@ import {
printTreeView,
copyTracedFiles,
isReservedPage,
isServerComponentPage,
} from './utils'
import getBaseWebpackConfig from './webpack-config'
import { PagesManifest } from './webpack/plugins/pages-manifest-plugin'
Expand Down Expand Up @@ -489,7 +489,6 @@ export default async function build(
.traceChild('create-pages-mapping')
.traceFn(() =>
createPagesMapping({
hasServerComponents,
isDev: false,
pageExtensions: config.pageExtensions,
pagesType: 'pages',
Expand All @@ -506,7 +505,6 @@ export default async function build(
.traceFn(() =>
createPagesMapping({
pagePaths: appPaths!,
hasServerComponents,
isDev: false,
pagesType: 'app',
pageExtensions: config.pageExtensions,
Expand All @@ -518,7 +516,6 @@ export default async function build(
let mappedRootPaths: { [page: string]: string } = {}
if (rootPaths.length > 0) {
mappedRootPaths = createPagesMapping({
hasServerComponents,
isDev: false,
pageExtensions: config.pageExtensions,
pagePaths: rootPaths,
Expand Down Expand Up @@ -1267,21 +1264,17 @@ export default async function build(
)
: appPaths?.find((p) => p.startsWith(actualPage + '/page.'))

const pageRuntime =
const staticInfo =
pagesDir && pageType === 'pages' && pagePath
? (
await getPageStaticInfo({
pageFilePath: join(pagesDir, pagePath),
nextConfig: config,
})
).runtime
: undefined

if (hasServerComponents && pagePath) {
if (isServerComponentPage(config, pagePath)) {
isServerComponent = true
}
}
? await getPageStaticInfo({
pageFilePath: join(pagesDir, pagePath),
nextConfig: config,
})
: {}
const pageRuntime = staticInfo.runtime
isServerComponent =
pageType === 'app' &&
staticInfo.rsc !== RSC_MODULE_TYPES.client
huozhi marked this conversation as resolved.
Show resolved Hide resolved

if (
// Only calculate page static information if the page is not an
Expand Down
27 changes: 0 additions & 27 deletions packages/next/build/utils.ts
Expand Up @@ -1327,33 +1327,6 @@ export function detectConflictingPaths(
}
}

/**
* With RSC we automatically add .server and .client to page extensions. This
* function allows to remove them for cases where we just need to strip out
* the actual extension keeping the .server and .client.
*/
export function withoutRSCExtensions(pageExtensions: string[]): string[] {
return pageExtensions.filter(
(ext) => !ext.startsWith('client.') && !ext.startsWith('server.')
)
}

export function isServerComponentPage(
nextConfig: NextConfigComplete,
filePath: string
): boolean {
if (!nextConfig.experimental.serverComponents) {
return false
}

const rawPageExtensions = withoutRSCExtensions(
nextConfig.pageExtensions || []
)
return rawPageExtensions.some((ext) => {
return filePath.endsWith(`.server.${ext}`)
})
}

export async function copyTracedFiles(
dir: string,
distDir: string,
Expand Down
51 changes: 15 additions & 36 deletions packages/next/build/webpack-config.ts
Expand Up @@ -56,7 +56,6 @@ import type {
} from './webpack/plugins/telemetry-plugin'
import type { Span } from '../trace'
import type { MiddlewareMatcher } from './analysis/get-page-static-info'
import { withoutRSCExtensions } from './utils'
import browserslist from 'next/dist/compiled/browserslist'
import loadJsConfig from './load-jsconfig'
import { loadBindings } from './swc'
Expand Down Expand Up @@ -687,13 +686,7 @@ export default async function getBaseWebpackConfig(
babel: getBabelOrSwcLoader(),
}

const rawPageExtensions = hasServerComponents
? withoutRSCExtensions(config.pageExtensions)
: config.pageExtensions

const serverComponentsRegex = new RegExp(
`\\.server\\.(${rawPageExtensions.join('|')})$`
)
const pageExtensions = config.pageExtensions

const babelIncludeRegexes: RegExp[] = [
/next[\\/]dist[\\/]shared[\\/]lib/,
Expand Down Expand Up @@ -801,7 +794,7 @@ export default async function getBaseWebpackConfig(
if (dev) {
customAppAliases[`${PAGES_DIR_ALIAS}/_app`] = [
...(pagesDir
? rawPageExtensions.reduce((prev, ext) => {
? pageExtensions.reduce((prev, ext) => {
prev.push(path.join(pagesDir, `_app.${ext}`))
return prev
}, [] as string[])
Expand All @@ -810,7 +803,7 @@ export default async function getBaseWebpackConfig(
]
customAppAliases[`${PAGES_DIR_ALIAS}/_error`] = [
...(pagesDir
? rawPageExtensions.reduce((prev, ext) => {
? pageExtensions.reduce((prev, ext) => {
prev.push(path.join(pagesDir, `_error.${ext}`))
return prev
}, [] as string[])
Expand All @@ -819,7 +812,7 @@ export default async function getBaseWebpackConfig(
]
customDocumentAliases[`${PAGES_DIR_ALIAS}/_document`] = [
...(pagesDir
? rawPageExtensions.reduce((prev, ext) => {
? pageExtensions.reduce((prev, ext) => {
prev.push(path.join(pagesDir, `_document.${ext}`))
return prev
}, [] as string[])
Expand Down Expand Up @@ -874,7 +867,7 @@ export default async function getBaseWebpackConfig(
...getReactProfilingInProduction(),

[RSC_MOD_REF_PROXY_ALIAS]:
'next/dist/build/webpack/loaders/next-flight-client-loader/module-proxy',
'next/dist/build/webpack/loaders/next-flight-loader/module-proxy',

...(isClient || isEdgeServer
? {
Expand Down Expand Up @@ -1151,11 +1144,6 @@ export default async function getBaseWebpackConfig(
},
}

const serverComponentCodeCondition = {
test: serverComponentsRegex,
include: [dir, /next[\\/]dist[\\/]pages/],
}

const rscSharedRegex =
/(node_modules\/react\/|\/shared\/lib\/(head-manager-context|router-context|flush-effects)\.js|node_modules\/styled-jsx\/)/

Expand Down Expand Up @@ -1451,8 +1439,7 @@ export default async function getBaseWebpackConfig(
'next-image-loader',
'next-serverless-loader',
'next-style-loader',
'next-flight-client-loader',
'next-flight-server-loader',
'next-flight-loader',
'next-flight-client-entry-loader',
'noop-loader',
'next-middleware-loader',
Expand Down Expand Up @@ -1492,24 +1479,17 @@ export default async function getBaseWebpackConfig(
? [
// RSC server compilation loaders
{
...serverComponentCodeCondition,
test: codeCondition.test,
include: [
dir,
// To let the internal client components passing through flight loader
/next[\\/]dist[\\/]client[\\/]/,
],
issuerLayer: WEBPACK_LAYERS.server,
use: {
loader: 'next-flight-server-loader',
loader: 'next-flight-loader',
},
},
// {
// test: clientComponentRegex,
// issuerLayer: WEBPACK_LAYERS.server,
// use: {
// loader: 'next-flight-client-loader',
// },
// },
// _app should be treated as a client component as well as all its dependencies.
{
test: new RegExp(`_app\\.(${rawPageExtensions.join('|')})$`),
layer: WEBPACK_LAYERS.client,
},
]
: []
: []),
Expand Down Expand Up @@ -1841,11 +1821,10 @@ export default async function getBaseWebpackConfig(
isClient &&
new AppBuildManifestPlugin({ dev }),
hasServerComponents &&
!!config.experimental.appDir &&
(isClient
? new FlightManifestPlugin({
dev,
appDir: !!config.experimental.appDir,
pageExtensions: rawPageExtensions,
})
: new FlightClientEntryPlugin({
dev,
Expand Down Expand Up @@ -1995,7 +1974,7 @@ export default async function getBaseWebpackConfig(

const configVars = JSON.stringify({
crossOrigin: config.crossOrigin,
pageExtensions: rawPageExtensions,
pageExtensions: pageExtensions,
trailingSlash: config.trailingSlash,
buildActivity: config.devIndicators.buildActivity,
buildActivityPosition: config.devIndicators.buildActivityPosition,
Expand Down
2 changes: 1 addition & 1 deletion packages/next/build/webpack/config/blocks/css/index.ts
Expand Up @@ -501,7 +501,7 @@ export const css = curry(async function css(
// If it's inside the app dir, but not importing from a layout file,
// throw an error.
and: [ctx.rootDirectory],
not: [/layout(\.client|\.server)?\.(js|mjs|jsx|ts|tsx)$/],
not: [/layout\.(js|mjs|jsx|ts|tsx)$/],
}
: undefined,
use: {
Expand Down