feat(tsview): add database and S3 handlers, tswatch/watch scripts, web utilities, assets and release config
This commit is contained in:
@@ -3,6 +3,10 @@ import * as paths from './paths.js';
|
||||
import type * as interfaces from './interfaces/index.js';
|
||||
import { TsViewConfig } from './config/index.js';
|
||||
import { ViewServer } from './server/index.js';
|
||||
import { exec } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
/**
|
||||
* Main TsView class.
|
||||
@@ -99,25 +103,88 @@ export class TsView {
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the viewer server
|
||||
* @param port - Optional port number (if not provided, finds a free port from 3010+)
|
||||
* Load configuration from npmextra.json
|
||||
*/
|
||||
public async start(port?: number): Promise<number> {
|
||||
const actualPort = port ?? await this.findFreePort(3010);
|
||||
private loadNpmextraConfig(cwd?: string): interfaces.INpmextraConfig {
|
||||
const npmextra = new plugins.npmextra.Npmextra(cwd || process.cwd());
|
||||
const config = npmextra.dataFor<interfaces.INpmextraConfig>('@git.zone/tsview', {});
|
||||
return config || {};
|
||||
}
|
||||
|
||||
this.server = new ViewServer(this, actualPort);
|
||||
await this.server.start();
|
||||
|
||||
console.log(`TsView server started on http://localhost:${actualPort}`);
|
||||
|
||||
// Open browser
|
||||
/**
|
||||
* Kill process running on the specified port
|
||||
*/
|
||||
private async killProcessOnPort(port: number): Promise<void> {
|
||||
try {
|
||||
await plugins.smartopen.openUrl(`http://localhost:${actualPort}`);
|
||||
} catch (err) {
|
||||
// Ignore browser open errors
|
||||
// Get PID using lsof (works on Linux and macOS)
|
||||
const { stdout } = await execAsync(`lsof -ti :${port}`);
|
||||
const pids = stdout.trim().split('\n').filter(Boolean);
|
||||
for (const pid of pids) {
|
||||
console.log(`Killing process ${pid} on port ${port}`);
|
||||
await execAsync(`kill -9 ${pid}`);
|
||||
}
|
||||
// Brief wait for port to be released
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
} catch (e) {
|
||||
// No process on port or lsof not available, ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the viewer server
|
||||
* @param cliPort - Optional port number from CLI (highest priority)
|
||||
*/
|
||||
public async start(cliPort?: number): Promise<number> {
|
||||
const npmextraConfig = await this.loadNpmextraConfig();
|
||||
|
||||
let port: number;
|
||||
let portWasExplicitlySet = false;
|
||||
|
||||
if (cliPort) {
|
||||
// CLI has highest priority
|
||||
port = cliPort;
|
||||
portWasExplicitlySet = true;
|
||||
} else if (npmextraConfig.port) {
|
||||
// Config port specified
|
||||
port = npmextraConfig.port;
|
||||
portWasExplicitlySet = true;
|
||||
} else {
|
||||
// Auto-find free port
|
||||
port = await this.findFreePort(3010);
|
||||
}
|
||||
|
||||
return actualPort;
|
||||
// Check if port is busy and handle accordingly
|
||||
const network = new plugins.smartnetwork.SmartNetwork();
|
||||
const isFree = await network.isLocalPortUnused(port);
|
||||
|
||||
if (!isFree) {
|
||||
if (npmextraConfig.killIfBusy) {
|
||||
console.log(`Port ${port} is busy. Killing existing process...`);
|
||||
await this.killProcessOnPort(port);
|
||||
} else if (portWasExplicitlySet) {
|
||||
throw new Error(`Port ${port} is busy. Set "killIfBusy": true in npmextra.json to auto-kill, or use a different port.`);
|
||||
} else {
|
||||
// Auto port was already free, shouldn't happen, but fallback
|
||||
port = await this.findFreePort(port + 1);
|
||||
}
|
||||
}
|
||||
|
||||
this.server = new ViewServer(this, port);
|
||||
await this.server.start();
|
||||
|
||||
console.log(`TsView server started on http://localhost:${port}`);
|
||||
|
||||
// Open browser (default: true, can be disabled via config)
|
||||
const shouldOpenBrowser = npmextraConfig.openBrowser !== false;
|
||||
if (shouldOpenBrowser) {
|
||||
try {
|
||||
await plugins.smartopen.openUrl(`http://localhost:${port}`);
|
||||
} catch (err) {
|
||||
// Ignore browser open errors
|
||||
}
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user