@tsdiapi/cli
Version:
A command-line tool for creating and managing scalable TSDIAPI projects with built-in plugin integration and configuration support.
258 lines (253 loc) • 11.3 kB
JavaScript
import chalk from "chalk";
import inquirer from "inquirer";
import path from "path";
import fs from "fs-extra";
import ora from 'ora';
import figlet from "figlet";
import boxen from "boxen";
import { pastel } from "gradient-string";
import { getCdCommand, isDirSuitableToNewProject, isPathSuitableToNewProject } from '../utils/cwd.js';
import { installBaseDependencies, runUnsafeNpmScript } from '../utils/npm.js';
import { DefaultHost } from '../config.js';
import { DefaultPort } from "../config.js";
import { generateFeature } from './generate.js';
import { buildHandlebarsTemplate } from '../utils/handlebars.js';
import { findTSDIAPIServerProject } from '../utils/app-finder.js';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export async function startFastProject(projectDir) {
try {
if (!fs.existsSync(projectDir)) {
console.error(chalk.red("❌ Project directory does not exist:", projectDir));
process.exit(1);
}
console.log(chalk.yellow("⚡ Starting fast development mode..."));
await runUnsafeNpmScript(projectDir, "fast-dev");
}
catch (error) {
console.error(chalk.red("❌ An unexpected error occurred during project initialization."), error.message);
}
}
export async function initProject(_installpath, options) {
try {
let installpath = _installpath;
if (options?.startMode) {
const cwd = path.resolve(process.cwd(), installpath);
const currentDirectory = await findTSDIAPIServerProject(cwd);
if (currentDirectory) {
console.log(chalk.green(`🎯 Found existing TSDIAPI project at: ${chalk.bold(currentDirectory)}`));
await startFastProject(currentDirectory);
return;
}
}
console.log(pastel.multiline(figlet.textSync("TSDIAPI", { horizontalLayout: "full" })));
console.log(chalk.yellow("\n✨ Welcome to the TSDIAPI project initializer!\n"));
const questions = [];
// 📌 Шаг 3: Проверяем путь
let projectDir = isPathSuitableToNewProject(installpath);
if (!projectDir) {
return process.exit(1);
}
const dirAvailable = isDirSuitableToNewProject(installpath);
if (!dirAvailable) {
console.log(chalk.red(`❌ Error: Directory "${projectDir}" is not empty.`));
try {
const { newDir } = await inquirer.prompt([{
type: "input",
name: "newDir",
message: "📁 Enter a new directory name:",
validate: async (input) => {
return isDirSuitableToNewProject(input) ? true : "❌ Directory is not empty or invalid.";
}
}]);
installpath = newDir;
console.log(chalk.yellow(`📁 Using new directory: ${installpath}`));
projectDir = path.resolve(process.cwd(), newDir);
}
catch (error) {
console.error(chalk.red("❌ An unexpected error occurred during project initialization."), error.message);
process.exit(1);
}
}
let projectName = path.basename(projectDir);
options.name = projectName;
if (!options?.skipAll) {
questions.push({
type: "input",
name: "name",
message: "📦 Project name:",
default: projectName,
validate: (input) => input ? true : "❌ Project name is required."
});
questions.push({
type: "input",
name: "host",
message: "🌍 Host:",
default: DefaultHost,
validate: (input) => input ? true : "❌ Host is required."
});
questions.push({
type: "number",
name: "port",
message: "🔌 Port:",
default: DefaultPort,
validate: (input) => (input >= 1 && input <= 65535) ? true : "❌ Port must be between 1 and 65535."
});
}
const answers = questions.length ? await inquirer.prompt(questions) : {
...options,
name: projectName,
port: DefaultPort,
host: DefaultHost
};
answers.name = answers.name || projectName;
answers.host = answers.host || DefaultHost;
if (options) {
for (const [key, value] of Object.entries(options)) {
if (value !== undefined) {
answers[key] = value;
}
}
}
if (!answers.port) {
answers.port = DefaultPort;
}
// 🏗️ Шаг 5: Генерация файлов
const spinner = ora({
text: chalk.blue(`💾 Setting up project at ${chalk.bold(projectDir)}...`),
spinner: "dots"
}).start();
await installation(projectDir, answers);
spinner.succeed(chalk.green("Project files generated successfully!"));
// 🎯 Шаг 7: Финальный вывод
const cdCommand = getCdCommand(installpath);
console.log(chalk.green("\n🎉 Project successfully initialized!\n"));
try {
const { newFeatureAccepted } = await inquirer.prompt([{
type: "confirm",
name: "newFeatureAccepted",
message: "🚀 Do you want to create a new feature?",
default: false
}]);
if (newFeatureAccepted) {
const { featureName } = await inquirer.prompt([{
type: "input",
name: "featureName",
message: "🚀 Enter the name of the feature:",
validate: (input) => input ? true : "❌ Feature name is required."
}]);
await generateFeature(featureName, projectDir, true);
}
}
catch (error) {
console.error(chalk.red("❌ An unexpected error occurred during project initialization."), error.message);
}
const message = `
${chalk.yellow.bold('📦 Need more functionality? Extend your server with TSDIAPI plugins!')}
${chalk.cyan('◆')} Supports ${chalk.green('Prisma, Email, Sockets, Cron Jobs, and more.')}.
${chalk.cyan('◆')} Fully automated setup for easy integration.
${chalk.cyan('◆')} Just install with: ${chalk.cyan.bold('tsdiapi plugins add <pluginName>')}
${chalk.cyan('◆')} Or manually configure: ${chalk.cyan.bold('tsdiapi plugins config <pluginName>')}
${chalk.blue.bold('🌐 Explore all available plugins here:')} ${chalk.blue('https://www.npmjs.com/search?q=@tsdiapi')}
${chalk.magenta.bold('✨ More plugins coming soon!')}
${chalk.gray('────────────────────────────────────────────')}
${chalk.gray('💡 Want to contribute or ask something?')}
${chalk.cyan('📧 Contact:')} ${chalk.white('unbywyd@gmail.com')}
`;
console.log(boxen(message, {
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'blue'
}));
if (!options?.startMode) {
console.log(chalk.yellow("📌 Next steps:"));
if (cdCommand) {
console.log(`🔹 ${chalk.cyan(`${cdCommand}`)}`);
}
console.log(`🔹 ${chalk.cyan("npm run dev")}`);
}
console.log(chalk.green("\n🚀 Happy coding with TSDIAPI!\n"));
// 🚀 Шаг 8: Запуск в быстром режиме (если выбрано)
if (options?.startMode) {
await startFastProject(projectDir);
}
}
catch (error) {
console.error(chalk.red("❌ An unexpected error occurred during project initialization."), error.message);
process.exit(1);
}
}
export async function installation(projectDir, options) {
console.log(chalk.blue("\n🛠️ Initializing project structure...\n"));
const spinner = ora({
text: chalk.yellow("🚀 Copying base project files..."),
spinner: "dots"
}).start();
try {
const sourceDir = path.resolve(__dirname, "../files/root");
await fs.copy(sourceDir, projectDir);
spinner.succeed(chalk.green("Project files copied successfully!"));
const payload = {
...options,
port: options.port || DefaultPort,
};
const envFiles = [
{ path: ".env.development", isProduction: false },
{ path: ".env.production", isProduction: true },
];
for (const env of envFiles) {
const envPath = path.join(projectDir, env.path);
const envContent = buildHandlebarsTemplate("env", { ...payload, isProduction: env.isProduction });
if (envContent) {
await fs.writeFile(envPath, envContent);
}
}
// 📦 Генерация package.json
const packagePath = path.join(projectDir, "package.json");
const packageContent = buildHandlebarsTemplate("package", payload);
if (packageContent) {
await fs.writeFile(packagePath, packageContent);
}
const gitignore = `
node_modules
# Keep environment variables out of version control
#.env
#.env.development
#.env.production
dist
logs/*
`;
await fs.writeFile(path.join(projectDir, ".gitignore"), gitignore);
// 📂 Создание нужных директорий
await fs.ensureDir(path.join(projectDir, "src/api/features"));
await fs.ensureDir(path.join(projectDir, "src/public"));
// 🏡 Главная страница
const homePagePath = path.join(projectDir, "src/public/index.html");
const homePageContent = buildHandlebarsTemplate("home", payload);
if (homePageContent) {
await fs.writeFile(homePagePath, homePageContent);
}
// ⚙️ Конфигурация приложения
const appConfigPath = path.join(projectDir, "src/app.config.ts");
const appConfigContent = buildHandlebarsTemplate("app_config", payload);
if (appConfigContent) {
await fs.writeFile(appConfigPath, appConfigContent);
}
// 🏗 Основной файл
const mainPath = path.join(projectDir, "src/main.ts");
const mainContent = buildHandlebarsTemplate("main", payload);
if (mainContent) {
await fs.writeFile(mainPath, mainContent);
}
await installBaseDependencies(projectDir);
}
catch (error) {
spinner.fail(chalk.red("❌ Error during project setup!"));
console.error(chalk.red(`Error: ${error.message}`));
process.exit(1);
}
}
//# sourceMappingURL=init-project.js.map