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