Skip to content

Commit d8e6d07

Browse files
committed
feat: implement port availability check in serve command
1 parent 47c87b4 commit d8e6d07

File tree

1 file changed

+44
-3
lines changed

1 file changed

+44
-3
lines changed

packages/cli/src/commands/serve.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,62 @@
11
import { Command } from 'commander';
22
import path from 'path';
33
import fs from 'fs';
4+
import net from 'net';
45
import chalk from 'chalk';
56
import { bundleRequire } from 'bundle-require';
67

8+
// Helper to find available port
9+
const getAvailablePort = async (startPort: number): Promise<number> => {
10+
const isPortAvailable = (port: number): Promise<boolean> => {
11+
return new Promise((resolve) => {
12+
const server = net.createServer();
13+
server.once('error', (err: any) => {
14+
resolve(false);
15+
});
16+
server.once('listening', () => {
17+
server.close(() => resolve(true));
18+
});
19+
server.listen(port);
20+
});
21+
};
22+
23+
let port = startPort;
24+
while (!(await isPortAvailable(port))) {
25+
port++;
26+
if (port > startPort + 100) {
27+
throw new Error(`Could not find an available port starting from ${startPort}`);
28+
}
29+
}
30+
return port;
31+
};
32+
733
export const serveCommand = new Command('serve')
834
.description('Start ObjectStack server with plugins from configuration')
935
.argument('[config]', 'Configuration file path', 'objectstack.config.ts')
1036
.option('-p, --port <port>', 'Server port', '3000')
1137
.option('--no-server', 'Skip starting HTTP server plugin')
1238
.action(async (configPath, options) => {
39+
let port = parseInt(options.port);
40+
try {
41+
const availablePort = await getAvailablePort(port);
42+
if (availablePort !== port) {
43+
port = availablePort;
44+
}
45+
} catch (e) {
46+
// Ignore error and try with original port, or let it fail later
47+
}
48+
1349
console.log(chalk.bold(`\n🚀 ObjectStack Server`));
1450
console.log(chalk.dim(`------------------------`));
1551
console.log(`📂 Config: ${chalk.blue(configPath)}`);
16-
console.log(`🌐 Port: ${chalk.blue(options.port)}`);
52+
if (parseInt(options.port) !== port) {
53+
console.log(`🌐 Port: ${chalk.blue(port)} ${chalk.yellow(`(requested: ${options.port} in use)`)}`);
54+
} else {
55+
console.log(`🌐 Port: ${chalk.blue(port)}`);
56+
}
1757
console.log('');
1858

59+
1960
const absolutePath = path.resolve(process.cwd(), configPath);
2061

2162
if (!fs.existsSync(absolutePath)) {
@@ -69,9 +110,9 @@ export const serveCommand = new Command('serve')
69110
if (options.server !== false) {
70111
try {
71112
const { HonoServerPlugin } = await import('@objectstack/plugin-hono-server');
72-
const serverPlugin = new HonoServerPlugin({ port: parseInt(options.port) });
113+
const serverPlugin = new HonoServerPlugin({ port });
73114
kernel.use(serverPlugin);
74-
console.log(chalk.green(` ✓ Registered HTTP server plugin (port: ${options.port})`));
115+
console.log(chalk.green(` ✓ Registered HTTP server plugin (port: ${port})`));
75116
} catch (e: any) {
76117
console.warn(chalk.yellow(` ⚠ HTTP server plugin not available: ${e.message}`));
77118
}

0 commit comments

Comments
 (0)