-
Notifications
You must be signed in to change notification settings - Fork 0
/
prune.ts
98 lines (90 loc) · 3.36 KB
/
prune.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { Flags, flush, handle } from '@oclif/core'
import { ArgInput } from '@oclif/core/lib/parser'
import { eachSeries } from 'async'
import { PruneFlags, PrunePluginVaultOpts } from '../../commands'
import FactoryCommand, { FactoryFlags } from '../../providers/command'
import { safeLoadConfig } from '../../providers/config'
import { listInstalledPlugins, removePluginDir } from '../../providers/plugins'
import { vaultsSelector } from '../../providers/vaults'
import { VAULTS_PATH_FLAG_DESCRIPTION } from '../../utils/constants'
import { logger } from '../../utils/logger'
/**
* Prune command list and remove plugins that aren't referred in config file.
*/
export default class Prune extends FactoryCommand {
static readonly aliases = ['pp', 'plugins prune']
static override readonly description = `Prune existing plugin(s) from vaults that are unspecified in the config file.`
static override readonly examples = [
'<%= config.bin %> <%= command.id %> --path=/path/to/vaults',
'<%= config.bin %> <%= command.id %> --path=/path/to/vaults/*/.obsidian',
'<%= config.bin %> <%= command.id %> --path=/path/to/vaults/**/.obsidian',
]
static override readonly flags = {
path: Flags.string({
char: 'p',
description: VAULTS_PATH_FLAG_DESCRIPTION,
default: '',
}),
...this.commonFlags,
}
/**
* Executes the command.
* Parses the arguments and flags, and calls the action method.
* Handles errors and ensures flushing of logs.
*/
public async run() {
try {
const { args, flags } = await this.parse(Prune)
await this.action(args, this.flagsInterceptor(flags))
} catch (error) {
this.handleError(error)
} finally {
flush()
}
}
/**
* Main action method for the command.
* Loads vaults, selects vaults, loads configuration, and prunes unused plugins.
* @param {ArgInput} args - The arguments passed to the command.
* @param {CustomFlags} flags - The flags passed to the command.
* @returns {Promise<void>}
*/
private async action(
args: ArgInput,
flags: FactoryFlags<PruneFlags>,
): Promise<void> {
const { path } = flags
const {
success: loadConfigSuccess,
data: config,
error: loadConfigError,
} = await safeLoadConfig(flags.config)
if (!loadConfigSuccess) {
logger.error('Failed to load config', { error: loadConfigError })
process.exit(1)
}
const vaults = await this.loadVaults(path)
const selectedVaults = await vaultsSelector(vaults)
const vaultsWithConfig = selectedVaults.map((vault) => ({ vault, config }))
const prunePluginsIterator = async (opts: PrunePluginVaultOpts) => {
const { vault, config } = opts
const childLogger = logger.child({ vault })
const installedPlugins = await listInstalledPlugins(vault.path)
const referencedPlugins = config.plugins.map(({ id }) => id)
const toBePruned = installedPlugins.filter(
({ id }) => !referencedPlugins.includes(id),
)
for (const plugin of toBePruned) {
childLogger.debug(`Pruning plugin`, { plugin })
await removePluginDir(plugin.id, vault.path)
}
childLogger.info(`Pruned ${toBePruned.length} plugins`)
}
eachSeries(vaultsWithConfig, prunePluginsIterator, (error) => {
if (error) {
logger.debug('Error pruning plugins', { error })
handle(error)
}
})
}
}