UNPKG

next

Version:

The React Framework

272 lines (271 loc) • 11.1 kB
import '../../server/web/globals'; import { adapter } from '../../server/web/adapter'; import { IncrementalCache } from '../../server/lib/incremental-cache'; import { initializeCacheHandlers } from '../../server/use-cache/handlers'; import Document from 'VAR_MODULE_DOCUMENT'; import * as appMod from 'VAR_MODULE_APP'; import * as userlandPage from 'VAR_USERLAND'; import * as userlandErrorPage from 'VAR_MODULE_GLOBAL_ERROR'; // OPTIONAL_IMPORT:* as userland500Page // OPTIONAL_IMPORT:incrementalCacheHandler // TODO: re-enable this once we've refactored to use implicit matches // const renderToHTML = undefined import RouteModule from '../../server/route-modules/pages/module'; import { WebNextRequest, WebNextResponse } from '../../server/base-http/web'; import { getTracer, SpanKind } from '../../server/lib/trace/tracer'; import { BaseServerSpan } from '../../server/lib/trace/constants'; // INJECT:nextConfig // INJECT:pageRouteModuleOptions // INJECT:errorRouteModuleOptions // INJECT:user500RouteModuleOptions // Initialize the cache handlers interface. initializeCacheHandlers(); globalThis.nextConfig = nextConfig; const pageMod = { ...userlandPage, routeModule: new RouteModule({ ...pageRouteModuleOptions, components: { App: appMod.default, Document }, userland: userlandPage }) }; const errorMod = { ...userlandErrorPage, routeModule: new RouteModule({ ...errorRouteModuleOptions, components: { App: appMod.default, Document }, userland: userlandErrorPage }) }; // FIXME: this needs to be made compatible with the template const error500Mod = userland500Page ? { ...userland500Page, routeModule: new RouteModule({ ...user500RouteModuleOptions, components: { App: appMod.default, Document }, userland: userland500Page }) } : null; export const ComponentMod = pageMod; async function requestHandler(req, _event) { var _nextConfig_experimental_amp, _nextConfig_i18n; let srcPage = 'VAR_PAGE'; const relativeUrl = `${req.nextUrl.pathname}${req.nextUrl.search}`; const baseReq = new WebNextRequest(req); const pageRouteModule = pageMod.routeModule; const prepareResult = await pageRouteModule.prepare(baseReq, null, { srcPage, multiZoneDraftMode: false }); if (!prepareResult) { return new Response('Bad Request', { status: 400 }); } const { query, params, buildId, isNextDataRequest, buildManifest, prerenderManifest, reactLoadableManifest, clientReferenceManifest, subresourceIntegrityManifest, dynamicCssManifest } = prepareResult; const renderContext = { page: srcPage, query, params, sharedContext: { buildId, deploymentId: process.env.NEXT_DEPLOYMENT_ID, customServer: undefined }, renderContext: { isFallback: false, isDraftMode: false, developmentNotFoundSourcePage: undefined }, renderOpts: { params, page: srcPage, supportsDynamicResponse: true, Component: pageMod.Component, ComponentMod: pageMod, pageConfig: pageMod.pageConfig, routeModule: pageMod.routeModule, strictNextHead: nextConfig.experimental.strictNextHead ?? true, canonicalBase: nextConfig.amp.canonicalBase || '', previewProps: prerenderManifest.preview, ampOptimizerConfig: (_nextConfig_experimental_amp = nextConfig.experimental.amp) == null ? void 0 : _nextConfig_experimental_amp.optimizer, basePath: nextConfig.basePath, assetPrefix: nextConfig.assetPrefix, images: nextConfig.images, optimizeCss: nextConfig.experimental.optimizeCss, nextConfigOutput: nextConfig.output, nextScriptWorkers: nextConfig.experimental.nextScriptWorkers, disableOptimizedLoading: nextConfig.experimental.disableOptimizedLoading, domainLocales: (_nextConfig_i18n = nextConfig.i18n) == null ? void 0 : _nextConfig_i18n.domains, distDir: '', crossOrigin: nextConfig.crossOrigin ? nextConfig.crossOrigin : undefined, largePageDataBytes: nextConfig.experimental.largePageDataBytes, // Only the `publicRuntimeConfig` key is exposed to the client side // It'll be rendered as part of __NEXT_DATA__ on the client side runtimeConfig: Object.keys(nextConfig.publicRuntimeConfig).length > 0 ? nextConfig.publicRuntimeConfig : undefined, isExperimentalCompile: nextConfig.experimental.isExperimentalCompile, // `htmlLimitedBots` is passed to server as serialized config in string format experimental: { clientTraceMetadata: nextConfig.experimental.clientTraceMetadata }, buildManifest, subresourceIntegrityManifest, reactLoadableManifest, clientReferenceManifest, dynamicCssManifest } }; let finalStatus = 200; const renderResultToResponse = (result)=>{ // Handle null responses if (result.isNull) { finalStatus = 500; return new Response(null, { status: 500 }); } // Extract metadata const { metadata } = result; finalStatus = metadata.statusCode || 200; const headers = new Headers(); // Set content type const contentType = result.contentType || 'text/html; charset=utf-8'; headers.set('Content-Type', contentType); // Add metadata headers if (metadata.headers) { for (const [key, value] of Object.entries(metadata.headers)){ if (value !== undefined) { if (Array.isArray(value)) { // Handle multiple header values for (const v of value){ headers.append(key, String(v)); } } else { headers.set(key, String(value)); } } } } // Handle static response if (!result.isDynamic) { const body = result.toUnchunkedString(); headers.set('Content-Length', String(new TextEncoder().encode(body).length)); return new Response(body, { status: finalStatus, headers }); } // Handle dynamic/streaming response // For edge runtime, we need to create a readable stream that pipes from the result const { readable, writable } = new TransformStream(); // Start piping the result to the writable stream // This is done asynchronously to avoid blocking the response creation result.pipeTo(writable).catch((err)=>{ console.error('Error piping RenderResult to response:', err); }); return new Response(readable, { status: finalStatus, headers }); }; const invokeRender = async (span)=>{ try { const result = await pageRouteModule.render(// @ts-expect-error we don't type this for edge baseReq, new WebNextResponse(undefined), { ...renderContext, renderOpts: { ...renderContext.renderOpts, getServerSideProps: pageMod.getServerSideProps, Component: pageMod.default || pageMod, ComponentMod: pageMod, pageConfig: pageMod.config, isNextDataRequest } }).finally(()=>{ if (!span) return; span.setAttributes({ 'http.status_code': finalStatus, 'next.rsc': false }); const rootSpanAttributes = tracer.getRootSpanAttributes(); // We were unable to get attributes, probably OTEL is not enabled if (!rootSpanAttributes) { return; } if (rootSpanAttributes.get('next.span_type') !== BaseServerSpan.handleRequest) { console.warn(`Unexpected root span type '${rootSpanAttributes.get('next.span_type')}'. Please report this Next.js issue https://github.com/vercel/next.js`); return; } const route = rootSpanAttributes.get('next.route'); if (route) { const name = `${req.method} ${route}`; span.setAttributes({ 'next.route': route, 'http.route': route, 'next.span_name': name }); span.updateName(name); } else { span.updateName(`${req.method} ${relativeUrl}`); } }); return renderResultToResponse(result); } catch (err) { const errModule = error500Mod || errorMod; const errRouteModule = errModule.routeModule; if (errRouteModule.isDev) { throw err; } await errRouteModule.onRequestError(baseReq, err, { routerKind: 'Pages Router', routePath: srcPage, routeType: 'render', revalidateReason: undefined }); const errResult = await errRouteModule.render(// @ts-expect-error we don't type this for edge baseReq, new WebNextResponse(undefined), { ...renderContext, page: error500Mod ? '/500' : '/_error', renderOpts: { ...renderContext.renderOpts, getServerSideProps: errModule.getServerSideProps, Component: errModule.default || errModule, ComponentMod: errModule, pageConfig: errModule.config } }); return renderResultToResponse(errResult); } }; const tracer = getTracer(); // TODO: activeSpan code path is for when wrapped by // next-server can be removed when this is no longer used return tracer.withPropagatedContext(req.headers, ()=>tracer.trace(BaseServerSpan.handleRequest, { spanName: `${req.method} ${relativeUrl}`, kind: SpanKind.SERVER, attributes: { 'http.method': req.method, 'http.target': relativeUrl, 'http.route': srcPage } }, invokeRender)); } export default function nHandler(opts) { return adapter({ ...opts, IncrementalCache, handler: requestHandler, incrementalCacheHandler, bypassNextUrl: true }); } //# sourceMappingURL=edge-ssr.js.map