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.
This commit is contained in:
Alan Agius 2024-10-10 13:17:23 +00:00 committed by Alan Agius
parent 01f747534b
commit bfa8fec9b1
10 changed files with 49 additions and 30 deletions

View File

@ -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();

View File

@ -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<URL> {
const { default: handler } = await loadEsmModuleFromMemory('./server.mjs');
const { reqHandler } = await loadEsmModuleFromMemory('./server.mjs');
const { createWebRequestFromNodeRequest, writeResponseToNodeResponse } =
await loadEsmModule<typeof import('@angular/ssr/node')>('@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 {

View File

@ -24,7 +24,7 @@ interface MainServerBundleExports {
* Represents the exports available from the server bundle.
*/
interface ServerBundleExports {
default: unknown;
reqHandler?: unknown;
}
export function loadEsmModuleFromMemory(

View File

@ -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);

View File

@ -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

View File

@ -110,7 +110,7 @@ export default async function () {
});
}
export default createNodeRequestHandler(server);
export const reqHandler = createNodeRequestHandler(server);
`,
});

View File

@ -79,7 +79,7 @@ export default async function () {
});
}
export default createNodeRequestHandler(server);
export const reqHandler = createNodeRequestHandler(server);
`,
});

View File

@ -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);
});

View File

@ -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));
`,
});

View File

@ -62,7 +62,7 @@ export default async function () {
}
const server = app();
export default createRequestHandler(server.fetch);
export const reqHandler = createRequestHandler(server.fetch);
`,
});