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

allow using the nodejs runtime for route handlers when using turbopack #48791

Merged
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
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// PAGE and PATHNAME is set from rust code
declare const PAGE: string, PATHNAME: string

import { EdgeRouteModuleWrapper } from 'next/dist/server/web/edge-route-module-wrapper'

import RouteModule from 'ROUTE_MODULE'
import * as userland from 'ENTRY'
import { PAGE, PATHNAME } from 'BOOTSTRAP_CONFIG'

// TODO: (wyattjoh) - perform the option construction in Rust to allow other modules to accept different options
const routeModule = new RouteModule({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// IPC need to be the first import to allow it to catch errors happening during
// the other imports
import startHandler from '../../internal/api-server-handler'

import 'next/dist/server/node-polyfill-fetch.js'

import { join } from 'path'
import { parse as parseUrl } from 'node:url'

import {
NodeNextRequest,
NodeNextResponse,
} from 'next/dist/server/base-http/node'

import { runEdgeFunction } from '../../internal/edge'
import { attachRequestMeta } from '../../internal/next-request-helpers'

import chunkGroup from 'ROUTE_CHUNK_GROUP'

startHandler(async ({ request, response, query, params, path }) => {
const req = new NodeNextRequest(request)
const res = new NodeNextResponse(response)

const parsedUrl = parseUrl(req.url!, true)
attachRequestMeta(req, parsedUrl, request.headers.host!)

const edgeInfo = {
name: 'edge',
paths: chunkGroup.map((chunk) =>
join(process.cwd(), '.next/server/app', chunk)
),
wasm: [],
env: Object.keys(process.env),
assets: [],
}

await runEdgeFunction({
edgeInfo,
outputDir: 'app',
req,
res,
query,
params,
path,
onWarning(warning) {
console.warn(warning)
},
})
})
59 changes: 35 additions & 24 deletions packages/next-swc/crates/next-core/js/src/entry/app/route.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,50 @@
// IPC need to be the first import to allow it to catch errors happening during
// the other imports
import startHandler from '../../internal/api-server-handler'
import { runEdgeFunction } from '../../internal/edge'

import { join } from 'path'
import '../../polyfill/app-polyfills'

import 'next/dist/server/node-polyfill-fetch.js'

import chunkGroup from 'ROUTE_CHUNK_GROUP'
import { parse as parseUrl } from 'node:url'

import {
NodeNextRequest,
NodeNextResponse,
} from 'next/dist/server/base-http/node'
import { sendResponse } from 'next/dist/server/send-response'
import { NextRequestAdapter } from 'next/dist/server/web/spec-extension/adapters/next-request'
import { RouteHandlerManagerContext } from 'next/dist/server/future/route-handler-managers/route-handler-manager'

import { attachRequestMeta } from '../../internal/next-request-helpers'

import RouteModule from 'ROUTE_MODULE'
import * as userland from 'ENTRY'
import { PAGE, PATHNAME } from 'BOOTSTRAP_CONFIG'

const routeModule = new RouteModule({
userland,
pathname: PATHNAME,
resolvedPagePath: `app/${PAGE}`,
nextConfigOutput: undefined,
})

startHandler(async ({ request, response, query, params, path }) => {
const edgeInfo = {
name: 'edge',
paths: chunkGroup.map((chunk) =>
join(process.cwd(), '.next/server/app', chunk)
),
wasm: [],
env: Object.keys(process.env),
assets: [],
}
await runEdgeFunction({
edgeInfo,
outputDir: 'app',
req: new NodeNextRequest(request),
res: new NodeNextResponse(response),
query,
const req = new NodeNextRequest(request)
const res = new NodeNextResponse(response)

const parsedUrl = parseUrl(req.url!, true)
attachRequestMeta(req, parsedUrl, request.headers.host!)

const context: RouteHandlerManagerContext = {
params,
path,
onWarning(warning) {
console.warn(warning)
staticGenerationContext: {
supportsDynamicHTML: true,
},
})
}

const routeResponse = await routeModule.handle(
NextRequestAdapter.fromNodeNextRequest(req),
context
)

await sendResponse(req, res, routeResponse)
})
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
declare const NAME: string
declare const PAGE: string

import { adapter, enhanceGlobals } from 'next/dist/server/web/adapter'
import { NAME, PAGE } from 'BOOTSTRAP_CONFIG'

enhanceGlobals()

Expand Down
14 changes: 11 additions & 3 deletions packages/next-swc/crates/next-core/js/src/entry/server-api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,31 @@ import startHandler from '../internal/api-server-handler'

import 'next/dist/server/node-polyfill-fetch.js'

import * as allExports from 'INNER'
import { parse as parseQuery } from 'node:querystring'
import { parse as parseUrl } from 'node:url'

import { apiResolver } from 'next/dist/server/api-utils/node'
import {
NodeNextRequest,
NodeNextResponse,
} from 'next/dist/server/base-http/node'
import { parse } from 'node:querystring'

import { attachRequestMeta } from '../internal/next-request-helpers'

import * as allExports from 'INNER'

startHandler(({ request, response, query, params, path }) => {
const parsedQuery = parse(query)
const parsedQuery = parseQuery(query)

const mergedQuery = { ...parsedQuery, ...params }

// This enables `req.cookies` in API routes.
const req = new NodeNextRequest(request)
const res = new NodeNextResponse(response)

const parsedUrl = parseUrl(req.url!, true)
attachRequestMeta(req, parsedUrl, request.headers.host!)

return apiResolver(
req.originalRequest,
res.originalResponse,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
// IPC need to be the first import to allow it to catch errors happening during
// the other imports
import startHandler from '../internal/api-server-handler'
import { runEdgeFunction } from '../internal/edge'

import { join } from 'path'

import 'next/dist/server/node-polyfill-fetch.js'

import chunkGroup from 'INNER_EDGE_CHUNK_GROUP'
import { join } from 'node:path'
import { parse as parseUrl } from 'node:url'

import {
NodeNextRequest,
NodeNextResponse,
} from 'next/dist/server/base-http/node'

import { attachRequestMeta } from '../internal/next-request-helpers'
import { runEdgeFunction } from '../internal/edge'

import chunkGroup from 'INNER_EDGE_CHUNK_GROUP'

startHandler(async ({ request, response, query, params, path }) => {
const req = new NodeNextRequest(request)
const res = new NodeNextResponse(response)

const parsedUrl = parseUrl(req.url!, true)
attachRequestMeta(req, parsedUrl, request.headers.host!)

const edgeInfo = {
name: 'edge',
paths: chunkGroup.map((chunk: string) =>
Expand All @@ -24,11 +33,12 @@ startHandler(async ({ request, response, query, params, path }) => {
env: Object.keys(process.env),
assets: [],
}

await runEdgeFunction({
edgeInfo,
outputDir: 'pages',
req: new NodeNextRequest(request),
res: new NodeNextResponse(response),
req,
res,
query,
params,
path,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
// the other imports
import { IPC } from '@vercel/turbopack-node/ipc/index'

import type { RenderData } from 'types/turbopack'
import type { Ipc } from '@vercel/turbopack-node/ipc/index'
import type { ClientRequest, IncomingMessage, Server } from 'node:http'
import type { ServerResponse } from 'node:http'
import { Buffer } from 'node:buffer'

import type { Ipc } from '@vercel/turbopack-node/ipc/index'

import type { RenderData } from 'types/turbopack'
import { createServer, makeRequest } from '../internal/server'
import { toPairs } from '../internal/headers'
import { Buffer } from 'node:buffer'

const ipc = IPC as Ipc<IpcIncomingMessage, IpcOutgoingMessage>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { TLSSocket } from 'tls'
import {
addRequestMeta,
NextUrlWithParsedQuery,
} from 'next/dist/server/request-meta'
import { NodeNextRequest } from 'next/dist/server/base-http/node'
import { BaseNextRequest } from 'next/dist/server/base-http'
import { getCloneableBody } from 'next/dist/server/body-streams'

export function attachRequestMeta(
req: BaseNextRequest,
parsedUrl: NextUrlWithParsedQuery,
host: string
) {
const protocol = (
(req as NodeNextRequest).originalRequest?.socket as TLSSocket
)?.encrypted
? 'https'
: 'http'

const initUrl = `${protocol}://${host}${req.url}`

addRequestMeta(req, '__NEXT_INIT_URL', initUrl)
addRequestMeta(req, '__NEXT_INIT_QUERY', { ...parsedUrl.query })
addRequestMeta(req, '_protocol', protocol)
addRequestMeta(req, '__NEXT_CLONABLE_BODY', getCloneableBody(req.body))
}
6 changes: 6 additions & 0 deletions packages/next-swc/crates/next-core/js/types/rust.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ declare module 'ROUTE_MODULE' {
}
}

declare module 'BOOTSTRAP_CONFIG' {
export const NAME: string
export const PAGE: string
export const PATHNAME: string
}

declare module 'CLIENT_MODULE' {
export const __turbopack_module_id__: string
}
Expand Down