Skip to content

Commit

Permalink
allow using the nodejs runtime for route handlers when using turbop…
Browse files Browse the repository at this point in the history
…ack (#48791)

### What?
We previously ran all route handlers with the edge runtime, which means
you can't use built in node.js modules

With this PR, the runtime can be selected as documented in the next.js
docs

Fixes WEB-873
  • Loading branch information
ForsakenHarmony committed Apr 28, 2023
1 parent e7c9d3c commit 59e9b43
Show file tree
Hide file tree
Showing 30 changed files with 869 additions and 570 deletions.
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
49 changes: 49 additions & 0 deletions packages/next-swc/crates/next-core/js/src/entry/app/edge-route.ts
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

0 comments on commit 59e9b43

Please sign in to comment.