UNPKG

next

Version:

The React Framework

1,093 lines • 52.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; require("./node-polyfill-fetch"); require("./node-polyfill-web-streams"); var _utils = require("../shared/lib/utils"); var _fs = _interopRequireDefault(require("fs")); var _path = require("path"); var _http = require("http"); var _requestMeta = require("./request-meta"); var _constants = require("../shared/lib/constants"); var _recursiveReaddirSync = require("./lib/recursive-readdir-sync"); var _url = require("url"); var _compression = _interopRequireDefault(require("next/dist/compiled/compression")); var _httpProxy = _interopRequireDefault(require("next/dist/compiled/http-proxy")); var _pathMatch = require("../shared/lib/router/utils/path-match"); var _sandbox = require("./web/sandbox"); var _node = require("./base-http/node"); var _sendPayload = require("./send-payload"); var _serveStatic = require("./serve-static"); var _node1 = require("./api-utils/node"); var _render = require("./render"); var _appRender = require("./app-render"); var _parseUrl = require("../shared/lib/router/utils/parse-url"); var Log = _interopRequireWildcard(require("../build/output/log")); var _requireHook = _interopRequireDefault(require("../build/webpack/require-hook")); var _baseServer = _interopRequireWildcard(require("./base-server")); Object.keys(_baseServer).forEach(function(key) { if (key === "default" || key === "__esModule") return; if (key in exports && exports[key] === _baseServer[key]) return; Object.defineProperty(exports, key, { enumerable: true, get: function() { return _baseServer[key]; } }); }); var _require = require("./require"); var _denormalizePagePath = require("../shared/lib/page-path/denormalize-page-path"); var _normalizePagePath = require("../shared/lib/page-path/normalize-page-path"); var _loadComponents = require("./load-components"); var _isError = _interopRequireWildcard(require("../lib/is-error")); var _utils1 = require("./web/utils"); var _relativizeUrl = require("../shared/lib/router/utils/relativize-url"); var _prepareDestination = require("../shared/lib/router/utils/prepare-destination"); var _normalizeLocalePath = require("../shared/lib/i18n/normalize-locale-path"); var _routeMatcher = require("../shared/lib/router/utils/route-matcher"); var _env = require("@next/env"); var _serverRouteUtils = require("./server-route-utils"); var _querystring = require("../shared/lib/router/utils/querystring"); var _responseCache = _interopRequireDefault(require("../server/response-cache")); var _removeTrailingSlash = require("../shared/lib/router/utils/remove-trailing-slash"); var _getNextPathnameInfo = require("../shared/lib/router/utils/get-next-pathname-info"); var _bodyStreams = require("./body-streams"); var _apiUtils = require("./api-utils"); var _utils2 = require("../shared/lib/router/utils"); var _utils3 = require("./utils"); class NextNodeServer extends _baseServer.default { constructor(options){ // Initialize super class super(options); /** * This sets environment variable to be used at the time of SSR by head.tsx. * Using this from process.env allows targeting both serverless and SSR by calling * `process.env.__NEXT_OPTIMIZE_CSS`. */ if (this.renderOpts.optimizeFonts) { process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(true); } if (this.renderOpts.optimizeCss) { process.env.__NEXT_OPTIMIZE_CSS = JSON.stringify(true); } if (this.renderOpts.nextScriptWorkers) { process.env.__NEXT_SCRIPT_WORKERS = JSON.stringify(true); } if (!this.minimalMode) { const { ImageOptimizerCache } = require("./image-optimizer"); this.imageResponseCache = new _responseCache.default(new ImageOptimizerCache({ distDir: this.distDir, nextConfig: this.nextConfig }), this.minimalMode); } if (!options.dev) { // pre-warm _document and _app as these will be // needed for most requests (0, _loadComponents).loadComponents(this.distDir, "/_document", this._isLikeServerless).catch(()=>{}); (0, _loadComponents).loadComponents(this.distDir, "/_app", this._isLikeServerless).catch(()=>{}); } } compression = this.nextConfig.compress && this.nextConfig.target === "server" ? (0, _compression).default() : undefined; loadEnvConfig({ dev , forceReload }) { (0, _env).loadEnvConfig(this.dir, dev, Log, forceReload); } getPublicDir() { return (0, _path).join(this.dir, _constants.CLIENT_PUBLIC_FILES_PATH); } getHasStaticDir() { return _fs.default.existsSync((0, _path).join(this.dir, "static")); } getPagesManifest() { return require((0, _path).join(this.serverDistDir, _constants.PAGES_MANIFEST)); } getAppPathsManifest() { if (this.nextConfig.experimental.appDir) { const appPathsManifestPath = (0, _path).join(this.serverDistDir, _constants.APP_PATHS_MANIFEST); return require(appPathsManifestPath); } } getBuildId() { const buildIdFile = (0, _path).join(this.distDir, _constants.BUILD_ID_FILE); try { return _fs.default.readFileSync(buildIdFile, "utf8").trim(); } catch (err) { if (!_fs.default.existsSync(buildIdFile)) { throw new Error(`Could not find a production build in the '${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id`); } throw err; } } generateImageRoutes() { return [ { match: (0, _pathMatch).getPathMatch("/_next/image"), type: "route", name: "_next/image catchall", fn: async (req, res, _params, parsedUrl)=>{ if (this.minimalMode) { res.statusCode = 400; res.body("Bad Request").send(); return { finished: true }; } const { getHash , ImageOptimizerCache , sendResponse , ImageError } = require("./image-optimizer"); if (!this.imageResponseCache) { throw new Error("invariant image optimizer cache was not initialized"); } const imagesConfig = this.nextConfig.images; if (imagesConfig.loader !== "default") { await this.render404(req, res); return { finished: true }; } const paramsResult = ImageOptimizerCache.validateParams(req.originalRequest, parsedUrl.query, this.nextConfig, !!this.renderOpts.dev); if ("errorMessage" in paramsResult) { res.statusCode = 400; res.body(paramsResult.errorMessage).send(); return { finished: true }; } const cacheKey = ImageOptimizerCache.getCacheKey(paramsResult); try { var ref; const cacheEntry = await this.imageResponseCache.get(cacheKey, async ()=>{ const { buffer , contentType , maxAge } = await this.imageOptimizer(req, res, paramsResult); const etag = getHash([ buffer ]); return { value: { kind: "IMAGE", buffer, etag, extension: (0, _serveStatic).getExtension(contentType) }, revalidate: maxAge }; }, {}); if ((cacheEntry == null ? void 0 : (ref = cacheEntry.value) == null ? void 0 : ref.kind) !== "IMAGE") { throw new Error("invariant did not get entry from image response cache"); } sendResponse(req.originalRequest, res.originalResponse, paramsResult.href, cacheEntry.value.extension, cacheEntry.value.buffer, paramsResult.isStatic, cacheEntry.isMiss ? "MISS" : cacheEntry.isStale ? "STALE" : "HIT", imagesConfig.contentSecurityPolicy, cacheEntry.revalidate || 0, Boolean(this.renderOpts.dev)); } catch (err) { if (err instanceof ImageError) { res.statusCode = err.statusCode; res.body(err.message).send(); return { finished: true }; } throw err; } return { finished: true }; } }, ]; } generateStaticRoutes() { return this.hasStaticDir ? [ { // It's very important to keep this route's param optional. // (but it should support as many params as needed, separated by '/') // Otherwise this will lead to a pretty simple DOS attack. // See more: https://github.com/vercel/next.js/issues/2617 match: (0, _pathMatch).getPathMatch("/static/:path*"), name: "static catchall", fn: async (req, res, params, parsedUrl)=>{ const p = (0, _path).join(this.dir, "static", ...params.path); await this.serveStatic(req, res, p, parsedUrl); return { finished: true }; } }, ] : []; } setImmutableAssetCacheControl(res) { res.setHeader("Cache-Control", "public, max-age=31536000, immutable"); } generateFsStaticRoutes() { return [ { match: (0, _pathMatch).getPathMatch("/_next/static/:path*"), type: "route", name: "_next/static catchall", fn: async (req, res, params, parsedUrl)=>{ // make sure to 404 for /_next/static itself if (!params.path) { await this.render404(req, res, parsedUrl); return { finished: true }; } if (params.path[0] === _constants.CLIENT_STATIC_FILES_RUNTIME || params.path[0] === "chunks" || params.path[0] === "css" || params.path[0] === "image" || params.path[0] === "media" || params.path[0] === this.buildId || params.path[0] === "pages" || params.path[1] === "pages") { this.setImmutableAssetCacheControl(res); } const p = (0, _path).join(this.distDir, _constants.CLIENT_STATIC_FILES_PATH, ...params.path || []); await this.serveStatic(req, res, p, parsedUrl); return { finished: true }; } }, ]; } generatePublicRoutes() { if (!_fs.default.existsSync(this.publicDir)) return []; const publicFiles = new Set((0, _recursiveReaddirSync).recursiveReadDirSync(this.publicDir).map((p)=>encodeURI(p.replace(/\\/g, "/")))); return [ { match: (0, _pathMatch).getPathMatch("/:path*"), matchesBasePath: true, name: "public folder catchall", fn: async (req, res, params, parsedUrl)=>{ const pathParts = params.path || []; const { basePath } = this.nextConfig; // if basePath is defined require it be present if (basePath) { const basePathParts = basePath.split("/"); // remove first empty value basePathParts.shift(); if (!basePathParts.every((part, idx)=>{ return part === pathParts[idx]; })) { return { finished: false }; } pathParts.splice(0, basePathParts.length); } let path = `/${pathParts.join("/")}`; if (!publicFiles.has(path)) { // In `next-dev-server.ts`, we ensure encoded paths match // decoded paths on the filesystem. So we need do the // opposite here: make sure decoded paths match encoded. path = encodeURI(path); } if (publicFiles.has(path)) { await this.serveStatic(req, res, (0, _path).join(this.publicDir, ...pathParts), parsedUrl); return { finished: true }; } return { finished: false }; } }, ]; } _validFilesystemPathSet = null; getFilesystemPaths() { if (this._validFilesystemPathSet) { return this._validFilesystemPathSet; } const pathUserFilesStatic = (0, _path).join(this.dir, "static"); let userFilesStatic = []; if (this.hasStaticDir && _fs.default.existsSync(pathUserFilesStatic)) { userFilesStatic = (0, _recursiveReaddirSync).recursiveReadDirSync(pathUserFilesStatic).map((f)=>(0, _path).join(".", "static", f)); } let userFilesPublic = []; if (this.publicDir && _fs.default.existsSync(this.publicDir)) { userFilesPublic = (0, _recursiveReaddirSync).recursiveReadDirSync(this.publicDir).map((f)=>(0, _path).join(".", "public", f)); } let nextFilesStatic = []; nextFilesStatic = !this.minimalMode && _fs.default.existsSync((0, _path).join(this.distDir, "static")) ? (0, _recursiveReaddirSync).recursiveReadDirSync((0, _path).join(this.distDir, "static")).map((f)=>(0, _path).join(".", (0, _path).relative(this.dir, this.distDir), "static", f)) : []; return this._validFilesystemPathSet = new Set([ ...nextFilesStatic, ...userFilesPublic, ...userFilesStatic, ]); } sendRenderResult(req, res, options) { return (0, _sendPayload).sendRenderResult({ req: req.originalRequest, res: res.originalResponse, ...options }); } sendStatic(req, res, path) { return (0, _serveStatic).serveStatic(req.originalRequest, res.originalResponse, path); } handleCompression(req, res) { if (this.compression) { this.compression(req.originalRequest, res.originalResponse, ()=>{}); } } async handleUpgrade(req, socket, head) { await this.router.execute(req, socket, (0, _url).parse(req.url, true), head); } async proxyRequest(req, res, parsedUrl, upgradeHead) { const { query } = parsedUrl; delete parsedUrl.query; parsedUrl.search = (0, _baseServer).stringifyQuery(req, query); const target = (0, _url).format(parsedUrl); const proxy = new _httpProxy.default({ target, changeOrigin: true, ignorePath: true, xfwd: true, ws: true, // we limit proxy requests to 30s by default, in development // we don't time out WebSocket requests to allow proxying proxyTimeout: upgradeHead && this.renderOpts.dev ? undefined : 30000 }); await new Promise((proxyResolve, proxyReject)=>{ let finished = false; proxy.on("error", (err)=>{ console.error(`Failed to proxy ${target}`, err); if (!finished) { finished = true; proxyReject(err); } }); // if upgrade head is present treat as WebSocket request if (upgradeHead) { proxy.on("proxyReqWs", (proxyReq)=>{ proxyReq.on("close", ()=>{ if (!finished) { finished = true; proxyResolve(true); } }); }); proxy.ws(req, res, upgradeHead); proxyResolve(true); } else { proxy.on("proxyReq", (proxyReq)=>{ proxyReq.on("close", ()=>{ if (!finished) { finished = true; proxyResolve(true); } }); }); proxy.web(req.originalRequest, res.originalResponse); } }); return { finished: true }; } async runApi(req, res, query, params, page, builtPagePath) { const handledAsEdgeFunction = await this.runEdgeFunction({ req, res, query, params, page }); if (handledAsEdgeFunction) { return true; } const pageModule = await require(builtPagePath); query = { ...query, ...params }; delete query.__nextLocale; delete query.__nextDefaultLocale; if (!this.renderOpts.dev && this._isLikeServerless) { if (typeof pageModule.default === "function") { (0, _baseServer).prepareServerlessUrl(req, query); await pageModule.default(req, res); return true; } } await (0, _node1).apiResolver(req.originalRequest, res.originalResponse, query, pageModule, { ...this.renderOpts.previewProps, revalidate: (newReq, newRes)=>this.getRequestHandler()(new _node.NodeNextRequest(newReq), new _node.NodeNextResponse(newRes)), // internal config so is not typed trustHostHeader: this.nextConfig.experimental.trustHostHeader }, this.minimalMode, this.renderOpts.dev, page); return true; } async renderHTML(req, res, pathname, query, renderOpts) { // Due to the way we pass data by mutating `renderOpts`, we can't extend the // object here but only updating its `serverComponentManifest` field. // https://github.com/vercel/next.js/blob/df7cbd904c3bd85f399d1ce90680c0ecf92d2752/packages/next/server/render.tsx#L947-L952 renderOpts.serverComponentManifest = this.serverComponentManifest; if (this.nextConfig.experimental.appDir && (renderOpts.isAppPath || query.__flight__)) { const isPagesDir = !renderOpts.isAppPath; return (0, _appRender).renderToHTMLOrFlight(req.originalRequest, res.originalResponse, pathname, query, renderOpts, isPagesDir); } return (0, _render).renderToHTML(req.originalRequest, res.originalResponse, pathname, query, renderOpts); } streamResponseChunk(res, chunk) { res.originalResponse.write(chunk); // When both compression and streaming are enabled, we need to explicitly // flush the response to avoid it being buffered by gzip. if (this.compression && "flush" in res.originalResponse) { res.originalResponse.flush(); } } async imageOptimizer(req, res, paramsResult) { const { imageOptimizer } = require("./image-optimizer"); return imageOptimizer(req.originalRequest, res.originalResponse, paramsResult, this.nextConfig, (newReq, newRes, newParsedUrl)=>this.getRequestHandler()(new _node.NodeNextRequest(newReq), new _node.NodeNextResponse(newRes), newParsedUrl)); } getPagePath(pathname, locales) { return (0, _require).getPagePath(pathname, this.distDir, this._isLikeServerless, this.renderOpts.dev, locales, this.nextConfig.experimental.appDir); } async findPageComponents(pathname, query = {}, params = null, isAppDir = false) { let paths = [ // try serving a static AMP version first query.amp ? (0, _normalizePagePath).normalizePagePath(pathname) + ".amp" : null, pathname, ].filter(Boolean); if (query.__nextLocale) { paths = [ ...paths.map((path)=>`/${query.__nextLocale}${path === "/" ? "" : path}`), ...paths, ]; } for (const pagePath of paths){ try { const components = await (0, _loadComponents).loadComponents(this.distDir, pagePath, !this.renderOpts.dev && this._isLikeServerless, this.renderOpts.serverComponents, this.nextConfig.experimental.appDir); if (query.__nextLocale && typeof components.Component === "string" && !(pagePath == null ? void 0 : pagePath.startsWith(`/${query.__nextLocale}`))) { continue; } return { components, query: { ...components.getStaticProps ? { amp: query.amp, __nextDataReq: query.__nextDataReq, __nextLocale: query.__nextLocale, __nextDefaultLocale: query.__nextDefaultLocale, __flight__: query.__flight__ } : query, // For appDir params is excluded. ...(isAppDir ? {} : params) || {} } }; } catch (err) { // we should only not throw if we failed to find the page // in the pages-manifest if (!(err instanceof _utils.PageNotFoundError)) { throw err; } } } return null; } getFontManifest() { return (0, _require).requireFontManifest(this.distDir, this._isLikeServerless); } getServerComponentManifest() { if (!this.nextConfig.experimental.serverComponents) return undefined; return require((0, _path).join(this.distDir, "server", _constants.FLIGHT_MANIFEST + ".json")); } getCacheFilesystem() { return { readFile: (f)=>_fs.default.promises.readFile(f, "utf8"), readFileSync: (f)=>_fs.default.readFileSync(f, "utf8"), writeFile: (f, d)=>_fs.default.promises.writeFile(f, d, "utf8"), mkdir: (dir)=>_fs.default.promises.mkdir(dir, { recursive: true }), stat: (f)=>_fs.default.promises.stat(f) }; } normalizeReq(req) { return req instanceof _http.IncomingMessage ? new _node.NodeNextRequest(req) : req; } normalizeRes(res) { return res instanceof _http.ServerResponse ? new _node.NodeNextResponse(res) : res; } getRequestHandler() { const handler = super.getRequestHandler(); return async (req, res, parsedUrl)=>{ return handler(this.normalizeReq(req), this.normalizeRes(res), parsedUrl); }; } async render(req, res, pathname, query, parsedUrl, internal = false) { return super.render(this.normalizeReq(req), this.normalizeRes(res), pathname, query, parsedUrl, internal); } async renderToHTML(req, res, pathname, query) { return super.renderToHTML(this.normalizeReq(req), this.normalizeRes(res), pathname, query); } async renderError(err, req, res, pathname, query, setHeaders) { return super.renderError(err, this.normalizeReq(req), this.normalizeRes(res), pathname, query, setHeaders); } async renderErrorToHTML(err, req, res, pathname, query) { return super.renderErrorToHTML(err, this.normalizeReq(req), this.normalizeRes(res), pathname, query); } async render404(req, res, parsedUrl, setHeaders) { return super.render404(this.normalizeReq(req), this.normalizeRes(res), parsedUrl, setHeaders); } async serveStatic(req, res, path, parsedUrl) { if (!this.isServeableUrl(path)) { return this.render404(req, res, parsedUrl); } if (!(req.method === "GET" || req.method === "HEAD")) { res.statusCode = 405; res.setHeader("Allow", [ "GET", "HEAD" ]); return this.renderError(null, req, res, path); } try { await this.sendStatic(req, res, path); } catch (error) { if (!(0, _isError).default(error)) throw error; const err = error; if (err.code === "ENOENT" || err.statusCode === 404) { this.render404(req, res, parsedUrl); } else if (err.statusCode === 412) { res.statusCode = 412; return this.renderError(err, req, res, path); } else { throw err; } } } getStaticRoutes() { return this.hasStaticDir ? [ { // It's very important to keep this route's param optional. // (but it should support as many params as needed, separated by '/') // Otherwise this will lead to a pretty simple DOS attack. // See more: https://github.com/vercel/next.js/issues/2617 match: (0, _pathMatch).getPathMatch("/static/:path*"), name: "static catchall", fn: async (req, res, params, parsedUrl)=>{ const p = (0, _path).join(this.dir, "static", ...params.path); await this.serveStatic(req, res, p, parsedUrl); return { finished: true }; } }, ] : []; } isServeableUrl(untrustedFileUrl) { // This method mimics what the version of `send` we use does: // 1. decodeURIComponent: // https://github.com/pillarjs/send/blob/0.17.1/index.js#L989 // https://github.com/pillarjs/send/blob/0.17.1/index.js#L518-L522 // 2. resolve: // https://github.com/pillarjs/send/blob/de073ed3237ade9ff71c61673a34474b30e5d45b/index.js#L561 let decodedUntrustedFilePath; try { // (1) Decode the URL so we have the proper file name decodedUntrustedFilePath = decodeURIComponent(untrustedFileUrl); } catch { return false; } // (2) Resolve "up paths" to determine real request const untrustedFilePath = (0, _path).resolve(decodedUntrustedFilePath); // don't allow null bytes anywhere in the file path if (untrustedFilePath.indexOf("\0") !== -1) { return false; } // Check if .next/static, static and public are in the path. // If not the path is not available. if ((untrustedFilePath.startsWith((0, _path).join(this.distDir, "static") + _path.sep) || untrustedFilePath.startsWith((0, _path).join(this.dir, "static") + _path.sep) || untrustedFilePath.startsWith((0, _path).join(this.dir, "public") + _path.sep)) === false) { return false; } // Check against the real filesystem paths const filesystemUrls = this.getFilesystemPaths(); const resolved = (0, _path).relative(this.dir, untrustedFilePath); return filesystemUrls.has(resolved); } generateRewrites({ restrictedRedirectPaths }) { let beforeFiles = []; let afterFiles = []; let fallback = []; if (!this.minimalMode) { const buildRewrite = (rewrite, check = true)=>{ const rewriteRoute = (0, _serverRouteUtils).getCustomRoute({ type: "rewrite", rule: rewrite, restrictedRedirectPaths }); return { ...rewriteRoute, check, type: rewriteRoute.type, name: `Rewrite route ${rewriteRoute.source}`, match: rewriteRoute.match, matchesBasePath: true, matchesLocale: true, matchesLocaleAPIRoutes: true, matchesTrailingSlash: true, fn: async (req, res, params, parsedUrl, upgradeHead)=>{ const { newUrl , parsedDestination } = (0, _prepareDestination).prepareDestination({ appendParamsToQuery: true, destination: rewriteRoute.destination, params: params, query: parsedUrl.query }); // external rewrite, proxy it if (parsedDestination.protocol) { return this.proxyRequest(req, res, parsedDestination, upgradeHead); } (0, _requestMeta).addRequestMeta(req, "_nextRewroteUrl", newUrl); (0, _requestMeta).addRequestMeta(req, "_nextDidRewrite", newUrl !== req.url); return { finished: false, pathname: newUrl, query: parsedDestination.query }; } }; }; if (Array.isArray(this.customRoutes.rewrites)) { afterFiles = this.customRoutes.rewrites.map((r)=>buildRewrite(r)); } else { beforeFiles = this.customRoutes.rewrites.beforeFiles.map((r)=>buildRewrite(r, false)); afterFiles = this.customRoutes.rewrites.afterFiles.map((r)=>buildRewrite(r)); fallback = this.customRoutes.rewrites.fallback.map((r)=>buildRewrite(r)); } } return { beforeFiles, afterFiles, fallback }; } getMiddlewareManifest() { if (this.minimalMode) return null; const manifest = require((0, _path).join(this.serverDistDir, _constants.MIDDLEWARE_MANIFEST)); return manifest; } /** * Return a list of middleware routing items. This method exists to be later * overridden by the development server in order to use a different source * to get the list. */ getMiddleware() { var ref; const manifest = this.getMiddlewareManifest(); const rootMiddleware = manifest == null ? void 0 : (ref = manifest.middleware) == null ? void 0 : ref["/"]; if (!rootMiddleware) { return; } return { match: getMiddlewareMatcher(rootMiddleware), page: "/" }; } getEdgeFunctions() { const manifest = this.getMiddlewareManifest(); if (!manifest) { return []; } return Object.keys(manifest.functions).map((page)=>({ match: getMiddlewareMatcher(manifest.functions[page]), page })); } getEdgeRoutes() { const edgeFunctions = this.getEdgeFunctions(); const middleware = this.getMiddleware(); return edgeFunctions.concat(middleware ? [ middleware ] : []); } /** * Get information for the edge function located in the provided page * folder. If the edge function info can't be found it will throw * an error. */ getEdgeFunctionInfo(params) { const manifest = require((0, _path).join(this.serverDistDir, _constants.MIDDLEWARE_MANIFEST)); let foundPage; try { foundPage = (0, _denormalizePagePath).denormalizePagePath((0, _normalizePagePath).normalizePagePath(params.page)); } catch (err) { return null; } let pageInfo = params.middleware ? manifest.middleware[foundPage] : manifest.functions[foundPage]; if (!pageInfo) { if (!params.middleware) { throw new _utils.PageNotFoundError(foundPage); } return null; } var _env1, _wasm, _assets; return { name: pageInfo.name, paths: pageInfo.files.map((file)=>(0, _path).join(this.distDir, file)), env: (_env1 = pageInfo.env) != null ? _env1 : [], wasm: ((_wasm = pageInfo.wasm) != null ? _wasm : []).map((binding)=>({ ...binding, filePath: (0, _path).join(this.distDir, binding.filePath) })), assets: ((_assets = pageInfo.assets) != null ? _assets : []).map((binding)=>{ return { ...binding, filePath: (0, _path).join(this.distDir, binding.filePath) }; }) }; } /** * Checks if a middleware exists. This method is useful for the development * server where we need to check the filesystem. Here we just check the * middleware manifest. */ async hasMiddleware(pathname) { const info = this.getEdgeFunctionInfo({ page: pathname, middleware: true }); return Boolean(info && info.paths.length > 0); } /** * A placeholder for a function to be defined in the development server. * It will make sure that the root middleware or an edge function has been compiled * so that we can run it. */ async ensureMiddleware() {} async ensureEdgeFunction(_pathname) {} /** * This method gets all middleware matchers and execute them when the request * matches. It will make sure that each middleware exists and is compiled and * ready to be invoked. The development server will decorate it to add warns * and errors with rich traces. */ async runMiddleware(params) { // Middleware is skipped for on-demand revalidate requests if ((0, _apiUtils).checkIsManualRevalidate(params.request, this.renderOpts.previewProps).isManualRevalidate) { return { finished: false }; } const normalizedPathname = (0, _removeTrailingSlash).removeTrailingSlash(params.parsed.pathname || ""); // For middleware to "fetch" we must always provide an absolute URL const query = (0, _querystring).urlQueryToSearchParams(params.parsed.query).toString(); const locale = params.parsed.query.__nextLocale; const url = `${(0, _requestMeta).getRequestMeta(params.request, "_protocol")}://${this.hostname}:${this.port}${locale ? `/${locale}` : ""}${params.parsed.pathname}${query ? `?${query}` : ""}`; if (!url.startsWith("http")) { throw new Error("To use middleware you must provide a `hostname` and `port` to the Next.js Server"); } const page = {}; if (await this.hasPage(normalizedPathname)) { page.name = params.parsedUrl.pathname; } else if (this.dynamicRoutes) { for (const dynamicRoute of this.dynamicRoutes){ const matchParams = dynamicRoute.match(normalizedPathname); if (matchParams) { page.name = dynamicRoute.page; page.params = matchParams; break; } } } const allHeaders = new Headers(); let result = null; const method = (params.request.method || "GET").toUpperCase(); const middleware = this.getMiddleware(); if (!middleware) { return { finished: false }; } if (!await this.hasMiddleware(middleware.page)) { console.warn(`The Edge Function for ${middleware.page} was not found`); return { finished: false }; } if (middleware && middleware.match(normalizedPathname)) { await this.ensureMiddleware(); const middlewareInfo = this.getEdgeFunctionInfo({ page: middleware.page, middleware: true }); if (!middlewareInfo) { throw new _utils.MiddlewareNotFoundError(); } result = await (0, _sandbox).run({ distDir: this.distDir, name: middlewareInfo.name, paths: middlewareInfo.paths, env: middlewareInfo.env, edgeFunctionEntry: middlewareInfo, request: { headers: params.request.headers, method, nextConfig: { basePath: this.nextConfig.basePath, i18n: this.nextConfig.i18n, trailingSlash: this.nextConfig.trailingSlash }, url: url, page: page, body: (0, _requestMeta).getRequestMeta(params.request, "__NEXT_CLONABLE_BODY") }, useCache: !this.nextConfig.experimental.runtime, onWarning: params.onWarning }); for (let [key, value] of result.response.headers){ if (key !== "x-middleware-next") { allHeaders.append(key, value); } } if (!this.renderOpts.dev) { result.waitUntil.catch((error)=>{ console.error(`Uncaught: middleware waitUntil errored`, error); }); } } if (!result) { this.render404(params.request, params.response, params.parsed); return { finished: true }; } else { for (let [key, value] of allHeaders){ result.response.headers.set(key, value); } } return result; } generateCatchAllMiddlewareRoute(devReady) { if (this.minimalMode) return []; const edgeCatchAllRoute = { match: (0, _pathMatch).getPathMatch("/:path*"), type: "route", name: "edge functions catchall", fn: async (req, res, _params, parsed)=>{ const edgeFunctions = this.getEdgeFunctions(); if (!edgeFunctions.length) return { finished: false }; const { query , pathname } = parsed; const normalizedPathname = (0, _removeTrailingSlash).removeTrailingSlash(pathname || ""); let page = normalizedPathname; let params = undefined; let pageFound = !(0, _utils2).isDynamicRoute(page); if (this.dynamicRoutes) { for (const dynamicRoute of this.dynamicRoutes){ params = dynamicRoute.match(normalizedPathname) || undefined; if (params) { page = dynamicRoute.page; pageFound = true; break; } } } if (!pageFound) { return { finished: false }; } const edgeSSRResult = await this.runEdgeFunction({ req, res, query, params, page }); return { finished: !!edgeSSRResult }; } }; const middlewareCatchAllRoute = { match: (0, _pathMatch).getPathMatch("/:path*"), matchesBasePath: true, matchesLocale: true, type: "route", name: "middleware catchall", fn: async (req, res, _params, parsed)=>{ const middleware = this.getMiddleware(); if (!middleware) { return { finished: false }; } const initUrl = (0, _requestMeta).getRequestMeta(req, "__NEXT_INIT_URL"); const parsedUrl = (0, _parseUrl).parseUrl(initUrl); const pathnameInfo = (0, _getNextPathnameInfo).getNextPathnameInfo(parsedUrl.pathname, { nextConfig: this.nextConfig }); parsedUrl.pathname = pathnameInfo.pathname; const normalizedPathname = (0, _removeTrailingSlash).removeTrailingSlash(parsed.pathname || ""); if (!middleware.match(normalizedPathname)) { return { finished: false }; } let result; try { result = await this.runMiddleware({ request: req, response: res, parsedUrl: parsedUrl, parsed: parsed }); } catch (err) { if ((0, _isError).default(err) && err.code === "ENOENT") { await this.render404(req, res, parsed); return { finished: true }; } if (err instanceof _utils.DecodeError) { res.statusCode = 400; this.renderError(err, req, res, parsed.pathname || ""); return { finished: true }; } const error = (0, _isError).getProperError(err); console.error(error); res.statusCode = 500; this.renderError(error, req, res, parsed.pathname || ""); return { finished: true }; } if ("finished" in result) { return result; } if (result.response.headers.has("x-middleware-rewrite")) { const value = result.response.headers.get("x-middleware-rewrite"); const rel = (0, _relativizeUrl).relativizeURL(value, initUrl); result.response.headers.set("x-middleware-rewrite", rel); } if (result.response.headers.has("Location")) { const value = result.response.headers.get("Location"); const rel = (0, _relativizeUrl).relativizeURL(value, initUrl); result.response.headers.set("Location", rel); } if (!result.response.headers.has("x-middleware-rewrite") && !result.response.headers.has("x-middleware-next") && !result.response.headers.has("Location")) { result.response.headers.set("x-middleware-refresh", "1"); } result.response.headers.delete("x-middleware-next"); for (const [key, value] of Object.entries((0, _utils1).toNodeHeaders(result.response.headers))){ if ([ "x-middleware-rewrite", "x-middleware-redirect", "x-middleware-refresh", ].includes(key)) { continue; } if (key !== "content-encoding" && value !== undefined) { res.setHeader(key, value); } } res.statusCode = result.response.status; res.statusMessage = result.response.statusText; const location = result.response.headers.get("Location"); if (location) { res.statusCode = result.response.status; if (res.statusCode === 308) { res.setHeader("Refresh", `0;url=${location}`); } res.body(location).send(); return { finished: true }; } if (result.response.headers.has("x-middleware-rewrite")) { const rewritePath = result.response.headers.get("x-middleware-rewrite"); const parsedDestination = (0, _parseUrl).parseUrl(rewritePath); const newUrl = parsedDestination.pathname; if (parsedDestination.protocol && (parsedDestination.port ? `${parsedDestination.hostname}:${parsedDestination.port}` : parsedDestination.hostname) !== req.headers.host) { return this.proxyRequest(req, res, parsedDestination); } if (this.nextConfig.i18n) { const localePathResult = (0, _normalizeLocalePath).normalizeLocalePath(newUrl, this.nextConfig.i18n.locales); if (localePathResult.detectedLocale) { parsedDestination.query.__nextLocale = localePathResult.detectedLocale; } } (0, _requestMeta).addRequestMeta(req, "_nextRewroteUrl", newUrl); (0, _requestMeta).addRequestMeta(req, "_nextDidRewrite", newUrl !== req.url); return { finished: false, pathname: newUrl, query: parsedDestination.query }; } if (result.response.headers.has("x-middleware-refresh")) { res.statusCode = result.response.status; for await (const chunk of result.response.body || []){ this.streamResponseChunk(res, chunk); } res.send(); return { finished: true }; } return { finished: false }; } }; const routes = []; if (!this.renderOpts.dev || devReady) { if (this.getMiddleware()) routes[0] = middlewareCatchAllRoute; if (this.getEdgeFunctions().length) routes[1] = edgeCatchAllRoute; } return routes; } getPrerenderManifest() { if (this._cachedPreviewManifest) { return this._cachedPreviewManifest; } const manifest = require((0, _path).join(this.distDir, _constants.PRERENDER_MANIFEST)); return this._cachedPreviewManifest = manifest; } getRoutesManifest() { return require((0, _path).join(this.distDir, _constants.ROUTES_MANIFEST)); } attachRequestMeta(req, parsedUrl) { var ref, ref1; const protocol = ((ref1 = (ref = req.originalRequest) == null ? void 0 : ref.socket) == null ? void 0 : ref1.encrypted) ? "https" : "http"; // When there are hostname and port we build an absolute URL const initUrl = this.hostname && this.port ? `${protocol}://${this.hostname}:${this.port}${req.url}` : req.url; (0, _requestMeta).addRequestMeta(req, "__NEXT_INIT_URL", initUrl); (0, _requestMeta).addRequestMeta(req, "__NEXT_INIT_QUERY", { ...parsedUrl.query }); (0, _requestMeta).addRequestMeta(req, "_protocol", protocol); (0, _requestMeta).addRequestMeta(req, "__NEXT_CLONABLE_BODY", (0, _bodyStreams).getClonableBody(req.body)); } async runEdgeFunction(params) { let middlewareInfo; try { await this.ensureEdgeFunction(params.page); middlewareInfo = this.getEdgeFunctionInfo({ page: params.page, middleware: false }); } catch { return null; } if (!middlewareInfo) { return null; } // For middleware to "fetch" we must always provide an absolute URL const url = (0, _requestMeta).getRequestMeta(params.req, "__NEXT_INIT_URL"); if (!url.startsWith("http")) { throw new Error("To use middleware you must provide a `hostname` and `port` to the Next.js Server"); } const result = await (0, _sandbox).run({ distDir: this.distDir, name: middlewareInfo.name, paths: middlewareInfo.paths, env: middlewareInfo.env, edgeFunctionEntry: middlewareInfo, request: { headers: params.req.headers, method: params.req.method, nextConfig: { basePath: this.nextConfig.basePath, i18n: this.nextConfig.i18n, trailingSlash: this.nextConfig.trailingSlash }, url, page: { name: params.page, ...params.params && { params: params.params } }, body: (0, _requestMeta).g