diff --git a/src/config/config.ts b/src/config/config.ts index 523f81aa0..392983d7e 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -8,9 +8,8 @@ import {format} from 'util' import {Options, Plugin as IPlugin} from '../interfaces/plugin' import {Config as IConfig, ArchTypes, PlatformTypes, LoadOptions} from '../interfaces/config' import {Command, Hook, Hooks, PJSON, Topic} from '../interfaces' -import {Debug} from './util' import * as Plugin from './plugin' -import {compact, flatMap, loadJSON, uniq} from './util' +import {Debug, compact, flatMap, loadJSON, uniq, permutations} from './util' import {isProd} from '../util' import ModuleLoader from '../module-loader' @@ -81,6 +80,8 @@ export class Config implements IConfig { topicSeparator: ':' | ' ' = ':' + flexibleTaxonomy!: boolean + protected warned = false private _commands?: Command.Plugin[] @@ -122,6 +123,7 @@ export class Config implements IConfig { this.windows = this.platform === 'win32' this.bin = this.pjson.oclif.bin || this.name this.dirname = this.pjson.oclif.dirname || this.name + this.flexibleTaxonomy = this.pjson.oclif.flexibleTaxonomy || false // currently, only colons or spaces are valid separators if (this.pjson.oclif.topicSeparator && [':', ' '].includes(this.pjson.oclif.topicSeparator)) this.topicSeparator = this.pjson.oclif.topicSeparator! if (this.platform === 'win32') this.dirname = this.dirname.replace('/', '\\') @@ -383,7 +385,20 @@ export class Config implements IConfig { get commands(): Command.Plugin[] { if (this._commands) return this._commands - this._commands = flatMap(this.plugins, p => p.commands) + if (this.flexibleTaxonomy) { + const commands = flatMap(this.plugins, p => p.commands) + this._commands = [...commands] + for (const cmd of commands) { + const parts = cmd.id.split(':') + const combos = permutations(parts).flatMap(c => c.join(':')) + for (const combo of combos) { + this._commands.push({...cmd, id: combo}) + } + } + } else { + this._commands = flatMap(this.plugins, p => p.commands) + } + return this._commands } diff --git a/src/config/util.ts b/src/config/util.ts index a622e03a8..754a9b08f 100644 --- a/src/config/util.ts +++ b/src/config/util.ts @@ -61,3 +61,27 @@ export function Debug(...scope: string[]): (..._: any) => void { if (d.enabled) displayWarnings() return (...args: any[]) => d(...args) } + +// Adapted from https://github.com/angus-c/just/blob/master/packages/array-permutations/index.js +export function permutations(arr: string[]): Array { + if (arr.length === 0) return [] + if (arr.length === 1) return [arr] + + const output = [] + const partialPermutations = permutations(arr.slice(1)) + const first = arr[0] + + for (let i = 0, len = partialPermutations.length; i < len; i++) { + const partial = partialPermutations[i] + + for (let j = 0, len2 = partial.length; j <= len2; j++) { + const start = partial.slice(0, j) + const end = partial.slice(j) + const merged = start.concat(first, end) + + output.push(merged) + } + } + + return output +} diff --git a/src/interfaces/pjson.ts b/src/interfaces/pjson.ts index 5b64ad54f..ba2739440 100644 --- a/src/interfaces/pjson.ts +++ b/src/interfaces/pjson.ts @@ -16,6 +16,7 @@ export namespace PJSON { schema?: number; description?: string; topicSeparator?: ':' | ' '; + flexibleTaxonomy?: boolean; hooks?: { [name: string]: (string | string[]) }; commands?: string; default?: string;