From 49b4dae5703f2d7ee05c38464322e66b58a0ab68 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Wed, 24 Aug 2022 14:49:47 -0500 Subject: [PATCH] Handle edge runtime for app (#39910) Continuation of https://github.com/vercel/next.js/pull/38817 this adds handling to allow leveraging the `experimental-edge` runtime for `app`. ## Bug - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` Co-authored-by: Jiachi Liu --- .../build/analysis/get-page-static-info.ts | 2 +- packages/next/build/entries.ts | 16 +++++ packages/next/build/index.ts | 4 ++ .../webpack/loaders/get-module-build-info.ts | 1 + .../loaders/next-edge-ssr-loader/index.ts | 35 ++++++++++- .../loaders/next-edge-ssr-loader/render.ts | 9 +++ .../loaders/next-edge-ssr-loader/utils.ts | 8 --- .../plugins/flight-client-entry-plugin.ts | 33 +++++++--- .../webpack/plugins/flight-manifest-plugin.ts | 9 ++- .../webpack/plugins/middleware-plugin.ts | 2 + packages/next/server/app-render.tsx | 8 ++- packages/next/server/base-server.ts | 31 ++++----- packages/next/server/dev/hot-reloader.ts | 19 ++++++ packages/next/server/dev/next-dev-server.ts | 7 ++- packages/next/server/next-server.ts | 18 +++++- packages/next/server/web-server.ts | 63 ++++++++++++------- .../app-dir/app/app/dashboard/page.server.js | 4 ++ .../app/slow-page-with-loading/page.server.js | 4 ++ test/e2e/app-dir/app/pages/api/hello.js | 7 +++ 19 files changed, 215 insertions(+), 65 deletions(-) delete mode 100644 packages/next/build/webpack/loaders/next-edge-ssr-loader/utils.ts create mode 100644 test/e2e/app-dir/app/pages/api/hello.js diff --git a/packages/next/build/analysis/get-page-static-info.ts b/packages/next/build/analysis/get-page-static-info.ts index 98347d077035..5e9857a65195 100644 --- a/packages/next/build/analysis/get-page-static-info.ts +++ b/packages/next/build/analysis/get-page-static-info.ts @@ -256,5 +256,5 @@ export async function getPageStaticInfo(params: { } } - return { ssr: false, ssg: false } + return { ssr: false, ssg: false, runtime: nextConfig.experimental?.runtime } } diff --git a/packages/next/build/entries.ts b/packages/next/build/entries.ts index 50132451edcd..210c7226c34d 100644 --- a/packages/next/build/entries.ts +++ b/packages/next/build/entries.ts @@ -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 = { @@ -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 { @@ -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], @@ -453,6 +467,8 @@ export async function createEntrypoints(params: CreateEntrypointsParams) { isServerComponent, page, middleware: staticInfo?.middleware, + pagesType, + appDirLoader, }) }, }) diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 3a586cec50f7..3a24961a3fca 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -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' diff --git a/packages/next/build/webpack/loaders/get-module-build-info.ts b/packages/next/build/webpack/loaders/get-module-build-info.ts index 2e5365d7b3fe..eccedc148468 100644 --- a/packages/next/build/webpack/loaders/get-module-build-info.ts +++ b/packages/next/build/webpack/loaders/get-module-build-info.ts @@ -30,6 +30,7 @@ export interface EdgeMiddlewareMeta { export interface EdgeSSRMeta { isServerComponent: boolean + isAppDir?: boolean page: string } diff --git a/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts b/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts index 52ec6985f443..c09e23cb7e68 100644 --- a/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts +++ b/packages/next/build/webpack/loaders/next-edge-ssr-loader/index.ts @@ -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) { @@ -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, @@ -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' @@ -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' @@ -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}, @@ -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)}, }) diff --git a/packages/next/build/webpack/loaders/next-edge-ssr-loader/render.ts b/packages/next/build/webpack/loaders/next-edge-ssr-loader/render.ts index f20365696874..2424b0cdffbe 100644 --- a/packages/next/build/webpack/loaders/next-edge-ssr-loader/render.ts +++ b/packages/next/build/webpack/loaders/next-edge-ssr-loader/render.ts @@ -20,7 +20,10 @@ export function getRender({ Document, buildManifest, reactLoadableManifest, + appRenderToHTML, + pagesRenderToHTML, serverComponentManifest, + serverCSSManifest, config, buildId, }: { @@ -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 @@ -58,7 +64,10 @@ export function getRender({ supportsDynamicHTML: true, disableOptimizedLoading: true, serverComponentManifest, + serverCSSManifest, }, + appRenderToHTML, + pagesRenderToHTML, loadComponent: async (pathname) => { if (pathname === page) { return { diff --git a/packages/next/build/webpack/loaders/next-edge-ssr-loader/utils.ts b/packages/next/build/webpack/loaders/next-edge-ssr-loader/utils.ts deleted file mode 100644 index 76d2f6fe0f34..000000000000 --- a/packages/next/build/webpack/loaders/next-edge-ssr-loader/utils.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { stringifyRequest } from '../../stringify-request' - -export function getStringifiedAbsolutePath(target: any, path: string) { - return stringifyRequest( - target, - target.utils.absolutify(target.rootContext, path) - ) -} diff --git a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts index 358f8707bbac..fc873e08e0a5 100644 --- a/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-client-entry-plugin.ts @@ -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] >() @@ -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 } ) diff --git a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts index e3e721c06f8a..6b4d0f328bfc 100644 --- a/packages/next/build/webpack/plugins/flight-manifest-plugin.ts +++ b/packages/next/build/webpack/plugins/flight-manifest-plugin.ts @@ -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) @@ -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 diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index 48bc6528bc88..11432fe8d730 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -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 { @@ -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( diff --git a/packages/next/server/app-render.tsx b/packages/next/server/app-render.tsx index 012a0682d01a..f61757c58ee2 100644 --- a/packages/next/server/app-render.tsx +++ b/packages/next/server/app-render.tsx @@ -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' @@ -423,7 +422,7 @@ export async function renderToHTMLOrFlight( const { buildManifest, serverComponentManifest, - serverCSSManifest, + serverCSSManifest = {}, supportsDynamicHTML, ComponentMod, } = renderOpts @@ -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. diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index 99b44c8378d8..df3413e07faf 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -208,6 +208,7 @@ export default abstract class Server { crossOrigin?: string supportsDynamicHTML?: boolean serverComponentManifest?: any + serverCSSManifest?: any renderServerComponentData?: boolean serverComponentProps?: any largePageDataBytes?: number @@ -1493,26 +1494,26 @@ export default abstract class Server { 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') { diff --git a/packages/next/server/dev/hot-reloader.ts b/packages/next/server/dev/hot-reloader.ts index ffb1e74d8b9a..6bb16c5e975c 100644 --- a/packages/next/server/dev/hot-reloader.ts +++ b/packages/next/server/dev/hot-reloader.ts @@ -603,6 +603,23 @@ export default class HotReloader { onEdgeServer: () => { // TODO-APP: verify if child entry should support. if (!isEdgeServerCompilation || !isEntry) return + const isApp = this.appDir && bundlePath.startsWith('app/') + const appDirLoader = + isApp && this.appDir + ? getAppEntry({ + name: bundlePath, + pagePath: posix.join( + APP_DIR_ALIAS, + relative( + this.appDir!, + entryData.absolutePagePath + ).replace(/\\/g, '/') + ), + appDir: this.appDir!, + pageExtensions: this.config.pageExtensions, + }).import + : undefined + entries[entryKey].status = BUILDING entrypoints[bundlePath] = finalizeEntrypoint({ compilerType: COMPILER_NAMES.edgeServer, @@ -616,6 +633,8 @@ export default class HotReloader { page, pages: this.pagesMapping, isServerComponent, + appDirLoader, + pagesType: isApp ? 'app' : undefined, }), appDir: this.config.experimental.appDir, }) diff --git a/packages/next/server/dev/next-dev-server.ts b/packages/next/server/dev/next-dev-server.ts index 799928af22db..b5a89b46f6a1 100644 --- a/packages/next/server/dev/next-dev-server.ts +++ b/packages/next/server/dev/next-dev-server.ts @@ -536,7 +536,13 @@ export default class DevServer extends Server { this.edgeFunctions = [] const edgeRoutes = Array.from(edgeRoutesSet) getSortedRoutes(edgeRoutes).forEach((page) => { + let appPath = this.getOriginalAppPath(page) + + if (typeof appPath === 'string') { + page = appPath + } const isRootMiddleware = page === '/' && !!middlewareMatcher + const middlewareRegex = isRootMiddleware ? { re: middlewareMatcher!, groups: {} } : getMiddlewareRegex(page, { catchAll: false }) @@ -545,7 +551,6 @@ export default class DevServer extends Server { page, re: middlewareRegex.re, } - if (isRootMiddleware) { this.middleware = routeItem } else { diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 26aafbee55c3..2d8e3aac0a33 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -811,16 +811,23 @@ export default class NextNodeServer extends BaseServer { ctx: RequestContext, bubbleNoFallback: boolean ) { - const edgeFunctions = this.getEdgeFunctions() + const appPath = this.getOriginalAppPath(ctx.pathname) + let page = ctx.pathname + + if (typeof appPath === 'string') { + page = appPath + } + + const edgeFunctions = this.getEdgeFunctions() || [] for (const item of edgeFunctions) { - if (item.page === ctx.pathname) { + if (item.page === page) { await this.runEdgeFunction({ req: ctx.req, res: ctx.res, query: ctx.query, params: ctx.renderOpts.params, - page: ctx.pathname, + page, }) return null } @@ -1926,6 +1933,11 @@ export default class NextNodeServer extends BaseServer { onWarning?: (warning: Error) => void }): Promise { let middlewareInfo: ReturnType | undefined + let appPath = this.getOriginalAppPath(params.page) + + if (typeof appPath === 'string') { + params.page = appPath + } await this.ensureEdgeFunction(params.page) middlewareInfo = this.getEdgeFunctionInfo({ diff --git a/packages/next/server/web-server.ts b/packages/next/server/web-server.ts index 935f05e91b1f..657b05bbc375 100644 --- a/packages/next/server/web-server.ts +++ b/packages/next/server/web-server.ts @@ -10,7 +10,6 @@ import type { DynamicRoutes, PageChecker, Route } from './router' import type { NextConfig } from './config-shared' import BaseServer from './base-server' -import { renderToHTML } from './render' import { byteLength } from './api-utils/web' import { generateETag } from './lib/etag' import { addRequestMeta } from './request-meta' @@ -20,6 +19,8 @@ import getRouteFromAssetPath from '../shared/lib/router/utils/get-route-from-ass import { detectDomainLocale } from '../shared/lib/i18n/detect-domain-locale' import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path' import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash' +import type { BaseNextRequest, BaseNextResponse } from './base-http' +import type { UrlWithParsedQuery } from 'url' interface WebServerOptions extends Options { webServerConfig: { @@ -29,6 +30,8 @@ interface WebServerOptions extends Options { ) => Promise extendRenderOpts: Partial & Pick + pagesRenderToHTML?: typeof import('./render').renderToHTML + appRenderToHTML?: typeof import('./app-render').renderToHTMLOrFlight } } @@ -58,8 +61,16 @@ export default class NextWebServer extends BaseServer { redirects: [], } } - protected async hasPage() { - return false + protected async run( + req: BaseNextRequest, + res: BaseNextResponse, + parsedUrl: UrlWithParsedQuery + ): Promise { + parsedUrl.pathname = this.serverOptions.webServerConfig.page + super.run(req, res, parsedUrl) + } + protected async hasPage(page: string) { + return page === this.serverOptions.webServerConfig.page } protected getPublicDir() { // Public files are not handled by the web server. @@ -114,12 +125,11 @@ export default class NextWebServer extends BaseServer { } } protected getServerComponentManifest() { - // @TODO: Need to return `extendRenderOpts.serverComponentManifest` here. - return undefined + return this.serverOptions.webServerConfig.extendRenderOpts + .serverComponentManifest } protected getServerCSSManifest() { - // TODO-APP: Support web server. - return undefined + return this.serverOptions.webServerConfig.extendRenderOpts.serverCSSManifest } protected generateRoutes(): { @@ -319,21 +329,30 @@ export default class NextWebServer extends BaseServer { query: NextParsedUrlQuery, renderOpts: RenderOpts ): Promise { - return renderToHTML( - { - url: req.url, - cookies: req.cookies, - headers: req.headers, - } as any, - {} as any, - pathname, - query, - { - ...renderOpts, - disableOptimizedLoading: true, - runtime: 'experimental-edge', - } - ) + const { pagesRenderToHTML, appRenderToHTML } = + this.serverOptions.webServerConfig + const curRenderToHTML = pagesRenderToHTML || appRenderToHTML + + if (curRenderToHTML) { + return await curRenderToHTML( + { + url: req.url, + cookies: req.cookies, + headers: req.headers, + } as any, + {} as any, + pathname, + query, + { + ...renderOpts, + disableOptimizedLoading: true, + runtime: 'experimental-edge', + }, + !!pagesRenderToHTML + ) + } else { + throw new Error(`Invariant: curRenderToHTML is missing`) + } } protected async sendRenderResult( _req: WebNextRequest, diff --git a/test/e2e/app-dir/app/app/dashboard/page.server.js b/test/e2e/app-dir/app/app/dashboard/page.server.js index 9c11228fd54a..a4773bfaba83 100644 --- a/test/e2e/app-dir/app/app/dashboard/page.server.js +++ b/test/e2e/app-dir/app/app/dashboard/page.server.js @@ -11,3 +11,7 @@ export default function DashboardPage(props) { ) } + +export const config = { + runtime: 'experimental-edge', +} diff --git a/test/e2e/app-dir/app/app/slow-page-with-loading/page.server.js b/test/e2e/app-dir/app/app/slow-page-with-loading/page.server.js index b37126cffe30..ea73712c5e08 100644 --- a/test/e2e/app-dir/app/app/slow-page-with-loading/page.server.js +++ b/test/e2e/app-dir/app/app/slow-page-with-loading/page.server.js @@ -10,3 +10,7 @@ export async function getServerSideProps() { export default function SlowPage(props) { return

{props.message}

} + +export const config = { + runtime: 'experimental-edge', +} diff --git a/test/e2e/app-dir/app/pages/api/hello.js b/test/e2e/app-dir/app/pages/api/hello.js new file mode 100644 index 000000000000..47779ec1fa09 --- /dev/null +++ b/test/e2e/app-dir/app/pages/api/hello.js @@ -0,0 +1,7 @@ +export default function api(req) { + return new Response('hello') +} + +export const config = { + runtime: 'experimental-edge', +}