diff --git a/src/commands/migrate_command.ts b/src/commands/migrate_command.ts new file mode 100644 index 0000000..9554e2a --- /dev/null +++ b/src/commands/migrate_command.ts @@ -0,0 +1,38 @@ +import Subcommand from "./subcommand.js"; +import {Command} from "commander"; +import ModManager from "../mod-manager.js"; +import MinecraftUtils from "../util/minecraft_utils.js"; +import Mods from "../mods/mods.js"; +import PrintUtils from "../util/print_utils.js"; +import MigrateError from "../errors/migrate_error.js"; + +export default class MigrateCommand implements Subcommand { + registerCommand(program: Command): void { + program.command("migrate") + .description("Migrates all mods to provided Minecraft version") + .argument("[version]", "The Minecraft version to try and migrate to") + .option("-f, --force", "Migrates all mods, but discards any non essential mods that are not available", false) + .action((version, options) => { + ModManager.execute(async () => { + const force = options.force; + + // If no version is provided, prompt user for one + if (version === "" || version == undefined) { + version = await MinecraftUtils.getMinecraftVersionFromInput("What Minecraft version would you like to migrate to?"); + } + + // Start the migration + try { + await Mods.migrate(version, force) + } catch (e) { + if (e instanceof MigrateError) { + PrintUtils.error(e.message) + } else { + throw e; + } + } + }) + }) + } + +} \ No newline at end of file diff --git a/src/commands/migrate_possible.ts b/src/commands/migrate_possible.ts index 0858060..6c2bf74 100644 --- a/src/commands/migrate_possible.ts +++ b/src/commands/migrate_possible.ts @@ -20,25 +20,10 @@ export default class MigratePossibleCommand implements Subcommand { version = await MinecraftUtils.getMinecraftVersionFromInput("What Minecraft version would you like to migrate to?"); } - // If version is valid, check if migration is possible - if (await MinecraftUtils.isValidVersion(version)) { - try { - const possible = await Mods.isMigratePossible(version, force); - - if (possible) { - PrintUtils.success(`It is possible to migrate to version ${version}`) - } else { - PrintUtils.error(`It is not possible to migrate to version ${version}`) - } - } catch (e) { - if (!force) { - PrintUtils.error("There are no mods installed, try `mod-manager install -h` to learn more!") - } else { - PrintUtils.error("There are no mods installed that are marked essential. Try `mod-manager essential -h` to learn more!") - } - } - } else { - PrintUtils.error(`${version} is not a valid Minecraft version`); + try { + await Mods.isMigratePossible(version, force); + } catch (e) { + PrintUtils.error(e.message) } } ) diff --git a/src/errors/migrate_error.ts b/src/errors/migrate_error.ts new file mode 100644 index 0000000..ef5b4c6 --- /dev/null +++ b/src/errors/migrate_error.ts @@ -0,0 +1,7 @@ +export default class MigrateError extends Error { + constructor(message: string | undefined) { + super(message); + this.name = this.constructor.name; + Error.captureStackTrace(this, this.constructor); + } +} \ No newline at end of file diff --git a/src/mod-manager.ts b/src/mod-manager.ts index b35a6f1..120fd72 100644 --- a/src/mod-manager.ts +++ b/src/mod-manager.ts @@ -13,6 +13,7 @@ import EssentialCommand from "./commands/essential_command.js"; import {readFileSync, unlinkSync} from "fs"; import UpdateCommand from "./commands/upgrade_command.js"; import MigratePossibleCommand from "./commands/migrate_possible.js"; +import MigrateCommand from "./commands/migrate_command.js"; export default class ModManager { @@ -27,7 +28,8 @@ export default class ModManager { new UninstallCommand(), new EssentialCommand(), new UpdateCommand(), - new MigratePossibleCommand() + new MigratePossibleCommand(), + new MigrateCommand() ]; static FilePaths = class { diff --git a/src/mods/mods.ts b/src/mods/mods.ts index 3b9fe10..1415a9c 100644 --- a/src/mods/mods.ts +++ b/src/mods/mods.ts @@ -7,6 +7,7 @@ import {readFileSync, unlinkSync, writeFileSync} from "fs"; 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"; export default class Mods { private static readonly MOD_SOURCES: Array = [ @@ -125,7 +126,7 @@ export default class Mods { // Remove mod from list mods = mods.filter(item => !Mods.areModsEqual(item, modToMark)); - // Toggle essnetial status, and write back to file + // Toggle essential status, and write back to file modToMark.essential = !modToMark.essential; mods.push(modToMark) this.writeFile(mods); @@ -212,7 +213,15 @@ export default class Mods { const mods = !force ? this.getTrackedMods() : this.getEssentialMods(); if (Util.isArrayEmpty(mods)) { - throw new Error("Mods list is empty") + const msg = !force ? "" + + "There are no mods installed, try `mod-manager install -h` to learn more!" : "" + + "There are no mods installed that are marked essential. Try `mod-manager essential -h` to learn more!"; + + throw new Error(msg) + } + + if (!await MinecraftUtils.isValidVersion(version)) { + throw new Error(`${version} is not a valid Minecraft version`) } let availableList = []; @@ -240,10 +249,72 @@ export default class Mods { availableList = availableList.filter(available => !available) // If the array is empty, all the mods reported as available, and a migration is possible - return Util.isArrayEmpty(availableList); + const possible = Util.isArrayEmpty(availableList); + + // Report and return whether it is possible + if (possible) { + PrintUtils.success(`It is possible to migrate to version ${version}`) + } else { + PrintUtils.error(`It is not possible to migrate to version ${version}`) + } + + return possible; } private static getEssentialMods() { return this.getTrackedMods().filter(mod => mod.essential); } + + /** + * Migrates to the provided version of minecraft + * @param version + * @param force + */ + static async migrate(version: string, force: boolean) { + const mods = this.getTrackedMods(); + + if (Util.isArrayEmpty(mods)) { + throw new MigrateError("There are no mods installed right now. Try `mod-manager install -h` to learn more!") + } + + if (!await MinecraftUtils.isValidVersion(version)) { + throw new MigrateError(`${version} is not a valid version of Minecraft`) + } + + if (!await Mods.isMigratePossible(version, force)) { + throw new MigrateError(`It is not possible to migrate to ${version}.`) + } + + // For every tracked mod + for (let mod of mods) { + const source = this.getSourceFromName(mod.source); + + const spinner = new PrintUtils.Spinner(`Uninstalling ${mod.name} ${mod.version}...`); + spinner.start(); + + try { + // Uninstall it + this.silentUninstall(mod); + + // Get the latest version + const latestVersion = await source.getLatestVersion(mod.id, version) + + // Install the new mod + spinner.updateText(`Installing ${mod.name} ${latestVersion.version_number}..`) + const newMod = await source.install(latestVersion, mod.essential) + this.trackMod(newMod) + + spinner.succeed(`Successfully installed ${newMod.name} ${newMod.version}`) + } catch (e) { + // If a mod is not available, but is essential, throw error, else, warn user, and continue. + if (mod.essential) { + throw new Error("Attempted to migrate a mod that is not available for migration") + } else { + spinner.warn(`${mod.name} is not available. Discarding...`) + } + } + } + + PrintUtils.success(`Successfully migrated to ${version}`) + } } \ No newline at end of file