Skip to content
This repository was archived by the owner on Aug 22, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Config from '@oclif/config'
import Help from '@oclif/plugin-help'

import {Command} from '.'
import {splitArgv} from './util'

export class Main extends Command {
static run(argv = process.argv.slice(2), options?: Config.LoadOptions) {
Expand All @@ -15,7 +16,7 @@ export class Main extends Command {
}

async run() {
let [id, ...argv] = this.argv
const {id, argv} = splitArgv(this.argv, this.config.commandIDs)
this.parse({strict: false, '--': false, ...this.ctor as any})
if (!this.config.findCommand(id)) {
let topic = this.config.findTopic(id)
Expand Down
33 changes: 33 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
export function compact<T>(a: (T | undefined)[]): T[] {
return a.filter((a): a is T => !!a)
}

/**
* Splits argv to command ID and the rest of the array.
*
* For space-separated subcommands, the ID is built from multiple items in
* the source array. For colon-separated subcommands, the argv item is the ID.
*/
export function splitArgv(argv: string[], commandIDs: string[]) {
let argvIndex = 0
let id = ''
let idCandidate = argv[argvIndex]

while (commandIDs.includes(idCandidate) || !id) {
if (commandIDs.includes(idCandidate)) {
id = idCandidate
}
argvIndex++
if (argvIndex > argv.length) {
break
}

idCandidate += ` ${argv[argvIndex]}`
}

if (id === '') {
throw new Error('Command ID not found')
}

return {
id,
argv: argv.slice(argvIndex),
}
}
61 changes: 61 additions & 0 deletions test/util.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {expect} from 'fancy-test'

import {splitArgv} from '../src/util'

describe('splitArgv', () => {
it('Colon support (no breaking change)', () => {
expectEqual(
splitArgv(['user:add', 'Peter'], ['user:add']),
{id: 'user:add', argv: ['Peter']}
)
})

it('Space-separated when topic itself is in the list', () => {
expectEqual(
splitArgv(['user', 'add', 'Peter'], ['user', 'user add']),
{id: 'user add', argv: ['Peter']}
)
})

it('Space-separated when topic is missing from the list', () => {
expectEqual(
splitArgv(['user', 'add', 'Peter'], ['user add']),
{id: 'user add', argv: ['Peter']}
)
})

it('Similar but not really - topic level', () => {
expect(() => splitArgv(['user', 'add', 'Peter'], ['user2'])).to.throw(Error)
})

it('Similar but not really - nested level', () => {
expect(() => splitArgv(['user', 'add', 'Peter'], ['user add2'])).to.throw(Error)
})

it('Multiple nesting levels', () => {
expectEqual(
splitArgv(['user', 'config', 'frontend', 'add', 'key', 'value'], ['user config frontend add']),
{id: 'user config frontend add', argv: ['key', 'value']}
)
})

it('Multiple nesting levels, ending in the middle', () => {
expectEqual(
splitArgv(['user', 'config', 'frontend', 'add', 'key', 'value'], ['user config frontend']),
{id: 'user config frontend', argv: ['add', 'key', 'value']}
)
})

it('Flags', () => {
expectEqual(
splitArgv(['user', 'add', '--name', 'Peter'], ['user add']),
{id: 'user add', argv: ['--name', 'Peter']}
)
})

})

// Small strongly-typed helper
function expectEqual(expected: ReturnType<typeof splitArgv>, actual: ReturnType<typeof splitArgv>) {
expect(expected).to.deep.equal(actual)
}