From 27504a2b0ee2b348ad61e50261153c15ed278551 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Wed, 31 Jan 2024 11:01:33 -0700 Subject: [PATCH] feat: reset --reinstall --hard (#777) * feat: reset --reinstall --hard * chore: code review * fix: show linked plugin version * chore: missing arrow, reinstall 'hearder' --------- Co-authored-by: Eric Willhoit --- src/commands/plugins/reset.ts | 77 +++++++++++++++++++++++++++++++++-- src/plugins.ts | 6 ++- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/src/commands/plugins/reset.ts b/src/commands/plugins/reset.ts index c4552a35..206cb483 100644 --- a/src/commands/plugins/reset.ts +++ b/src/commands/plugins/reset.ts @@ -1,20 +1,89 @@ -import {Command} from '@oclif/core' +/* eslint-disable no-await-in-loop */ +import {Command, Flags} from '@oclif/core' import chalk from 'chalk' +import {rm} from 'node:fs/promises' +import {join} from 'node:path' import Plugins from '../../plugins.js' export default class Reset extends Command { + static flags = { + hard: Flags.boolean({ + summary: 'Delete node_modules and package manager related files in addition to uninstalling plugins.', + }), + reinstall: Flags.boolean({ + summary: 'Reinstall all plugins after uninstalling.', + }), + } + static summary = 'Remove all user-installed and linked plugins.' async run(): Promise { + const {flags} = await this.parse(Reset) const plugins = new Plugins(this.config) const userPlugins = await plugins.list() - this.log(`Uninstalling ${userPlugins.length} plugin${userPlugins.length === 0 ? '' : 's'}`) + this.log(`Found ${userPlugins.length} plugin${userPlugins.length === 0 ? '' : 's'}:`) for (const plugin of userPlugins) { - this.log(`• ${plugin.name} ${chalk.dim(`(${plugin.type})`)}`) + this.log( + `- ${plugin.name} ${chalk.dim(this.config.plugins.get(plugin.name)?.version)} ${chalk.dim(`(${plugin.type})`)}`, + ) } - await Promise.all(userPlugins.map(async (plugin) => plugins.uninstall(plugin.name))) + if (flags.hard) { + const filesToDelete = [ + join(this.config.dataDir, 'node_modules'), + join(this.config.dataDir, 'package.json'), + join(this.config.dataDir, 'yarn.lock'), + join(this.config.dataDir, 'package-lock.json'), + ] + + this.log('✅ Removed the following files:') + for (const file of filesToDelete) { + this.log(`- ${file}`) + } + + await Promise.all(filesToDelete.map((file) => rm(file, {force: true, recursive: true}))) + + for (const plugin of userPlugins) { + this.log(`✅ ${plugin.type === 'link' ? 'Unlinked' : 'Uninstalled'} ${plugin.name}`) + } + } else { + // These need to run sequentially so as to avoid write conflicts to the package.json + for (const plugin of userPlugins) { + try { + await plugins.uninstall(plugin.name) + this.log(`✅ ${plugin.type === 'link' ? 'Unlinked' : 'Uninstalled'} ${plugin.name}`) + } catch { + this.warn(`Failed to uninstall ${plugin.name}`) + } + } + } + + if (flags.reinstall) { + this.log('Reinstall flag passed:') + // These need to run sequentially so as to avoid write conflicts to the package.json + for (const plugin of userPlugins) { + if (plugin.type === 'link') { + try { + const newPlugin = await plugins.link(plugin.root, {install: false}) + const newVersion = chalk.dim(`-> ${newPlugin.version}`) + this.log(`✅ Relinked ${plugin.name} ${newVersion}`) + } catch { + this.warn(`Failed to relink ${plugin.name}`) + } + } + + if (plugin.type === 'user') { + try { + const newPlugin = await plugins.install(plugin.name, {tag: plugin.tag}) + const newVersion = chalk.dim(`-> ${newPlugin.version}`) + this.log(`✅ Reinstalled ${plugin.name}@${plugin.tag} ${newVersion}`) + } catch { + this.warn(`Failed to reinstall ${plugin.name}`) + } + } + } + } } } diff --git a/src/plugins.ts b/src/plugins.ts index bc20bb01..37e30b20 100644 --- a/src/plugins.ts +++ b/src/plugins.ts @@ -158,14 +158,16 @@ export default class Plugins { } } - async link(p: string, {install}: {install: boolean}): Promise { + async link(p: string, {install}: {install: boolean}): Promise { const c = await Config.load(resolve(p)) - ux.action.start(`${this.config.name}: linking plugin ${c.name}`) + this.isValidPlugin(c) // refresh will cause yarn.lock to install dependencies, including devDeps if (install) await this.refresh({all: false, prod: false}, c.root) await this.add({name: c.name, root: c.root, type: 'link'}) + + return c } async list(): Promise<(Interfaces.PJSON.PluginTypes.Link | Interfaces.PJSON.PluginTypes.User)[]> {