// 同时启动展示端(5173)和管理端(5175,经展示端的 /admin/ 访问);Ctrl+C 一起退出。 // 不经过 shell:直接用 node 跑各自目录下的 vite,输出带前缀,端口被占时直接报错。 import { spawn } from 'node:child_process' import { fileURLToPath } from 'node:url' const vite = fileURLToPath(new URL('./node_modules/vite/bin/vite.js', import.meta.url)) // 颜色:和 Vite 自己的输出一致(绿色的 ➜、青色/蓝色的链接);设了 NO_COLOR 就不上色 const color = !process.env.NO_COLOR const paint = (code, text) => (color ? `[${code}m${text}` : text) const bold = (t) => paint('1', t) const dim = (t) => paint('2', t) const green = (t) => paint('32', t) const cyan = (t) => paint('36', t) const magenta = (t) => paint('35', t) const link = (url) => paint('36;4', url) const apps = [ { name: 'display', tint: cyan, cwd: fileURLToPath(new URL('./display/', import.meta.url)) }, { name: 'admin ', tint: magenta, cwd: fileURLToPath(new URL('./admin/', import.meta.url)) }, ] console.log('') console.log(` ${green('➜')} ${bold('展示端')} ${link('http://localhost:5173/')}`) console.log(` ${green('➜')} ${bold('管理端')} ${link('http://localhost:5173/admin/')} ${dim('(也可以直接开 http://localhost:5175/admin/)')}`) console.log('') const children = apps.map(({ name, tint, cwd }) => { const child = spawn(process.execPath, [vite], { cwd, stdio: ['inherit', 'pipe', 'pipe'], env: { ...process.env, ...(color ? { FORCE_COLOR: '1' } : {}) } }) const prefix = (stream, out) => stream.on('data', (chunk) => { for (const line of String(chunk).split(/\r?\n/)) if (line.trim()) out.write(`${tint(`[${name}]`)} ${line}\n`) }) prefix(child.stdout, process.stdout) prefix(child.stderr, process.stderr) child.on('exit', (code) => { if (code) { console.error(`${tint(`[${name}]`)} ${paint('31', `退出了(${code}),一起停掉`)}`) stop() process.exitCode = code } }) return child }) function stop() { for (const c of children) c.kill() } process.on('SIGINT', stop) process.on('exit', stop)