Skip to content

Commit

Permalink
feat(command): add .versionOption() and .helpOption() method's
Browse files Browse the repository at this point in the history
  • Loading branch information
c4spar committed Jul 14, 2020
1 parent c8dc229 commit 85d66b9
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 30 deletions.
70 changes: 68 additions & 2 deletions packages/command/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -874,7 +874,7 @@ $ deno run https://deno.land/x/cliffy/examples/command/examples.ts help

## Auto generated help

The help information is auto-generated based on the information you have defined on your program.
The help information is auto-generated based on the information you have defined on your command's.

### Help option

Expand All @@ -884,9 +884,13 @@ The `--help` and `-h` option flag prints the auto generated help.
import { Command } from 'https://deno.land/x/cliffy/command.ts';

await new Command()
.name( 'help-option-and-command' )
.version( '0.1.0' )
.description( 'Sample description ...' )
.env( 'EXAMPLE_ENVIRONMENT_VARIABLE=<value:boolean>', 'Environment variable description ...' )
.example( 'Some example', 'Example content ...\n\nSome more example content ...' )
.command( 'help', new HelpCommand() )
.command( 'completions', new CompletionsCommand() )
.parse( Deno.args );
```

Expand All @@ -896,9 +900,39 @@ $ deno run https://deno.land/x/cliffy/examples/command/help-option-and-command.t

![](../../assets/img/help.png)

#### Customize help option

The help option is completely customizable with the `.helpOption()` method. The first argument are the flags
followed by the description. The third argument can be an action handler or an options object.
The second and third argument's are optional.

```typescript
await new Command()
.helpOption( '-i, --info', 'Print help info.', function( this: Command ) {
console.log( 'some help info ...', this.getHelp() );
} )
.parse( Deno.args );
```

You can also override the default options of the help option. The options are the same as for the `.option()` method.

```typescript
await new Command()
.helpOption( ' -x, --xhelp', 'Print help info.', { global: true } )
.parse( Deno.args );
```

To disable the help option you can pass false to the `.helpOption()` method.

```typescript
await new Command()
.helpOption( false )
.parse( Deno.args );
```

### Help command

There is also a predefined `HelpCommand` which prints the same help output as the `--help` option.
There is also a predefined `HelpCommand` which prints the auto generated help.
The help command must be registered manuelly and can be optionally registered as global command to make them also
available on all child commands.

Expand Down Expand Up @@ -1038,6 +1072,38 @@ $ deno run https://deno.land/x/cliffy/examples/command/version-options.ts --vers
0.0.1
```

### Customize version option

The version option is completely customizable with the `.versionOption()` method. The first argument are the flags
followed by the description. The third argument can be an action handler or an options object.
The second and third argument's are optional.

```typescript
await new Command()
.version( '0.1.0' )
.versionOption( ' -x, --xversion', 'Print version info.', function( this: Command ) {
console.log( 'Version: %s', this.getVersion() );
} )
.parse( Deno.args );
```

You can also override the default options of the version option. The options are the same as for the `.option()` method.

```typescript
await new Command()
.version( '0.1.0' )
.versionOption( ' -x, --xversion', 'Print version info.', { global: true } )
.parse( Deno.args );
```

The version option can be also disable.

```typescript
await new Command()
.versionOption( false )
.parse( Deno.args );
```

## Credits

Benjamin Fischer [@c4spar](https://github.com/c4spar)
Expand Down
81 changes: 60 additions & 21 deletions packages/command/lib/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ const permissions: any = ( Deno as any ).permissions;
const envPermissionStatus: any = permissions && permissions.query && await permissions.query( { name: 'env' } );
const hasEnvPermissions: boolean = !!envPermissionStatus && envPermissionStatus.state === 'granted';

interface IDefaultOption<O = any, A extends Array<any> = any> {
flags: string;
desc?: string
opts?: ICommandOption<O, A>;
}

/**
* Base command implementation without pre configured command's and option's.
*/
Expand All @@ -36,25 +42,45 @@ export class Command<O = any, A extends Array<any> = any> {
private _globalParent?: Command;
private ver: string = '0.0.0';
private desc: IDescription = '';
private fn: IAction<O, A> | undefined;
private fn?: IAction<O, A>;
private options: IOption<O, A>[] = [];
private commands: Map<string, Command> = new Map();
private examples: IExample[] = [];
private envVars: IEnvVariable[] = [];
private aliases: string[] = [];
private completions: Map<string, ICompleteSettings> = new Map();
private cmd: Command = this;
private argsDefinition: string | undefined;
private argsDefinition?: string;
private isExecutable: boolean = false;
private throwOnError: boolean = false;
private _allowEmpty: boolean = true;
private _stopEarly: boolean = false;
private defaultCommand: string | undefined;
private defaultCommand?: string;
private _useRawArgs: boolean = false;
private args: IArgumentDetails[] = [];
private isHidden: boolean = false;
private isGlobal: boolean = false;
private hasDefaults: Boolean = false;
private _versionOption?: IDefaultOption<O, A> | false;
private _helpOption?: IDefaultOption<O, A> | false;

public versionOption( flags: string | false, desc?: string, opts?: IAction<O, A> | ICommandOption<O, A> ): this {
this._versionOption = flags === false ? flags : {
flags,
desc,
opts: typeof opts === 'function' ? { action: opts } : opts
};
return this;
}

public helpOption( flags: string | false, desc?: string, opts?: IAction<O, A> | ICommandOption<O, A> ): this {
this._helpOption = flags === false ? flags : {
flags,
desc,
opts: typeof opts === 'function' ? { action: opts } : opts
};
return this;
}

/**
* Add new sub-command.
Expand Down Expand Up @@ -579,29 +605,42 @@ export class Command<O = any, A extends Array<any> = any> {
}

private registerDefaults(): this {

if ( this.getParent() || this.hasDefaults ) {
return this;
}
this.hasDefaults = true;

this.reset()
.option( '-V, --version', 'Show the version number for this program.', {
standalone: true,
prepend: true,
action: function ( this: Command ) {
this.log( this.getVersion() );
Deno.exit( 0 );
}
} )
.option( '-h, --help', 'Show this help.', {
standalone: true,
global: true,
prepend: true,
action: function ( this: Command ) {
this.help();
Deno.exit( 0 );
}
} );
this.reset();

if ( this._versionOption !== false ) {
this.option(
this._versionOption?.flags || '-V, --version',
this._versionOption?.desc || 'Show the version number for this program.',
Object.assign( {
standalone: true,
prepend: true,
action: async function ( this: Command ) {
await Deno.stdout.writeSync( encode( this.getVersion() + '\n' ) );
Deno.exit( 0 );
}
}, this._versionOption?.opts ?? {} ) );
}

if ( this._helpOption !== false ) {
this.option(
this._helpOption?.flags || '-h, --help',
this._helpOption?.desc || 'Show this help.',
Object.assign( {
standalone: true,
global: true,
prepend: true,
action: function ( this: Command ) {
this.help();
Deno.exit( 0 );
}
}, this._helpOption?.opts ?? {} ) );
}

return this;
};
Expand Down
56 changes: 49 additions & 7 deletions packages/command/test/command/help-command_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import { HelpCommand } from '../../commands/help.ts';
import { Command } from '../../lib/command.ts';
import { assertEquals } from '../lib/assert.ts';

Deno.test( 'command: help command', async () => {

const cmd: Command = new Command()
function command( defaultOptions?: boolean ) {
const cmd = new Command()
.throwErrors()

.version( '1.0.0' )
.description( 'Test description ...' )
.option( '-t, --test [val:string]', 'test description' )
.description( 'Test description ...' );

if ( defaultOptions === false ) {
cmd.versionOption( false )
.helpOption( false );
}

return cmd.option( '-t, --test [val:string]', 'test description' )
.option( '-D, --default [val:string]', 'I have a default value!', { default: 'test' } )
.option( '-r, --required [val:string]', 'I am required!', { required: true } )
.option( '-H, --hidden [val:string]', 'Nobody knows about me!', { hidden: true } )
Expand All @@ -37,8 +41,11 @@ Deno.test( 'command: help command', async () => {
.hidden()

.reset();
}

const output: string = cmd.getHelp();
Deno.test( 'command: help command', async () => {

const output: string = command().getHelp();

assertEquals( `
Usage: COMMAND
Expand Down Expand Up @@ -72,3 +79,38 @@ Deno.test( 'command: help command', async () => {
`, stripeColors( output ) );
} );

Deno.test( 'command: help command', async () => {

const output: string = command( false ).getHelp();

assertEquals( `
Usage: COMMAND
Version: v1.0.0
Description:
Test description ...
Options:
-t, --test [val:string] - test description
-D, --default [val:string] - I have a default value! (Default: test)
-r, --required [val:string] - I am required! (required)
-d, --depends [val:string] - I depend on test! (depends: test)
-c, --conflicts [val:string] - I conflict with test! (conflicts: test)
-a, --all <val:string> - I have many hints! (required, Default: test, depends: test, conflicts: depends)
Commands:
help [command:command] - Show this help or the help of a sub-command.
completions - Generate shell completions for zsh and bash.
sub-command <input:string> <output:string> - sub command description.
Environment variables:
SOME_ENV_VAR <value:number> - Description ...
SOME_ENV_VAR_2 <value:string> - Description 2 ...
`, stripeColors( output ) );
} );

0 comments on commit 85d66b9

Please sign in to comment.