next
Version:
The React Framework
190 lines (189 loc) • 7.83 kB
JavaScript
import "../require-hook";
import "../node-polyfill-fetch";
import url from "url";
import path from "path";
import http from "http";
import { findPageFile } from "./find-page-file";
import { getRequestMeta } from "../request-meta";
import setupDebug from "next/dist/compiled/debug";
import { getCloneableBody } from "../body-streams";
import { findPagesDir } from "../../lib/find-pages-dir";
import { setupFsCheck } from "./router-utils/filesystem";
import { proxyRequest } from "./router-utils/proxy-request";
import { getResolveRoutes } from "./router-utils/resolve-routes";
import { PERMANENT_REDIRECT_STATUS } from "../../shared/lib/constants";
import { splitCookiesString, toNodeOutgoingHttpHeaders } from "../web/utils";
import { signalFromNodeResponse } from "../web/spec-extension/adapters/next-request";
import { getMiddlewareRouteMatcher } from "../../shared/lib/router/utils/middleware-route-matcher";
import { pipeReadable } from "../pipe-readable";
const debug = setupDebug("next:router-server");
export async function makeResolver(dir, nextConfig, middleware, serverAddr) {
const fsChecker = await setupFsCheck({
dir,
dev: true,
minimalMode: false,
config: nextConfig
});
const { appDir , pagesDir } = findPagesDir(dir, !!nextConfig.experimental.appDir);
fsChecker.ensureCallback(async (item)=>{
let result = null;
if (item.type === "appFile") {
if (!appDir) {
throw new Error("no app dir present");
}
result = await findPageFile(appDir, item.itemPath, nextConfig.pageExtensions, true);
} else if (item.type === "pageFile") {
if (!pagesDir) {
throw new Error("no pages dir present");
}
result = await findPageFile(pagesDir, item.itemPath, nextConfig.pageExtensions, false);
}
if (!result) {
throw new Error(`failed to find page file ${item.type} ${item.itemPath}`);
}
});
const distDir = path.join(dir, nextConfig.distDir);
const middlewareInfo = middleware ? {
name: "middleware",
paths: middleware.files.map((file)=>path.join(process.cwd(), file)),
wasm: [],
assets: []
} : {};
const middlewareServerPort = await new Promise((resolve)=>{
const srv = http.createServer(async (req, res)=>{
const cloneableBody = getCloneableBody(req);
try {
const { run } = require("../web/sandbox");
const result = await run({
distDir,
name: middlewareInfo.name || "/",
paths: middlewareInfo.paths || [],
edgeFunctionEntry: middlewareInfo,
request: {
headers: req.headers,
method: req.method || "GET",
nextConfig: {
i18n: nextConfig.i18n,
basePath: nextConfig.basePath,
trailingSlash: nextConfig.trailingSlash
},
url: `http://${serverAddr.hostname || "localhost"}:${serverAddr.port || 3000}${req.url}`,
body: cloneableBody,
signal: signalFromNodeResponse(res)
},
useCache: true,
onWarning: console.warn
});
for (let [key, value] of result.response.headers){
if (key.toLowerCase() !== "set-cookie") continue;
// Clear existing header.
result.response.headers.delete(key);
// Append each cookie individually.
const cookies = splitCookiesString(value);
for (const cookie of cookies){
result.response.headers.append(key, cookie);
}
}
for (const [key, value] of Object.entries(toNodeOutgoingHttpHeaders(result.response.headers))){
if (key !== "content-encoding" && value !== undefined) {
res.setHeader(key, value);
}
}
res.statusCode = result.response.status;
if (result.response.body) {
await pipeReadable(result.response.body, res);
} else {
res.end();
}
} catch (err) {
console.error(err);
res.statusCode = 500;
res.end("Internal Server Error");
}
});
srv.on("listening", ()=>{
resolve(srv.address().port);
});
srv.listen(0);
});
if (middleware == null ? void 0 : middleware.files.length) {
var _middleware_matcher;
fsChecker.middlewareMatcher = getMiddlewareRouteMatcher(((_middleware_matcher = middleware.matcher) == null ? void 0 : _middleware_matcher.map((item)=>({
regexp: item,
originalSource: item
}))) || [
{
regexp: ".*",
originalSource: "/:path*"
}
]);
}
const resolveRoutes = getResolveRoutes(fsChecker, nextConfig, {
dir,
port: serverAddr.port || 3000,
hostname: serverAddr.hostname,
isNodeDebugging: false,
dev: true,
workerType: "render"
}, {
pages: {
async initialize () {
return {
port: middlewareServerPort,
hostname: "127.0.0.1"
};
},
async deleteCache () {},
async clearModuleContext () {},
async deleteAppClientCache () {},
async propagateServerField () {}
}
}, {});
return async function resolveRoute(req, res) {
const routeResult = await resolveRoutes(req, new Set(), false, signalFromNodeResponse(res));
const { matchedOutput , bodyStream , statusCode , parsedUrl , resHeaders , finished } = routeResult;
debug("requestHandler!", req.url, {
matchedOutput,
statusCode,
resHeaders,
bodyStream: !!bodyStream,
parsedUrl: {
pathname: parsedUrl.pathname,
query: parsedUrl.query
},
finished
});
for (const key of Object.keys(resHeaders || {})){
res.setHeader(key, resHeaders[key]);
}
if (!bodyStream && statusCode && statusCode > 300 && statusCode < 400) {
const destination = url.format(parsedUrl);
res.statusCode = statusCode;
res.setHeader("location", destination);
if (statusCode === PERMANENT_REDIRECT_STATUS) {
res.setHeader("Refresh", `0;url=${destination}`);
}
res.end(destination);
return;
}
// handle middleware body response
if (bodyStream) {
res.statusCode = statusCode || 200;
return await pipeReadable(bodyStream, res);
}
if (finished && parsedUrl.protocol) {
var _getRequestMeta;
await proxyRequest(req, res, parsedUrl, undefined, (_getRequestMeta = getRequestMeta(req, "__NEXT_CLONABLE_BODY")) == null ? void 0 : _getRequestMeta.cloneBodyStream(), nextConfig.experimental.proxyTimeout);
return;
}
res.setHeader("x-nextjs-route-result", "1");
res.end();
return {
type: "rewrite",
statusCode: 200,
headers: resHeaders,
url: url.format(parsedUrl)
};
};
}
//# sourceMappingURL=route-resolver.js.map