From 9a2902456b1fc7c795ccec60420767bccbfd637e Mon Sep 17 00:00:00 2001 From: Richard Zampieri Date: Mon, 18 Nov 2024 20:50:45 -0800 Subject: [PATCH] feat: add remove provider command and enhance add provider functionality --- src/cli.ts | 3 +- src/help/form.ts | 7 +- src/providers/add/cli.ts | 31 ++++++- src/providers/add/form.ts | 166 +++++++++++++++++++++++--------------- 4 files changed, 137 insertions(+), 70 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 56aaa19..03c941b 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -13,7 +13,7 @@ import { generateProject } from "./generate"; import { helpCommand } from "./help/cli"; import { infoProject } from "./info"; import { createProject } from "./new"; -import { addProviderCMD } from "./providers"; +import { addProviderCMD, removeProviderCMD } from "./providers"; import { createExternalProviderCMD } from "./providers/create/cli"; import { printError } from "./utils/cli-ui"; import { scriptsCommand } from "./scripts"; @@ -34,6 +34,7 @@ yargs(hideBin(process.argv)) .command(prodCommand) .command(createExternalProviderCMD()) .command(addProviderCMD()) + .command(removeProviderCMD()) .command(generateProject()) .command(scriptsCommand()) .command(infoProject()) diff --git a/src/help/form.ts b/src/help/form.ts index a027853..812641a 100644 --- a/src/help/form.ts +++ b/src/help/form.ts @@ -27,7 +27,12 @@ const helpForm = async (): Promise => { ["dto", "g d", "Generate a dto"], ["entity", "g e", "Generate an entity"], ["provider", "g p", "Generate internal provider"], - ["provider", "add", "Add external provider to the project"], + [ + "provider", + "add", + "Add provider to the project. Use -d to add as dev dependency", + ], + ["provider", "remove", "Remove provider from the project"], ["provider", "create", "Create external provider"], ["module", "g mo", "Generate a module"], ["middleware", "g mi", "Generate a middleware"], diff --git a/src/providers/add/cli.ts b/src/providers/add/cli.ts index 7d92fe6..55bc610 100644 --- a/src/providers/add/cli.ts +++ b/src/providers/add/cli.ts @@ -1,5 +1,5 @@ import { Argv, CommandModule } from "yargs"; -import { addExternalProvider } from "./form"; +import { addProvider, removeProvider } from "./form"; // eslint-disable-next-line @typescript-eslint/ban-types type CommandModuleArgs = {}; @@ -17,13 +17,36 @@ export const addProviderCMD = (): CommandModule => { .option("version", { describe: "The provider version to be installed", type: "string", - default: "latest", + default: false, alias: "v", + }) + .option("dev", { + describe: "Add provider as a dev dependency", + type: "boolean", + default: false, + alias: "d", }); return yargs; }, - handler: async ({ provider, version }) => { - await addExternalProvider(provider, version); + handler: async ({ provider, version, dev }) => { + await addProvider(provider, version, dev); + }, + }; +}; + +export const removeProviderCMD = (): CommandModule => { + return { + command: "remove ", + describe: "Remove provider from the project.", + builder: (yargs: Argv): Argv => { + yargs.positional("provider", { + describe: "The provider to be removed from the project", + type: "string", + }); + return yargs; + }, + handler: async ({ provider: packageName }) => { + await removeProvider(packageName); }, }; }; diff --git a/src/providers/add/form.ts b/src/providers/add/form.ts index 88e99e1..8d53299 100644 --- a/src/providers/add/form.ts +++ b/src/providers/add/form.ts @@ -4,103 +4,141 @@ import fs from "node:fs"; import { exit } from "node:process"; import { printError } from "../../utils/cli-ui"; -export const addExternalProvider = async ( - provider: string, - version: string, -): Promise => { - await installProvider(provider, version); +type PackageManagerConfig = { + install: string; + addDev: string; + remove: string; }; -async function installProvider(provider: string, version: string) { - const packageManager = fs.existsSync( - "package-lock.json" || "yarn.lock" || "pnpm-lock.yaml", - ) - ? "npm" - : fs.existsSync("yarn.lock") - ? "yarn" - : fs.existsSync("pnpm-lock.yaml") - ? "pnpm" - : null; - - if (packageManager) { - console.log(`Installing ${provider} provider ...`); - const currentVersion = version === "latest" ? "" : `@${version}`; - await execProcess({ - commandArg: packageManager, - args: ["add", `${provider}${currentVersion}`, "--prefer-offline"], - directory: process.cwd(), - }); - } else { - printError( - "No package manager found in the project", - "install-provider", - ); - return; +type PackageManager = { + npm: PackageManagerConfig; + yarn: PackageManagerConfig; + pnpm: PackageManagerConfig; +}; + +const PACKAGE_MANAGERS: PackageManager = { + npm: { + install: "install", + addDev: "install --save-dev", + remove: "uninstall", + }, + yarn: { + install: "add", + addDev: "add --dev", + remove: "remove", + }, + pnpm: { + install: "add", + addDev: "add --save-dev", + remove: "remove", + }, +}; + +function detectPackageManager(): string | null { + const lockFiles = ["package-lock.json", "yarn.lock", "pnpm-lock.yaml"]; + const managers = Object.keys(PACKAGE_MANAGERS); + + for (let i = 0; i < lockFiles.length; i++) { + if (fs.existsSync(lockFiles[i])) { + return managers[i]; + } } + return null; } async function execProcess({ - commandArg, + command, args, directory, }: { - commandArg: string; + command: string; args: string[]; directory: string; -}) { +}): Promise { return new Promise((resolve, reject) => { - const isWindows: boolean = process.platform === "win32"; - const command: string = isWindows ? `${commandArg}.cmd` : commandArg; + const isWindows = process.platform === "win32"; + const execCommand = isWindows ? `${command}.cmd` : command; - const installProcess = spawn(command, args, { + const processRunner = spawn(execCommand, args, { cwd: directory, shell: true, }); - console.log( - chalk.bold.blue(`Executing: ${commandArg} ${args.join(" ")}`), - ); + console.log(chalk.bold.blue(`Executing: ${command} ${args.join(" ")}`)); console.log( chalk.yellow("-------------------------------------------------"), ); - installProcess.stdout.on("data", (data) => { - console.log(chalk.green(data.toString().trim())); // Display regular messages in green + processRunner.stdout.on("data", (data) => { + console.log(chalk.green(data.toString().trim())); }); - installProcess.stderr.on("data", (data) => { - console.error(chalk.red(data.toString().trim())); // Display error messages in red + processRunner.stderr.on("data", (data) => { + console.error(chalk.red(data.toString().trim())); }); - installProcess.on("close", (code) => { + processRunner.on("close", (code) => { if (code === 0) { console.log( - chalk.bold.green( - "-------------------------------------------------", - ), + chalk.bold.green("Operation completed successfully!\n"), ); - console.log(chalk.bold.green("Installation Done!\n")); - resolve("Installation Done!"); + resolve(); } else { console.error( - chalk.bold.red("---------------------------------------"), - ); - console.error( - chalk.bold.red( - `Command ${command} ${args.join( - " ", - )} exited with code ${code}`, - ), - ); - reject( - new Error( - `Command ${command} ${args.join( - " ", - )} exited with code ${code}`, - ), + chalk.bold.red(`Command failed with exit code ${code}`), ); + reject(new Error(`Command failed with exit code ${code}`)); exit(1); } }); }); } + +export async function addProvider( + packageName: string, + version?: string, + isDevDependency = false, +): Promise { + const packageManager = detectPackageManager(); + + if (!packageManager) { + printError("No package manager found in the project", "add-package"); + return; + } + + const pkgManagerConfig: PackageManagerConfig = + PACKAGE_MANAGERS[packageManager as keyof PackageManager]; + + const command = isDevDependency + ? pkgManagerConfig.addDev + : pkgManagerConfig.install; + const versionSuffix = version && version !== "latest" ? `@${version}` : ""; + + console.log( + `${isDevDependency ? "Adding devDependency" : "Installing"} ${packageName}...`, + ); + await execProcess({ + command: packageManager, + args: [...command.split(" "), `${packageName}${versionSuffix}`], + directory: process.cwd(), + }); +} + +export async function removeProvider(packageName: string): Promise { + const packageManager = detectPackageManager(); + + if (!packageManager) { + printError("No package manager found in the project", "remove-package"); + return; + } + + const command = + PACKAGE_MANAGERS[packageManager as keyof PackageManager].remove; + + console.log(`Removing ${packageName}...`); + await execProcess({ + command: packageManager, + args: [...command.split(" "), packageName], + directory: process.cwd(), + }); +}