UNPKG

next

Version:

The React Framework

1,083 lines • 84.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _compression = _interopRequireDefault(require("next/dist/compiled/compression")); var _fs = _interopRequireDefault(require("fs")); var _httpProxy = _interopRequireDefault(require("next/dist/compiled/http-proxy")); var _path = require("path"); var _querystring = require("querystring"); var _url = require("url"); var _loadCustomRoutes = require("../lib/load-custom-routes"); var _constants = require("../shared/lib/constants"); var _utils = require("../shared/lib/router/utils"); var envConfig = _interopRequireWildcard(require("../shared/lib/runtime-config")); var _utils1 = require("../shared/lib/utils"); var _apiUtils = require("./api-utils"); var _config = require("./config"); var _pathMatch = _interopRequireDefault(require("../shared/lib/router/utils/path-match")); var _recursiveReaddirSync = require("./lib/recursive-readdir-sync"); var _loadComponents = require("./load-components"); var _normalizePagePath = require("./normalize-page-path"); var _render = require("./render"); var _require = require("./require"); var _router = _interopRequireWildcard(require("./router")); var _prepareDestination = _interopRequireWildcard(require("../shared/lib/router/utils/prepare-destination")); var _sendPayload = require("./send-payload"); var _serveStatic = require("./serve-static"); var _incrementalCache = require("./incremental-cache"); var _utils2 = require("./utils"); var _renderResult = _interopRequireDefault(require("./render-result")); var _env = require("@next/env"); require("./node-polyfill-fetch"); var _normalizeTrailingSlash = require("../client/normalize-trailing-slash"); var _getRouteFromAssetPath = _interopRequireDefault(require("../shared/lib/router/utils/get-route-from-asset-path")); var _denormalizePagePath = require("./denormalize-page-path"); var _normalizeLocalePath = require("../shared/lib/i18n/normalize-locale-path"); var Log = _interopRequireWildcard(require("../build/output/log")); var _detectDomainLocale = require("../shared/lib/i18n/detect-domain-locale"); var _escapePathDelimiters = _interopRequireDefault(require("../shared/lib/router/utils/escape-path-delimiters")); var _utils3 = require("../build/webpack/loaders/next-serverless-loader/utils"); var _responseCache = _interopRequireDefault(require("./response-cache")); var _parseNextUrl = require("../shared/lib/router/utils/parse-next-url"); var _isError = _interopRequireDefault(require("../lib/is-error")); var _parseUrl = require("../shared/lib/router/utils/parse-url"); var _constants1 = require("../lib/constants"); var _response = require("./web/spec-extension/response"); var _sandbox = require("./web/sandbox"); var _utils4 = require("./web/utils"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = { }; if (obj != null) { for(var key in obj){ if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : { }; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } const getCustomRouteMatcher = (0, _pathMatch).default(true); class Server { constructor({ dir ='.' , quiet =false , conf , dev =false , minimalMode =false , customServer =true }){ var ref, ref1, ref2; this.middlewareBetaWarning = (0, _utils1).execOnce(()=>{ Log.warn(`using beta Middleware (not covered by semver) - https://nextjs.org/docs/messages/beta-middleware`); }); this.customErrorNo404Warn = (0, _utils1).execOnce(()=>{ Log.warn(`You have added a custom /_error page without a custom /404 page. This prevents the 404 page from being auto statically optimized.\nSee here for info: https://nextjs.org/docs/messages/custom-error-no-custom-404`); }); this._validFilesystemPathSet = null; this.dir = (0, _path).resolve(dir); this.quiet = quiet; (0, _env).loadEnvConfig(this.dir, dev, Log); this.nextConfig = conf; this.distDir = (0, _path).join(this.dir, this.nextConfig.distDir); this.publicDir = (0, _path).join(this.dir, _constants.CLIENT_PUBLIC_FILES_PATH); this.hasStaticDir = !minimalMode && _fs.default.existsSync((0, _path).join(this.dir, 'static')); // Only serverRuntimeConfig needs the default // publicRuntimeConfig gets it's default in client/index.js const { serverRuntimeConfig ={ } , publicRuntimeConfig , assetPrefix , generateEtags , compress , } = this.nextConfig; this.buildId = this.readBuildId(); this.minimalMode = minimalMode; this.renderOpts = { poweredByHeader: this.nextConfig.poweredByHeader, canonicalBase: this.nextConfig.amp.canonicalBase || '', buildId: this.buildId, generateEtags, previewProps: this.getPreviewProps(), customServer: customServer === true ? true : undefined, ampOptimizerConfig: (ref = this.nextConfig.experimental.amp) === null || ref === void 0 ? void 0 : ref.optimizer, basePath: this.nextConfig.basePath, images: JSON.stringify(this.nextConfig.images), optimizeFonts: !!this.nextConfig.optimizeFonts && !dev, fontManifest: this.nextConfig.optimizeFonts && !dev ? (0, _require).requireFontManifest(this.distDir, this._isLikeServerless) : null, optimizeImages: !!this.nextConfig.experimental.optimizeImages, optimizeCss: this.nextConfig.experimental.optimizeCss, disableOptimizedLoading: this.nextConfig.experimental.disableOptimizedLoading, domainLocales: (ref1 = this.nextConfig.i18n) === null || ref1 === void 0 ? void 0 : ref1.domains, distDir: this.distDir, concurrentFeatures: this.nextConfig.experimental.concurrentFeatures }; // Only the `publicRuntimeConfig` key is exposed to the client side // It'll be rendered as part of __NEXT_DATA__ on the client side if (Object.keys(publicRuntimeConfig).length > 0) { this.renderOpts.runtimeConfig = publicRuntimeConfig; } if (compress && this.nextConfig.target === 'server') { this.compression = (0, _compression).default(); } // Initialize next/config with the environment configuration envConfig.setConfig({ serverRuntimeConfig, publicRuntimeConfig }); this.serverBuildDir = (0, _path).join(this.distDir, this._isLikeServerless ? _constants.SERVERLESS_DIRECTORY : _constants.SERVER_DIRECTORY); const pagesManifestPath = (0, _path).join(this.serverBuildDir, _constants.PAGES_MANIFEST); const middlewareManifestPath = (0, _path).join((0, _path).join(this.distDir, _constants.SERVER_DIRECTORY), _constants.MIDDLEWARE_MANIFEST); if (!dev) { this.pagesManifest = require(pagesManifestPath); if (!this.minimalMode) { this.middlewareManifest = require(middlewareManifestPath); } } this.customRoutes = this.getCustomRoutes(); this.router = new _router.default(this.generateRoutes()); this.setAssetPrefix(assetPrefix); this.incrementalCache = new _incrementalCache.IncrementalCache({ dev, distDir: this.distDir, pagesDir: (0, _path).join(this.distDir, this._isLikeServerless ? _constants.SERVERLESS_DIRECTORY : _constants.SERVER_DIRECTORY, 'pages'), locales: (ref2 = this.nextConfig.i18n) === null || ref2 === void 0 ? void 0 : ref2.locales, max: this.nextConfig.experimental.isrMemoryCacheSize, flushToDisk: !minimalMode && this.nextConfig.experimental.isrFlushToDisk }); this.responseCache = new _responseCache.default(this.incrementalCache); /** * 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_IMAGES`. * TODO(atcastle@): Remove this when experimental.optimizeImages are being cleaned up. */ if (this.renderOpts.optimizeFonts) { process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(true); } if (this.renderOpts.optimizeImages) { process.env.__NEXT_OPTIMIZE_IMAGES = JSON.stringify(true); } if (this.renderOpts.optimizeCss) { process.env.__NEXT_OPTIMIZE_CSS = JSON.stringify(true); } } logError(err) { if (this.quiet) return; console.error(err); } async handleRequest(req, res, parsedUrl) { var ref23, ref1, ref2, ref3, ref4, ref5; const urlParts = (req.url || '').split('?'); const urlNoQuery = urlParts[0]; if (urlNoQuery === null || urlNoQuery === void 0 ? void 0 : urlNoQuery.match(/(\\|\/\/)/)) { const cleanUrl = (0, _utils1).normalizeRepeatedSlashes(req.url); res.setHeader('Location', cleanUrl); res.setHeader('Refresh', `0;url=${cleanUrl}`); res.statusCode = 308; res.end(cleanUrl); return; } (0, _apiUtils).setLazyProp({ req: req }, 'cookies', (0, _apiUtils).getCookieParser(req.headers)); // Parse url if parsedUrl not provided if (!parsedUrl || typeof parsedUrl !== 'object') { const url = req.url; parsedUrl = (0, _url).parse(url, true); } const { basePath , i18n } = this.nextConfig; // Parse the querystring ourselves if the user doesn't handle querystring parsing if (typeof parsedUrl.query === 'string') { parsedUrl.query = (0, _querystring).parse(parsedUrl.query); } req.__NEXT_INIT_URL = req.url; req.__NEXT_INIT_QUERY = Object.assign({ }, parsedUrl.query); const url = (0, _parseNextUrl).parseNextUrl({ headers: req.headers, nextConfig: this.nextConfig, url: (ref23 = req.url) === null || ref23 === void 0 ? void 0 : ref23.replace(/^\/+/, '/') }); if (url.basePath) { req._nextHadBasePath = true; req.url = req.url.replace(basePath, '') || '/'; } if (this.minimalMode && req.headers['x-matched-path'] && typeof req.headers['x-matched-path'] === 'string') { var ref, ref21; const reqUrlIsDataUrl = (ref = req.url) === null || ref === void 0 ? void 0 : ref.includes('/_next/data'); const matchedPathIsDataUrl = (ref21 = req.headers['x-matched-path']) === null || ref21 === void 0 ? void 0 : ref21.includes('/_next/data'); const isDataUrl = reqUrlIsDataUrl || matchedPathIsDataUrl; let parsedPath = (0, _url).parse(isDataUrl ? req.url : req.headers['x-matched-path'], true); const { pathname , query } = parsedPath; let matchedPathname = pathname; let matchedPathnameNoExt = isDataUrl ? matchedPathname.replace(/\.json$/, '') : matchedPathname; if (i18n) { const localePathResult = (0, _normalizeLocalePath).normalizeLocalePath(matchedPathname || '/', i18n.locales); if (localePathResult.detectedLocale) { parsedUrl.query.__nextLocale = localePathResult.detectedLocale; } } if (isDataUrl) { matchedPathname = (0, _denormalizePagePath).denormalizePagePath(matchedPathname); matchedPathnameNoExt = (0, _denormalizePagePath).denormalizePagePath(matchedPathnameNoExt); } const pageIsDynamic = (0, _utils).isDynamicRoute(matchedPathnameNoExt); const combinedRewrites = []; combinedRewrites.push(...this.customRoutes.rewrites.beforeFiles); combinedRewrites.push(...this.customRoutes.rewrites.afterFiles); combinedRewrites.push(...this.customRoutes.rewrites.fallback); const utils = (0, _utils3).getUtils({ pageIsDynamic, page: matchedPathnameNoExt, i18n: this.nextConfig.i18n, basePath: this.nextConfig.basePath, rewrites: combinedRewrites }); utils.handleRewrites(req, parsedUrl); // interpolate dynamic params and normalize URL if needed if (pageIsDynamic) { let params = { }; Object.assign(parsedUrl.query, query); const paramsResult = utils.normalizeDynamicRouteParams(parsedUrl.query); if (paramsResult.hasValidParams) { params = paramsResult.params; } else if (req.headers['x-now-route-matches']) { const opts = { }; params = utils.getParamsFromRouteMatches(req, opts, parsedUrl.query.__nextLocale || ''); if (opts.locale) { parsedUrl.query.__nextLocale = opts.locale; } } else { params = utils.dynamicRouteMatcher(matchedPathnameNoExt); } if (params) { params = utils.normalizeDynamicRouteParams(params).params; matchedPathname = utils.interpolateDynamicPath(matchedPathname, params); req.url = utils.interpolateDynamicPath(req.url, params); } if (reqUrlIsDataUrl && matchedPathIsDataUrl) { req.url = (0, _url).format({ ...parsedPath, pathname: matchedPathname }); } Object.assign(parsedUrl.query, params); utils.normalizeVercelUrl(req, true); } parsedUrl.pathname = `${basePath || ''}${matchedPathname === '/' && basePath ? '' : matchedPathname}`; } req.__nextHadTrailingSlash = (ref1 = url.locale) === null || ref1 === void 0 ? void 0 : ref1.trailingSlash; if ((ref2 = url.locale) === null || ref2 === void 0 ? void 0 : ref2.domain) { req.__nextIsLocaleDomain = true; } if ((ref3 = url.locale) === null || ref3 === void 0 ? void 0 : ref3.path.detectedLocale) { req.url = (0, _url).format(url); req.__nextStrippedLocale = true; if (url.pathname === '/api' || url.pathname.startsWith('/api/')) { return this.render404(req, res, parsedUrl); } } if (!this.minimalMode || !parsedUrl.query.__nextLocale) { var ref; if (url === null || url === void 0 ? void 0 : (ref = url.locale) === null || ref === void 0 ? void 0 : ref.locale) { parsedUrl.query.__nextLocale = url.locale.locale; } } if (url === null || url === void 0 ? void 0 : (ref4 = url.locale) === null || ref4 === void 0 ? void 0 : ref4.defaultLocale) { parsedUrl.query.__nextDefaultLocale = url.locale.defaultLocale; } if ((ref5 = url.locale) === null || ref5 === void 0 ? void 0 : ref5.redirect) { res.setHeader('Location', url.locale.redirect); res.statusCode = _constants.TEMPORARY_REDIRECT_STATUS; res.end(); return; } res.statusCode = 200; try { return await this.run(req, res, parsedUrl); } catch (err) { if (this.minimalMode || this.renderOpts.dev) { throw err; } this.logError((0, _isError).default(err) ? err : new Error(err + '')); res.statusCode = 500; res.end('Internal Server Error'); } } getRequestHandler() { return this.handleRequest.bind(this); } setAssetPrefix(prefix) { this.renderOpts.assetPrefix = prefix ? prefix.replace(/\/$/, '') : ''; } // Backwards compatibility async prepare() { } // Backwards compatibility async close() { } setImmutableAssetCacheControl(res) { res.setHeader('Cache-Control', 'public, max-age=31536000, immutable'); } getCustomRoutes() { const customRoutes = require((0, _path).join(this.distDir, _constants.ROUTES_MANIFEST)); let rewrites; // rewrites can be stored as an array when an array is // returned in next.config.js so massage them into // the expected object format if (Array.isArray(customRoutes.rewrites)) { rewrites = { beforeFiles: [], afterFiles: customRoutes.rewrites, fallback: [] }; } else { rewrites = customRoutes.rewrites; } return Object.assign(customRoutes, { rewrites }); } getPrerenderManifest() { if (this._cachedPreviewManifest) { return this._cachedPreviewManifest; } const manifest = require((0, _path).join(this.distDir, _constants.PRERENDER_MANIFEST)); return this._cachedPreviewManifest = manifest; } getPreviewProps() { return this.getPrerenderManifest().preview; } getMiddleware() { var ref; const middleware = ((ref = this.middlewareManifest) === null || ref === void 0 ? void 0 : ref.middleware) || { }; return Object.keys(middleware).map((page)=>({ match: (0, _utils).getRouteMatcher((0, _utils).getMiddlewareRegex(page, _constants1.MIDDLEWARE_ROUTE.test(middleware[page].name))), page }) ); } async hasMiddleware(pathname, _isSSR) { try { return (0, _require).getMiddlewareInfo({ dev: this.renderOpts.dev, distDir: this.distDir, page: pathname, serverless: this._isLikeServerless }).paths.length > 0; } catch (_) { } return false; } async ensureMiddleware(_pathname, _isSSR) { } async runMiddleware(params) { this.middlewareBetaWarning(); const page = { }; if (await this.hasPage(params.parsedUrl.pathname)) { page.name = params.parsedUrl.pathname; } else if (this.dynamicRoutes) { for (const dynamicRoute of this.dynamicRoutes){ const matchParams = dynamicRoute.match(params.parsedUrl.pathname); if (matchParams) { page.name = dynamicRoute.page; page.params = matchParams; break; } } } const subreq = params.request.headers[`x-middleware-subrequest`]; const subrequests = typeof subreq === 'string' ? subreq.split(':') : []; const allHeaders = new Headers(); let result = null; for (const middleware of this.middleware || []){ if (middleware.match(params.parsedUrl.pathname)) { if (!await this.hasMiddleware(middleware.page, middleware.ssr)) { console.warn(`The Edge Function for ${middleware.page} was not found`); continue; } await this.ensureMiddleware(middleware.page, middleware.ssr); const middlewareInfo = (0, _require).getMiddlewareInfo({ dev: this.renderOpts.dev, distDir: this.distDir, page: middleware.page, serverless: this._isLikeServerless }); if (subrequests.includes(middlewareInfo.name)) { result = { response: _response.NextResponse.next(), waitUntil: Promise.resolve() }; continue; } result = await (0, _sandbox).run({ name: middlewareInfo.name, paths: middlewareInfo.paths, request: { headers: params.request.headers, method: params.request.method || 'GET', nextConfig: { basePath: this.nextConfig.basePath, i18n: this.nextConfig.i18n, trailingSlash: this.nextConfig.trailingSlash }, url: params.request.__NEXT_INIT_URL, page: page }, ssr: !!this.nextConfig.experimental.concurrentFeatures }); for (let [key, value] of result.response.headers){ allHeaders.append(key, value); } if (!this.renderOpts.dev) { result.waitUntil.catch((error)=>{ console.error(`Uncaught: middleware waitUntil errored`, error); }); } if (!result.response.headers.has('x-middleware-next')) { break; } } } if (!result) { this.render404(params.request, params.response, params.parsed); } else { for (let [key, value] of allHeaders){ result.response.headers.set(key, value); } } return result; } generateRoutes() { var ref; const server = this; const publicRoutes = _fs.default.existsSync(this.publicDir) ? this.generatePublicRoutes() : []; const staticFilesRoute = 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, _router).route('/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 }; } }, ] : []; const fsRoutes = [ { match: (0, _router).route('/_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 }; } }, { match: (0, _router).route('/_next/data/:path*'), type: 'route', name: '_next/data catchall', fn: async (req, res, params, _parsedUrl)=>{ // Make sure to 404 for /_next/data/ itself and // we also want to 404 if the buildId isn't correct if (!params.path || params.path[0] !== this.buildId) { await this.render404(req, res, _parsedUrl); return { finished: true }; } // remove buildId from URL params.path.shift(); const lastParam = params.path[params.path.length - 1]; // show 404 if it doesn't end with .json if (typeof lastParam !== 'string' || !lastParam.endsWith('.json')) { await this.render404(req, res, _parsedUrl); return { finished: true }; } // re-create page's pathname let pathname = `/${params.path.join('/')}`; pathname = (0, _getRouteFromAssetPath).default(pathname, '.json'); const { i18n } = this.nextConfig; if (i18n) { const { host } = (req === null || req === void 0 ? void 0 : req.headers) || { }; // remove port from host and remove port if present const hostname = host === null || host === void 0 ? void 0 : host.split(':')[0].toLowerCase(); const localePathResult = (0, _normalizeLocalePath).normalizeLocalePath(pathname, i18n.locales); const { defaultLocale } = (0, _detectDomainLocale).detectDomainLocale(i18n.domains, hostname) || { }; let detectedLocale = ''; if (localePathResult.detectedLocale) { pathname = localePathResult.pathname; detectedLocale = localePathResult.detectedLocale; } _parsedUrl.query.__nextLocale = detectedLocale; _parsedUrl.query.__nextDefaultLocale = defaultLocale || i18n.defaultLocale; if (!detectedLocale) { _parsedUrl.query.__nextLocale = _parsedUrl.query.__nextDefaultLocale; await this.render404(req, res, _parsedUrl); return { finished: true }; } } const parsedUrl = (0, _url).parse(pathname, true); await this.render(req, res, pathname, { ..._parsedUrl.query, _nextDataReq: '1' }, parsedUrl); return { finished: true }; } }, { match: (0, _router).route('/_next/image'), type: 'route', name: '_next/image catchall', fn: (req, res, _params, parsedUrl)=>{ if (this.minimalMode) { res.statusCode = 400; res.end('Bad Request'); return { finished: true }; } const { imageOptimizer } = require('./image-optimizer'); return imageOptimizer(server, req, res, parsedUrl, server.nextConfig, server.distDir, this.renderOpts.dev); } }, { match: (0, _router).route('/_next/:path*'), type: 'route', name: '_next catchall', // This path is needed because `render()` does a check for `/_next` and the calls the routing again fn: async (req, res, _params, parsedUrl)=>{ await this.render404(req, res, parsedUrl); return { finished: true }; } }, ...publicRoutes, ...staticFilesRoute, ]; const restrictedRedirectPaths = [ '/_next' ].map((p)=>this.nextConfig.basePath ? `${this.nextConfig.basePath}${p}` : p ); const getCustomRoute = (r, type)=>{ const match = getCustomRouteMatcher(r.source, !r.internal ? (regex)=>(0, _loadCustomRoutes).modifyRouteRegex(regex, type === 'redirect' ? restrictedRedirectPaths : undefined) : undefined); return { ...r, type, match, name: type, fn: async (_req, _res, _params, _parsedUrl)=>({ finished: false }) }; }; // Headers come very first const headers = this.minimalMode ? [] : this.customRoutes.headers.map((r)=>{ const headerRoute = getCustomRoute(r, 'header'); return { match: headerRoute.match, has: headerRoute.has, type: headerRoute.type, name: `${headerRoute.type} ${headerRoute.source} header route`, fn: async (_req, res, params, _parsedUrl)=>{ const hasParams = Object.keys(params).length > 0; for (const header of headerRoute.headers){ let { key , value } = header; if (hasParams) { key = (0, _prepareDestination).compileNonPath(key, params); value = (0, _prepareDestination).compileNonPath(value, params); } res.setHeader(key, value); } return { finished: false }; } }; }); // since initial query values are decoded by querystring.parse // we need to re-encode them here but still allow passing through // values from rewrites/redirects const stringifyQuery = (req, query)=>{ const initialQueryValues = Object.values(req.__NEXT_INIT_QUERY); return (0, _querystring).stringify(query, undefined, undefined, { encodeURIComponent (value) { if (initialQueryValues.some((val)=>val === value )) { return encodeURIComponent(value); } return value; } }); }; const proxyRequest = async (req, res, parsedUrl)=>{ const { query } = parsedUrl; delete parsedUrl.query; parsedUrl.search = stringifyQuery(req, query); const target = (0, _url).format(parsedUrl); const proxy = new _httpProxy.default({ target, changeOrigin: true, ignorePath: true, xfwd: true, proxyTimeout: 30000 }); await new Promise((proxyResolve, proxyReject)=>{ let finished = false; proxy.on('proxyReq', (proxyReq)=>{ proxyReq.on('close', ()=>{ if (!finished) { finished = true; proxyResolve(true); } }); }); proxy.on('error', (err)=>{ if (!finished) { finished = true; proxyReject(err); } }); proxy.web(req, res); }); return { finished: true }; }; const redirects = this.minimalMode ? [] : this.customRoutes.redirects.map((redirect)=>{ const redirectRoute = getCustomRoute(redirect, 'redirect'); return { internal: redirectRoute.internal, type: redirectRoute.type, match: redirectRoute.match, has: redirectRoute.has, statusCode: redirectRoute.statusCode, name: `Redirect route ${redirectRoute.source}`, fn: async (req, res, params, parsedUrl)=>{ const { parsedDestination } = (0, _prepareDestination).default(redirectRoute.destination, params, parsedUrl.query, false); const { query } = parsedDestination; delete parsedDestination.query; parsedDestination.search = stringifyQuery(req, query); let updatedDestination = (0, _url).format(parsedDestination); if (updatedDestination.startsWith('/')) { updatedDestination = (0, _utils1).normalizeRepeatedSlashes(updatedDestination); } res.setHeader('Location', updatedDestination); res.statusCode = (0, _loadCustomRoutes).getRedirectStatus(redirectRoute); // Since IE11 doesn't support the 308 header add backwards // compatibility using refresh header if (res.statusCode === 308) { res.setHeader('Refresh', `0;url=${updatedDestination}`); } res.end(updatedDestination); return { finished: true }; } }; }); const buildRewrite = (rewrite, check = true)=>{ const rewriteRoute = getCustomRoute(rewrite, 'rewrite'); return { ...rewriteRoute, check, type: rewriteRoute.type, name: `Rewrite route ${rewriteRoute.source}`, match: rewriteRoute.match, fn: async (req, res, params, parsedUrl)=>{ const { newUrl , parsedDestination } = (0, _prepareDestination).default(rewriteRoute.destination, params, parsedUrl.query, true); // external rewrite, proxy it if (parsedDestination.protocol) { return proxyRequest(req, res, parsedDestination); } req._nextRewroteUrl = newUrl; req._nextDidRewrite = req._nextRewroteUrl !== req.url; return { finished: false, pathname: newUrl, query: parsedDestination.query }; } }; }; let beforeFiles = []; let afterFiles = []; let fallback = []; if (!this.minimalMode) { 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) ); } } let catchAllMiddleware; if (!this.minimalMode) { catchAllMiddleware = { match: (0, _router).route('/:path*'), type: 'route', name: 'middleware catchall', fn: async (req, res, _params, parsed)=>{ var ref; const fullUrl = req.__NEXT_INIT_URL; const parsedUrl = (0, _parseNextUrl).parseNextUrl({ url: fullUrl, headers: req.headers, nextConfig: { basePath: this.nextConfig.basePath, i18n: this.nextConfig.i18n, trailingSlash: this.nextConfig.trailingSlash } }); if (!((ref = this.middleware) === null || ref === void 0 ? void 0 : ref.some((m)=>m.match(parsedUrl.pathname) ))) { return { finished: false }; } let result = null; 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 }; } const error = (0, _isError).default(err) ? err : new Error(err + ''); console.error(error); res.statusCode = 500; this.renderError(error, req, res, parsed.pathname || ''); return { finished: true }; } if (result === null) { return { finished: true }; } 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, _utils4).toNodeHeaders(result.response.headers))){ if (key !== 'content-encoding' && value !== undefined) { res.setHeader(key, value); } } const preflight = req.method === 'HEAD' && req.headers['x-middleware-preflight']; if (preflight) { res.writeHead(200); res.end(); return { finished: true }; } 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.end(); return { finished: true }; } if (result.response.headers.has('x-middleware-rewrite')) { const rewrite = result.response.headers.get('x-middleware-rewrite'); const rewriteParsed = (0, _parseUrl).parseUrl(rewrite); if (rewriteParsed.protocol) { return proxyRequest(req, res, rewriteParsed); } req._nextRewroteUrl = rewrite; req._nextDidRewrite = req._nextRewroteUrl !== req.url; return { finished: false, pathname: rewriteParsed.pathname, query: { ...parsedUrl.query, ...rewriteParsed.query } }; } if (result.response.headers.has('x-middleware-refresh')) { res.writeHead(result.response.status); for await (const chunk of result.response.body || []){ res.write(chunk); } res.end(); return { finished: true }; } return { finished: false }; } }; } const catchAllRoute = { match: (0, _router).route('/:path*'), type: 'route', name: 'Catchall render', fn: async (req, res, _params, parsedUrl)=>{ let { pathname , query } = parsedUrl; if (!pathname) { throw new Error('pathname is undefined'); } // next.js core assumes page path without trailing slash pathname = (0, _normalizeTrailingSlash).removePathTrailingSlash(pathname); if (this.nextConfig.i18n) { var ref; const localePathResult = (0, _normalizeLocalePath).normalizeLocalePath(pathname, (ref = this.nextConfig.i18n) === null || ref === void 0 ? void 0 : ref.locales); if (localePathResult.detectedLocale) { pathname = localePathResult.pathname; parsedUrl.query.__nextLocale = localePathResult.detectedLocale; } } const bubbleNoFallback = !!query._nextBubbleNoFallback; if (pathname.match(_constants1.MIDDLEWARE_ROUTE)) { await this.render404(req, res, parsedUrl); return { finished: true }; } if (pathname === '/api' || pathname.startsWith('/api/')) { delete query._nextBubbleNoFallback; const handled = await this.handleApiRequest(req, res, pathname, query); if (handled) { return { finished: true }; } } try { await this.render(req, res, pathname, query, parsedUrl); return { finished: true }; } catch (err) { if (err instanceof NoFallbackError && bubbleNoFallback) { return { finished: false }; } throw err; } } }; const { useFileSystemPublicRoutes } = this.nextConfig; if (useFileSystemPublicRoutes) { this.dynamicRoutes = this.getDynamicRoutes(); if (!this.minimalMode) { this.middleware = this.getMiddleware(); } } return { headers, fsRoutes, rewrites: { beforeFiles, afterFiles, fallback }, redirects, catchAllRoute, catchAllMiddleware, useFileSystemPublicRoutes, dynamicRoutes: this.dynamicRoutes, basePath: this.nextConfig.basePath, pageChecker: this.hasPage.bind(this), locales: ((ref = this.nextConfig.i18n) === null || ref === void 0 ? void 0 : ref.locales) || [] }; } async getPagePath(pathname, locales) { return (0, _require).getPagePath(pathname, this.distDir, this._isLikeServerless, this.renderOpts.dev, locales); } async hasPage(pathname) { let found = false; try { var ref; found = !!await this.getPagePath(pathname, (ref = this.nextConfig.i18n) === null || ref === void 0 ? void 0 : ref.locales); } catch (_) { } return found; } async _beforeCatchAllRender(_req, _res, _params, _parsedUrl) { return false; } // Used to build API page in development async ensureApiPage(_pathname) { } /** * Resolves `API` request, in development builds on demand * @param req http request * @param res http response * @param pathname path of request */ async handleApiRequest(req, res, pathname, query) { let page = pathname; let params = false; let pageFound = await this.hasPage(page); if (!pageFound && this.dynamicRoutes) { for (const dynamicRoute of this.dynamicRoutes){ params = dynamicRoute.match(pathname); if (dynamicRoute.page.startsWith('/api') && params) { page = dynamicRoute.page; pageFound = true; break; } } } if (!pageFound) { return false; } // Make sure the page is built before getting the path // or else it won't be in the manifest yet await this.ensureApiPage(page); let builtPagePath; try { builtPagePath = await this.getPagePath(page); } catch (err) { if ((0, _isError).default(err) && err.code === 'ENOENT') { return false; } throw err; } 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') { prepareServerlessUrl(req, query); await pageModule.default(req, res); return true; } } await (0, _apiUtils).apiResolver(req, res, query, pageModule, this.renderOpts.previewProps, this.minimalMode, this.renderOpts.dev, page); return true; } generatePublicRoutes() { const publicFiles = new Set((0, _recursiveReaddirSync).recursiveReadDirSync(this.publicDir).map((p)=>encodeURI(p.replace(/\\/g, '/')) )); return [ { match: (0, _router).route('/:path*'), 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 }; } }, ]; } getDynamicRoutes() { const addedPages = new Set(); return (0, _utils).getSortedRoutes(Object.keys(this.pagesManifest).map((page)=>{ var ref; return (0, _normalizeLocalePath).normalizeLocalePath(page, (ref = this.nextConfig.i18n) === null || ref === void 0 ? void 0 : ref.locales).pathname; })).map((page)=>{ if (addedPages.has(page) || !(0, _utils).isDynamicRoute(page)) return null; addedPages.add(page); return { page, match: (0, _utils).getRouteMatcher((0, _utils).getRouteRegex(page)) }; }).filter((item)=>Boolean(item) ); } handleCompression(req, res) { if (this.compression) { this.compression(req, res, ()=>{ }); } } async run(req, res, parsedUrl) { this.handleCompression(req, r