UNPKG

mantis-app

Version:

M.A.N.T.I.S (MongoDB, Analog, Nx, Tailwind CSS, Ionic, Storybook) is not just a CLI tool; it's your passport to a seamless full-stack project launch.

864 lines (850 loc) 27.6 kB
import { Command, program } from 'commander'; import Logger from '@ptkdev/logger'; import fs from 'fs'; import path from 'path'; import shell from 'shelljs'; import { fileURLToPath } from 'url'; import figlet from 'figlet'; import gradient from 'gradient-string'; import 'chalk-animation'; import chalk from 'chalk'; import fs$1 from 'fs-extra'; import { installDependencies } from 'nypm'; import { execa } from 'execa'; import Enquirer from 'enquirer'; import { MongoClient } from 'mongodb'; import ora from 'ora'; import logSymbols from 'log-symbols'; import { execSync } from 'child_process'; import Spinnies from 'spinnies'; import portscanner from 'portscanner'; var __defProp$2 = Object.defineProperty; var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$2 = (obj, key, value) => { __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; const __dirname$1 = fileURLToPath(new URL(".", import.meta.url)); class OrbitLogger { constructor(context, options) { __publicField$2(this, "logger"); __publicField$2(this, "context"); __publicField$2(this, "logDir", path.join(__dirname$1, "../logs/")); __publicField$2(this, "defaultOptions", { language: "en", colors: true, debug: false, info: true, warning: true, error: true, sponsor: true, write: true, type: "log", rotate: { size: "10M", encoding: "utf8" }, path: { debug_log: path.join(this.logDir, "debug.log"), error_log: path.join(this.logDir, "errors.log") } }); this.context = context; this.logger = new Logger(options || this.defaultOptions); if (!fs.existsSync(this.logDir)) { fs.mkdirSync(this.logDir, { recursive: true }); shell.touch(this.defaultOptions?.path?.debug_log); shell.touch(this.defaultOptions?.path?.error_log); } } debug(message, dump, tag) { this.logger.debug( `${message}${dump ? " [DUMP]\u23EC: " : ""}`, tag || this.context ); if (dump) { this.logger.debug(JSON.stringify(dump)); } } info(message, tag) { return this.logger.info(message, tag || this.context); } warning(message, tag) { return this.logger.warning(message, tag || this.context); } error(message, tag) { return this.logger.error(message, tag || this.context); } sponsor(message, tag) { return this.logger.sponsor(message, tag || this.context); } stackoverflow(message, error_string, tag) { return this.logger.stackoverflow( message, error_string, tag || this.context ); } docs(message, url, tag) { return this.logger.docs(message, url, tag || this.context); } } const clearConsole = () => { process.stdout.write("\x1Bc"); console.log(""); }; const mantisGradientColors = ["#d9ed92", "#52b69a", "#184e77"]; function printWithMantisGradient(text) { const mantisGradient = gradient(mantisGradientColors); return console.log(mantisGradient(text)); } function printWithBadge(options) { const { color = "cyan", text, badgeName = "MANTIS" } = options; let cliPrefix = ""; if (chalk[color]) { cliPrefix = `${chalk[color](">")} ${chalk.reset.inverse.bold[color](` ${badgeName} `)}`; } else { cliPrefix = `${chalk.keyword(color)(">")}${chalk.reset.inverse.bold.keyword( color )(` ${badgeName} `)}`; } return `${cliPrefix} ${text}`; } const welcome = async () => { clearConsole(); const text = figlet.textSync("MANTIS-CLI", { horizontalLayout: "full" }); return printWithMantisGradient(text); }; const name = "mantis-app"; const version = "0.9.2"; const author = "FuturizeWorld"; const license = "SEE LICENSE IN <LICENSE> FILE"; const description = "M.A.N.T.I.S (MongoDB, Analog, Nx, Tailwind CSS, Ionic, Storybook) is not just a CLI tool; it's your passport to a seamless full-stack project launch."; const keywords = [ "MongoDB", "Analog", "Nx", "Tailwind", "CSS", "Ionic", "Storybook" ]; const homepage = "https://github.com/mantis-apps/mantis-cli#mantis-cli"; const repository = { type: "git", url: "git+https://github.com/mantis-apps/mantis-cli.git" }; const bugs = { url: "https://github.com/mantis-apps/mantis-cli/issues" }; const engines = { node: ">= 18" }; const type = "module"; const bin = { "mantis-app": "./bin/mantis-app.mjs" }; const files = [ "dist", "templates" ]; const scripts = { "start:dev": "npm run build && node dist/mantis-app.mjs", build: "unbuild", "type-check": "tsc --pretty --noEmit", format: "prettier --write", "format:all": "npm run format -- .", lint: "eslint --fix", "lint:all": "npm run lint -- .", start: "node dist/mantis.mjs", prepublishOnly: "npm run build", release: "release-it", prepare: "husky", "install:locally": "npm run build && npm install -g ." }; const dependencies = { "@ptkdev/logger": "^1.8.0", chalk: "4.1.2", "chalk-animation": "1.6.0", "cli-table3": "^0.6.3", clui: "^0.3.6", commander: "^11.1.0", configstore: "^6.0.0", enquirer: "^2.4.1", execa: "^8.0.1", figlet: "^1.7.0", "fs-extra": "^11.2.0", "gradient-string": "^2.0.2", "log-symbols": "^6.0.0", mongodb: "^6.3.0", "node-emoji": "^2.1.3", nypm: "^0.3.6", ora: "^8.0.1", portscanner: "^2.2.0", shelljs: "^0.8.5", spinnies: "^0.5.1", "tree-kill": "^1.2.2" }; const devDependencies = { "@commitlint/cli": "18.4.3", "@commitlint/config-angular": "18.4.3", "@release-it/conventional-changelog": "^8.0.1", "@types/chalk-animation": "^1.6.3", "@types/figlet": "^1.5.8", "@types/fs-extra": "^11.0.4", "@types/gradient-string": "^1.1.5", "@types/inquirer": "^9.0.7", "@types/node": "^20.9.4", "@types/node-emoji": "^2.1.0", "@types/portscanner": "^2.1.4", "@types/shelljs": "^0.8.15", "@types/spinnies": "^0.5.3", "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.3.1", commitizen: "^4.3.0", "cross-env": "^7.0.3", "cz-conventional-changelog": "^3.3.0", eslint: "^8.57.0", husky: "^9.0.11", "lint-staged": "^15.1.0", prettier: "^3.2.5", "release-it": "^17.0.0", rimraf: "^5.0.5", tslib: "^2.6.2", typescript: "^5.3.2", unbuild: "^2.0.0" }; const packageJson = { name: name, version: version, author: author, license: license, description: description, keywords: keywords, homepage: homepage, repository: repository, bugs: bugs, engines: engines, type: type, bin: bin, files: files, scripts: scripts, dependencies: dependencies, devDependencies: devDependencies, "lint-staged": { "**/*.{ts,json}": [ ] } }; function changeDirectory(targetDir) { try { process.chdir(targetDir); new OrbitLogger("DIRECTORY-CHANGER").info( `Changed directory to: ${targetDir}` ); } catch (error) { new OrbitLogger("DIRECTORY-CHANGER").error( `Error changing directory: ${error.message}` ); throw error; } } function createDirectory(options, paths) { paths = typeof paths === "string" ? [paths] : paths; const dirTypo = typeof paths === "string" ? "Directory" : "Directories"; try { shell.mkdir(options, paths); new OrbitLogger("DIRECTORY-CREATOR").info( `${dirTypo} created: ${paths.join(", ")}` ); } catch (error) { new OrbitLogger("DIRECTORY-CREATOR").error( `Error creating ${dirTypo}: ${error.message}` ); throw error; } } function moveFile(sources, dest) { try { shell.mv(sources, dest); new OrbitLogger("FILE-MOVER").info( `Moved ${Array.isArray(sources) ? sources.join(", ") : sources} to ${dest}` ); } catch (error) { new OrbitLogger("FILE-MOVER").error( `Error moving file(s): ${error.message}` ); throw error; } } function removeFile(options, files) { try { shell.rm(options, files); new OrbitLogger("FILE-REMOVER").info( `Removed ${Array.isArray(files) ? files.join(", ") : files}` ); } catch (error) { new OrbitLogger("FILE-REMOVER").error( `Error removing file(s): ${error.message}` ); throw error; } } const logger$1 = new OrbitLogger("FILE-REPLACER"); function replaceInFiles({ dir, searchStr, replaceStr }) { if (searchStr === replaceStr) return; try { fs.readdirSync(dir).forEach((file) => { const filePath = path.join(dir, file); const stats = fs.statSync(filePath); if (stats.isFile()) { const content = fs.readFileSync(filePath, "utf8"); if (content.match(searchStr)) { const newContent = content.replace(searchStr, replaceStr); fs.writeFileSync(filePath, newContent, "utf8"); } } else if (stats.isDirectory()) { replaceInFiles({ dir: filePath, searchStr, replaceStr }); } }); } catch (error) { logger$1.error(`Error replacing in files: ${error.message}`); throw error; } } function replaceInFile(filePath, searchStr, replaceStr) { const logger2 = new OrbitLogger("FILE-REPLACER"); try { let content = fs.readFileSync(filePath, "utf8"); const originalContent = content; content = content.replace(new RegExp(searchStr, "g"), replaceStr); fs.writeFileSync(filePath, content, "utf8"); logger2.info(`Replaced occurrences in file: ${filePath}`); if (originalContent === content) { logger2.info(`No replacements made in file: ${filePath}`); } } catch (error) { logger2.error(`Error replacing in file: ${filePath}, ${error.message}`); throw error; } } const __dirname = fileURLToPath(new URL(".", import.meta.url)); const initCommand = new Command("init").description("Create a basic mantis app").action(async () => { const templateName = "mantis-todo"; const templatePath = path.resolve( __dirname, `../templates/${templateName}` ); const workspaceName = await promptForWorkspaceName({ defaultName: templateName }); const workspacePath = path.join(process.cwd(), workspaceName); const { isLocal, dbUrl } = await promptForDbUrl(); await copyTemplate({ template: { name: templateName, path: templatePath }, workspace: { name: workspaceName, path: workspacePath }, secrets: { dbUrl } }); await installDependenciesWithMessage(workspacePath); await startApplications(workspacePath, { startLocalDb: isLocal }); }); const promptForWorkspaceName = async ({ defaultName }) => { const { workspaceName } = await Enquirer.prompt([ { type: "input", name: "workspaceName", message: "Enter the name of the workspace to create:", initial: defaultName, validate: async (value) => { if (value.trim().length <= 0) return "Workspace name must not be empty"; if (await fs$1.pathExists(value)) { return `Workspace already exists: ${value}`; } return true; } } ]); return workspaceName; }; const promptForDbUrl = async () => { const { dbType } = await Enquirer.prompt({ type: "select", name: "dbType", message: "How do you want to connect to the database?", choices: [ { name: "Create a local development mongo db for me (default)", value: "local" }, { name: "Provide a mongo connection string", value: "dbUrl" } ], // This is to workaround a bug // https://github.com/enquirer/enquirer/issues/121 result(choice) { return this.map(choice)[choice]; } }); if (dbType === "local") { return { isLocal: true, dbUrl: "mongodb://127.0.0.1:27017" }; } const { dbUrl } = await Enquirer.prompt([ { type: "input", name: "dbUrl", message: "Enter the url for the mongo db to connect to:", validate: async (value) => { if (value.trim().length <= 0) return "Db url must not be empty"; try { await MongoClient.connect(value); return true; } catch (error) { return `Db url seems to be invalid: ${error.message}`; } } } ]); return { isLocal: false, dbUrl }; }; const copyTemplate = async ({ template, workspace, secrets: { dbUrl } }) => { const spinner = ora("Creating workspace").start(); await fs$1.copy(template.path, workspace.path); await renameGitignoreFiles(workspace.path); const envPath = path.join(workspace.path, "apps/web-client/.env.local"); await fs$1.ensureFile(envPath); await fs$1.appendFile(envPath, ` MONGODB_URI='${dbUrl}'`); replaceInFiles({ dir: workspace.path, searchStr: template.name, replaceStr: workspace.name }); spinner.succeed("Created workspace"); }; const renameGitignoreFiles = async (workspacePath) => { const gitignoreFiles = await fs$1.readdir(workspacePath, { withFileTypes: true, recursive: true }); await Promise.all( gitignoreFiles.filter((entry) => entry.isFile() && entry.name === "_gitignore").map(async (entry) => { await fs$1.rename( path.join(entry.path, entry.name), path.join(entry.path, ".gitignore") ); }) ); }; const promptForPackageManagerSelect = async () => { const temp = await Enquirer.prompt({ type: "select", name: "name", message: "Select the package manager you'd like to use:", choices: ["npm", "bun", "pnpm", "yarn"], // This is to workaround a bug // https://github.com/enquirer/enquirer/issues/121 result(choice) { return this.map(choice)[choice]; } }); return temp; }; const installDependenciesWithMessage = async (workspacePath) => { const pm = await promptForPackageManagerSelect(); const spinner = ora("Installing dependencies").start(); await installDependencies({ packageManager: pm.name, cwd: workspacePath, silent: true }); spinner.succeed("Installed dependencies"); }; const startApplications = async (workspacePath, { startLocalDb }) => { console.log(`${logSymbols.info} Starting applications...`); try { await execa( "npx", [ "nx", "run-many", startLocalDb ? "--targets=serve,start-local-db" : "--target=serve", "--projects=web-client,mobile-client" ], { cwd: workspacePath, stdio: "inherit" } ); } catch (err) { if (err && typeof err === "object" && "exitCode" in err && // 130 is the exit code for SIGINT (ctrl + c) // i.e. the user cancelled the process err.exitCode === 130) { return; } console.error( `${logSymbols.error} Failed to start applications: ${err instanceof Error ? err.message : "Unknown cause"}` ); } }; function isValidVariableName(variableName) { const variableNameRegex = /^[a-zA-Z_][a-zA-Z0-9_]*$/; return variableNameRegex.test(variableName); } var __defProp$1 = Object.defineProperty; var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField$1 = (obj, key, value) => { __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; class Action { constructor(loggerContext) { __publicField$1(this, "logger"); this.logger = new OrbitLogger(loggerContext); } } const spinnies = new Spinnies(); function execCommand(options) { const { command, cwd, useSpinner = false } = options; try { if (useSpinner) { spinnies.add("execCommand", { text: "Executing command..." }); } if (options.crossPlatform) { execSync(`shx ${command}`, { stdio: "inherit", cwd }); } else { execSync(command, { stdio: "inherit", cwd }); } if (useSpinner) { spinnies.succeed("execCommand", { text: "Command executed successfully." }); } } catch (error) { if (useSpinner) { spinnies.fail("execCommand", { text: "Failed to execute command." }); } new OrbitLogger("COMMAND-EXECUTOR").error( `Error executing command: ${error.message}` ); throw error; } } async function checkPortAvailability(options) { const { port, envName, host = "127.0.0.1" } = options; const logger = new OrbitLogger("PORT-CHECKER"); let checkPort = port; if (envName && process.env[envName]) { logger.info( `Using Environment Defined Port for ${envName}: [${process.env[envName]}]` ); checkPort = Number(process.env[envName]); } try { const status = await portscanner.checkPortStatus(checkPort, host); if (status === "closed") { logger.info(`Port ${checkPort} on host ${host} is available`); return true; } else { logger.warning(`Port ${checkPort} on host ${host} is already in use`); return false; } } catch (error) { logger.error( `Error checking port ${checkPort} on host ${host}: ${error.message}` ); throw error; } } async function findAvailablePort(options) { const { startPort, endPort, host = "127.0.0.1" } = options; const logger = new OrbitLogger("PORT-FINDER"); try { const availablePort = await portscanner.findAPortNotInUse( startPort, endPort, host ); logger.info(`Found available port: ${availablePort}`); return availablePort; } catch (error) { logger.error(`Error finding available port: ${error.message}`); throw error; } } var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; class StartAction extends Action { constructor(options) { super("[START-ACTION]"); __publicField(this, "options"); __publicField(this, "createMobileApp", false); this.options = options; } async execute() { printWithMantisGradient(`\u{1F6D6} NX WORKSPACE CREATION \u{1F6E0}\uFE0F`); this.logger.debug(`Started [START] Action...`, this.options); console.log(printWithBadge({ text: "Hello World !" })); const { workspace, createMobileApp } = this.options && this.options.workspace ? this.options : await this.getWorkspaceInfo(); this.createMobileApp = createMobileApp || false; this.logger.debug(`Prompts Answers: ${workspace} - ${createMobileApp}`); try { this.createNxWorkspace(workspace); changeDirectory(path.resolve(workspace)); this.installDependencies(); this.createApplications(workspace); this.createLibraryDirectories(); this.installAngularPackage(); this.generateFeatureLibrary(); this.moveContent(workspace); this.cleanupDirectories(workspace); this.replaceHomePage(); this.updateImportPaths(workspace); this.launchApplications(); this.logger.info("Workspace setup complete!"); } catch (error) { this.logger.error( `Error occurred during workspace setup: ${error.message}` ); throw new Error(error); } } async getWorkspaceInfo() { return Enquirer.prompt([ { type: "input", name: "workspace", message: "Enter the name of the workspace (default is mantis):", initial: "mantis", validate: (value) => isValidVariableName(value) } ]); } createNxWorkspace(workspace) { try { this.logger.info(`Creating NX workspace in ${process.cwd()}...`); const command = `npx -y create-nx-workspace ${workspace} --preset nest --name ${workspace} --appName server --nxCloud false --docker true --create`; execCommand({ command, useSpinner: false }); this.logger.info(`NX workspace '${workspace}' created successfully.`); } catch (error) { this.logger.error(`Failed to create NX workspace: ${error.message}`); throw new Error(error); } } installDependencies() { this.logger.info("Installing necessary plugins and dependencies..."); execCommand({ command: "npm install --save-dev @nxext/ionic-angular @nxext/capacitor", useSpinner: false }); } createApplications(workspace) { const slugifiedName = workspace.toLowerCase().replace(/\s+/g, "-"); this.logger.info(`Creating Ionic applications in ${process.cwd()}...`); execCommand({ command: `npx nx generate @nxext/ionic-angular:application ${slugifiedName}-app --template=blank`, useSpinner: false }); if (this.createMobileApp) { execCommand({ command: `npx nx generate @nxext/ionic-angular:application ${slugifiedName}-mobile --template=blank`, useSpinner: false }); } } createLibraryDirectories() { this.logger.info("Creating library directories..."); const LibsFolders = ["libs/shared/home", "libs/web"]; if (this.createMobileApp) LibsFolders.push("libs/mobile"); createDirectory("-p", LibsFolders); } installAngularPackage() { this.logger.info("Installing @nrwl/angular package..."); execCommand({ command: "npm install --save-dev @nrwl/angular", useSpinner: false }); } generateFeatureLibrary() { this.logger.info("Generating new feature library..."); execCommand({ command: "npx nx generate @nx/angular:library --name=feature --directory=shared/home --inlineStyle=true --inlineTemplate=true --standalone=true --no-interactive", useSpinner: false }); } moveContent(workspace) { const slugifiedName = workspace.toLowerCase().replace(/\s+/g, "-"); const workspaceRoot = process.cwd(); const sourcePath = `${workspaceRoot}/apps/${slugifiedName}-app/src/app/home`; const destinationPath = `${workspaceRoot}/libs/shared/home/feature/src/lib`; if (fs.existsSync(sourcePath)) { this.logger.info("Moving content..."); moveFile(`${sourcePath}/*`, destinationPath); } else { this.logger.error(`Directory ${sourcePath} not found.`); } } cleanupDirectories(workspace) { const slugifiedName = workspace.toLowerCase().replace(/\s+/g, "-"); const workspaceRoot = process.cwd(); const webAppPath = `${workspaceRoot}/apps/${slugifiedName}-app/src/app/home`; const mobileAppPath = `${workspaceRoot}/apps/${slugifiedName}-mobile/src/app/home`; const featureLibPath = `${workspaceRoot}/libs/shared/home/feature/src/lib/shared-home-feature`; const pathsToRemove = [webAppPath, featureLibPath]; if (this.createMobileApp) pathsToRemove.push(mobileAppPath); this.logger.info("Cleaning up unnecessary directories..."); removeFile("-rf", pathsToRemove); } replaceHomePage() { const workspaceRoot = process.cwd(); const featureLibPath = `${workspaceRoot}/libs/shared/home/feature/src/lib`; this.logger.info( "Replacing instances of HomePage with HomePageComponent..." ); replaceInFiles({ dir: featureLibPath, searchStr: "HomePage", replaceStr: "HomePageComponent" }); } updateImportPaths(workspace) { const workspaceRoot = process.cwd(); const slugifiedName = workspace.toLowerCase().replace(/\s+/g, "-"); const featureLibIndexPath = path.resolve( `${workspaceRoot}/libs/shared/home/feature/src/index.ts` ); const webAppRoutingModulePath = path.resolve( `${workspaceRoot}/apps/${slugifiedName}-app/src/app/app-routing.module.ts` ); const webAppGlobalStylesPath = path.resolve( `${workspaceRoot}/apps/${slugifiedName}-app/src/styles.scss` ); const mobileAppRoutingModulePath = this.createMobileApp ? path.resolve( `${workspaceRoot}/apps/${slugifiedName}-mobile/src/app/app-routing.module.ts` ) : ""; const sharedHomeFeaturePath = `@${workspace}/shared/home/feature`; this.logger.info("-> Updating import paths..."); replaceInFile(webAppGlobalStylesPath, "~@", "@"); replaceInFile( featureLibIndexPath, "lib/shared-home-feature/shared-home-feature.component", "lib/home.module" ); replaceInFile( webAppRoutingModulePath, "import\\('./home/home.module'\\)\\.then\\(\\(m\\) => m\\.HomePageModule\\)", `import('${sharedHomeFeaturePath}').then((m) => m.HomePageComponentModule)` ); if (this.createMobileApp) replaceInFile( mobileAppRoutingModulePath, "import\\('./home/home.module'\\)\\.then\\(\\(m\\) => m\\.HomePageModule\\)", `import('${sharedHomeFeaturePath}').then((m) => m.HomePageComponentModule)` ); } installConcurrentlyPackage() { this.logger.info("Installing concurrently package..."); execCommand({ command: "npm install --save-dev concurrently", useSpinner: false }); } async checkPorts() { const webPort = Number(process.env.WebPort) || 4200; const mobilePort = Number(process.env.MobilePort) || 4300; const serverPort = Number(process.env.ServerPort) || 3e3; const isWebPortAvailable = await checkPortAvailability({ port: webPort }); const isMobilePortAvailable = this.createMobileApp ? await checkPortAvailability({ port: mobilePort }) : true; const isServerPortAvailable = await checkPortAvailability({ port: serverPort }); if (!isWebPortAvailable) { this.logger.warning(`Port ${webPort} (Web) is already in use`); process.env.WebPort = (await findAvailablePort({ startPort: webPort, endPort: webPort + 99 })).toString(); } if (!isMobilePortAvailable) { this.logger.warning(`Port ${mobilePort} (Mobile) is already in use`); process.env.MobilePort = (await findAvailablePort({ startPort: mobilePort, endPort: mobilePort + 99 })).toString(); } if (!isServerPortAvailable) { this.logger.warning(`Port ${serverPort} (Server) is already in use`); process.env.ServerPort = (await findAvailablePort({ startPort: serverPort, endPort: serverPort + 99 })).toString(); } } launchApplications() { const command = "npx nx run-many --target=serve --all --maxParallel=100"; try { this.logger.info("Launching applications..."); execCommand({ command, useSpinner: true }); this.logger.sponsor("Applications launched successfully."); } catch (error) { this.logger.error(`Error launching applications: ${error.message}`); throw new Error(error); } } } const startCommand = new Command("start").description("Description of someCommand").option("-w, --workspace <name>", "Specify the name of the workspace").option("--createMobileApp", "Create a Mobile App").action(async () => { await new StartAction({ workspace: "" }).execute(); }); const logger = new OrbitLogger("[BOOTSTRAP]"); const handleExit = () => { logger.info("Exiting MANTIS-CLI \u{1F613}..."); process.exit(1); }; const bootstrap = async () => { try { await welcome(); logger.debug("\u{1F680} STARTED MANTIS-CLI \u{1F680}"); process.on("SIGINT", handleExit); program.version( packageJson.version, "-v, --version", "Output the current version." ).usage("<command> [options]").option("-v, --verbose", "Enable verbose mode").helpOption("-h, --help", "Output usage information."); program.addCommand(initCommand); program.addCommand(startCommand); await program.parseAsync(process.argv); if (!process.argv.slice(2).length) { program.outputHelp(); } } catch (error) { if (!error) { return handleExit(); } logger.error("Error During Bootstrap"); console.error(error); } finally { process.removeListener("SIGINT", handleExit); } }; void bootstrap();