Skip to content

Commit

Permalink
feat: add support for alias expansion
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Feb 2, 2023
1 parent 8c8be07 commit 6773717
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 2 deletions.
37 changes: 36 additions & 1 deletion src/kernel.ts
Expand Up @@ -151,6 +151,13 @@ export class Kernel<Command extends AbstractBaseCommand> {
*/
#aliases: Map<string, string> = new Map()

/**
* An collection of expansion arguments and flags set on
* an alias. The key is the alias name and the value is
* everything after it.
*/
#aliasExpansions: Map<string, string[]> = new Map()

/**
* A collection of commands by the command name. This allows us to keep only
* the unique commands and also keep the loader reference to know which
Expand Down Expand Up @@ -224,6 +231,16 @@ export class Kernel<Command extends AbstractBaseCommand> {
*/
async #exec<T extends Command>(commandName: string, argv: string[]): Promise<InstanceType<T>> {
const Command = await this.find<T>(commandName)

/**
* Expand aliases
*/
const aliasExpansions = this.#aliasExpansions.get(commandName)
if (aliasExpansions) {
argv = aliasExpansions.concat(argv)
debug('expanding alias %O, cli args %O', commandName, argv)
}

const commandInstance = await this.#create<T>(Command, argv)

/**
Expand All @@ -244,6 +261,15 @@ export class Kernel<Command extends AbstractBaseCommand> {
try {
const Command = await this.find(commandName)

/**
* Expand aliases
*/
const aliasExpansions = this.#aliasExpansions.get(commandName)
if (aliasExpansions) {
argv = aliasExpansions.concat(argv)
debug('expanding alias %O, cli args %O', commandName, argv)
}

/**
* Parse CLI argv and also merge global flags parser options.
*/
Expand Down Expand Up @@ -352,8 +378,17 @@ export class Kernel<Command extends AbstractBaseCommand> {
/**
* Register alias for a comamnd name.
*/
addAlias(alias: string, commandName: string): this {
addAlias(alias: string, command: string): this {
const [commandName, ...expansions] = command.split(' ')
this.#aliases.set(alias, commandName)

if (expansions.length) {
debug('registering alias %O for command %O with options %O', alias, commandName, expansions)
this.#aliasExpansions.set(alias, expansions)
} else {
debug('registering alias %O for command %O', alias, commandName)
}

return this
}

Expand Down
53 changes: 53 additions & 0 deletions tests/kernel/exec.spec.ts
Expand Up @@ -9,6 +9,7 @@

import { test } from '@japa/runner'
import { Kernel } from '../../src/kernel.js'
import { args, flags } from '../../index.js'
import { BaseCommand } from '../../src/commands/base.js'
import { ListLoader } from '../../src/loaders/list_loader.js'

Expand Down Expand Up @@ -216,4 +217,56 @@ test.group('Kernel | exec', () => {
'Unknown flag "--help". The mentioned flag is not accepted by the command'
)
})

test('execute command using alias', async ({ assert }) => {
const kernel = Kernel.create()

class MakeController extends BaseCommand {
static commandName = 'make:controller'
async run() {
return 'executed'
}
}

kernel.addLoader(new ListLoader([MakeController]))
kernel.addAlias('mc', 'make:controller')
const command = await kernel.exec('mc', [])

assert.equal(command.result, 'executed')
assert.equal(command.result, 'executed')
assert.equal(command.exitCode, 0)

assert.isUndefined(kernel.exitCode)
assert.equal(kernel.getState(), 'booted')
})

test('expand alias before executing command', async ({ assert }) => {
const kernel = Kernel.create()

class MakeController extends BaseCommand {
static commandName = 'make:controller'

@args.string()
declare name: string

@flags.boolean()
declare resource: boolean

@flags.boolean()
declare singular: boolean

async run() {
assert.equal(this.name, 'user')
assert.isTrue(this.resource)
assert.isTrue(this.singular)
}
}

kernel.addLoader(new ListLoader([MakeController]))
kernel.addAlias('mc', 'make:controller --resource')
const command = await kernel.exec('mc', ['user', '--singular'])

assert.equal(command.exitCode, 0)
assert.isUndefined(kernel.exitCode)
})
})
18 changes: 18 additions & 0 deletions tests/kernel/find.spec.ts
Expand Up @@ -53,6 +53,24 @@ test.group('Kernel | find', () => {
assert.strictEqual(await kernel.find('controller'), MakeController)
})

test('find command using the command alias with flags', async ({ assert }) => {
const kernel = Kernel.create()

class MakeController extends BaseCommand {
static commandName = 'make:controller'
}

class MakeModel extends BaseCommand {
static commandName = 'make:model'
}

kernel.addLoader(new ListLoader([MakeController, MakeModel]))
kernel.addAlias('controller', 'make:controller --resource')
await kernel.boot()

assert.strictEqual(await kernel.find('controller'), MakeController)
})

test('raise error when unable to find command', async ({ assert }) => {
const kernel = Kernel.create()

Expand Down
54 changes: 53 additions & 1 deletion tests/kernel/handle.spec.ts
Expand Up @@ -13,6 +13,7 @@ import { Kernel } from '../../src/kernel.js'
import { CommandOptions } from '../../src/types.js'
import { BaseCommand } from '../../src/commands/base.js'
import { ListLoader } from '../../src/loaders/list_loader.js'
import { args, flags } from '../../index.js'

test.group('Kernel | handle', (group) => {
group.each.teardown(() => {
Expand Down Expand Up @@ -220,7 +221,7 @@ test.group('Kernel | handle', (group) => {
assert.equal(kernel.getState(), 'completed')
})

test('test if a command is a main command or not', async ({ assert }) => {
test('test if a command is a main command', async ({ assert }) => {
const kernel = Kernel.create()
class MakeController extends BaseCommand {
static commandName = 'make:controller'
Expand All @@ -238,4 +239,55 @@ test.group('Kernel | handle', (group) => {
assert.equal(kernel.exitCode, 0)
assert.equal(kernel.getState(), 'completed')
})

test('execute command using alias', async ({ assert }) => {
const kernel = Kernel.create()
class MakeController extends BaseCommand {
static commandName = 'make:controller'
async run() {
assert.equal(this.kernel.getState(), 'running')
assert.strictEqual(this.kernel.getMainCommand(), this)
return 'executed'
}
}

kernel.addLoader(new ListLoader([MakeController]))
kernel.addAlias('mc', 'make:controller')
await kernel.handle(['mc'])

assert.equal(kernel.exitCode, 0)
assert.equal(kernel.getState(), 'completed')
})

test('expand alias before executing command', async ({ assert }) => {
const kernel = Kernel.create()
class MakeController extends BaseCommand {
static commandName = 'make:controller'

@args.string()
declare name: string

@flags.boolean()
declare resource: boolean

@flags.boolean()
declare singular: boolean

async run() {
assert.equal(this.kernel.getState(), 'running')
assert.strictEqual(this.kernel.getMainCommand(), this)
assert.equal(this.name, 'user')
assert.isTrue(this.resource)
assert.isTrue(this.singular)
return 'executed'
}
}

kernel.addLoader(new ListLoader([MakeController]))
kernel.addAlias('mc', 'make:controller --resource')
await kernel.handle(['mc', 'user', '--singular'])

assert.equal(kernel.exitCode, 0)
assert.equal(kernel.getState(), 'completed')
})
})
16 changes: 16 additions & 0 deletions tests/kernel/main.spec.ts
Expand Up @@ -307,4 +307,20 @@ test.group('Kernel', () => {
const kernel = Kernel.create()
assert.isTrue(kernel.shortcircuit())
})

test('define aliases with flags', async ({ assert }) => {
const kernel = Kernel.create()

class MakeController extends BaseCommand {
static commandName = 'make:controller'
}

kernel.addLoader(new ListLoader([MakeController]))
kernel.addAlias('resource', 'make:controller --resource')
await kernel.boot()

assert.deepEqual(kernel.getCommandAliases('make:controller'), ['resource'])
assert.deepEqual(kernel.getAliases(), ['resource'])
assert.deepEqual(kernel.getAliasCommand('resource')?.commandName, 'make:controller')
})
})

0 comments on commit 6773717

Please sign in to comment.