// Portable test launcher: built-in modules only; never invokes npm or a global Node. const http = require('node:http'); const fs = require('node:fs'); const path = require('node:path'); const { spawn } = require('node:child_process'); const root = __dirname; const types = { '.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', '.svg':'image/svg+xml', '.png':'image/png', '.jpg':'image/jpeg', '.jpeg':'image/jpeg', '.gif':'image/gif', '.webp':'image/webp', '.ico':'image/x-icon', '.ttf':'font/ttf', '.woff':'font/woff', '.woff2':'font/woff2', '.txt':'text/plain; charset=utf-8' }; const server = http.createServer((req, res) => { if (!['GET','HEAD'].includes(req.method)) { res.writeHead(405); res.end(); return; } let pathname; try { pathname = decodeURIComponent(new URL(req.url, 'http://127.0.0.1').pathname); } catch { res.writeHead(400); res.end(); return; } // Dashboard is a production export and must be served from its own URL root. const dashboard = pathname === '/dashboard' || pathname.startsWith('/dashboard/'); const dashboardPath = dashboard ? (pathname === '/dashboard' ? '/dashboard/' : pathname) : pathname; // Source Reference/static example paths keep their repository-relative imports. const reference = ['/docs/','/packages/','/src/','/public/'].some(prefix => pathname.startsWith(prefix)); const base = dashboard ? path.join(root, 'dist-dashboard') : (reference ? root : path.join(root, 'storybook-static')); const relativePath = dashboard ? dashboardPath.slice('/dashboard'.length) || '/' : (pathname === '/favicon.ico' ? '/favicon.svg' : pathname); let file = path.resolve(base, '.' + relativePath); const relative = path.relative(base, file); if (relative.startsWith('..') || path.isAbsolute(relative) || pathname.includes('\\')) { res.writeHead(403); res.end(); return; } fs.stat(file, (error, stat) => { if (!error && stat.isDirectory()) file = path.join(file, 'index.html'); fs.stat(file, (error, stat) => { if (error || !stat.isFile()) { res.writeHead(404); res.end('Not found'); return; } res.writeHead(200, { 'Content-Type': types[path.extname(file).toLowerCase()] || 'application/octet-stream', 'Content-Length':stat.size, 'Cache-Control':'no-store', 'X-Content-Type-Options':'nosniff' }); if (req.method === 'HEAD') { res.end(); return; } const stream = fs.createReadStream(file); stream.on('error', () => res.destroy()); stream.pipe(res); }); }); }); let port = 6007; let browserOpened = false; function openDefaultBrowser(url) { if (browserOpened) return; browserOpened = true; const command = process.env.ComSpec || 'cmd.exe'; const browser = spawn(command, ['/c', 'start', '', url], { detached: true, stdio: 'ignore', windowsHide: true }); browser.on('error', error => { console.error(`Browser auto-open failed: ${error.message}`); console.error(`Open this address manually: ${url}`); }); browser.unref(); } server.on('error', error => { if (error.code === 'EADDRINUSE' || error.code === 'EACCES') { if (port === 0) { console.error(error.message); process.exitCode = 1; return; } port = port < 6023 ? port + 1 : 0; server.listen(port, '127.0.0.1'); } else { console.error(error.message); process.exitCode = 1; } }); server.on('listening', () => { const url = `http://127.0.0.1:${server.address().port}/`; console.log(`HM Design System Portable Test\nNode ${process.version} (${process.arch})\nStorybook: ${url}\nDashboard: ${url}dashboard/\nStatic status example: ${url}docs/patterns/dashboard-status-static.html\nPress Ctrl+C to stop this preview.\n`); openDefaultBrowser(url); }); process.on('SIGINT', () => server.close(() => process.exit(0))); process.on('SIGTERM', () => server.close(() => process.exit(0))); server.listen(port, '127.0.0.1');