diff --git a/src/config/config.ts b/src/config/config.ts index 6521b01fd..8fae728b4 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -10,7 +10,7 @@ import {Config as IConfig, ArchTypes, PlatformTypes, LoadOptions} from '../inter import {Command, CompletableOptionFlag, Hook, Hooks, PJSON, Topic} from '../interfaces' import * as Plugin from './plugin' import {Debug, compact, loadJSON, collectUsableIds, getCommandIdPermutations} from './util' -import {isProd} from '../util' +import {ensureArgArray, isProd} from '../util' import ModuleLoader from '../module-loader' import {getHelpFlagAdditions} from '../help/util' @@ -786,10 +786,7 @@ export async function toCached(c: Command.Class, plugin?: IPlugin): Promise ({...arg, name})) - const argsPromise = normalized.map(async a => ({ + const argsPromise = ensureArgArray(c.args).map(async a => ({ name: a.name, description: a.description, required: a.required, diff --git a/src/help/command.ts b/src/help/command.ts index 787b49e9b..bd7f5e93d 100644 --- a/src/help/command.ts +++ b/src/help/command.ts @@ -1,7 +1,7 @@ import * as Chalk from 'chalk' import stripAnsi = require('strip-ansi') -import {castArray, compact, sortBy} from '../util' +import {castArray, compact, ensureArgArray, sortBy} from '../util' import * as Interfaces from '../interfaces' import {Example} from '../interfaces/command' import {HelpFormatter, HelpSection, HelpSectionRenderer} from './formatter' @@ -41,7 +41,7 @@ export class CommandHelp extends HelpFormatter { return v }), f => [!f.char, f.char, f.name]) - const args = (cmd.args || []).filter(a => !a.hidden) + const args = ensureArgArray(cmd.args).filter(a => !a.hidden) const output = compact(this.sections().map(({header, generate}) => { const body = generate({cmd, flags, args}, header) // Generate can return a list of sections diff --git a/src/help/docopts.ts b/src/help/docopts.ts index 9e25ef8ca..86e445a48 100644 --- a/src/help/docopts.ts +++ b/src/help/docopts.ts @@ -1,4 +1,5 @@ import {Interfaces} from '..' +import {ensureArgArray} from '../util' type Flag = Interfaces.Command.Flag type Flags = Flag[] @@ -82,7 +83,7 @@ export class DocOpts { public toString(): string { const opts = this.cmd.id === '.' || this.cmd.id === '' ? [] : ['<%= command.id %>'] if (this.cmd.args) { - const a = this.cmd.args?.map(arg => `[${arg.name.toUpperCase()}]`) || [] + const a = ensureArgArray(this.cmd.args).map(arg => `[${arg.name.toUpperCase()}]`) || [] opts.push(...a) } diff --git a/src/util.ts b/src/util.ts index 1479b5cbe..5b0c38a15 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,3 +1,5 @@ +import {ArgInput} from './interfaces' + export function compact(a: (T | undefined)[]): T[] { return a.filter((a): a is T => Boolean(a)) } @@ -59,3 +61,14 @@ export function sumBy(arr: T[], fn: (i: T) => number): number { export function capitalize(s: string): string { return s ? s.charAt(0).toUpperCase() + s.slice(1).toLowerCase() : '' } + +/** + * Ensure that the args are in an array instead of an object. This is required to ensure + * forwards compatibility with the new arg format in v2. + * + * @param args The args to ensure are in an array + * @returns ArgInput + */ +export function ensureArgArray(args?: ArgInput | {[name: string]: any}): ArgInput { + return Array.isArray(args) ? args ?? [] : Object.entries(args ?? {}).map(([name, arg]) => ({...arg, name})) +} diff --git a/test/util.test.ts b/test/util.test.ts index 6172b4e0f..e58df3639 100644 --- a/test/util.test.ts +++ b/test/util.test.ts @@ -1,5 +1,5 @@ import {expect} from 'chai' -import {maxBy, sumBy, capitalize} from '../src/util' +import {maxBy, sumBy, capitalize, ensureArgArray} from '../src/util' describe('capitalize', () => { it('capitalizes the string', () => { @@ -33,3 +33,18 @@ describe('maxBy', () => { expect(maxBy(arr, i => i.x)).to.equal(arr[1]) }) }) + +describe('ensureArgArray', () => { + it('should convert arg object to array', () => { + const args = { + foo: {name: 'foo', description: 'foo desc', required: true}, + bar: {name: 'bar', description: 'bar desc'}, + } + const expected = [ + {name: 'foo', description: 'foo desc', required: true}, + {name: 'bar', description: 'bar desc'}, + ] + + expect(ensureArgArray(args)).to.deep.equal(expected) + }) +})