73 lines
2.5 KiB
JavaScript
73 lines
2.5 KiB
JavaScript
import { spawn } from 'node:child_process';
|
|
import { existsSync, readFileSync } from 'node:fs';
|
|
import { dirname, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const scriptDirectory = dirname(fileURLToPath(import.meta.url));
|
|
const repositoryRoot = resolve(scriptDirectory, '../../..');
|
|
const environmentFile = resolve(repositoryRoot, '.env');
|
|
const localEnvironment = existsSync(environmentFile)
|
|
? parseEnvironment(readFileSync(environmentFile, 'utf8'))
|
|
: {};
|
|
const apiPort = process.env.API_PORT ?? localEnvironment.API_PORT ?? '8200';
|
|
const swaggerEnabled =
|
|
(process.env.SWAGGER_ENABLED ??
|
|
localEnvironment.SWAGGER_ENABLED ??
|
|
'true') === 'true';
|
|
const nodeEnvironment =
|
|
process.env.NODE_ENV ?? localEnvironment.NODE_ENV ?? 'development';
|
|
const swaggerUrl = `http://localhost:${apiPort}/api-docs`;
|
|
const tsxCommand = process.platform === 'win32' ? 'tsx.cmd' : 'tsx';
|
|
const apiProcess = spawn(tsxCommand, ['watch', 'src/main.ts'], {
|
|
shell: process.platform === 'win32',
|
|
stdio: 'inherit',
|
|
});
|
|
|
|
if (
|
|
process.platform === 'darwin' &&
|
|
nodeEnvironment === 'development' &&
|
|
swaggerEnabled
|
|
) {
|
|
void openSwaggerAfterApiIsReady();
|
|
}
|
|
|
|
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
process.on(signal, () => apiProcess.kill(signal));
|
|
}
|
|
|
|
apiProcess.on('exit', (code, signal) => {
|
|
if (signal) process.exitCode = 1;
|
|
else process.exitCode = code ?? 0;
|
|
});
|
|
|
|
function parseEnvironment(content) {
|
|
return Object.fromEntries(
|
|
content
|
|
.split(/\r?\n/)
|
|
.map((line) => line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/))
|
|
.filter(Boolean)
|
|
.map((match) => [match[1], match[2]]),
|
|
);
|
|
}
|
|
|
|
async function openSwaggerAfterApiIsReady() {
|
|
const deadline = Date.now() + 30_000;
|
|
while (Date.now() < deadline) {
|
|
try {
|
|
const response = await fetch(`http://localhost:${apiPort}/health`);
|
|
if (response.ok) {
|
|
// 开发启动器进程不会随 tsx watch 的热重启重建,因此只会打开一次。
|
|
spawn('open', [swaggerUrl], {
|
|
detached: true,
|
|
stdio: 'ignore',
|
|
}).unref();
|
|
return;
|
|
}
|
|
} catch {
|
|
// API 仍在启动或热重启,继续等待到超时为止。
|
|
}
|
|
await new Promise((resolveDelay) => setTimeout(resolveDelay, 250));
|
|
}
|
|
console.warn(`Swagger 未在 30 秒内就绪:${swaggerUrl}`);
|
|
}
|