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
JavaScript
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();