Files
cli/src/command/init.ts

238 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { program, Command } from '@/program.ts';
import { checkFileExists, envisionPath, getConfig, writeConfig } from '@/module/index.ts';
import path from 'path';
import fs from 'fs';
import { chalk } from '@/module/chalk.ts';
import inquirer from 'inquirer';
import { spawn, spawnSync } from 'child_process';
import { checkPm2, installDep, installDeps } from '@/uitls/npm.ts';
const command = new Command('init').description('初始化应用').action((optison) => {
console.log('init');
}); //
program.addCommand(command);
const setMainAppConfig = async (mainAppPath: string) => {
const config = getConfig();
config.mainAppPath = mainAppPath;
writeConfig(config);
};
const initMain = async () => {
const mainAppPath = path.join(envisionPath, 'main-app');
const pkgPath = path.join(mainAppPath, 'package.json');
if (!checkFileExists(pkgPath)) {
fs.mkdirSync(mainAppPath, { recursive: true });
const pkg = {
name: 'main-app',
version: '1.0.0',
type: 'module',
main: 'dist/app.mjs',
scripts: {
start: 'node dist/app.mjs',
pm2: 'pm2 start dist/app.mjs --name main-app',
},
dependencies: {
'@kevisual/router': 'latest',
'@kevisual/local-app-manager': 'latest',
'@kevisual/use-config': 'latest',
},
};
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
console.log(chalk.green('初始化 main-app 成功'));
}
// 在对应的路径,安装依赖
installDeps({ appPath: mainAppPath, sync: true });
// 创建 dist/app.mjs
const code = `import { App } from '@kevisual/router';
import { app as LocalApp } from '@kevisual/local-app-manager';
import { useConfig } from '@kevisual/use-config';
const config = useConfig();
const host = config.host || '127.0.0.1';
LocalApp.listen(config.port, host, () => {
console.log(\`LocalApp is running on http://\${host}:\${config.port}\`);
});
`;
const codeDistPath = path.join(mainAppPath, 'dist');
if (!checkFileExists(codeDistPath)) {
fs.mkdirSync(codeDistPath, { recursive: true });
}
fs.writeFileSync(path.join(codeDistPath, 'app.mjs'), code);
console.log(chalk.green('创建 app.mjs 成功'));
// 创建 app.config.json5
const answers = await inquirer.prompt([
{
type: 'input',
name: 'port',
message: '请输入端口号',
default: '11015',
},
{
type: 'input',
name: 'host',
message: '请输入host',
default: '127.0.0.1',
},
{
type: 'input',
name: 'appsPath',
message: '请输入apps路径',
default: path.join(mainAppPath, 'apps'),
},
]);
const json5 = {
port: Number(answers.port),
host: answers.host,
appsPath: answers.appsPath,
};
fs.writeFileSync(path.join(mainAppPath, 'app.config.json5'), JSON.stringify(json5, null, 2));
//
console.log(chalk.green('创建 app.config.json5 成功'));
setMainAppConfig(mainAppPath);
writeConfig({ ...getConfig(), appsPath: answers.appsPath });
};
const checkPid = (pid: number) => {
try {
process.kill(pid, 0);
return true;
} catch (err) {
if (err.code === 'ESRCH') {
// 进程不存在
console.log(`进程 ${pid} 不存在`);
return false;
} else if (err.code === 'EPERM') {
// 没有权限访问该进程
console.log(`没有权限检查进程 ${pid}`);
return true;
} else {
// 其他错误
console.error(`检查进程 ${pid} 时出错:`, err);
return false;
}
}
};
const mainApp = new Command('main')
.description('初始化main的应用') //
.option('-i, --init', '初始化main应用')
.option('-s, --start', '启动main应用')
.option('-r, --restart', '重启main应用')
.option('-e, --exit', '停止main应用')
.option('-p, --pm2', '使用pm2管理只作为启动')
.action(async (options) => {
if (options.init) {
await initMain();
return;
} else if (!options.start && !options.restart && !options.exit && !options.pm2) {
console.warn(chalk.yellow('请使用 --init 初始化 main 应用'));
}
const runChild = () => {
const logDir = path.join(envisionPath, 'log');
const logFile = path.join(logDir, 'child.log');
// 确保日志目录存在
fs.mkdirSync(logDir, { recursive: true });
// 如果日志文件不存在,创建一个
if (!checkFileExists(logFile)) {
fs.writeFileSync(logFile, '');
}
// 如果日志文件大于 10M重命名
const stats = fs.statSync(logFile);
if (stats.size > 1024 * 1024 * 10) {
fs.renameSync(logFile, path.join(logDir, `child-${Date.now()}.log`));
fs.writeFileSync(logFile, '');
}
// 打开日志文件(追加模式)
const logStream = fs.openSync(logFile, 'a');
if (options.pm2) {
if (!checkPm2()) {
console.log('安装pm2');
installDep({ isGlobal: true, sync: true, dep: 'pm2' });
console.log(chalk.green('安装pm2成功'));
}
}
let comm = options.pm2 ? 'pm2' : 'node';
const args = options.pm2 ? ['start', 'dist/app.mjs', '--name', 'main-app'] : ['dist/app.mjs'];
const childProcess = spawn(comm, args, {
cwd: getConfig().mainAppPath,
stdio: ['ignore', logStream, logStream], // 忽略 stdio, 重定向到文件
detached: true, // 使子进程独立运行
});
childProcess.unref(); // 使子进程独立运行
if (!options.pm2) {
writeConfig({ ...getConfig(), mainAppPid: childProcess.pid });
}
};
const config = getConfig();
if (!config.mainAppPath) {
console.log('请先初始化 main 应用');
return;
}
if (options.start) {
if (config.mainAppPid) {
// 检查是否有进程
if (checkPid(config.mainAppPid)) {
console.log('main app 已经启动');
return;
}
}
runChild();
}
if (options.restart) {
if (config.mainAppPid) {
// 检查是否有进程
if (checkPid(config.mainAppPid)) {
process.kill(config.mainAppPid);
}
}
runChild();
} else if (options.exit) {
if (config.mainAppPid) {
// 检查是否有进程
if (checkPid(config.mainAppPid)) {
process.kill(config.mainAppPid);
}
writeConfig({ ...config, mainAppPid: null });
console.log('main app 已经停止');
}
}
});
const mainPathCommand = new Command('path').action(() => {
const config = getConfig();
const appPath = path.resolve(config.mainAppPath);
console.log(`cd ${appPath}`);
});
mainApp.addCommand(mainPathCommand);
const mainLogCommand = new Command('log')
.option('-t, --tail', '查看日志')
.option('-f, --follow', '跟踪日志')
.description('查看main的日志')
.action((options) => {
const logDir = path.join(envisionPath, 'log');
const logFile = path.join(logDir, 'child.log');
if (!checkFileExists(logFile)) {
console.log('日志文件不存在');
return;
}
if (options.tail) {
const childProcess = spawn('tail', ['-n', '20', '-f', logFile]);
childProcess.stdout?.pipe(process.stdout);
childProcess.stderr?.pipe(process.stderr);
return;
}
if (options.follow) {
const childProcess = spawn('tail', ['-n', '50', '-f', logFile]);
childProcess.stdout?.pipe(process.stdout);
childProcess.stderr?.pipe(process.stderr);
return;
}
const log = fs.readFileSync(logFile, 'utf-8');
console.log(log);
});
mainApp.addCommand(mainLogCommand);
program.addCommand(mainApp);