From efd1f546079d5d0c9abe26df372ab657cfd389b0 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Mon, 28 Aug 2023 14:34:37 -0700 Subject: [PATCH] feat: reload Config before running command (#770) * feat: reload Config before running command * fix: better implementation * fix: better implementation --- src/config/config.ts | 31 ++++++++++++++++++++++++++++--- src/interfaces/plugin.ts | 2 ++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/config/config.ts b/src/config/config.ts index a92acbe32..982bddbdc 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -20,11 +20,13 @@ import {Performance} from '../performance' import {settings} from '../settings' import {userInfo as osUserInfo} from 'node:os' import {sep} from 'node:path' +import {lt} from 'semver' // eslint-disable-next-line new-cap const debug = Debug() const _pjson = requireJson(__dirname, '..', '..', 'package.json') +const BASE = `${_pjson.name}@${_pjson.version}` function channelFromVersion(version: string) { const m = version.match(/[^-]+(?:-([^.]+))?/) @@ -69,7 +71,7 @@ class Permutations extends Map> { } export class Config implements IConfig { - private _base = `${_pjson.name}@${_pjson.version}` + private _base = BASE public arch!: ArchTypes public bin!: string @@ -111,7 +113,9 @@ export class Config implements IConfig { private _commandIDs!: string[] - constructor(public options: Options) {} + constructor(public options: Options) { + if (options.config) Object.assign(this, options.config) + } static async load(opts: LoadOptions = module.filename || __dirname): Promise { // Handle the case when a file URL string is passed in such as 'import.meta.url'; covert to file path. @@ -120,7 +124,26 @@ export class Config implements IConfig { } if (typeof opts === 'string') opts = {root: opts} - if (isConfig(opts)) return opts + if (isConfig(opts)) { + const currentConfigBase = BASE.replace('@oclif/core@', '') + const incomingConfigBase = opts._base.replace('@oclif/core@', '') + /** + * Reload the Config based on the version required by the command. + * This is needed because the command is given the Config instantiated + * by the root plugin, which may be a different version than the one + * required by the command. + * + * Doing this ensures that the command can freely use any method on Config that + * exists in the version of Config required by the command but may not exist on the + * root's instance of Config. + */ + if (lt(incomingConfigBase, currentConfigBase)) { + debug(`reloading config from ${opts._base} to ${BASE}`) + return new Config({...opts.options, config: opts}) + } + + return opts + } const config = new Config(opts) await config.load() @@ -129,6 +152,8 @@ export class Config implements IConfig { // eslint-disable-next-line complexity public async load(): Promise { + if (this.options.config) return + settings.performanceEnabled = (settings.performanceEnabled === undefined ? this.options.enablePerf : settings.performanceEnabled) ?? false const plugin = new Plugin.Plugin({root: this.options.root}) await plugin.load() diff --git a/src/interfaces/plugin.ts b/src/interfaces/plugin.ts index 303f20846..ba820cd79 100644 --- a/src/interfaces/plugin.ts +++ b/src/interfaces/plugin.ts @@ -1,3 +1,4 @@ +import {Config} from './config' import {Command} from '../command' import {PJSON} from './pjson' import {Topic} from './topic' @@ -20,6 +21,7 @@ export interface Options extends PluginOptions { channel?: string; version?: string; enablePerf?: boolean; + config?: Config } export interface Plugin {