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

Handle edge runtime for app #39910

Merged
merged 11 commits into from Aug 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion packages/next/build/analysis/get-page-static-info.ts
Expand Up @@ -256,5 +256,5 @@ export async function getPageStaticInfo(params: {
}
}

return { ssr: false, ssg: false }
return { ssr: false, ssg: false, runtime: nextConfig.experimental?.runtime }
}
16 changes: 16 additions & 0 deletions packages/next/build/entries.ts
Expand Up @@ -164,6 +164,8 @@ export function getEdgeServerEntry(opts: {
page: string
pages: { [page: string]: string }
middleware?: { pathMatcher?: RegExp }
pagesType?: 'app' | 'pages' | 'root'
appDirLoader?: string
}) {
if (isMiddlewareFile(opts.page)) {
const loaderParams: MiddlewareLoaderOptions = {
Expand Down Expand Up @@ -203,6 +205,8 @@ export function getEdgeServerEntry(opts: {
),
page: opts.page,
stringifiedConfig: JSON.stringify(opts.config),
pagesType: opts.pagesType,
appDirLoader: Buffer.from(opts.appDirLoader || '').toString('base64'),
}

return {
Expand Down Expand Up @@ -445,6 +449,16 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
}
},
onEdgeServer: () => {
const appDirLoader =
pagesType === 'app'
? getAppEntry({
name: serverBundlePath,
pagePath: mappings[page],
appDir: appDir!,
pageExtensions,
}).import
: ''

edgeServer[serverBundlePath] = getEdgeServerEntry({
...params,
absolutePagePath: mappings[page],
Expand All @@ -453,6 +467,8 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
isServerComponent,
page,
middleware: staticInfo?.middleware,
pagesType,
appDirLoader,
})
},
})
Expand Down
4 changes: 4 additions & 0 deletions packages/next/build/index.ts
Expand Up @@ -808,6 +808,10 @@ export default async function build(
? [
path.join(SERVER_DIRECTORY, FLIGHT_MANIFEST + '.js'),
path.join(SERVER_DIRECTORY, FLIGHT_MANIFEST + '.json'),
path.join(
SERVER_DIRECTORY,
FLIGHT_SERVER_CSS_MANIFEST + '.js'
),
path.join(
SERVER_DIRECTORY,
FLIGHT_SERVER_CSS_MANIFEST + '.json'
Expand Down
Expand Up @@ -30,6 +30,7 @@ export interface EdgeMiddlewareMeta {

export interface EdgeSSRMeta {
isServerComponent: boolean
isAppDir?: boolean
page: string
}

Expand Down
Expand Up @@ -12,6 +12,8 @@ export type EdgeSSRLoaderQuery = {
isServerComponent: boolean
page: string
stringifiedConfig: string
appDirLoader?: string
pagesType?: 'app' | 'pages' | 'root'
}

export default async function edgeSSRLoader(this: any) {
Expand All @@ -26,12 +28,21 @@ export default async function edgeSSRLoader(this: any) {
absoluteErrorPath,
isServerComponent,
stringifiedConfig,
appDirLoader: appDirLoaderBase64,
pagesType,
} = this.getOptions()

const appDirLoader = Buffer.from(
appDirLoaderBase64 || '',
'base64'
).toString()
const isAppDir = pagesType === 'app'

const buildInfo = getModuleBuildInfo(this._module)
buildInfo.nextEdgeSSR = {
isServerComponent: isServerComponent === 'true',
page: page,
isAppDir,
}
buildInfo.route = {
page,
Expand All @@ -46,6 +57,11 @@ export default async function edgeSSRLoader(this: any) {
? stringifyRequest(this, absolute500Path)
: null

const pageModPath = `${appDirLoader}${stringifiedPagePath.substring(
1,
stringifiedPagePath.length - 1
)}`

const transformed = `
import { adapter, enhanceGlobals } from 'next/dist/server/web/adapter'
import { getRender } from 'next/dist/build/webpack/loaders/next-edge-ssr-loader/render'
Expand All @@ -54,8 +70,21 @@ export default async function edgeSSRLoader(this: any) {

enhanceGlobals()

${
isAppDir
? `
const appRenderToHTML = require('next/dist/server/app-render').renderToHTMLOrFlight
const pagesRenderToHTML = null
const pageMod = require(${JSON.stringify(pageModPath)})
`
: `
const appRenderToHTML = null
const pagesRenderToHTML = require('next/dist/server/render').renderToHTML
const pageMod = require(${stringifiedPagePath})
`
}

const appMod = require(${stringifiedAppPath})
const pageMod = require(${stringifiedPagePath})
const errorMod = require(${stringifiedErrorPath})
const error500Mod = ${
stringified500Path ? `require(${stringified500Path})` : 'null'
Expand All @@ -64,6 +93,7 @@ export default async function edgeSSRLoader(this: any) {
const buildManifest = self.__BUILD_MANIFEST
const reactLoadableManifest = self.__REACT_LOADABLE_MANIFEST
const rscManifest = self.__RSC_MANIFEST
const rscCssManifest = self.__RSC_CSS_MANIFEST

const render = getRender({
dev: ${dev},
Expand All @@ -74,8 +104,11 @@ export default async function edgeSSRLoader(this: any) {
error500Mod,
Document,
buildManifest,
appRenderToHTML,
pagesRenderToHTML,
reactLoadableManifest,
serverComponentManifest: ${isServerComponent} ? rscManifest : null,
serverCSSManifest: ${isServerComponent} ? rscCssManifest : null,
config: ${stringifiedConfig},
buildId: ${JSON.stringify(buildId)},
})
Expand Down
Expand Up @@ -20,7 +20,10 @@ export function getRender({
Document,
buildManifest,
reactLoadableManifest,
appRenderToHTML,
pagesRenderToHTML,
serverComponentManifest,
serverCSSManifest,
config,
buildId,
}: {
Expand All @@ -30,10 +33,13 @@ export function getRender({
pageMod: any
errorMod: any
error500Mod: any
appRenderToHTML: any
pagesRenderToHTML: any
Document: DocumentType
buildManifest: BuildManifest
reactLoadableManifest: ReactLoadableManifest
serverComponentManifest: any
serverCSSManifest: any
appServerMod: any
config: NextConfig
buildId: string
Expand All @@ -58,7 +64,10 @@ export function getRender({
supportsDynamicHTML: true,
disableOptimizedLoading: true,
serverComponentManifest,
serverCSSManifest,
},
appRenderToHTML,
pagesRenderToHTML,
loadComponent: async (pathname) => {
if (pathname === page) {
return {
Expand Down

This file was deleted.

Expand Up @@ -72,19 +72,31 @@ export class FlightClientEntryPlugin {
// client component entry.
for (const [name, entry] of compilation.entries.entries()) {
// Check if the page entry is a server component or not.
const entryDependency = entry.dependencies?.[0]
const entryDependency: webpack.NormalModule | undefined =
entry.dependencies?.[0]
// Ensure only next-app-loader entries are handled.
if (!entryDependency || !entryDependency.request) continue

const request = entryDependency.request

if (
!entryDependency ||
!entryDependency.request ||
!entryDependency.request.startsWith('next-app-loader?')
) {
!request.startsWith('next-edge-ssr-loader?') &&
!request.startsWith('next-app-loader?')
)
continue
}

const entryModule: webpack.NormalModule =
let entryModule: webpack.NormalModule =
compilation.moduleGraph.getResolvedModule(entryDependency)

if (request.startsWith('next-edge-ssr-loader?')) {
entryModule.dependencies.forEach((dependency) => {
const modRequest: string | undefined = (dependency as any).request
if (modRequest?.includes('next-app-loader')) {
entryModule = compilation.moduleGraph.getResolvedModule(dependency)
}
})
}

const internalClientComponentEntryImports = new Set<
ClientComponentImports[0]
>()
Expand Down Expand Up @@ -152,12 +164,15 @@ export class FlightClientEntryPlugin {
name: PLUGIN_NAME,
// Have to be in the optimize stage to run after updating the CSS
// asset hash via extract mini css plugin.
// @ts-ignore TODO: Remove ignore when webpack 5 is stable
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH,
},
(assets: webpack.Compilation['assets']) => {
const manifest = JSON.stringify(flightCSSManifest)
assets[FLIGHT_SERVER_CSS_MANIFEST + '.json'] = new sources.RawSource(
JSON.stringify(flightCSSManifest)
manifest
) as unknown as webpack.sources.RawSource
assets[FLIGHT_SERVER_CSS_MANIFEST + '.js'] = new sources.RawSource(
'self.__RSC_CSS_MANIFEST=' + manifest
) as unknown as webpack.sources.RawSource
}
)
Expand Down
Expand Up @@ -94,7 +94,6 @@ export class FlightManifestPlugin {
name: PLUGIN_NAME,
// Have to be in the optimize stage to run after updating the CSS
// asset hash via extract mini css plugin.
// @ts-ignore TODO: Remove ignore when webpack 5 is stable
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH,
},
(assets) => this.createAsset(assets, compilation, compiler.context)
Expand Down Expand Up @@ -233,10 +232,14 @@ export class FlightManifestPlugin {
moduleExportedKeys.forEach((name) => {
let requiredChunks: ManifestChunks = []
if (!moduleExports[name]) {
const isRelatedChunk = (c: webpack.Chunk) =>
const isRelatedChunk = (c: webpack.Chunk) => {
// If current chunk is a page, it should require the related page chunk;
// If current chunk is a component, it should filter out the related page chunk;
chunk.name?.startsWith('pages/') || !c.name?.startsWith('pages/')
return (
chunk.name?.startsWith('pages/') ||
!c.name?.startsWith('pages/')
)
}

if (appDir) {
requiredChunks = chunkGroup.chunks
Expand Down
2 changes: 2 additions & 0 deletions packages/next/build/webpack/plugins/middleware-plugin.ts
Expand Up @@ -15,6 +15,7 @@ import {
MIDDLEWARE_MANIFEST,
MIDDLEWARE_REACT_LOADABLE_MANIFEST,
NEXT_CLIENT_SSR_ENTRY_SUFFIX,
FLIGHT_SERVER_CSS_MANIFEST,
} from '../../../shared/lib/constants'

export interface EdgeFunctionDefinition {
Expand Down Expand Up @@ -83,6 +84,7 @@ function getEntryFiles(entryFiles: string[], meta: EntryMetadata) {
if (meta.edgeSSR) {
if (meta.edgeSSR.isServerComponent) {
files.push(`server/${FLIGHT_MANIFEST}.js`)
files.push(`server/${FLIGHT_SERVER_CSS_MANIFEST}.js`)
files.push(
...entryFiles
.filter(
Expand Down
8 changes: 6 additions & 2 deletions packages/next/server/app-render.tsx
Expand Up @@ -17,7 +17,6 @@ import {
continueFromInitialStream,
} from './node-web-streams-helper'
import { isDynamicRoute } from '../shared/lib/router/utils'
import { tryGetPreviewData } from './api-utils/node'
import { htmlEscapeJsonString } from './htmlescape'
import { shouldUseReactRoot, stripInternalQueries } from './utils'
import { NextApiRequestCookies } from './api-utils'
Expand Down Expand Up @@ -423,7 +422,7 @@ export async function renderToHTMLOrFlight(
const {
buildManifest,
serverComponentManifest,
serverCSSManifest,
serverCSSManifest = {},
supportsDynamicHTML,
ComponentMod,
} = renderOpts
Expand Down Expand Up @@ -475,6 +474,11 @@ export async function renderToHTMLOrFlight(
*/
const loaderTree: LoaderTree = ComponentMod.tree

const tryGetPreviewData =
process.env.NEXT_RUNTIME === 'edge'
? () => false
: require('./api-utils/node').tryGetPreviewData

// Reads of this are cached on the `req` object, so this should resolve
// instantly. There's no need to pass this data down from a previous
// invoke, where we'd have to consider server & serverless.
Expand Down
31 changes: 16 additions & 15 deletions packages/next/server/base-server.ts
Expand Up @@ -208,6 +208,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
crossOrigin?: string
supportsDynamicHTML?: boolean
serverComponentManifest?: any
serverCSSManifest?: any
renderServerComponentData?: boolean
serverComponentProps?: any
largePageDataBytes?: number
Expand Down Expand Up @@ -1493,26 +1494,26 @@ export default abstract class Server<ServerOptions extends Options = Options> {
return path
}

protected async renderPageComponent(
ctx: RequestContext,
bubbleNoFallback: boolean
) {
// map the route to the actual bundle name
const getOriginalAppPath = (appPath: string) => {
if (this.nextConfig.experimental.appDir) {
const originalAppPath = this.appPathRoutes?.[appPath]
// map the route to the actual bundle name
protected getOriginalAppPath(route: string) {
if (this.nextConfig.experimental.appDir) {
const originalAppPath = this.appPathRoutes?.[route]

if (!originalAppPath) {
return null
}

return originalAppPath
if (!originalAppPath) {
return null
}
return null

return originalAppPath
}
return null
}

protected async renderPageComponent(
ctx: RequestContext,
bubbleNoFallback: boolean
) {
const { query, pathname } = ctx
const appPath = getOriginalAppPath(pathname)
const appPath = this.getOriginalAppPath(pathname)

let page = pathname
if (typeof appPath === 'string') {
Expand Down