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

Separate routing code from render servers #52492

Merged
merged 55 commits into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
7150291
Initial changes
ijjk Jun 25, 2023
f753141
dynamic routes
ijjk Jun 25, 2023
75e4afc
render worker handling
ijjk Jun 26, 2023
a5cc121
break out route resolving
ijjk Jun 26, 2023
b3c6a87
next data outputs handling
ijjk Jun 26, 2023
de35d92
update middleware handling
ijjk Jun 27, 2023
0880624
handle revalidate case
ijjk Jun 27, 2023
1c4ff6b
Handle basePath/i18n cases
ijjk Jun 27, 2023
e706fcb
start removing routing from servers
ijjk Jun 28, 2023
ab26f33
update standalone/custom server handling
ijjk Jun 28, 2023
ca1dcca
more standalone cases
ijjk Jun 28, 2023
13e34f0
fix turbopack type
ijjk Jun 28, 2023
9cf2ded
fix filtered header
ijjk Jun 28, 2023
a87cb3c
dev handling
ijjk Jul 7, 2023
e9fbbc2
middleware dev cases
ijjk Jul 8, 2023
2ba15a1
app dev cases
ijjk Jul 10, 2023
a2c9347
fix more test cases
ijjk Jul 10, 2023
6fa1344
lint fix and update turbo resolver
ijjk Jul 10, 2023
81817c8
custom-server ws case and error
ijjk Jul 10, 2023
6f682dc
add test debug env
ijjk Jul 10, 2023
33489a1
update test env
ijjk Jul 10, 2023
071274e
more cases
ijjk Jul 11, 2023
79364ba
more test cases
ijjk Jul 11, 2023
7b7ba1e
and some more
ijjk Jul 11, 2023
8214838
even more
ijjk Jul 11, 2023
91bb35a
error cases
ijjk Jul 11, 2023
626d615
custom server cases
ijjk Jul 12, 2023
6150890
trace and conflicting case
ijjk Jul 12, 2023
f86dc03
more cases
ijjk Jul 12, 2023
9f70518
add todo
ijjk Jul 12, 2023
7808e09
fix rebase
ijjk Jul 13, 2023
e2b58e9
suggestions from review
ijjk Jul 13, 2023
a70c2cf
fix windows paths
ijjk Jul 16, 2023
9c9e14a
remove extra field and fix cases
ijjk Jul 17, 2023
9207b2b
single IPC call for require cache clearing
ijjk Jul 17, 2023
aeb0653
restore process.title change
ijjk Jul 17, 2023
5e18f0d
fix format dev server errors
ijjk Jul 17, 2023
087425a
update test
ijjk Jul 17, 2023
27be9b6
handle previous turbopack route resolver
ijjk Jul 17, 2023
b5fc62c
use get-port for tests
ijjk Jul 17, 2023
f5df36d
fix middleware check and extra log
ijjk Jul 17, 2023
8c0f81a
Update instrumentation case
ijjk Jul 18, 2023
ab5a555
rework custom server handling
ijjk Jul 18, 2023
508b91b
fix arg
ijjk Jul 18, 2023
d094333
remove debug env
ijjk Jul 18, 2023
eac00c3
Update imports
ijjk Jul 18, 2023
76b29c8
add http-proxy memory leak cleanup
ijjk Jul 18, 2023
8894cc4
use tapPromise
ijjk Jul 18, 2023
f8a6240
update request handling
ijjk Jul 19, 2023
e917406
Update compress handling
ijjk Jul 19, 2023
a0a0700
fix etag handling
ijjk Jul 20, 2023
0143414
telemetry case
ijjk Jul 20, 2023
5154b10
Merge branch 'canary' into update/restruct-routing
ijjk Jul 20, 2023
403a363
update
ijjk Jul 20, 2023
ca81dd3
Merge branch 'canary' into update/restruct-routing
ijjk Jul 21, 2023
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
238 changes: 110 additions & 128 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import os from 'os'
import { Worker } from '../lib/worker'
import { defaultConfig } from '../server/config-shared'
import devalue from 'next/dist/compiled/devalue'
import { escapeStringRegexp } from '../shared/lib/escape-regexp'
import findUp from 'next/dist/compiled/find-up'
import { nanoid } from 'next/dist/compiled/nanoid/index.cjs'
import { pathToRegexp } from 'next/dist/compiled/path-to-regexp'
Expand Down Expand Up @@ -141,7 +140,12 @@ import { createClientRouterFilter } from '../lib/create-client-router-filter'
import { createValidFileMatcher } from '../server/lib/find-page-file'
import { startTypeChecking } from './type-check'
import { generateInterceptionRoutesRewrites } from '../lib/generate-interception-routes-rewrites'
import { baseOverrides, experimentalOverrides } from '../server/require-hook'
import { buildDataRoute } from '../server/lib/router-utils/build-data-route'
import {
baseOverrides,
defaultOverrides,
experimentalOverrides,
} from '../server/require-hook'

export type SsgRoute = {
initialRevalidateSeconds: number | false
Expand All @@ -166,6 +170,66 @@ export type PrerenderManifest = {
preview: __ApiPreviewProps
}

type CustomRoute = {
regex: string
statusCode?: number | undefined
permanent?: undefined
source: string
locale?: false | undefined
basePath?: false | undefined
destination?: string | undefined
}

export type RoutesManifest = {
version: number
pages404: boolean
basePath: string
redirects: Array<CustomRoute>
rewrites?:
| Array<CustomRoute>
| {
beforeFiles: Array<CustomRoute>
afterFiles: Array<CustomRoute>
fallback: Array<CustomRoute>
}
headers: Array<CustomRoute>
staticRoutes: Array<{
page: string
regex: string
namedRegex?: string
routeKeys?: { [key: string]: string }
}>
dynamicRoutes: Array<{
page: string
regex: string
namedRegex?: string
routeKeys?: { [key: string]: string }
}>
dataRoutes: Array<{
page: string
routeKeys?: { [key: string]: string }
dataRouteRegex: string
namedDataRouteRegex?: string
}>
i18n?: {
domains?: Array<{
http?: true
domain: string
locales?: string[]
defaultLocale: string
}>
locales: string[]
defaultLocale: string
localeDetection?: false
}
rsc: {
header: typeof RSC
varyHeader: typeof RSC_VARY_HEADER
}
skipMiddlewareUrlNormalize?: boolean
caseSensitive?: boolean
}

async function generateClientSsgManifest(
prerenderManifest: PrerenderManifest,
{
Expand Down Expand Up @@ -684,89 +748,45 @@ export default async function build(
}

const routesManifestPath = path.join(distDir, ROUTES_MANIFEST)
const routesManifest: {
version: number
pages404: boolean
basePath: string
redirects: Array<ReturnType<typeof buildCustomRoute>>
rewrites?:
| Array<ReturnType<typeof buildCustomRoute>>
| {
beforeFiles: Array<ReturnType<typeof buildCustomRoute>>
afterFiles: Array<ReturnType<typeof buildCustomRoute>>
fallback: Array<ReturnType<typeof buildCustomRoute>>
const routesManifest: RoutesManifest = nextBuildSpan
.traceChild('generate-routes-manifest')
.traceFn(() => {
const sortedRoutes = getSortedRoutes([
...pageKeys.pages,
...(pageKeys.app ?? []),
])
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))
}
headers: Array<ReturnType<typeof buildCustomRoute>>
staticRoutes: Array<{
page: string
regex: string
namedRegex?: string
routeKeys?: { [key: string]: string }
}>
dynamicRoutes: Array<{
page: string
regex: string
namedRegex?: string
routeKeys?: { [key: string]: string }
}>
dataRoutes: Array<{
page: string
routeKeys?: { [key: string]: string }
dataRouteRegex: string
namedDataRouteRegex?: string
}>
i18n?: {
domains?: Array<{
http?: true
domain: string
locales?: string[]
defaultLocale: string
}>
locales: string[]
defaultLocale: string
localeDetection?: false
}
rsc: {
header: typeof RSC
varyHeader: typeof RSC_VARY_HEADER
}
skipMiddlewareUrlNormalize?: boolean
caseSensitive?: boolean
} = nextBuildSpan.traceChild('generate-routes-manifest').traceFn(() => {
const sortedRoutes = getSortedRoutes([
...pageKeys.pages,
...(pageKeys.app ?? []),
])
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,
caseSensitive: !!config.experimental.caseSensitiveRoutes,
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,
rsc: {
header: RSC,
varyHeader: RSC_VARY_HEADER,
contentTypeHeader: RSC_CONTENT_TYPE_HEADER,
},
skipMiddlewareUrlNormalize: config.skipMiddlewareUrlNormalize,
}
})
return {
version: 3,
pages404: true,
caseSensitive: !!config.experimental.caseSensitiveRoutes,
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,
rsc: {
header: RSC,
varyHeader: RSC_VARY_HEADER,
contentTypeHeader: RSC_CONTENT_TYPE_HEADER,
},
skipMiddlewareUrlNormalize: config.skipMiddlewareUrlNormalize,
}
})

if (rewrites.beforeFiles.length === 0 && rewrites.fallback.length === 0) {
routesManifest.rewrites = rewrites.afterFiles.map((r: any) =>
Expand Down Expand Up @@ -903,6 +923,7 @@ export default async function build(
]
: []),
path.join(SERVER_DIRECTORY, APP_PATHS_MANIFEST),
path.join(APP_PATH_ROUTES_MANIFEST),
APP_BUILD_MANIFEST,
path.join(
SERVER_DIRECTORY,
Expand Down Expand Up @@ -1962,6 +1983,11 @@ export default async function build(
...Object.values(experimentalOverrides).map((override) =>
require.resolve(override)
),
...(config.experimental.turbotrace
? []
: Object.values(defaultOverrides).map((value) =>
require.resolve(value)
)),
]

// ensure we trace any dependencies needed for custom
Expand All @@ -1979,9 +2005,7 @@ export default async function build(
const vanillaServerEntries = [
...sharedEntriesSet,
isStandalone
? require.resolve(
'next/dist/server/lib/render-server-standalone'
)
? require.resolve('next/dist/server/lib/start-server')
: null,
require.resolve('next/dist/server/next-server'),
].filter(Boolean) as string[]
Expand Down Expand Up @@ -2158,49 +2182,7 @@ export default async function build(
...serverPropsPages,
...ssgPages,
]).map((page) => {
const pagePath = normalizePagePath(page)
const dataRoute = path.posix.join(
'/_next/data',
buildId,
`${pagePath}.json`
)

let dataRouteRegex: string
let namedDataRouteRegex: string | undefined
let routeKeys: { [named: string]: string } | undefined

if (isDynamicRoute(page)) {
const routeRegex = getNamedRouteRegex(
dataRoute.replace(/\.json$/, ''),
true
)

dataRouteRegex = normalizeRouteRegex(
routeRegex.re.source.replace(/\(\?:\\\/\)\?\$$/, `\\.json$`)
)
namedDataRouteRegex = routeRegex.namedRegex!.replace(
/\(\?:\/\)\?\$$/,
`\\.json$`
)
routeKeys = routeRegex.routeKeys
} else {
dataRouteRegex = normalizeRouteRegex(
new RegExp(
`^${path.posix.join(
'/_next/data',
escapeStringRegexp(buildId),
`${pagePath}.json`
)}$`
).source
)
}

return {
page,
routeKeys,
dataRouteRegex,
namedDataRouteRegex,
}
return buildDataRoute(page, buildId)
})

await fs.writeFile(
Expand Down
67 changes: 26 additions & 41 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1928,12 +1928,12 @@ export async function copyTracedFiles(
import path from 'path'
import { fileURLToPath } from 'url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
import { createServerHandler } from 'next/dist/server/lib/render-server-standalone.js'
import { startServer } from 'next/dist/server/lib/start-server.js'
`
: `
const http = require('http')
const path = require('path')
const { createServerHandler } = require('next/dist/server/lib/render-server-standalone')`
const { startServer } = require('next/dist/server/lib/start-server')`
}

const dir = path.join(__dirname)
Expand All @@ -1950,53 +1950,38 @@ if (!process.env.NEXT_MANUAL_SIG_HANDLE) {

const currentPort = parseInt(process.env.PORT, 10) || 3000
const hostname = process.env.HOSTNAME || 'localhost'
const keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10);
const isValidKeepAliveTimeout =
!Number.isNaN(keepAliveTimeout) &&
Number.isFinite(keepAliveTimeout) &&
keepAliveTimeout >= 0;
let keepAliveTimeout = parseInt(process.env.KEEP_ALIVE_TIMEOUT, 10);
const nextConfig = ${JSON.stringify({
...serverConfig,
distDir: `./${path.relative(dir, distDir)}`,
})}

process.env.__NEXT_PRIVATE_STANDALONE_CONFIG = JSON.stringify(nextConfig)

createServerHandler({
port: currentPort,
hostname,
dir,
conf: nextConfig,
keepAliveTimeout: isValidKeepAliveTimeout ? keepAliveTimeout : undefined,
}).then((nextHandler) => {
const server = http.createServer(async (req, res) => {
try {
await nextHandler(req, res)
} catch (err) {
console.error(err);
res.statusCode = 500
res.end('Internal Server Error')
}
})

if (isValidKeepAliveTimeout) {
server.keepAliveTimeout = keepAliveTimeout
}

server.listen(currentPort, async (err) => {
if (err) {
console.error("Failed to start server", err)
process.exit(1)
}

console.log(
'Listening on port',
currentPort,
'url: http://' + hostname + ':' + currentPort
)
});
if (
Number.isNaN(keepAliveTimeout) ||
!Number.isFinite(keepAliveTimeout) ||
keepAliveTimeout < 0
) {
keepAliveTimeout = undefined
}

}).catch(err => {
startServer({
dir,
isDev: false,
config: nextConfig,
hostname: hostname === 'localhost' ? '0.0.0.0' : hostname,
port: currentPort,
allowRetry: false,
keepAliveTimeout,
useWorkers: !!nextConfig.experimental?.appDir,
}).then(() => {
console.log(
'Listening on port',
currentPort,
'url: http://' + hostname + ':' + currentPort
)
}).catch((err) => {
console.error(err);
process.exit(1);
});`
Expand Down