Files
kmtnflower/server.ts
T
Hermes Agent a8b6e65831
CI Pipeline / backend-test (push) Successful in 9m12s
CI Pipeline / frontend-test (push) Successful in 24s
CI Pipeline / frontend-build (push) Successful in 30s
CI Pipeline / backend-lint (push) Successful in 9s
CI Pipeline / backend-test (pull_request) Successful in 10m0s
CI Pipeline / frontend-test (pull_request) Successful in 23s
CI Pipeline / frontend-build (pull_request) Successful in 30s
CI Pipeline / backend-lint (pull_request) Successful in 9s
CI Pipeline / quality-gate (push) Successful in 2s
CI Pipeline / quality-gate (pull_request) Successful in 2s
fix(ssr): enable static route rendering and fix self-import in server.ts
- Add AngularAppEngine.ɵallowStaticRouteRender flag so the engine serves
  prerendered static HTML from disk instead of falling back to SSR
- Rename default export to named const (requestHandler) to avoid self-import
  loop when the module is the entry point
- Use requestHandler directly in the main-module listener instead of
  importing './server.js' which caused TS2307 resolution failure
2026-08-06 18:10:39 +00:00

98 lines
3.0 KiB
TypeScript

import {
AngularNodeAppEngine,
createNodeRequestHandler,
isMainModule,
writeResponseToNodeResponse,
} from '@angular/ssr/node';
import { AngularAppEngine } from '@angular/ssr';
import * as http from 'node:http';
// Enable serving prerendered static HTML from disk. Without this flag the
// engine falls back to SSR for every route, ignoring the prerendered output.
AngularAppEngine.ɵallowStaticRouteRender = true;
/**
* Public routes prerendered at build time (SSG — Static Site Generation).
* These five routes are compiled to static HTML during `ng build` and
* served directly from disk at runtime for optimal performance and SEO.
* The list here mirrors `prerender-routes.txt` and the Prerender entries
* in `src/app/app.server.routes.ts`.
*/
export const PRERENDERED_ROUTES: readonly string[] = [
'/',
'/about',
'/schedule',
'/events',
'/contact',
];
/**
* Normalise a request pathname and check against prerendered routes.
* Handles both bare paths (`/about`) and trailing-slash variants (`/about/`).
*/
export function isPrerenderedRoute(url: string): boolean {
const pathname = new URL(url, 'http://localhost').pathname;
const normalised = pathname.endsWith('/') && pathname !== '/'
? pathname.slice(0, -1)
: pathname;
return PRERENDERED_ROUTES.includes(normalised);
}
/**
* Lazily instantiate AngularNodeAppEngine so the engine boots only when
* the first request arrives. The engine handles both prerendered route
* lookups and on-demand SSR bootstrapping internally.
*/
let appEngine: AngularNodeAppEngine | null = null;
function getEngine(): AngularNodeAppEngine {
if (!appEngine) {
appEngine = new AngularNodeAppEngine();
}
return appEngine;
}
/**
* Handle incoming requests using the Angular SSR engine.
*
* Prerendered routes (/ , /about, /schedule, /events, /contact) are served
* as pre-generated static HTML from the build output. All other routes
* fall back to server-side rendering (SSR) or the fallback 404 handler.
*/
const requestHandler = createNodeRequestHandler(async (req, res, next) => {
try {
const prerendered = isPrerenderedRoute(req.url || '/');
if (prerendered) {
console.log(`[SSR] Prerendered route: ${req.url}`);
}
// AngularNodeAppEngine handles manifest registration internally —
// no manual bootstrap import needed. For prerendered routes it
// returns pre-generated static HTML; for SSR routes it bootstraps
// the Angular app on demand.
const response = await getEngine().handle(req);
if (response) {
await writeResponseToNodeResponse(response, res);
} else {
next();
}
} catch (error) {
next(error);
}
});
export default requestHandler;
if (isMainModule(import.meta.url)) {
const httpServer = http.createServer();
httpServer.on('request', async (req: http.IncomingMessage, res: http.ServerResponse) => {
requestHandler(req, res, () => {
res.statusCode = 404;
res.end('Not Found');
});
});
httpServer.listen(4200, () => {
console.log(`Node SSR server listening on http://localhost:4200`);
});
}