Skip to content

Commit

Permalink
feat: arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
hongaar committed Dec 11, 2019
1 parent ef6f0fb commit c186a86
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 53 deletions.
20 changes: 1 addition & 19 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,19 +1 @@
*.log
pids
*.pid
*.seed
*sublime*
.vscode
lib-cov
coverage
.grunt
.gitpod.yml
dist/.local_storage*
build/Release
node_modules
test/util/playground.js
.idea/
electron*
test-reports/
dist
.eslintcache
node_modules/
12 changes: 7 additions & 5 deletions examples/simple.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { bandersnatch, command } from '../src'
import { program, command } from '../src'

const app = bandersnatch('simple cli app')
const app = program('simple cli app')

app.add(
command('say').runs(function(argv: any) {
console.log(argv)
})
command('say', 'Say something to the terminal')
.argument('word', 'The word to say', { required: true })
.action(async function(argv) {
console.log(argv)
})
)

app.run()
61 changes: 61 additions & 0 deletions src/argument.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Argv } from 'yargs'
import { ArgValue } from './command'

export function argument(name: string) {
return new Argument(name)
}

export class Argument {
private name: string
private description?: string
private required = false
private variadic = false
private defaultValue?: ArgValue
private defaultDescription?: string
private choices?: Function

constructor(name: string, description?: string) {
this.name = name
if (description) {
this.describe(description)
}
}

describe(description: string) {
this.description = description
return this
}

require() {
this.required = true
}

vary() {
this.variadic = true
}

default(value: ArgValue, description?: string) {
this.defaultValue = value

if (description) {
this.defaultDescription = description
}
}

toCommand() {
if (this.required) {
return `<${this.name}>`
}
if (this.variadic) {
return `[...${this.name}]`
}
return `[${this.name}]`
}

toBuilder(yargs: Argv) {
yargs.positional(this.name, {
default: this.defaultValue,
defaultDescription: this.defaultDescription
})
}
}
78 changes: 71 additions & 7 deletions src/command.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,87 @@
export function command(command: string) {
return new Command(command)
import { CommandModule } from 'yargs'
import { Argument } from './argument'
import { Option } from './option'

export type ArgValue = string | number | boolean

export interface ArgumentOptions {
required?: boolean
variadic?: boolean
default?: any
}

export interface HandlerFn<T = {}> {
(args: T): Promise<void>
}

export function command(command: string, description?: string) {
return new Command(command, description)
}

export class Command {
command: string
description?: string
handler?: Function
private command: string
private description?: string
private arguments: Argument[] = []
private options: Option[] = []
private handler?: Function

constructor(command: string) {
constructor(command: string, description?: string) {
this.command = command
if (description) {
this.describe(description)
}
}

describe(description: string) {
this.description = description
return this
}

runs(fn: Function) {
argument(name: string, description?: string, options: ArgumentOptions = {}) {
const argument = new Argument(name, description)

if (options.required) {
argument.require()
}
if (options.variadic) {
argument.vary()
}

this.arguments.push(argument)

return this
}

action(fn: HandlerFn) {
this.handler = fn
return this
}

toYargs() {
const module: CommandModule = {
command: this.buildCommand(),
aliases: [],
describe: this.description,
builder: yargs => {
this.arguments.forEach(argument => argument.toBuilder(yargs))
return yargs
},
handler: async argv => {
if (this.handler) {
await this.handler(argv)
}
}
}
return module
}

private buildCommand() {
const args = this.arguments.map(arg => arg.toCommand()).join(' ')

if (args !== '') {
return `${this.command} ${args}`
}

return this.command
}
}
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from './bandersnatch'
export * from './program'
export * from './command'
export * from './argument'
export * from './option'
18 changes: 18 additions & 0 deletions src/option.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export function option(name: string) {
return new Option(name)
}

export class Option {
private name: string
private description?: string
private choices?: Function

constructor(name: string) {
this.name = name
}

describe(description: string) {
this.description = description
return this
}
}
22 changes: 6 additions & 16 deletions src/bandersnatch.ts → src/program.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
import yargs from 'yargs/yargs'
import { Command } from '.'

export function bandersnatch(name?: string) {
return new Bandersnatch(name)
export function program(description?: string) {
return new Program(description)
}

export class Bandersnatch {
description: string | undefined
yargs = yargs(process.argv.slice(2))
export class Program {
private description: string | undefined
private yargs = yargs(process.argv.slice(2))

constructor(description?: string) {
this.description = description
}

add(command: Command) {
// See https://github.com/yargs/yargs/blob/master/docs/advanced.md#providing-a-command-module
this.yargs.command({
command: command.command,
aliases: [],
describe: command.description,
builder: {},
handler: function(argv) {
if (command.handler) {
command.handler(argv)
}
}
})
this.yargs.command(command.toYargs())
return this
}

Expand Down
5 changes: 0 additions & 5 deletions tests/index.spec.ts

This file was deleted.

5 changes: 5 additions & 0 deletions tests/program.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { program, Program } from '../src'

test('program should return new Program object', () => {
expect(program()).toBeInstanceOf(Program)
})

0 comments on commit c186a86

Please sign in to comment.