// Test server for e2e runs: static files from the repo root plus /ssr, // which server-renders the counter app and serves a hydration page — the // same app module the import map hands to the browser. import { createServer } from 'node:http' import { readFile } from 'node:fs/promises' import { join, normalize, extname, dirname } from 'node:path' import { fileURLToPath } from 'node:url' import { Readable } from 'node:stream' import createApp from '../../examples/counter/app.js' import createStreamingApp from '../../examples/streaming/app.js' const root = normalize(join(dirname(fileURLToPath(import.meta.url)), '..', '..')) const MIME = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.mjs': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json; charset=utf-8' } const IMPORT_MAP = JSON.stringify({ imports: { '@uhhm/buuh': '/packages/core/index.js', '@uhhm/buuh/timing': '/packages/core/lib/timing.js', '@uhhm/buuh-html': '/packages/html/browser.js', '@uhhm/buuh-html/raw': '/packages/html/raw.js', '@uhhm/buuh-html/morph': '/packages/html/morph.js', '@uhhm/buuh-html/hydrate': '/packages/html/hydrate.js' } }) function ssrPage () { const body = createApp().toString('/') // When a view owns , scripts belong in : type="module" is // deferred by definition, and body must contain only what the view // renders or hydration would (rightly) flag the extra nodes. return ` choo v8 ssr counter ${body} ` } export function startServer () { const server = createServer(async (req, res) => { const url = new URL(req.url, 'http://localhost') if (url.pathname === '/ssr') { res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }) res.end(ssrPage()) return } if (url.pathname === '/stream') { const delay = Number(url.searchParams.get('delay')) || 500 res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }) // padding nudges browsers to start parsing before the stream closes res.write('\n\nchoo streams\n' + '\n') const body = Readable.fromWeb(createStreamingApp({ delay }).toStream('/')) body.pipe(res, { end: false }) body.on('end', () => res.end('\n')) body.on('error', () => res.destroy()) return } let pathname = decodeURIComponent(url.pathname) if (pathname.endsWith('/')) pathname += 'index.html' const file = normalize(join(root, pathname)) if (!file.startsWith(root)) { res.writeHead(403).end() return } try { const data = await readFile(file) res.writeHead(200, { 'content-type': MIME[extname(file)] || 'application/octet-stream' }) res.end(data) } catch (e) { res.writeHead(404).end('not found: ' + pathname) } }) return new Promise((resolve) => { server.listen(0, '127.0.0.1', () => { resolve({ server, origin: `http://127.0.0.1:${server.address().port}`, close: () => new Promise((r) => server.close(r)) }) }) }) }