Require confirmation when uninstalling mods that are depended on

This commit is contained in:
Kallum Jones 2022-08-14 14:33:03 +01:00
parent 76f6c2c475
commit f7be866698
No known key found for this signature in database
GPG Key ID: D7F4589C4D7F81A9
6 changed files with 86 additions and 22 deletions

13
package-lock.json generated
View File

@ -19,7 +19,8 @@
"ora": "^6.1.2",
"pino": "^8.3.1",
"string-format": "^2.0.0",
"string-similarity-js": "^2.1.4"
"string-similarity-js": "^2.1.4",
"typescript-string-operations": "^1.4.1"
},
"devDependencies": {
"@types/node": "^18.6.3",
@ -835,6 +836,11 @@
"node": ">=4.2.0"
}
},
"node_modules/typescript-string-operations": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/typescript-string-operations/-/typescript-string-operations-1.4.1.tgz",
"integrity": "sha512-c+q+Tb0hxeebohdT9KpGUAm5zwxhU8pHeNOeuLCGFMXKN0OrldoAxtufrGLR3xSPCXDA4A3IBCEdRNNscVqLQg=="
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -1416,6 +1422,11 @@
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
"dev": true
},
"typescript-string-operations": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/typescript-string-operations/-/typescript-string-operations-1.4.1.tgz",
"integrity": "sha512-c+q+Tb0hxeebohdT9KpGUAm5zwxhU8pHeNOeuLCGFMXKN0OrldoAxtufrGLR3xSPCXDA4A3IBCEdRNNscVqLQg=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@ -21,7 +21,8 @@
"ora": "^6.1.2",
"pino": "^8.3.1",
"string-format": "^2.0.0",
"string-similarity-js": "^2.1.4"
"string-similarity-js": "^2.1.4",
"typescript-string-operations": "^1.4.1"
},
"devDependencies": {
"@types/node": "^18.6.3",

View File

@ -9,9 +9,9 @@ export default class UninstallCommand implements Subcommand {
.description("Uninstalls the provided mods")
.argument("<mods...>", "The mods to uninstall (as names or ids)")
.action((mods) => {
ModManager.execute(() => {
ModManager.execute(async () => {
for (let mod of mods) {
Mods.uninstall(mod);
await Mods.uninstall(mod);
}
})
})

View File

@ -7,6 +7,9 @@ import Util from "../util/util.js";
import ModManager from "../mod-manager.js";
import MinecraftUtils from "../util/minecraft_utils.js";
import MigrateError from "../errors/migrate_error.js";
import inquirer from "inquirer";
import { StringBuilder } from 'typescript-string-operations';
import chalk from "chalk";
export default class Mods {
private static readonly MOD_SOURCES: Array<ModSource> = [];
@ -97,26 +100,60 @@ export default class Mods {
return !Util.isArrayEmpty(modsWithId)
}
static uninstall(mod: string) {
private static getDependantMods(dependency: string) {
return this.getTrackedMods().filter(mod => mod.dependencies.includes(dependency))
}
static async uninstall(mod: string) {
// Find mod to uninstall
const modToUninstall = this.findMod(mod);
const spinner = new PrintUtils.Spinner(`Uninstalling ${mod}...`)
spinner.start();
const modToUninstall = this.findMod(mod);
// IF a matching mod is found, remove it
// If a matching mod is found, remove it
if (modToUninstall != undefined) {
this.silentUninstall(modToUninstall);
for (let dependency of modToUninstall.dependencies) {
if (!this.isDependedOn(dependency)) {
const dependencyMod = this.findMod(dependency);
if (dependencyMod != undefined) {
this.silentUninstall(dependencyMod)
}
}
// Ensure the user wants to delete this mod
let del = true;
if (Mods.isDependedOn(modToUninstall.id)) {
spinner.pause();
const dependantMods = Mods.getDependantMods(modToUninstall.id);
const answer = await inquirer.prompt([{
type: "input",
name: "delete",
message: chalk.yellowBright(`${modToUninstall.name} is depended on by ${Mods.modListToSting(dependantMods)}. Are you sure you would like to uninstall? (y/n)`),
async validate(input: any): Promise<string | boolean> {
const lowerInput = input.toLowerCase();
const valid = lowerInput === "y" || lowerInput === "n" ;
if (!valid) {
return "Please answer either y or n"
}
return valid
},
}])
del = Util.getBoolFromYesNo(answer.delete);
}
// If we are deleting this mod, uninstall it
if (del) {
spinner.start()
this.silentUninstall(modToUninstall);
// Remove any left over dependencies that are not depended on by any other mod
for (let dependency of modToUninstall.dependencies) {
if (!this.isDependedOn(dependency)) {
const dependencyMod = this.findMod(dependency);
if (dependencyMod != undefined) {
this.silentUninstall(dependencyMod)
}
}
}
spinner.succeed(`${modToUninstall.name} successfully uninstalled!`)
}
spinner.succeed(`${modToUninstall.name} successfully uninstalled!`)
} else {
spinner.error(`${mod} was not found.`)
}
@ -366,13 +403,14 @@ export default class Mods {
}
private static isDependedOn(dependency: string) {
const trackedMods = this.getTrackedMods();
return Mods.getDependantMods(dependency).length != 0
}
for (let trackedMod of trackedMods) {
if (trackedMod.dependencies.includes(dependency)) {
return true;
}
private static modListToSting(dependantMods: TrackedMod[]) {
const builder = new StringBuilder();
for (let dependantMod of dependantMods) {
builder.Append(dependantMod.name)
}
return false;
return builder.ToString();
}
}

View File

@ -24,6 +24,10 @@ export default class PrintUtils {
this.spinner.stopAndPersist();
}
public pause() {
this.spinner.stop();
}
public error(print: string | Error) {
if (print instanceof Error) {
this.spinner.fail(print.message)

View File

@ -21,4 +21,14 @@ export default class Util {
compare = compare.toLowerCase();
return stringSimilarity(master, compare) >= this.SIMILARITY_THRESHOLD;
}
static getBoolFromYesNo(answer: string) {
if (answer.toLowerCase() === "y") {
return true;
} else if (answer.toLowerCase() === "n") {
return false
} else {
throw new Error("Invalid answer to get a boolean value from")
}
}
}