Skip to content

Commit

Permalink
Add app-path-routes manifest (#38420)
Browse files Browse the repository at this point in the history
This outputs a separate manifest for leveraging during deploy to handle the new app outputs. Also ensures dynamic routes from `app` our output in the `routes-manifest` correctly along with fixing the `react-dom` import. 

x-ref: #37551
  • Loading branch information
ijjk committed Jul 7, 2022
1 parent aa0ba3c commit 2989c8a
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 29 deletions.
73 changes: 55 additions & 18 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ import {
SERVER_FILES_MANIFEST,
STATIC_STATUS_PAGES,
MIDDLEWARE_MANIFEST,
APP_PATHS_MANIFEST,
APP_PATH_ROUTES_MANIFEST,
} from '../shared/lib/constants'
import { getSortedRoutes, isDynamicRoute } from '../shared/lib/router/utils'
import { __ApiPreviewProps } from '../server/api-utils'
Expand Down Expand Up @@ -114,6 +116,7 @@ import { getNamedRouteRegex } from '../shared/lib/router/utils/route-regex'
import { flatReaddir } from '../lib/flat-readdir'
import { RemotePattern } from '../shared/lib/image-config'
import { eventSwcPlugins } from '../telemetry/events/swc-plugins'
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'

export type SsgRoute = {
initialRevalidateSeconds: number | false
Expand Down Expand Up @@ -385,10 +388,10 @@ export default async function build(
})
)

let mappedappPaths: { [page: string]: string } | undefined
let mappedAppPaths: { [page: string]: string } | undefined

if (appPaths && appDir) {
mappedappPaths = nextBuildSpan
mappedAppPaths = nextBuildSpan
.traceChild('create-app-mapping')
.traceFn(() =>
createPagesMapping({
Expand Down Expand Up @@ -427,7 +430,7 @@ export default async function build(
rootDir: dir,
rootPaths: mappedRootPaths,
appDir,
appPaths: mappedappPaths,
appPaths: mappedAppPaths,
pageExtensions: config.pageExtensions,
})
)
Expand Down Expand Up @@ -574,21 +577,36 @@ export default async function build(
defaultLocale: string
localeDetection?: false
}
} = nextBuildSpan.traceChild('generate-routes-manifest').traceFn(() => ({
version: 3,
pages404: true,
basePath: config.basePath,
redirects: redirects.map((r: any) => buildCustomRoute(r, 'redirect')),
headers: headers.map((r: any) => buildCustomRoute(r, 'header')),
dynamicRoutes: getSortedRoutes(pageKeys)
.filter(isDynamicRoute)
.map(pageToRoute),
staticRoutes: getSortedRoutes(pageKeys)
.filter((page) => !isDynamicRoute(page) && !isReservedPage(page))
.map(pageToRoute),
dataRoutes: [],
i18n: config.i18n || undefined,
}))
} = nextBuildSpan.traceChild('generate-routes-manifest').traceFn(() => {
const sortedRoutes = getSortedRoutes([
...pageKeys,
...Object.keys(mappedAppPaths || {}).map((key) =>
normalizeAppPath(key)
),
])
const dynamicRoutes: Array<ReturnType<typeof pageToRoute>> = []
const staticRoutes: typeof dynamicRoutes = []

for (const route of sortedRoutes) {
if (isDynamicRoute(route)) {
dynamicRoutes.push(pageToRoute(route))
} else if (!isReservedPage(route)) {
staticRoutes.push(pageToRoute(route))
}
}

return {
version: 3,
pages404: true,
basePath: config.basePath,
redirects: redirects.map((r: any) => buildCustomRoute(r, 'redirect')),
headers: headers.map((r: any) => buildCustomRoute(r, 'header')),
dynamicRoutes,
staticRoutes,
dataRoutes: [],
i18n: config.i18n || undefined,
}
})

if (rewrites.beforeFiles.length === 0 && rewrites.fallback.length === 0) {
routesManifest.rewrites = rewrites.afterFiles.map((r: any) =>
Expand Down Expand Up @@ -689,6 +707,7 @@ export default async function build(
REACT_LOADABLE_MANIFEST,
config.optimizeFonts ? path.join(serverDir, FONT_MANIFEST) : null,
BUILD_ID_FILE,
appDir ? path.join(serverDir, APP_PATHS_MANIFEST) : null,
]
.filter(nonNullable)
.map((file) => path.join(config.distDir, file)),
Expand Down Expand Up @@ -1618,6 +1637,24 @@ export default async function build(
'utf8'
)

if (appDir) {
const appPathsManifest = JSON.parse(
await promises.readFile(
path.join(distDir, serverDir, APP_PATHS_MANIFEST),
'utf8'
)
)
const appPathRoutes: Record<string, string> = {}

Object.keys(appPathsManifest).forEach((entry) => {
appPathRoutes[entry] = normalizeAppPath(entry) || '/'
})
await promises.writeFile(
path.join(distDir, APP_PATH_ROUTES_MANIFEST),
JSON.stringify(appPathRoutes, null, 2)
)
}

const middlewareManifest: MiddlewareManifest = JSON.parse(
await promises.readFile(
path.join(distDir, serverDir, MIDDLEWARE_MANIFEST),
Expand Down
6 changes: 4 additions & 2 deletions packages/next/server/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ import {
import { isDynamicRoute } from '../shared/lib/router/utils'
import { tryGetPreviewData } from './api-utils/node'
import { htmlEscapeJsonString } from './htmlescape'
import { stripInternalQueries } from './utils'
import { shouldUseReactRoot, stripInternalQueries } from './utils'
import { NextApiRequestCookies } from './api-utils'
import { matchSegment } from '../client/components/match-segments'

const ReactDOMServer = process.env.__NEXT_REACT_ROOT
// this needs to be required lazily so that `next-server` can set
// the env before we require
const ReactDOMServer = shouldUseReactRoot
? require('react-dom/server.browser')
: require('react-dom/server')

Expand Down
6 changes: 3 additions & 3 deletions packages/next/server/base-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
const { useFileSystemPublicRoutes } = this.nextConfig

if (useFileSystemPublicRoutes) {
this.appPathRoutes = this.getappPathRoutes()
this.appPathRoutes = this.getAppPathRoutes()
this.dynamicRoutes = this.getDynamicRoutes()
}

Expand Down Expand Up @@ -1022,7 +1022,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
.filter((item): item is RoutingItem => Boolean(item))
}

protected getappPathRoutes(): Record<string, string> {
protected getAppPathRoutes(): Record<string, string> {
const appPathRoutes: Record<string, string> = {}

Object.keys(this.appPathsManifest || {}).forEach((entry) => {
Expand All @@ -1031,7 +1031,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
return appPathRoutes
}

protected getappPathLayouts(pathname: string): string[] {
protected getAppPathLayouts(pathname: string): string[] {
const layoutPaths: string[] = []

if (this.appPathRoutes) {
Expand Down
1 change: 1 addition & 0 deletions packages/next/shared/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const PHASE_DEVELOPMENT_SERVER = 'phase-development-server'
export const PHASE_TEST = 'phase-test'
export const PAGES_MANIFEST = 'pages-manifest.json'
export const APP_PATHS_MANIFEST = 'app-paths-manifest.json'
export const APP_PATH_ROUTES_MANIFEST = 'app-path-routes-manifest.json'
export const BUILD_MANIFEST = 'build-manifest.json'
export const EXPORT_MARKER = 'export-marker.json'
export const EXPORT_DETAIL = 'export-detail.json'
Expand Down
15 changes: 9 additions & 6 deletions test/e2e/switchable-runtime/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,9 @@ describe('Switchable runtime', () => {
.click()
.waitForElementByCss('.node-rsc-ssr')

expect(await browser.elementByCss('body').text()).toContain(
'This is a SSR RSC page.'
await check(
() => browser.eval('document.documentElement.innerHTML'),
/This is a SSR RSC page/
)
expect(flightRequest).toContain('/node-rsc-ssr?__flight__=1')
})
Expand All @@ -94,8 +95,9 @@ describe('Switchable runtime', () => {
.click()
.waitForElementByCss('.node-rsc-ssg')

expect(await browser.elementByCss('body').text()).toContain(
'This is a SSG RSC page.'
await check(
() => browser.eval('document.documentElement.innerHTML'),
/This is a SSG RSC page/
)
})

Expand All @@ -107,8 +109,9 @@ describe('Switchable runtime', () => {
.click()
.waitForElementByCss('.node-rsc')

expect(await browser.elementByCss('body').text()).toContain(
'This is a static RSC page.'
await check(
() => browser.eval('document.documentElement.innerHTML'),
/This is a static RSC page/
)
})

Expand Down

0 comments on commit 2989c8a

Please sign in to comment.