|
| 1 | +import fs from 'node:fs/promises'; |
| 2 | +import path from 'node:path'; |
| 3 | +import { spawn } from 'node:child_process'; |
1 | 4 | import chalk from 'chalk'; |
2 | 5 | import { Command } from 'commander'; |
3 | 6 | import { loadCLIConfig } from '@/cli/utils/config'; |
4 | | -import { PACKAGE_ROOT } from '@/cli/utils/resolve'; |
5 | | -import { linkContent } from '@/cli/utils/scaffold'; |
| 7 | + |
| 8 | +function resolveServerEntry(projectRoot: string, preset?: string): string { |
| 9 | + if (preset === 'vercel' || preset === 'vercel-static') { |
| 10 | + return path.resolve(projectRoot, '.vercel/output/server/index.mjs'); |
| 11 | + } |
| 12 | + return path.resolve(projectRoot, '.output/server/index.mjs'); |
| 13 | +} |
6 | 14 |
|
7 | 15 | export const startCommand = new Command('start') |
8 | 16 | .description('Start production server') |
9 | 17 | .option('-p, --port <port>', 'Port number', '3000') |
10 | 18 | .option('--config <path>', 'Path to chronicle.yaml') |
11 | | - .option('--host <host>', 'Host address', 'localhost') |
| 19 | + .option('--host <host>', 'Host address', '0.0.0.0') |
| 20 | + .option('--preset <preset>', 'Deploy preset (must match build preset)') |
12 | 21 | .action(async options => { |
13 | | - const { config, projectRoot, configPath } = await loadCLIConfig(options.config); |
14 | | - const port = parseInt(options.port, 10); |
15 | | - await linkContent(projectRoot, config); |
| 22 | + const { config, projectRoot } = await loadCLIConfig(options.config); |
| 23 | + const preset = options.preset ?? config.preset; |
| 24 | + const serverEntry = resolveServerEntry(projectRoot, preset); |
16 | 25 |
|
17 | | - console.log(chalk.cyan('Starting production server...')); |
| 26 | + const exists = await fs.access(serverEntry).then(() => true, () => false); |
| 27 | + if (!exists) { |
| 28 | + console.error(chalk.red(`No build found at ${serverEntry}`)); |
| 29 | + console.error(chalk.red('Run `chronicle build` first.')); |
| 30 | + process.exit(1); |
| 31 | + } |
18 | 32 |
|
19 | | - const { preview } = await import('vite'); |
20 | | - const { createViteConfig } = await import('@/server/vite-config'); |
| 33 | + console.log(chalk.cyan('Starting production server...')); |
21 | 34 |
|
22 | | - const viteConfig = await createViteConfig({ packageRoot: PACKAGE_ROOT, projectRoot, configPath }); |
23 | | - const server = await preview({ |
24 | | - ...viteConfig, |
25 | | - preview: { port, host: options.host } |
| 35 | + const child = spawn(process.execPath, [serverEntry], { |
| 36 | + stdio: 'inherit', |
| 37 | + env: { |
| 38 | + ...process.env, |
| 39 | + PORT: options.port, |
| 40 | + HOST: options.host, |
| 41 | + }, |
26 | 42 | }); |
27 | 43 |
|
28 | | - server.printUrls(); |
29 | | - |
30 | 44 | let shuttingDown = false; |
31 | | - const shutdown = async () => { |
| 45 | + const shutdown = () => { |
32 | 46 | if (shuttingDown) return; |
33 | 47 | shuttingDown = true; |
34 | 48 | try { |
35 | | - await server.close(); |
36 | | - } catch { /* ignore close errors */ } |
37 | | - process.exit(0); |
| 49 | + child.kill('SIGTERM'); |
| 50 | + } catch { /* ignore if already exited */ } |
38 | 51 | }; |
39 | 52 | process.once('SIGINT', shutdown); |
40 | 53 | process.once('SIGTERM', shutdown); |
| 54 | + |
| 55 | + child.on('exit', (code) => { |
| 56 | + process.exit(code ?? 0); |
| 57 | + }); |
41 | 58 | }); |
0 commit comments