From bfa8fec9b17d421925a684e2b642dee70165a879 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Thu, 10 Oct 2024 13:17:23 +0000 Subject: [PATCH] fix(@angular/build): use named export `reqHandler` for server.ts request handling Some cloud providers, such as Cloudflare, expect the default export to follow a specific structure (e.g., an object with a `fetch` property). To prevent the need for creating a separate `server.ts` file for production builds, the request handler is now exported as `reqHandler` instead of a default export. --- .../tools/vite/middlewares/ssr-middleware.ts | 18 +++++----- .../utils/server-rendering/launch-server.ts | 10 +++--- .../server-rendering/load-esm-from-memory.ts | 2 +- .../application-builder/server.ts.template | 33 ++++++++++++++++--- ...tes-output-mode-server-platform-neutral.ts | 4 +-- ...er-routes-output-mode-static-http-calls.ts | 2 +- .../e2e/tests/vite/ssr-entry-express.ts | 2 +- .../e2e/tests/vite/ssr-entry-fastify.ts | 2 +- .../legacy-cli/e2e/tests/vite/ssr-entry-h3.ts | 4 +-- .../e2e/tests/vite/ssr-entry-hono.ts | 2 +- 10 files changed, 49 insertions(+), 30 deletions(-) diff --git a/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts b/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts index 8e0b6ae6d197..9917537fa290 100644 --- a/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts +++ b/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts @@ -92,20 +92,18 @@ export async function createAngularSsrExternalMiddleware( next: Connect.NextFunction, ) { (async () => { - const { default: handler, AngularAppEngine } = (await server.ssrLoadModule( - './server.mjs', - )) as { - default?: unknown; + const { reqHandler, AngularAppEngine } = (await server.ssrLoadModule('./server.mjs')) as { + reqHandler?: unknown; AngularAppEngine: typeof SSRAngularAppEngine; }; - - if (!isSsrNodeRequestHandler(handler) && !isSsrRequestHandler(handler)) { + if (!isSsrNodeRequestHandler(reqHandler) && !isSsrRequestHandler(reqHandler)) { if (!fallbackWarningShown) { // eslint-disable-next-line no-console console.warn( - `The default export in 'server.ts' does not provide a Node.js request handler. ` + + `The 'reqHandler' export in 'server.ts' is either undefined or does not provide a recognized request handler. ` + 'Using the internal SSR middleware instead.', ); + fallbackWarningShown = true; } @@ -130,10 +128,10 @@ export async function createAngularSsrExternalMiddleware( } // Forward the request to the middleware in server.ts - if (isSsrNodeRequestHandler(handler)) { - await handler(req, res, next); + if (isSsrNodeRequestHandler(reqHandler)) { + await reqHandler(req, res, next); } else { - const webRes = await handler(createWebRequestFromNodeRequest(req)); + const webRes = await reqHandler(createWebRequestFromNodeRequest(req)); if (!webRes) { next(); diff --git a/packages/angular/build/src/utils/server-rendering/launch-server.ts b/packages/angular/build/src/utils/server-rendering/launch-server.ts index 88e60032face..4d8f3fbdd259 100644 --- a/packages/angular/build/src/utils/server-rendering/launch-server.ts +++ b/packages/angular/build/src/utils/server-rendering/launch-server.ts @@ -20,23 +20,23 @@ export const DEFAULT_URL = new URL('http://ng-localhost/'); * @returns A promise that resolves to the URL of the running server. */ export async function launchServer(): Promise { - const { default: handler } = await loadEsmModuleFromMemory('./server.mjs'); + const { reqHandler } = await loadEsmModuleFromMemory('./server.mjs'); const { createWebRequestFromNodeRequest, writeResponseToNodeResponse } = await loadEsmModule('@angular/ssr/node'); - if (!isSsrNodeRequestHandler(handler) && !isSsrRequestHandler(handler)) { + if (!isSsrNodeRequestHandler(reqHandler) && !isSsrRequestHandler(reqHandler)) { return DEFAULT_URL; } const server = createServer((req, res) => { (async () => { // handle request - if (isSsrNodeRequestHandler(handler)) { - await handler(req, res, (e) => { + if (isSsrNodeRequestHandler(reqHandler)) { + await reqHandler(req, res, (e) => { throw e; }); } else { - const webRes = await handler(createWebRequestFromNodeRequest(req)); + const webRes = await reqHandler(createWebRequestFromNodeRequest(req)); if (webRes) { await writeResponseToNodeResponse(webRes, res); } else { diff --git a/packages/angular/build/src/utils/server-rendering/load-esm-from-memory.ts b/packages/angular/build/src/utils/server-rendering/load-esm-from-memory.ts index 7ebb197f9258..1d19a07e61de 100644 --- a/packages/angular/build/src/utils/server-rendering/load-esm-from-memory.ts +++ b/packages/angular/build/src/utils/server-rendering/load-esm-from-memory.ts @@ -24,7 +24,7 @@ interface MainServerBundleExports { * Represents the exports available from the server bundle. */ interface ServerBundleExports { - default: unknown; + reqHandler?: unknown; } export function loadEsmModuleFromMemory( diff --git a/packages/schematics/angular/ssr/files/application-builder/server.ts.template b/packages/schematics/angular/ssr/files/application-builder/server.ts.template index 010804ead0f6..e54727a29d0d 100644 --- a/packages/schematics/angular/ssr/files/application-builder/server.ts.template +++ b/packages/schematics/angular/ssr/files/application-builder/server.ts.template @@ -14,10 +14,21 @@ const browserDistFolder = resolve(serverDistFolder, '../<%= browserDistDirectory const app = express(); const angularApp = new AngularNodeAppEngine(); -// Example Express Rest API endpoints -// app.get('/api/**', (req, res) => { }); +/** + * Example Express Rest API endpoints can be defined here. + * Uncomment and define endpoints as necessary. + * + * Example: + * ```ts + * app.get('/api/**', (req, res) => { + * // Handle API request + * }); + * ``` + */ -// Serve static files from /<%= browserDistDirectory %> +/** + * Serve static files from /<%= browserDistDirectory %> + */ app.get( '**', express.static(browserDistFolder, { @@ -32,13 +43,22 @@ app.get( }), ); +/** + * Handle all other requests by rendering the Angular application. + */ app.get('**', (req, res, next) => { angularApp .render(req) - .then((response) => (response ? writeResponseToNodeResponse(response, res) : next())) + .then((response) => + response ? writeResponseToNodeResponse(response, res) : next(), + ) .catch(next); }); +/** + * Start the server if this module is the main entry point. + * The server listens on the port defined by the `PORT` environment variable, or defaults to 4000. + */ if (isMainModule(import.meta.url)) { const port = process.env['PORT'] || 4000; app.listen(port, () => { @@ -46,4 +66,7 @@ if (isMainModule(import.meta.url)) { }); } -export default createNodeRequestHandler(app); +/** + * The request handler used by the Angular CLI (dev-server and during build). + */ +export const reqHandler = createNodeRequestHandler(app); diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts index f323a65c9e3b..95f824379ef7 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-server-platform-neutral.ts @@ -82,9 +82,7 @@ export default async function () { ); app.use(router); - - const handler = toWebHandler(app); - export default createRequestHandler(handler); + export const reqHandler = createRequestHandler(toWebHandler(app)); `, }); // Generate components for the above routes diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts index 5f0623b921a3..e06942c4a797 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static-http-calls.ts @@ -110,7 +110,7 @@ export default async function () { }); } - export default createNodeRequestHandler(server); + export const reqHandler = createNodeRequestHandler(server); `, }); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-express.ts b/tests/legacy-cli/e2e/tests/vite/ssr-entry-express.ts index f5d185f37896..c9ec730a1da3 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-entry-express.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-entry-express.ts @@ -79,7 +79,7 @@ export default async function () { }); } - export default createNodeRequestHandler(server); + export const reqHandler = createNodeRequestHandler(server); `, }); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-fastify.ts b/tests/legacy-cli/e2e/tests/vite/ssr-entry-fastify.ts index a4391587f52d..53501e56f791 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-entry-fastify.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-entry-fastify.ts @@ -76,7 +76,7 @@ export default async function () { }); } - export default createNodeRequestHandler(async (req, res) => { + export const reqHandler = createNodeRequestHandler(async (req, res) => { await server.ready(); server.server.emit('request', req, res); }); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-h3.ts b/tests/legacy-cli/e2e/tests/vite/ssr-entry-h3.ts index ef9c85a2d56c..75111e02f374 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-entry-h3.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-entry-h3.ts @@ -69,8 +69,8 @@ export default async function () { } const server = app(); - const handler = toWebHandler(server); - export default createRequestHandler(handler); + + export const reqHandler = createRequestHandler(toWebHandler(server)); `, }); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-entry-hono.ts b/tests/legacy-cli/e2e/tests/vite/ssr-entry-hono.ts index 3ee3d4740d17..19fc8c09800c 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-entry-hono.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-entry-hono.ts @@ -62,7 +62,7 @@ export default async function () { } const server = app(); - export default createRequestHandler(server.fetch); + export const reqHandler = createRequestHandler(server.fetch); `, });