{"id":13475986,"url":"https://github.com/siokas/denomander","last_synced_at":"2025-04-07T19:32:30.299Z","repository":{"id":42700080,"uuid":"245916074","full_name":"siokas/denomander","owner":"siokas","description":"Deno command-line interfaces made easy","archived":false,"fork":false,"pushed_at":"2025-02-20T12:44:36.000Z","size":517,"stargazers_count":151,"open_issues_count":3,"forks_count":9,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-27T01:35:57.756Z","etag":null,"topics":["arguments-parser","cli","cli-app","command-line-interface","command-line-parser","deno","deno-arguments-parser","deno-tools","typescript"],"latest_commit_sha":null,"homepage":"https://doc.deno.land/https/deno.land/x/denomander/docs.ts","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/siokas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-09T01:13:46.000Z","updated_at":"2025-03-15T04:26:53.000Z","dependencies_parsed_at":"2024-11-17T14:11:00.803Z","dependency_job_id":"c529151b-ec8c-45d2-920b-d927f958a241","html_url":"https://github.com/siokas/denomander","commit_stats":{"total_commits":190,"total_committers":11,"mean_commits":"17.272727272727273","dds":"0.12631578947368416","last_synced_commit":"ed199f212d808beed80b4cc121e8e00ac5b171fa"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siokas%2Fdenomander","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siokas%2Fdenomander/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siokas%2Fdenomander/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siokas%2Fdenomander/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/siokas","download_url":"https://codeload.github.com/siokas/denomander/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247716176,"owners_count":20984185,"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":["arguments-parser","cli","cli-app","command-line-interface","command-line-parser","deno","deno-arguments-parser","deno-tools","typescript"],"created_at":"2024-07-31T16:01:25.415Z","updated_at":"2025-04-07T19:32:30.271Z","avatar_url":"https://github.com/siokas.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/siokas/siokas.github.io/master/img/denomander.png\" width=\"256\"\u003e\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\u003cimg alt=\"Github Actions\" src=\"https://github.com/siokas/denomander/workflows/DenoCI/badge.svg?branch=master\"\u003e\n\u003cimg alt=\"deno version\" src=\"https://img.shields.io/badge/deno-1.21.0-blue\"\u003e\n\u003ca href=\"https://nest.land/package/denomander\"\u003e\u003cimg src=\"https://nest.land/badge.svg\" alt=\"Published on nest.land\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n_Denomander_ is a solution for [Deno](https://deno.land) command-line\ninterfaces. It is inspired by [tj](https://github.com/tj)'s\n[commander.js](https://github.com/tj/commander.js) for Node.js.\n\n\u003e Denomander is a [Deno](https://deno.land) project so deno must be\n\u003e [installed](https://deno.land/manual/getting_started/installation).\n\n\u003e Alternatively, there is a Dockerfile in the root of the project to create an\n\u003e image running deno. To use it just build the Docker file\n\u003e `docker build -t deno .` Now you can run all the deno commands\n\u003e `docker run --rm -v $PWD:/app/ deno test`\n\n## Installation\n\nUsing deno.land\n\n```typescript\nimport Denomander from \"https://deno.land/x/denomander/mod.ts\";\n```\n\nUsing nest.land\n\n```typescript\nimport Denomander from \"https://x.nest.land/denomander/mod.ts\";\n```\n\n## Usage Example\n\nFirst, in your deno script, create a _program_, optionally passing a name,\ndescription and version. If not you can change them afterwards by setting the\n**app_name**, **app_description** and **app_version** variables.\n\n```typescript\nconst program = new Denomander({\n  app_name: \"My App Name\",\n  app_description: \"My App Description\",\n  app_version: \"1.0.1\",\n});\n```\n\nThere are three option types: **commands**, **options** and **required\noptions**.\n\n### Options\n\nTo set an option just call the **option()** method passing the **short and long\nflags** separated by a space and the **description**. The value can be accessed\nas properties.\n\n```typescript\nprogram\n  .command(\"serve\", \"Simple Server\")\n  .option(\"-a --address\", \"Define the address\")\n  .option(\"-p --port\", \"Define the port\")\n  .parse(Deno.args);\n\nif (program.address) {\n  const port = program.port || \"8000\";\n  console.log(`Server is running on ${program.address}:${port}`);\n}\n```\n\nYou may define the option's short and long flags by separating them with a\n**space**, **comma** or **| (vertical bar or \"pipe\")**.\n\n```typescript\nprogram\n  .command(\"serve\", \"Start up the server\")\n  .option(\"-a, --address\", \"Define the address\")\n  .option(\"-p | --port\", \"Define the port\")\n  .parse(Deno.args);\n\nconsole.log(`Server is running on ${program.address}:${program.port}`);\n```\n\n### Required Options\n\nThe implementation of required option is exactly same as the optional option but\nyou have to call the **requiredOption()** method instead.\n\n```typescript\nprogram\n  .command(\"serve\", \"Start up the server\")\n  .requiredOption(\"-p --port\", \"Define the port\")\n  .option(\"-a --address\", \"Define the address\")\n  .parse(Deno.args);\n\n// The port is required so it must have a value\nlet address = program.address || \"localhost\";\nconsole.log(`Server run on ${address}:${program.port}`);\n```\n\n### Global Options and Base Command Options\n\nYou have the option to define options which belong to all commands (global\noption) and options which belong to no command (base command option ex.\n`--help`, `--version`).\n\n```typescript\nprogram\n  .baseOption(\"-q --quiet\", \"Do not output any message\")\n  .globalOption(\"-c --color\", \"Define the output color\")\n  .parse(Deno.args);\n```\n\n#### Custom Option Processing\n\nYou may specify a function to do custom processing of option values. The\ncallback function receives a parameter of the pre-processed value.\n\n```typescript\nfunction parseInteger(value: string): number {\n  return parseInt(value);\n}\n\nfunction uppercase(text: string): string {\n  return text.toUpperCase();\n}\n\nprogram\n  .command(\"multiply\", \"Multiply x and y options\")\n  .option(\"-x --xnumber\", \"First Number\", parseInteger)\n  .option(\"-y --ynumber\", \"First Number\", parseInteger)\n  .action(() =\u003e {\n    console.log(program.xnumber * program.ynumber);\n  });\n\nprogram\n  .command(\"commit\", \"Commit Description\")\n  .requiredOption(\"-m --message\", \"Commit Message\", uppercase)\n  .action(() =\u003e {\n    console.log(program.message);\n  });\n```\n\n#### Default Option Value\n\nYou may define a default value for options (in case no value is passed by the\nuser, the app returns the specified default value as the value of the option)\n\n```typescript\nprogram\n  .command(\"foo\", \"Foo Test\")\n  .option(\"-d --default\", \"Default Value\", uppercase, \"bar\")\n  .action(() =\u003e {\n    console.log(program.default);\n  });\n```\n\n#### Option choices\n\nYou may define a list (array) of accepted choices for each option. If the user\nenters anything that is not in this list, a validation error\n(OPTION_CHOICE_ERROR) is thrown. To define accepted choices, you have to create\na custom option object and call the `choices()` method passing the array of the\naccepted choices:\n\n```typescript\nconst fruits = new Option({\n  flags: \"-f --fruits\",\n  description: \"Choose one of accepted choices\",\n}).choices([\"apple\", \"banana\", \"orange\"]);\n\nprogram\n  .command(\"choose\")\n  .addOption(fruits)\n  .action(() =\u003e {\n    console.log(`You chose ${program.fruits}`);\n  });\n```\n\n### Commands\n\nThere are two ways to implement the commands. The first is to use an action\nhandler by calling the **action()** method immediately after the command\ndefinition passing the callback function and the second is with custom one-line\nimplementation. **Multiple command arguments are now supported!**\n\nTo define a command just call the .command() method and pass the command name\n(optionally you may also pass the description and a callback function but if not\nyou may define them afterwards in their own methods). After the command you have\nthe option to declare argument(s) inside brackets []. If you want a not required\nargument just append a question mark (?) after the name of the argument.\n\n```typescript\nprogram\n  .command(\"mv [from] [to] [message?]\", \"Start the server\")\n  .action(({ from, to, message }: any) =\u003e {\n    // Do your actions here\n    console.log(`File is moved from ${from} to ${to}`);\n    if (message) {\n      console.log(\"message\");\n    }\n  });\n\nprogram.parse(Deno.args);\n\n// Command action calback is called in all 3 command names (actual command and two aliases)\n```\n\n#### Parse args with spread operator\n\nYou have to option to catch the rest of the args passed from the user (in an\narray)\n\n```typescript\nprogram\n  .command(\"find [args...]\")\n  .action(({ args }: any) =\u003e {\n    console.log(`Files to find (${args.length}): `);\n    console.log(args);\n  })\n  .description(\"find file\");\n\nprogram.parse(Deno.args);\n\n// Command example:\n\n// \u003e find file1 file2 file3\n// Files to find (3):\n// [ \"file1\", \"file2\", \"file3\" ]\n```\n\n#### Action Handler\n\n\u003e The argument(s) passed in the callback function is now an object so you may\n\u003e destructure the object and take your variable which has the same name with\n\u003e your command declaration!\n\n```typescript\nprogram\n  .command(\"clone [foldername]\")\n  .description(\"clone a repo\")\n  .action(({ foldername }: any) =\u003e {\n    console.log(\"The repo is cloned into: \" + foldername);\n  });\n\nprogram.parse(Deno.args);\n```\n\n#### Extra Parameters\n\n\u003e Any options available in the program are passed to the callback function.\n\n```typescript\nprogram\n  .command(\"clone [foldername]\")\n  .description(\"clone a repo\")\n  .option(\"-b --branch\", \"Branch to clone\")\n  .action(({ foldername }: any, { branch }: any) =\u003e {\n    console.log(\"Repo: \" + foldername);\n    console.log(\"Branch: \" + branch);\n  });\n\nprogram\n  .command(\"multiply\", \"Multiply x and y options\")\n  .option(\"-x --xnumber\", \"First Number\", parseInteger)\n  .option(\"-y --ynumber\", \"First Number\", parseInteger)\n  .action(({ xnumber, ynumber }: any) =\u003e {\n    console.log(xnumber * ynumber);\n  });\n\nprogram.parse(Deno.args);\n```\n\n#### Custom Implementation\n\n```typescript\nprogram.command(\"serve\", \"Start the server\");\n\nif (program.serve) {\n  console.log(\"The server has started...\");\n}\n\nprogram.parse(Deno.args);\n```\n\n### Alias\n\nAfter the command declaration you have the option to declare as many aliases as\nyou want for this spesific command.\n\n```typescript\nprogram\n  .command(\"serve\", \"Start the server\")\n  .alias(\"server\", \"start-server\")\n  .action(() =\u003e {\n    console.log(\"the server is started\");\n  });\n\nprogram.parse(Deno.args);\n\n// Command action calback is called in all 3 command names (actual command and two aliases)\n```\n\n### Sub Commands\n\nAfter the command declaration you have the option to declare as many sub-command\nas you want. You may add an action and description for each one.\n\n```typescript\nconst parent = program.command(\"parent\");\n\nparent\n  .subCommand(\"child1\", \"test\")\n  .action(() =\u003e {\n    console.log(\"parent + child 1 commands\");\n  })\n  .description(\"Sub Command Implementation\");\n\nparent\n  .subCommand(\"child2\", \"test\")\n  .action(() =\u003e {\n    console.log(\"parent + child 2 commands\");\n  })\n  .description(\"Another Sub Command Implementation\");\n\nprogram.parse(Deno.args);\n```\n\n### Option to Change Default Commands (help, version)\n\nIn order to change the default commands (help, version) just call the\ncorresponding method. In case of help pass the command and the description but\nin case of version you may also pass the actual version of the app and after\nthat the command and the description.\n\n```typescript\nprogram.setVersion(\"1.8.1\", \"-x --xversion\", \"Display the version of the app\");\n\nprogram.parse(args);\n```\n\n## Customize Error Messages\n\nThere are two ways to change the error messages. You may pass a fourth argument\nin new `Denomander()` constructor (errors object) or you may call the\n`.errorMessages()` method again passing the error messages in object.\n\n1.\n\n```typescript\nconst program = new Denomander({\n  app_name: \"My MY App\",\n  app_description: \"My MY Description\",\n  app_version: \"1.0.1\",\n  errors: {\n    INVALID_RULE: \"Invalid Rule\",\n    OPTION_NOT_FOUND: \"Option not found!\",\n    COMMAND_NOT_FOUND: \"Command not found!\",\n    REQUIRED_OPTION_NOT_FOUND: \"Required option is not specified!\",\n    REQUIRED_VALUE_NOT_FOUND: \"Required command value is not specified!\",\n    TOO_MANY_PARAMS: \"You have passed too many parameters\",\n  },\n});\n```\n\n2.\n\n```typescript\nprogram.errorMessages({\n  INVALID_RULE: \"Invalid Rule\",\n  OPTION_NOT_FOUND: \"Option not found!\",\n  COMMAND_NOT_FOUND: \"Command not found!\",\n  REQUIRED_OPTION_NOT_FOUND: \"Required option is not specified!\",\n  REQUIRED_VALUE_NOT_FOUND: \"Required command value is not specified!\",\n  TOO_MANY_PARAMS: \"You have passed too many parameters\",\n});\n```\n\n### Improved Error Experience (Option to Throw Errors)\n\nFrom v0.8 by default Denomander app does not throw the errors but instead it\noutputs the error message in the console and exits the app. If you want to throw\nall the errors just pass the `throw_errors: true` option inside the AppDetails\nin Denomander constructor.\n\n```typescript\nconst program = new Denomander({\n  app_name: \"My App Name\",\n  app_description: \"My App Description\",\n  app_version: \"1.0.1\",\n  throw_errors: true,\n});\n```\n\n## Used\n\n- [Deno](https://deno.land)\n- [Deno STD Libraries](https://deno.land/std/)\n- [FlatIcon](https://www.flaticon.com/) for the logo\n\n## Meta\n\nApostolos Siokas – [@siokas\\_](https://twitter.com/siokas_) –\napostolossiokas@gmail.com\n\n## Contributing\n\nAny kind of contribution is welcome!\n\n## License\n\nDistributed under the\n[MIT License](https://github.com/siokas/denomander/blob/master/LICENSE).\n\n[https://github.com/siokas/denomander](https://github.com/siokas/denomander)\n","funding_links":[],"categories":["TypeScript","基础设施","Uncategorized","Tools","cli","\u003ca name=\"TypeScript\"\u003e\u003c/a\u003eTypeScript"],"sub_categories":["Deno 源","Uncategorized","Online Playgrounds","XML","Assistants"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsiokas%2Fdenomander","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsiokas%2Fdenomander","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsiokas%2Fdenomander/lists"}