All Projects → vilic → Clime

vilic / Clime

⌨ The command-line interface framework for TypeScript.

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to Clime

Kotlin Argparser
Easy to use and concise yet powerful and robust command line argument parsing for Kotlin
Stars: ✭ 431 (+91.56%)
Mutual labels:  command-line-parser
Nclap
NClap is a .NET library for parsing command-line arguments and building interactive command shells. It's driven by a declarative attribute syntax, and easy to extend.
Stars: ✭ 51 (-77.33%)
Mutual labels:  command-line-parser
Typin
Declarative framework for interactive CLI applications
Stars: ✭ 126 (-44%)
Mutual labels:  command-line-parser
Command Line Args
A mature, feature-complete library to parse command-line options.
Stars: ✭ 525 (+133.33%)
Mutual labels:  command-line-parser
Libcmdf
Single-header library for writing CLI applications in C/C++
Stars: ✭ 30 (-86.67%)
Mutual labels:  command-line-parser
Ppx deriving cmdliner
Ppx_deriving plugin for generating command line interfaces from types (Cmdliner.Term.t)
Stars: ✭ 78 (-65.33%)
Mutual labels:  command-line-parser
Caporal.js
A full-featured framework for building command line applications (cli) with node.js
Stars: ✭ 3,279 (+1357.33%)
Mutual labels:  command-line-parser
Commander
🚀The framework to write type-safe and structured command line program easily in Swift.
Stars: ✭ 170 (-24.44%)
Mutual labels:  command-line-parser
Clii
Python 3.7+ function annotations -> CLI
Stars: ✭ 41 (-81.78%)
Mutual labels:  command-line-parser
Spectre.cli
An extremely opinionated command-line parser.
Stars: ✭ 121 (-46.22%)
Mutual labels:  command-line-parser
Clap
Create your command-line parser, with all of the bells and whistles, declaratively or procedurally.
Stars: ✭ 7,174 (+3088.44%)
Mutual labels:  command-line-parser
Andhow
Strongly typed, validated, easy to use Java configuration
Stars: ✭ 17 (-92.44%)
Mutual labels:  command-line-parser
Clikt
Multiplatform command line interface parsing for Kotlin
Stars: ✭ 1,658 (+636.89%)
Mutual labels:  command-line-parser
Airframe
Essential Building Blocks for Scala
Stars: ✭ 442 (+96.44%)
Mutual labels:  command-line-parser
Bash Argsparse
An high level argument parsing library for bash
Stars: ✭ 128 (-43.11%)
Mutual labels:  command-line-parser
Clamp
a Ruby command-line application framework
Stars: ✭ 387 (+72%)
Mutual labels:  command-line-parser
Conf
Go package for loading program configuration from multiple sources.
Stars: ✭ 70 (-68.89%)
Mutual labels:  command-line-parser
Argagg
A simple C++11 command line argument parser
Stars: ✭ 180 (-20%)
Mutual labels:  command-line-parser
Co
Art of C++. Flag, logging, unit-test, json, go-style coroutine and more.
Stars: ✭ 2,264 (+906.22%)
Mutual labels:  command-line-parser
Programoptions.hxx
Single-header program options parsing library for C++11
Stars: ✭ 112 (-50.22%)
Mutual labels:  command-line-parser

NPM Package Build Status Coverage Status

Clime

The command-line interface framework for TypeScript, fully tested with baseman.

Prerequisites

  • Node.js 6+
  • TypeScript compilation options in tsconfig.json
    • target needs to be set as 'es6' / 'es2015' or higher.
    • experimentalDecorators and emitDecoratorMetadata should both be enabled.

Install

yarn add clime
# or
npm install clime --save

Usage

Here is a basic example, an entry file (usually won't change much among time) and a single command:

src/cli.ts

#!/usr/bin/env node

import * as Path from 'path';
import {CLI, Shim} from 'clime';

// The second parameter is the path to folder that contains command modules.
let cli = new CLI('greet', Path.join(__dirname, 'commands'));

// Clime in its core provides an object-based command-line infrastructure.
// To have it work as a common CLI, a shim needs to be applied:
let shim = new Shim(cli);
shim.execute(process.argv);

src/commands/default.ts

import {Command, command, param} from 'clime';

@command({
  description: 'This is a command for printing a greeting message',
})
export default class extends Command {
  execute(
    @param({
      description: 'Your loud name',
      required: true,
    })
    name: string,
  ) {
    return `Hello, ${name}!`;
  }
}

Features

  • ☑ Type and schema based parameters/options casting
  • ☑ Object and promise based architecture
  • ☑ File path based multi-level subcommands
  • ☑ Automatic usage generating
  • ☑ Multiple command roots support New in v0.5

Parameter types and options schema

Clime provides a way in which you can get parameters and options you really want to: typed at compile time and casted at run time.

import {Command, Options, command, option, param, params} from 'clime';

export class SomeOptions extends Options {
  @option({
    flag: 't',
    description: 'timeout that does nothing',
  })
  timeout: number;

  // You can also create methods and properties.
  get timeoutInSeconds(): number {
    return this.timeout / 1000;
  }
}

@command()
export default class extends Command {
  execute(
    @param({
      required: true,
      description: 'required parameter foo',
    })
    foo: string,
    @param({
      description: 'optional parameter bar',
    })
    bar: number,
    @params({
      type: String,
      description: 'extra parameters',
    })
    args: string[],
    options: SomeOptions,
  ) {
    return 'Hello, Clime!';
  }
}

And this is what you get for usage/help information:

USAGE

  command <foo> [bar] [...args] [...options]

  foo  - required parameter foo
  bar  - optional parameter bar
  args - extra parameters

OPTIONS

  -t, --timeout <timeout> - timeout that does nothing

Casting from string

Clime will automatically cast parameters to number, boolean based on their types. It also defines interface StringCastable that allows user-defined classes to be casted from parameters.

Please note that StringCastable is correspondent to the type of constructor instead of instance, so no implements should be present.

For example:

import {CastingContext} from 'clime';

class File {
  constructor(public path: string) {}

  static cast(path: string, context: CastingContext<File>): File {
    return new File(Path.resolve(context.cwd, path));
  }
}

Validators

A validator or validators can be specified for parameters and options validation. A validator can either be an instance that implements interface Validator<T>, a function that matches type ValidatorFunction<T> or a regular expression.

For the validators in forms other than regular expression, it is the casted value that will be tested against. And for regular expression validator, the source string will be tested against instead.

Expected error

A useful way to distinguish expected errors (e.g., errors that might be caused by incorrect user input) from other errors is to throw instances of ExpectedError class or its subclasses. And a validator for example, usually throw instances of ExpectedError.

Preserving metadata without command-line parameters

As TypeScript only emits metadata for target decorated by decorators, if no command-line parameter added, Clime won't be able to know information of options and context parameter. Thus a @metadata decorator that does nothing at run time is provided for preserving these metadata:

@command()
export default class extends Command {
  @metadata
  execute(options: SomeOptions) {
    return 'Hello, Clime!';
  }
}

It is required to have this @metadata decorator if no other decorator is applied to method execute.

Context

Context is an object contains information like current working directory and commands sequence. You can have context passed in by adding the last parameter of execute method with type Context:

@command()
export default class extends Command {
  @metadata
  execute(context: Context) {
    return 'Hello, Clime!';
  }
}

Subcommands

Clime provides an easy way to create subcommands. The default entry of a clime command is default.js (default.ts before compilation of course). Any other .js files under the same folder are considered as subcommand files.

Clime allows multi-level subcommands based on file structures. For three-level commands like below:

command

command foo
command foo biu
command foo yo

command bar
command bar bia
command bar pia

The file structure could be:

- commands
  - default.ts
  - foo.ts
  - foo
    - biu.ts
    - yo.ts
  - bar
    - default.ts
    - bia.ts
    - pia.ts

You may notice that the level-n entry could be either at the same level of the level-(n+1) commands with name default.ts (like default.ts in bar), or at the same level of the folder of level-(n+1) commands (like foo.ts and folder foo).

Command entry with description only

Clime allows an entry of a group of subcommands to provide only descriptions rather than an actual command. Just export description and brief directly from the entry module to do so:

export const description = 'Some detailed description';

// Used when listing as subcommands.
export const brief = 'brief description';

Configuring subcommand definitions using subcommands field

Clime has to load every subcommand modules under a specific command to know their briefs. To avoid this, you may export a subcommands array with subcommand definitions like below:

import {SubcommandDefinition} from 'clime';

export const subcommands: SubcommandDefinition[] = [
  {
    name: 'foo',
    brief: 'A subcommand named foo',
  },
  {
    name: 'bar',
    brief: 'A subcommand named bar',
  },
];

Further more, those definition entries also allow you to add alias or aliases for subcommands:

import {SubcommandDefinition} from 'clime';

export const subcommands: SubcommandDefinition[] = [
  {
    name: 'foo',
    alias: 'f',
    brief: 'A subcommand named foo',
  },
  {
    name: 'bar',
    aliases: ['b', 'bb'],
    brief: 'A subcommand named bar',
  },
];

Multiple command roots support

For some CLI tools, it would be nice to support project specific commands. And Clime has this ability built-in.

For example, you may define the cli object like below to load commands from both CLI tool commands path as well as project path:

let cli = new CLI('greet', [
  Path.join(__dirname, 'commands'),
  'project-commands',
]);

And you can also add different labels for those command directories:

let cli = new CLI('greet', [
  {
    label: 'Built-in',
    path: Path.join(__dirname, 'commands'),
  },
  {
    label: 'Extra',
    path: 'project-commands',
  },
]);

Testable

As the core of Clime is not coupled with stream-based command line, commands written with Clime can be easily tested.

For example:

import {Command, Context, command, metadata} from 'clime';

export class TestContext extends Context {
  promptForQuery(): Promise<string> {
    // Using library like Inquirer.js to interact with user.
    return Promise.resolve('result');
  }
}

@command()
export default class TestCommand extends Command {
  @metadata
  execute(context: TestContext) {
    return context.promptForQuery();
  }
}

To test this command, we just need to extend TestContext, override promptForQuery and call execute with new context.

If this command is meant to stop somewhere and exit the process, we can define a ExitSignal class that implements Printable interface:

import {Printable} from 'clime';

export class ExitSignal implements Printable {
  constructor(public code: number) {}

  print(): void {
    process.exit(this.code);
  }
}

export function exit(code = 0): void {
  throw new ExitSignal(code);
}

Because print method would only be executed by the shim, your test can safely catch the exit signal and assert its correctness.

License

MIT License.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].