.
This commit is contained in:
+301
@@ -0,0 +1,301 @@
|
||||
import '../../server/web/globals';
|
||||
import { adapter } from '../../server/web/adapter';
|
||||
import { IncrementalCache } from '../../server/lib/incremental-cache';
|
||||
import * as cacheHandlers 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
|
||||
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';
|
||||
import { HTML_CONTENT_TYPE_HEADER } from '../../lib/constants';
|
||||
import { toNodeOutgoingHttpHeaders } from '../../server/web/utils';
|
||||
// INJECT:pageRouteModuleOptions
|
||||
// INJECT:errorRouteModuleOptions
|
||||
// INJECT:user500RouteModuleOptions
|
||||
// INJECT_RAW:cacheHandlerImports
|
||||
const pageMod = {
|
||||
...userlandPage,
|
||||
routeModule: new RouteModule({
|
||||
...pageRouteModuleOptions,
|
||||
components: {
|
||||
App: appMod.default,
|
||||
Document
|
||||
},
|
||||
userland: userlandPage,
|
||||
distDir: process.env.__NEXT_RELATIVE_DIST_DIR || '',
|
||||
relativeProjectDir: process.env.__NEXT_RELATIVE_PROJECT_DIR || ''
|
||||
})
|
||||
};
|
||||
const errorMod = {
|
||||
...userlandErrorPage,
|
||||
routeModule: new RouteModule({
|
||||
...errorRouteModuleOptions,
|
||||
components: {
|
||||
App: appMod.default,
|
||||
Document
|
||||
},
|
||||
userland: userlandErrorPage,
|
||||
distDir: process.env.__NEXT_RELATIVE_DIST_DIR || '',
|
||||
relativeProjectDir: process.env.__NEXT_RELATIVE_PROJECT_DIR || ''
|
||||
})
|
||||
};
|
||||
// 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,
|
||||
distDir: process.env.__NEXT_RELATIVE_DIST_DIR || '',
|
||||
relativeProjectDir: process.env.__NEXT_RELATIVE_PROJECT_DIR || ''
|
||||
})
|
||||
} : null;
|
||||
export const ComponentMod = pageMod;
|
||||
async function requestHandler(req, _event) {
|
||||
var _nextConfig_i18n;
|
||||
let srcPage = 'VAR_DEFINITION_PATHNAME';
|
||||
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, nextConfig, deploymentId, isNextDataRequest, buildManifest, prerenderManifest, reactLoadableManifest, subresourceIntegrityManifest, dynamicCssManifest, clientAssetToken } = prepareResult;
|
||||
cacheHandlers.initializeCacheHandlers(nextConfig.cacheMaxMemorySize);
|
||||
// INJECT_RAW:cacheHandlerRegistration
|
||||
const renderContext = {
|
||||
page: srcPage,
|
||||
query,
|
||||
params,
|
||||
sharedContext: {
|
||||
buildId,
|
||||
deploymentId,
|
||||
clientAssetToken,
|
||||
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,
|
||||
previewProps: prerenderManifest.preview,
|
||||
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,
|
||||
isExperimentalCompile: nextConfig.experimental.isExperimentalCompile,
|
||||
// `htmlLimitedBots` is passed to server as serialized config in string format
|
||||
experimental: {
|
||||
clientTraceMetadata: nextConfig.experimental.clientTraceMetadata
|
||||
},
|
||||
buildManifest,
|
||||
subresourceIntegrityManifest,
|
||||
reactLoadableManifest,
|
||||
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 || HTML_CONTENT_TYPE_HEADER;
|
||||
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} ${srcPage}`);
|
||||
}
|
||||
});
|
||||
return renderResultToResponse(result);
|
||||
} catch (err) {
|
||||
const errModule = error500Mod || errorMod;
|
||||
const errRouteModule = errModule.routeModule;
|
||||
if (errRouteModule.isDev) {
|
||||
throw err;
|
||||
}
|
||||
const silenceLog = false;
|
||||
await errRouteModule.onRequestError(baseReq, err, {
|
||||
routerKind: 'Pages Router',
|
||||
routePath: srcPage,
|
||||
routeType: 'render',
|
||||
revalidateReason: undefined
|
||||
}, silenceLog);
|
||||
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();
|
||||
return tracer.withPropagatedContext(req.headers, ()=>tracer.trace(BaseServerSpan.handleRequest, {
|
||||
spanName: `${req.method} ${srcPage}`,
|
||||
kind: SpanKind.SERVER,
|
||||
attributes: {
|
||||
'http.method': req.method,
|
||||
'http.target': relativeUrl,
|
||||
'http.route': srcPage
|
||||
}
|
||||
}, invokeRender));
|
||||
}
|
||||
const internalHandler = (opts)=>{
|
||||
return adapter({
|
||||
...opts,
|
||||
IncrementalCache,
|
||||
handler: requestHandler,
|
||||
incrementalCacheHandler,
|
||||
bypassNextUrl: true,
|
||||
page: 'VAR_DEFINITION_PATHNAME'
|
||||
});
|
||||
};
|
||||
export async function handler(request, ctx) {
|
||||
const result = await internalHandler({
|
||||
request: {
|
||||
url: request.url,
|
||||
method: request.method,
|
||||
headers: toNodeOutgoingHttpHeaders(request.headers),
|
||||
nextConfig: {
|
||||
basePath: process.env.__NEXT_BASE_PATH,
|
||||
i18n: process.env.__NEXT_I18N_CONFIG,
|
||||
trailingSlash: Boolean(process.env.__NEXT_TRAILING_SLASH),
|
||||
experimental: {
|
||||
cacheLife: process.env.__NEXT_CACHE_LIFE,
|
||||
authInterrupts: Boolean(process.env.__NEXT_EXPERIMENTAL_AUTH_INTERRUPTS),
|
||||
clientParamParsingOrigins: process.env.__NEXT_CLIENT_PARAM_PARSING_ORIGINS
|
||||
}
|
||||
},
|
||||
page: {
|
||||
name: 'VAR_DEFINITION_PATHNAME'
|
||||
},
|
||||
body: request.method !== 'GET' && request.method !== 'HEAD' ? request.body ?? undefined : undefined,
|
||||
waitUntil: ctx.waitUntil,
|
||||
requestMeta: ctx.requestMeta,
|
||||
signal: ctx.signal || new AbortController().signal
|
||||
}
|
||||
});
|
||||
ctx.waitUntil == null ? void 0 : ctx.waitUntil.call(ctx, result.waitUntil);
|
||||
return result.response;
|
||||
}
|
||||
// backwards compat
|
||||
export default internalHandler;
|
||||
|
||||
//# sourceMappingURL=edge-ssr.js.map
|
||||
Reference in New Issue
Block a user