{"id":14156646,"url":"https://github.com/vilicvane/clime","last_synced_at":"2025-04-09T14:09:54.820Z","repository":{"id":57200386,"uuid":"53428599","full_name":"vilicvane/clime","owner":"vilicvane","description":"⌨ The command-line interface framework for TypeScript.","archived":false,"fork":false,"pushed_at":"2023-10-26T17:10:47.000Z","size":585,"stargazers_count":253,"open_issues_count":21,"forks_count":10,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-04-01T18:05:19.030Z","etag":null,"topics":["clime","command-line-parser","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vilicvane.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2016-03-08T16:45:29.000Z","updated_at":"2025-02-04T15:39:34.000Z","dependencies_parsed_at":"2022-09-16T15:11:19.728Z","dependency_job_id":"316db31f-8f86-453b-90b4-79147612a9b7","html_url":"https://github.com/vilicvane/clime","commit_stats":null,"previous_names":["vilicvane/clime","vilic/clime"],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fclime","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fclime/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fclime/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fclime/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vilicvane","download_url":"https://codeload.github.com/vilicvane/clime/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248054194,"owners_count":21039952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clime","command-line-parser","typescript"],"created_at":"2024-08-17T08:07:31.845Z","updated_at":"2025-04-09T14:09:54.790Z","avatar_url":"https://github.com/vilicvane.png","language":"TypeScript","funding_links":[],"categories":["typescript"],"sub_categories":[],"readme":"[![NPM Package](https://badge.fury.io/js/clime.svg)](https://www.npmjs.com/package/clime)\n[![Build Status](https://travis-ci.org/vilic/clime.svg?branch=master)](https://travis-ci.org/vilic/clime)\n[![Coverage Status](https://s3.amazonaws.com/assets.coveralls.io/badges/coveralls_98.svg)](https://coveralls.io/github/vilic/clime?branch=master)\n\n# Clime\n\nThe command-line interface framework for TypeScript, fully tested with [baseman](https://github.com/vilic/baseman).\n\n## Prerequisites\n\n- Node.js 6+\n- TypeScript compilation options in `tsconfig.json`\n  - `target` needs to be set as `'es6'` / `'es2015'` or higher.\n  - `experimentalDecorators` and `emitDecoratorMetadata` should both be enabled.\n\n## Install\n\n```sh\nyarn add clime\n# or\nnpm install clime --save\n```\n\n## Usage\n\nHere is a basic example, an entry file (usually won't change much among time) and a single command:\n\n**src/cli.ts**\n\n```ts\n#!/usr/bin/env node\n\nimport * as Path from 'path';\nimport {CLI, Shim} from 'clime';\n\n// The second parameter is the path to folder that contains command modules.\nlet cli = new CLI('greet', Path.join(__dirname, 'commands'));\n\n// Clime in its core provides an object-based command-line infrastructure.\n// To have it work as a common CLI, a shim needs to be applied:\nlet shim = new Shim(cli);\nshim.execute(process.argv);\n```\n\n**src/commands/default.ts**\n\n```ts\nimport {Command, command, param} from 'clime';\n\n@command({\n  description: 'This is a command for printing a greeting message',\n})\nexport default class extends Command {\n  execute(\n    @param({\n      description: 'Your loud name',\n      required: true,\n    })\n    name: string,\n  ) {\n    return `Hello, ${name}!`;\n  }\n}\n```\n\n## Features\n\n- ☑ Type and schema based parameters/options casting\n- ☑ Object and promise based architecture\n- ☑ File path based multi-level subcommands\n- ☑ Automatic usage generating\n- ☑ Multiple command roots support \u003csup\u003eNew in v0.5\u003c/sup\u003e\n\n### Parameter types and options schema\n\nClime provides a way in which you can get parameters and options you really want to: **typed** at compile time and **casted** at run time.\n\n```ts\nimport {Command, Options, command, option, param, params} from 'clime';\n\nexport class SomeOptions extends Options {\n  @option({\n    flag: 't',\n    description: 'timeout that does nothing',\n  })\n  timeout: number;\n\n  // You can also create methods and properties.\n  get timeoutInSeconds(): number {\n    return this.timeout / 1000;\n  }\n}\n\n@command()\nexport default class extends Command {\n  execute(\n    @param({\n      required: true,\n      description: 'required parameter foo',\n    })\n    foo: string,\n    @param({\n      description: 'optional parameter bar',\n    })\n    bar: number,\n    @params({\n      type: String,\n      description: 'extra parameters',\n    })\n    args: string[],\n    options: SomeOptions,\n  ) {\n    return 'Hello, Clime!';\n  }\n}\n```\n\nAnd this is what you get for usage/help information:\n\n```shell\nUSAGE\n\n  command \u003cfoo\u003e [bar] [...args] [...options]\n\n  foo  - required parameter foo\n  bar  - optional parameter bar\n  args - extra parameters\n\nOPTIONS\n\n  -t, --timeout \u003ctimeout\u003e - timeout that does nothing\n```\n\n#### Casting from string\n\nClime will automatically cast parameters to `number`, `boolean` based on their types.\nIt also defines interface `StringCastable` that allows user-defined classes to be casted from parameters.\n\n\u003e Please note that `StringCastable` is correspondent to the type of constructor instead of instance, so no `implements` should be present.\n\nFor example:\n\n```ts\nimport {CastingContext} from 'clime';\n\nclass File {\n  constructor(public path: string) {}\n\n  static cast(path: string, context: CastingContext\u003cFile\u003e): File {\n    return new File(Path.resolve(context.cwd, path));\n  }\n}\n```\n\n#### Validators\n\nA `validator` or `validators` can be specified for parameters and options validation.\nA validator can either be an instance that implements interface `Validator\u003cT\u003e`, a function that matches type `ValidatorFunction\u003cT\u003e` or a regular expression.\n\nFor 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.\n\n#### Expected error\n\nA 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.\nAnd a validator for example, usually throw instances of `ExpectedError`.\n\n#### Preserving metadata without command-line parameters\n\nAs 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.\nThus a `@metadata` decorator that does nothing at run time is provided for preserving these metadata:\n\n```ts\n@command()\nexport default class extends Command {\n  @metadata\n  execute(options: SomeOptions) {\n    return 'Hello, Clime!';\n  }\n}\n```\n\nIt is required to have this `@metadata` decorator if no other decorator is applied to method `execute`.\n\n#### Context\n\nContext is an object contains information like current working directory and commands sequence.\nYou can have `context` passed in by adding the last parameter of `execute` method with type `Context`:\n\n```ts\n@command()\nexport default class extends Command {\n  @metadata\n  execute(context: Context) {\n    return 'Hello, Clime!';\n  }\n}\n```\n\n### Subcommands\n\nClime provides an easy way to create subcommands. The default entry of a clime command is `default.js` (`default.ts` before compilation of course).\nAny other `.js` files under the same folder are considered as subcommand files.\n\nClime allows multi-level subcommands based on file structures. For three-level commands like below:\n\n```text\ncommand\n\ncommand foo\ncommand foo biu\ncommand foo yo\n\ncommand bar\ncommand bar bia\ncommand bar pia\n```\n\nThe file structure could be:\n\n```text\n- commands\n  - default.ts\n  - foo.ts\n  - foo\n    - biu.ts\n    - yo.ts\n  - bar\n    - default.ts\n    - bia.ts\n    - pia.ts\n```\n\nYou 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`),\nor at the same level of the folder of level-`(n+1)` commands (like `foo.ts` and folder `foo`).\n\n#### Command entry with description only\n\nClime allows an entry of a group of subcommands to provide only descriptions rather than an actual command.\nJust export `description` and `brief` directly from the entry module to do so:\n\n```ts\nexport const description = 'Some detailed description';\n\n// Used when listing as subcommands.\nexport const brief = 'brief description';\n```\n\n#### Configuring subcommand definitions using `subcommands` field\n\nClime has to load every subcommand modules under a specific command to know their briefs.\nTo avoid this, you may export a `subcommands` array with subcommand definitions like below:\n\n```ts\nimport {SubcommandDefinition} from 'clime';\n\nexport const subcommands: SubcommandDefinition[] = [\n  {\n    name: 'foo',\n    brief: 'A subcommand named foo',\n  },\n  {\n    name: 'bar',\n    brief: 'A subcommand named bar',\n  },\n];\n```\n\nFurther more, those definition entries also allow you to add alias or aliases for subcommands:\n\n```ts\nimport {SubcommandDefinition} from 'clime';\n\nexport const subcommands: SubcommandDefinition[] = [\n  {\n    name: 'foo',\n    alias: 'f',\n    brief: 'A subcommand named foo',\n  },\n  {\n    name: 'bar',\n    aliases: ['b', 'bb'],\n    brief: 'A subcommand named bar',\n  },\n];\n```\n\n## Multiple command roots support\n\nFor some CLI tools, it would be nice to support project specific commands. And Clime has this ability built-in.\n\nFor example, you may define the `cli` object like below to load commands from both CLI tool commands path as well as project path:\n\n```ts\nlet cli = new CLI('greet', [\n  Path.join(__dirname, 'commands'),\n  'project-commands',\n]);\n```\n\nAnd you can also add different labels for those command directories:\n\n```ts\nlet cli = new CLI('greet', [\n  {\n    label: 'Built-in',\n    path: Path.join(__dirname, 'commands'),\n  },\n  {\n    label: 'Extra',\n    path: 'project-commands',\n  },\n]);\n```\n\n## Testable\n\nAs the core of Clime is not coupled with stream-based command line, commands written with Clime can be easily tested.\n\nFor example:\n\n```ts\nimport {Command, Context, command, metadata} from 'clime';\n\nexport class TestContext extends Context {\n  promptForQuery(): Promise\u003cstring\u003e {\n    // Using library like Inquirer.js to interact with user.\n    return Promise.resolve('result');\n  }\n}\n\n@command()\nexport default class TestCommand extends Command {\n  @metadata\n  execute(context: TestContext) {\n    return context.promptForQuery();\n  }\n}\n```\n\nTo test this command, we just need to extend `TestContext`, override `promptForQuery` and call `execute` with new context.\n\nIf this command is meant to stop somewhere and exit the process, we can define a `ExitSignal` class that implements `Printable` interface:\n\n```ts\nimport {Printable} from 'clime';\n\nexport class ExitSignal implements Printable {\n  constructor(public code: number) {}\n\n  print(): void {\n    process.exit(this.code);\n  }\n}\n\nexport function exit(code = 0): void {\n  throw new ExitSignal(code);\n}\n```\n\nBecause `print` method would only be executed by the shim, your test can safely catch the exit signal and assert its correctness.\n\n## License\n\nMIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvilicvane%2Fclime","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvilicvane%2Fclime","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvilicvane%2Fclime/lists"}