From c5910ea4093b85ab401080b0f62e2a0bcbf944cd Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Mon, 9 Jun 2025 12:20:35 +0200 Subject: [PATCH 1/2] Disable background route preloading for Next >= 14 --- .changeset/giant-feet-share.md | 5 +++++ .../open-next/src/build/patch/patches/patchNextServer.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/giant-feet-share.md diff --git a/.changeset/giant-feet-share.md b/.changeset/giant-feet-share.md new file mode 100644 index 000000000..b8d896704 --- /dev/null +++ b/.changeset/giant-feet-share.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/aws": patch +--- + +Disable background route preloading for Next >= 14 diff --git a/packages/open-next/src/build/patch/patches/patchNextServer.ts b/packages/open-next/src/build/patch/patches/patchNextServer.ts index a843cb910..410502550 100644 --- a/packages/open-next/src/build/patch/patches/patchNextServer.ts +++ b/packages/open-next/src/build/patch/patches/patchNextServer.ts @@ -118,7 +118,7 @@ export const patchNextServer: CodePatcher = { }, // Disable Next background preloading done at creation of `NextServer` { - versions: ">=15.0.0", + versions: ">=14.0.0", field: { pathFilter, contentFilter: /this\.nextConfig\.experimental\.preloadEntriesOnStart/, From dfcdb2f3fdb79ed8cecbf8e1211f809a24a93534 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 10 Jun 2025 12:05:35 +0200 Subject: [PATCH 2/2] fixup! add test for Next 14 --- .../patch/patches/patchNextServer.test.ts | 666 +++++++++++++++++- 1 file changed, 661 insertions(+), 5 deletions(-) diff --git a/packages/tests-unit/tests/build/patch/patches/patchNextServer.test.ts b/packages/tests-unit/tests/build/patch/patches/patchNextServer.test.ts index f09d9fb31..91e207928 100644 --- a/packages/tests-unit/tests/build/patch/patches/patchNextServer.test.ts +++ b/packages/tests-unit/tests/build/patch/patches/patchNextServer.test.ts @@ -20,7 +20,7 @@ getMiddlewareManifest() { } `; -const nextServerMinimalCode = ` +const next15ServerMinimalCode = ` class NextNodeServer extends _baseserver.default { constructor(options){ var _options_conf_experimental_sri, _options_conf_experimental; @@ -424,9 +424,9 @@ getMiddlewareManifest() {return null;} `); }); - it("should disable preloading", async () => { + it("should disable preloading for Next 15", async () => { expect( - patchCode(nextServerMinimalCode, disablePreloadingRule), + patchCode(next15ServerMinimalCode, disablePreloadingRule), ).toMatchInlineSnapshot(` "class NextNodeServer extends _baseserver.default { constructor(options){ @@ -799,10 +799,666 @@ async imageOptimizer(req, res, paramsResult, previousCacheEntry) { `); }); + it("should disable preloading for Next 1", async () => { + const next14ServerMinimalCode = ` +class NextNodeServer extends _baseserver.default { + constructor(options){ + // Initialize super class + super(options); + this.handleNextImageRequest = async (req, res, parsedUrl)=>{ + if (!parsedUrl.pathname || !parsedUrl.pathname.startsWith("/_next/image")) { + return false; + } + if (this.minimalMode || this.nextConfig.output === "export" || process.env.NEXT_MINIMAL) { + res.statusCode = 400; + res.body("Bad Request").send(); + return true; + // the \`else\` branch is needed for tree-shaking + } else { + const { ImageOptimizerCache } = require("./image-optimizer"); + const imageOptimizerCache = new ImageOptimizerCache({ + distDir: this.distDir, + nextConfig: this.nextConfig + }); + const { getHash, 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" || imagesConfig.unoptimized) { + await this.render404(req, res); + return 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 true; + } + const cacheKey = ImageOptimizerCache.getCacheKey(paramsResult); + try { + var _cacheEntry_value; + const { getExtension } = require("./serve-static"); + 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: getExtension(contentType) + }, + revalidate: maxAge + }; + }, { + incrementalCache: imageOptimizerCache + }); + if ((cacheEntry == null ? void 0 : (_cacheEntry_value = cacheEntry.value) == null ? void 0 : _cacheEntry_value.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, cacheEntry.revalidate || 0, Boolean(this.renderOpts.dev)); + return true; + } catch (err) { + if (err instanceof ImageError) { + res.statusCode = err.statusCode; + res.body(err.message).send(); + return true; + } + throw err; + } + } + }; + this.handleCatchallRenderRequest = async (req, res, parsedUrl)=>{ + let { pathname, query } = parsedUrl; + if (!pathname) { + throw new Error("Invariant: pathname is undefined"); + } + // This is a catch-all route, there should be no fallbacks so mark it as + // such. + query._nextBubbleNoFallback = "1"; + try { + var _this_i18nProvider; + // next.js core assumes page path without trailing slash + pathname = (0, _removetrailingslash.removeTrailingSlash)(pathname); + const options = { + i18n: (_this_i18nProvider = this.i18nProvider) == null ? void 0 : _this_i18nProvider.fromQuery(pathname, query) + }; + const match = await this.matchers.match(pathname, options); + // If we don't have a match, try to render it anyways. + if (!match) { + await this.render(req, res, pathname, query, parsedUrl, true); + return true; + } + // Add the match to the request so we don't have to re-run the matcher + // for the same request. + (0, _requestmeta.addRequestMeta)(req, "match", match); + // TODO-APP: move this to a route handler + const edgeFunctionsPages = this.getEdgeFunctionsPages(); + for (const edgeFunctionsPage of edgeFunctionsPages){ + // If the page doesn't match the edge function page, skip it. + if (edgeFunctionsPage !== match.definition.page) continue; + if (this.nextConfig.output === "export") { + await this.render404(req, res, parsedUrl); + return true; + } + delete query._nextBubbleNoFallback; + delete query[_approuterheaders.NEXT_RSC_UNION_QUERY]; + const handled = await this.runEdgeFunction({ + req, + res, + query, + params: match.params, + page: match.definition.page, + match, + appPaths: null + }); + // If we handled the request, we can return early. + if (handled) return true; + } + // If the route was detected as being a Pages API route, then handle + // it. + // TODO: move this behavior into a route handler. + if ((0, _pagesapiroutematch.isPagesAPIRouteMatch)(match)) { + if (this.nextConfig.output === "export") { + await this.render404(req, res, parsedUrl); + return true; + } + delete query._nextBubbleNoFallback; + const handled = await this.handleApiRequest(req, res, query, match); + if (handled) return true; + } + await this.render(req, res, pathname, query, parsedUrl, true); + return true; + } catch (err) { + if (err instanceof _baseserver.NoFallbackError) { + throw err; + } + try { + if (this.renderOpts.dev) { + const { formatServerError } = require("../lib/format-server-error"); + formatServerError(err); + await this.logErrorWithOriginalStack(err); + } else { + this.logError(err); + } + res.statusCode = 500; + await this.renderError(err, req, res, pathname, query); + return true; + } catch {} + throw err; + } + }; + this.handleCatchallMiddlewareRequest = async (req, res, parsed)=>{ + const isMiddlewareInvoke = (0, _requestmeta.getRequestMeta)(req, "middlewareInvoke"); + if (!isMiddlewareInvoke) { + return false; + } + const handleFinished = ()=>{ + (0, _requestmeta.addRequestMeta)(req, "middlewareInvoke", true); + res.body("").send(); + return true; + }; + const middleware = this.getMiddleware(); + if (!middleware) { + return handleFinished(); + } + const initUrl = (0, _requestmeta.getRequestMeta)(req, "initURL"); + const parsedUrl = (0, _parseurl.parseUrl)(initUrl); + const pathnameInfo = (0, _getnextpathnameinfo.getNextPathnameInfo)(parsedUrl.pathname, { + nextConfig: this.nextConfig, + i18nProvider: this.i18nProvider + }); + parsedUrl.pathname = pathnameInfo.pathname; + const normalizedPathname = (0, _removetrailingslash.removeTrailingSlash)(parsed.pathname || ""); + if (!middleware.match(normalizedPathname, req, parsedUrl.query)) { + return handleFinished(); + } + let result; + let bubblingResult = false; + try { + await this.ensureMiddleware(req.url); + result = await this.runMiddleware({ + request: req, + response: res, + parsedUrl: parsedUrl, + parsed: parsed + }); + if ("response" in result) { + if (isMiddlewareInvoke) { + bubblingResult = true; + const err = new Error(); + err.result = result; + err.bubble = true; + throw err; + } + for (const [key, value] of Object.entries((0, _utils1.toNodeOutgoingHttpHeaders)(result.response.headers))){ + if (key !== "content-encoding" && value !== undefined) { + res.setHeader(key, value); + } + } + res.statusCode = result.response.status; + const { originalResponse } = res; + if (result.response.body) { + await (0, _pipereadable.pipeToNodeResponse)(result.response.body, originalResponse); + } else { + originalResponse.end(); + } + return true; + } + } catch (err) { + if (bubblingResult) { + throw err; + } + if ((0, _iserror.default)(err) && err.code === "ENOENT") { + await this.render404(req, res, parsed); + return true; + } + if (err instanceof _utils.DecodeError) { + res.statusCode = 400; + await this.renderError(err, req, res, parsed.pathname || ""); + return true; + } + const error = (0, _iserror.getProperError)(err); + console.error(error); + res.statusCode = 500; + await this.renderError(error, req, res, parsed.pathname || ""); + return true; + } + return result.finished; + }; + this.isDev = options.dev ?? false; + /** + * This sets environment variable to be used at the time of SSR by head.tsx. + * Using this from process.env allows targeting SSR by calling + * \`process.env.__NEXT_OPTIMIZE_CSS\`. + */ if (this.renderOpts.optimizeFonts) { + process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(this.renderOpts.optimizeFonts); + } + if (this.renderOpts.optimizeCss) { + process.env.__NEXT_OPTIMIZE_CSS = JSON.stringify(true); + } + if (this.renderOpts.nextScriptWorkers) { + process.env.__NEXT_SCRIPT_WORKERS = JSON.stringify(true); + } + process.env.NEXT_DEPLOYMENT_ID = this.nextConfig.deploymentId || ""; + if (!this.minimalMode) { + this.imageResponseCache = new _responsecache.default(this.minimalMode); + } + const { appDocumentPreloading } = this.nextConfig.experimental; + const isDefaultEnabled = typeof appDocumentPreloading === "undefined"; + if (!options.dev && (appDocumentPreloading === true || !(this.minimalMode && isDefaultEnabled))) { + // pre-warm _document and _app as these will be + // needed for most requests + (0, _loadcomponents.loadComponents)({ + distDir: this.distDir, + page: "/_document", + isAppPath: false, + isDev: this.isDev + }).catch(()=>{}); + (0, _loadcomponents.loadComponents)({ + distDir: this.distDir, + page: "/_app", + isAppPath: false, + isDev: this.isDev + }).catch(()=>{}); + } + if (!options.dev && this.nextConfig.experimental.preloadEntriesOnStart) { + this.unstable_preloadEntries(); + } + if (!options.dev) { + const { dynamicRoutes = [] } = this.getRoutesManifest() ?? {}; + this.dynamicRoutes = dynamicRoutes.map((r)=>{ + // TODO: can we just re-use the regex from the manifest? + const regex = (0, _routeregex.getRouteRegex)(r.page); + const match = (0, _routematcher.getRouteMatcher)(regex); + return { + match, + page: r.page, + re: regex.re + }; + }); + } + // ensure options are set when loadConfig isn't called + (0, _setuphttpagentenv.setHttpClientAndAgentOptions)(this.nextConfig); + // Intercept fetch and other testmode apis. + if (this.serverOptions.experimentalTestProxy) { + process.env.NEXT_PRIVATE_TEST_PROXY = "true"; + const { interceptTestApis } = require("next/dist/experimental/testmode/server"); + interceptTestApis(); + } + this.middlewareManifestPath = (0, _path.join)(this.serverDistDir, _constants.MIDDLEWARE_MANIFEST); + // This is just optimization to fire prepare as soon as possible. It will be + // properly awaited later. We add the catch here to ensure that it does not + // cause a unhandled promise rejection. The promise rejection will be + // handled later on via the \`await\` when the request handler is called. + if (!options.dev) { + this.prepare().catch((err)=>{ + console.error("Failed to prepare server", err); + }); + } + } + async unstable_preloadEntries() { + const appPathsManifest = this.getAppPathsManifest(); + const pagesManifest = this.getPagesManifest(); + for (const page of Object.keys(pagesManifest || {})){ + await (0, _loadcomponents.loadComponents)({ + distDir: this.distDir, + page, + isAppPath: false, + isDev: this.isDev + }).catch(()=>{}); + } + for (const page of Object.keys(appPathsManifest || {})){ + await (0, _loadcomponents.loadComponents)({ + distDir: this.distDir, + page, + isAppPath: true, + isDev: this.isDev + }).then(async ({ ComponentMod })=>{ + const webpackRequire = ComponentMod.__next_app__.require; + if (webpackRequire == null ? void 0 : webpackRequire.m) { + for (const id of Object.keys(webpackRequire.m)){ + await webpackRequire(id); + } + } + }).catch(()=>{}); + } + } + async handleUpgrade() { + // The web server does not support web sockets, it's only used for HMR in + // development. + } + }`; + expect( + patchCode(next14ServerMinimalCode, disablePreloadingRule), + ).toMatchInlineSnapshot(` + "class NextNodeServer extends _baseserver.default { + constructor(options){ + // Initialize super class + super(options); + this.handleNextImageRequest = async (req, res, parsedUrl)=>{ + if (!parsedUrl.pathname || !parsedUrl.pathname.startsWith("/_next/image")) { + return false; + } + if (this.minimalMode || this.nextConfig.output === "export" || process.env.NEXT_MINIMAL) { + res.statusCode = 400; + res.body("Bad Request").send(); + return true; + // the \`else\` branch is needed for tree-shaking + } else { + const { ImageOptimizerCache } = require("./image-optimizer"); + const imageOptimizerCache = new ImageOptimizerCache({ + distDir: this.distDir, + nextConfig: this.nextConfig + }); + const { getHash, 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" || imagesConfig.unoptimized) { + await this.render404(req, res); + return 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 true; + } + const cacheKey = ImageOptimizerCache.getCacheKey(paramsResult); + try { + var _cacheEntry_value; + const { getExtension } = require("./serve-static"); + 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: getExtension(contentType) + }, + revalidate: maxAge + }; + }, { + incrementalCache: imageOptimizerCache + }); + if ((cacheEntry == null ? void 0 : (_cacheEntry_value = cacheEntry.value) == null ? void 0 : _cacheEntry_value.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, cacheEntry.revalidate || 0, Boolean(this.renderOpts.dev)); + return true; + } catch (err) { + if (err instanceof ImageError) { + res.statusCode = err.statusCode; + res.body(err.message).send(); + return true; + } + throw err; + } + } + }; + this.handleCatchallRenderRequest = async (req, res, parsedUrl)=>{ + let { pathname, query } = parsedUrl; + if (!pathname) { + throw new Error("Invariant: pathname is undefined"); + } + // This is a catch-all route, there should be no fallbacks so mark it as + // such. + query._nextBubbleNoFallback = "1"; + try { + var _this_i18nProvider; + // next.js core assumes page path without trailing slash + pathname = (0, _removetrailingslash.removeTrailingSlash)(pathname); + const options = { + i18n: (_this_i18nProvider = this.i18nProvider) == null ? void 0 : _this_i18nProvider.fromQuery(pathname, query) + }; + const match = await this.matchers.match(pathname, options); + // If we don't have a match, try to render it anyways. + if (!match) { + await this.render(req, res, pathname, query, parsedUrl, true); + return true; + } + // Add the match to the request so we don't have to re-run the matcher + // for the same request. + (0, _requestmeta.addRequestMeta)(req, "match", match); + // TODO-APP: move this to a route handler + const edgeFunctionsPages = this.getEdgeFunctionsPages(); + for (const edgeFunctionsPage of edgeFunctionsPages){ + // If the page doesn't match the edge function page, skip it. + if (edgeFunctionsPage !== match.definition.page) continue; + if (this.nextConfig.output === "export") { + await this.render404(req, res, parsedUrl); + return true; + } + delete query._nextBubbleNoFallback; + delete query[_approuterheaders.NEXT_RSC_UNION_QUERY]; + const handled = await this.runEdgeFunction({ + req, + res, + query, + params: match.params, + page: match.definition.page, + match, + appPaths: null + }); + // If we handled the request, we can return early. + if (handled) return true; + } + // If the route was detected as being a Pages API route, then handle + // it. + // TODO: move this behavior into a route handler. + if ((0, _pagesapiroutematch.isPagesAPIRouteMatch)(match)) { + if (this.nextConfig.output === "export") { + await this.render404(req, res, parsedUrl); + return true; + } + delete query._nextBubbleNoFallback; + const handled = await this.handleApiRequest(req, res, query, match); + if (handled) return true; + } + await this.render(req, res, pathname, query, parsedUrl, true); + return true; + } catch (err) { + if (err instanceof _baseserver.NoFallbackError) { + throw err; + } + try { + if (this.renderOpts.dev) { + const { formatServerError } = require("../lib/format-server-error"); + formatServerError(err); + await this.logErrorWithOriginalStack(err); + } else { + this.logError(err); + } + res.statusCode = 500; + await this.renderError(err, req, res, pathname, query); + return true; + } catch {} + throw err; + } + }; + this.handleCatchallMiddlewareRequest = async (req, res, parsed)=>{ + const isMiddlewareInvoke = (0, _requestmeta.getRequestMeta)(req, "middlewareInvoke"); + if (!isMiddlewareInvoke) { + return false; + } + const handleFinished = ()=>{ + (0, _requestmeta.addRequestMeta)(req, "middlewareInvoke", true); + res.body("").send(); + return true; + }; + const middleware = this.getMiddleware(); + if (!middleware) { + return handleFinished(); + } + const initUrl = (0, _requestmeta.getRequestMeta)(req, "initURL"); + const parsedUrl = (0, _parseurl.parseUrl)(initUrl); + const pathnameInfo = (0, _getnextpathnameinfo.getNextPathnameInfo)(parsedUrl.pathname, { + nextConfig: this.nextConfig, + i18nProvider: this.i18nProvider + }); + parsedUrl.pathname = pathnameInfo.pathname; + const normalizedPathname = (0, _removetrailingslash.removeTrailingSlash)(parsed.pathname || ""); + if (!middleware.match(normalizedPathname, req, parsedUrl.query)) { + return handleFinished(); + } + let result; + let bubblingResult = false; + try { + await this.ensureMiddleware(req.url); + result = await this.runMiddleware({ + request: req, + response: res, + parsedUrl: parsedUrl, + parsed: parsed + }); + if ("response" in result) { + if (isMiddlewareInvoke) { + bubblingResult = true; + const err = new Error(); + err.result = result; + err.bubble = true; + throw err; + } + for (const [key, value] of Object.entries((0, _utils1.toNodeOutgoingHttpHeaders)(result.response.headers))){ + if (key !== "content-encoding" && value !== undefined) { + res.setHeader(key, value); + } + } + res.statusCode = result.response.status; + const { originalResponse } = res; + if (result.response.body) { + await (0, _pipereadable.pipeToNodeResponse)(result.response.body, originalResponse); + } else { + originalResponse.end(); + } + return true; + } + } catch (err) { + if (bubblingResult) { + throw err; + } + if ((0, _iserror.default)(err) && err.code === "ENOENT") { + await this.render404(req, res, parsed); + return true; + } + if (err instanceof _utils.DecodeError) { + res.statusCode = 400; + await this.renderError(err, req, res, parsed.pathname || ""); + return true; + } + const error = (0, _iserror.getProperError)(err); + console.error(error); + res.statusCode = 500; + await this.renderError(error, req, res, parsed.pathname || ""); + return true; + } + return result.finished; + }; + this.isDev = options.dev ?? false; + /** + * This sets environment variable to be used at the time of SSR by head.tsx. + * Using this from process.env allows targeting SSR by calling + * \`process.env.__NEXT_OPTIMIZE_CSS\`. + */ if (this.renderOpts.optimizeFonts) { + process.env.__NEXT_OPTIMIZE_FONTS = JSON.stringify(this.renderOpts.optimizeFonts); + } + if (this.renderOpts.optimizeCss) { + process.env.__NEXT_OPTIMIZE_CSS = JSON.stringify(true); + } + if (this.renderOpts.nextScriptWorkers) { + process.env.__NEXT_SCRIPT_WORKERS = JSON.stringify(true); + } + process.env.NEXT_DEPLOYMENT_ID = this.nextConfig.deploymentId || ""; + if (!this.minimalMode) { + this.imageResponseCache = new _responsecache.default(this.minimalMode); + } + const { appDocumentPreloading } = this.nextConfig.experimental; + const isDefaultEnabled = typeof appDocumentPreloading === "undefined"; + if (!options.dev && (appDocumentPreloading === true || !(this.minimalMode && isDefaultEnabled))) {} + if (!options.dev && this.nextConfig.experimental.preloadEntriesOnStart) {} + if (!options.dev) { + const { dynamicRoutes = [] } = this.getRoutesManifest() ?? {}; + this.dynamicRoutes = dynamicRoutes.map((r)=>{ + // TODO: can we just re-use the regex from the manifest? + const regex = (0, _routeregex.getRouteRegex)(r.page); + const match = (0, _routematcher.getRouteMatcher)(regex); + return { + match, + page: r.page, + re: regex.re + }; + }); + } + // ensure options are set when loadConfig isn't called + (0, _setuphttpagentenv.setHttpClientAndAgentOptions)(this.nextConfig); + // Intercept fetch and other testmode apis. + if (this.serverOptions.experimentalTestProxy) { + process.env.NEXT_PRIVATE_TEST_PROXY = "true"; + const { interceptTestApis } = require("next/dist/experimental/testmode/server"); + interceptTestApis(); + } + this.middlewareManifestPath = (0, _path.join)(this.serverDistDir, _constants.MIDDLEWARE_MANIFEST); + // This is just optimization to fire prepare as soon as possible. It will be + // properly awaited later. We add the catch here to ensure that it does not + // cause a unhandled promise rejection. The promise rejection will be + // handled later on via the \`await\` when the request handler is called. + if (!options.dev) { + this.prepare().catch((err)=>{ + console.error("Failed to prepare server", err); + }); + } + } + async unstable_preloadEntries() { + const appPathsManifest = this.getAppPathsManifest(); + const pagesManifest = this.getPagesManifest(); + for (const page of Object.keys(pagesManifest || {})){ + await (0, _loadcomponents.loadComponents)({ + distDir: this.distDir, + page, + isAppPath: false, + isDev: this.isDev + }).catch(()=>{}); + } + for (const page of Object.keys(appPathsManifest || {})){ + await (0, _loadcomponents.loadComponents)({ + distDir: this.distDir, + page, + isAppPath: true, + isDev: this.isDev + }).then(async ({ ComponentMod })=>{ + const webpackRequire = ComponentMod.__next_app__.require; + if (webpackRequire == null ? void 0 : webpackRequire.m) { + for (const id of Object.keys(webpackRequire.m)){ + await webpackRequire(id); + } + } + }).catch(()=>{}); + } + } + async handleUpgrade() { + // The web server does not support web sockets, it's only used for HMR in + // development. + } + }" + `); + }); + describe("Drop babel dependency", () => { test("Drop body", () => { expect( - patchCode(nextServerMinimalCode, createEmptyBodyRule("runMiddleware")), + patchCode( + next15ServerMinimalCode, + createEmptyBodyRule("runMiddleware"), + ), ).toMatchInlineSnapshot(` "class NextNodeServer extends _baseserver.default { constructor(options){ @@ -1187,7 +1843,7 @@ async imageOptimizer(req, res, paramsResult, previousCacheEntry) { expect( patchCode( - nextServerMinimalCode, + next15ServerMinimalCode, createEmptyBodyRule("runEdgeFunction"), ), ).toMatchInlineSnapshot(`