rsnext/packages/next/server/web/sandbox/sandbox.ts
JJ Kasper 3bf8b2b4fe
Update to detect GSSP with edge runtime during build (#40076)
This updates to handle detecting `getStaticProps`/`getServerSideProps` correctly during build when `experimental-edge` is being used. This also fixes not parsing dynamic route params correctly with the edge runtime and sets up the handling needed for the static generation for app opened in the below mentioned PR.

## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

Fixes: [slack thread](https://vercel.slack.com/archives/C0289CGVAR2/p1661554455121189)
x-ref: https://github.com/vercel/next.js/pull/39884
2022-08-30 18:18:02 +00:00

109 lines
3.2 KiB
TypeScript

import type { NodejsRequestData, FetchEventResult, RequestData } from '../types'
import { getServerError } from 'next/dist/compiled/@next/react-dev-overlay/dist/middleware'
import { getModuleContext } from './context'
import { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin'
import { requestToBodyStream } from '../../body-streams'
import type { EdgeRuntime } from 'next/dist/compiled/edge-runtime'
export const ErrorSource = Symbol('SandboxError')
const FORBIDDEN_HEADERS = [
'content-length',
'content-encoding',
'transfer-encoding',
]
type RunnerFn = (params: {
name: string
env: string[]
onWarning?: (warn: Error) => void
paths: string[]
request: NodejsRequestData
useCache: boolean
edgeFunctionEntry: Pick<EdgeFunctionDefinition, 'wasm' | 'assets'>
distDir: string
}) => Promise<FetchEventResult>
/**
* Decorates the runner function making sure all errors it can produce are
* tagged with `edge-server` so they can properly be rendered in dev.
*/
function withTaggedErrors(fn: RunnerFn): RunnerFn {
return (params) =>
fn(params)
.then((result) => ({
...result,
waitUntil: result?.waitUntil?.catch((error) => {
// TODO: used COMPILER_NAMES.edgeServer instead. Verify that it does not increase the runtime size.
throw getServerError(error, 'edge-server')
}),
}))
.catch((error) => {
// TODO: used COMPILER_NAMES.edgeServer instead
throw getServerError(error, 'edge-server')
})
}
export const getRuntimeContext = async (params: {
name: string
onWarning?: any
useCache: boolean
env: string[]
edgeFunctionEntry: any
distDir: string
paths: string[]
}): Promise<EdgeRuntime<any>> => {
const { runtime, evaluateInContext } = await getModuleContext({
moduleName: params.name,
onWarning: params.onWarning ?? (() => {}),
useCache: params.useCache !== false,
env: params.env,
edgeFunctionEntry: params.edgeFunctionEntry,
distDir: params.distDir,
})
for (const paramPath of params.paths) {
evaluateInContext(paramPath)
}
return runtime
}
export const run = withTaggedErrors(async (params) => {
const runtime = await getRuntimeContext(params)
const subreq = params.request.headers[`x-middleware-subrequest`]
const subrequests = typeof subreq === 'string' ? subreq.split(':') : []
if (subrequests.includes(params.name)) {
return {
waitUntil: Promise.resolve(),
response: new runtime.context.Response(null, {
headers: {
'x-middleware-next': '1',
},
}),
}
}
const edgeFunction: (args: {
request: RequestData
}) => Promise<FetchEventResult> =
runtime.context._ENTRIES[`middleware_${params.name}`].default
const cloned = !['HEAD', 'GET'].includes(params.request.method)
? params.request.body?.cloneBodyStream()
: undefined
try {
const result = await edgeFunction({
request: {
...params.request,
body: cloned && requestToBodyStream(runtime.context, cloned),
},
})
for (const headerName of FORBIDDEN_HEADERS) {
result.response.headers.delete(headerName)
}
return result
} finally {
await params.request.body?.finalize()
}
})