#!/usr/bin/env node // Serve the localized production build so the language switcher actually works locally. // `ng build --localize` emits dist/.../browser/{nl,en}/ (each with base href /nl/ or /en/). // Plain `ng serve` (npm start) serves only nl at /, so switching 404s there — this static // server serves both locale subdirs with per-locale SPA fallback (a miss under //… // serves that locale's index.html), so deep-link switches resolve. Demo only, not prod infra. import { createServer, request as httpRequest } from 'node:http'; import { readFile } from 'node:fs/promises'; import { join, extname, normalize } from 'node:path'; const ROOT = 'dist/atomic-design-poc/browser'; const PORT = Number(process.env.PORT ?? 4300); // Reverse-proxy /api → backend so the served app is the whole demo, not just static pages. // Docker sets API_PROXY_TARGET=http://api:5000; locally it defaults to the dev backend. const API_TARGET = new URL(process.env.API_PROXY_TARGET ?? 'http://localhost:5000'); const MIME = { '.html': 'text/html', '.js': 'text/javascript', '.mjs': 'text/javascript', '.css': 'text/css', '.json': 'application/json', '.svg': 'image/svg+xml', '.ico': 'image/x-icon', '.woff2': 'font/woff2', '.png': 'image/png', }; const send = (res, status, body, type) => { res.writeHead(status, { 'content-type': type }); res.end(body); }; createServer(async (req, res) => { const rawUrl = req.url ?? '/'; // Proxy API calls to the backend (keep the raw url incl. query). if (rawUrl.startsWith('/api')) { const proxied = httpRequest( { hostname: API_TARGET.hostname, port: API_TARGET.port, path: rawUrl, method: req.method, headers: req.headers, }, (pr) => { res.writeHead(pr.statusCode ?? 502, pr.headers); pr.pipe(res); }, ); proxied.on('error', () => send(res, 502, 'Bad gateway (API)', 'text/plain')); req.pipe(proxied); return; } const url = decodeURIComponent(rawUrl.split('?')[0]); const rel = normalize(url).replace(/^(\.\.[/\\])+/, ''); // no path traversal // nl (source) is served at the ROOT (subPath ''); en lives under /en/. const isEn = url === '/en' || url.startsWith('/en/'); const indexPath = isEn ? join(ROOT, 'en', 'index.html') : join(ROOT, 'index.html'); try { // A real asset (nl at root, en under /en/) — otherwise fall through to the SPA index. if (url === '/' || url === '/en' || url === '/en/') throw new Error('serve index'); const file = await readFile(join(ROOT, rel)); send(res, 200, file, MIME[extname(rel)] ?? 'application/octet-stream'); } catch { // SPA fallback to the requested locale's index.html. try { send(res, 200, await readFile(indexPath), 'text/html'); } catch { send(res, 404, 'Not found', 'text/plain'); } } }).listen(PORT, () => { console.log(`Serving ${ROOT} at http://localhost:${PORT}/ (nl at /, en at /en/)`); });