{"$schema":"https://doc-kit.nodejs.org/schemas/api-doc/1.0.1.json","id":"contributing-commands","path":"/contributing/commands","type":"module","module":"contributing-commands","title":"Adding a command","introducedIn":null,"sourceLink":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"This page shows how to add a command to the `doc-kit` CLI\n([`@doc-kit/cli`](../packages/cli.html), in `packages/cli`). Add one for a new\naction. For a new output, write a [generator](../creating-generators.html)\ninstead: `doc-kit generate --target` already runs custom generators.\n\nThe CLI is built on [Commander](https://github.com/tj/commander.js). Each\ncommand is a module in `packages/cli/bin/commands/` whose default export is a\nCommander `Command`. `packages/cli/bin/cli.mjs` adds every command listed in\n`packages/cli/bin/commands/index.mjs` to the program. The commands that exist\ntoday are documented on the [CLI](../cli.html) page.","summary":"This page shows how to add a command to the `doc-kit` CLI (`@doc-kit/cli`, in `packages/cli`). Add one for a new action. For a new output, write a generator instead: `doc-kit generate --target` already runs custom generators.","examples":[],"children":[{"kind":"section","id":"create-the-command","name":"Create the command","title":"Create the command","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"```js displayName=\"packages/cli/bin/commands/my-command.mjs\"\nimport logger from '@doc-kit/core/logger/index.mjs';\nimport { Command, Option } from 'commander';\n\nimport { errorWrap } from '../utils.mjs';\n\nexport default new Command('my-command')\n  .description('Does something useful')\n  .addOption(new Option('-f, --force', 'Overwrite existing files'))\n  .action(\n    errorWrap(async opts => {\n      logger.info(`Running with force: ${Boolean(opts.force)}`);\n    })\n  );\n```\n\nWrap every action in `errorWrap` from `packages/cli/bin/utils.mjs`. It awaits\nthe action, logs anything it throws (synchronously or not) with the doc-kit\nlogger, and exits with code 1. To fail a command, throw an `Error`.\n\nLog through the logger from `@doc-kit/core/logger/index.mjs` rather than\n`console`. A `preAction` hook in `cli.mjs` sets its level from `--log-level`\nbefore any command runs.","summary":"Wrap every action in `errorWrap` from `packages/cli/bin/utils.mjs`. It awaits the action, logs anything it throws (synchronously or not) with the doc-kit logger, and exits with code 1. To fail a command, throw an `Error`.","examples":[{"language":"js","displayName":"packages/cli/bin/commands/my-command.mjs","code":"import logger from '@doc-kit/core/logger/index.mjs';\nimport { Command, Option } from 'commander';\n\nimport { errorWrap } from '../utils.mjs';\n\nexport default new Command('my-command')\n  .description('Does something useful')\n  .addOption(new Option('-f, --force', 'Overwrite existing files'))\n  .action(\n    errorWrap(async opts => {\n      logger.info(`Running with force: ${Boolean(opts.force)}`);\n    })\n  );"}],"children":[]},{"kind":"section","id":"register-it","name":"Register it","title":"Register it","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"Import the command in `packages/cli/bin/commands/index.mjs` and add it to the\nexported array:\n\n```js displayName=\"packages/cli/bin/commands/index.mjs\"\nimport generate from './generate.mjs';\nimport myCommand from './my-command.mjs';\nimport preview from './preview.mjs';\n\nexport default [generate, preview, myCommand];\n```\n\nNothing else needs to change. Commander builds the help text from the\ncommand's description and options, so `doc-kit --help` lists the new command\nand `doc-kit my-command --help` shows its options.","summary":"Import the command in `packages/cli/bin/commands/index.mjs` and add it to the exported array:","examples":[{"language":"js","displayName":"packages/cli/bin/commands/index.mjs","code":"import generate from './generate.mjs';\nimport myCommand from './my-command.mjs';\nimport preview from './preview.mjs';\n\nexport default [generate, preview, myCommand];"}],"children":[]},{"kind":"section","id":"options-and-arguments","name":"Options and arguments","title":"Options and arguments","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"Define options with Commander's `Option` class, as `generate.mjs` does, and\npositional arguments with `.argument()`. The action receives the positional\narguments first, then the parsed options, then the `Command` itself.\n\n```js displayName=\"packages/cli/bin/commands/my-command.mjs\"\nexport default new Command('my-command')\n  .argument('<name>', 'What to act on')\n  .addOption(new Option('-o, --output <directory>', 'The output directory'))\n  .addOption(new Option('--count <number>', 'How many').argParser(Number))\n  .addOption(\n    new Option('--mode <mode>', 'How to run')\n      .choices(['fast', 'safe'])\n      .default('safe')\n  )\n  .action(\n    errorWrap(async (name, opts) => {\n      // opts.output, opts.count, opts.mode\n    })\n  );\n```\n\n| Syntax               | Meaning                                         |\n| -------------------- | ----------------------------------------------- |\n| `--flag`             | Boolean, `true` when present                    |\n| `--name <value>`     | Takes a required value                          |\n| `--name [value]`     | Takes an optional value                         |\n| `--name <values...>` | Takes one or more values, collected in an array |\n\nValues arrive as strings unless you add an `.argParser()`. Multi-word flags\nare camelCased on the options object, so `--git-ref` becomes `opts.gitRef`.\nSee the [Commander options documentation](https://github.com/tj/commander.js#options)\nfor the full API.\n\n`--log-level` is defined on the program, not on each command, so it works on\neither side of the command name (`doc-kit --log-level debug my-command` or\n`doc-kit my-command --log-level debug`). Don't add it to your command.","summary":"Define options with Commander's `Option` class, as `generate.mjs` does, and positional arguments with `.argument()`. The action receives the positional arguments first, then the parsed options, then the `Command` itself.","examples":[{"language":"js","displayName":"packages/cli/bin/commands/my-command.mjs","code":"export default new Command('my-command')\n  .argument('<name>', 'What to act on')\n  .addOption(new Option('-o, --output <directory>', 'The output directory'))\n  .addOption(new Option('--count <number>', 'How many').argParser(Number))\n  .addOption(\n    new Option('--mode <mode>', 'How to run')\n      .choices(['fast', 'safe'])\n      .default('safe')\n  )\n  .action(\n    errorWrap(async (name, opts) => {\n      // opts.output, opts.count, opts.mode\n    })\n  );"}],"children":[]},{"kind":"section","id":"running-generators-from-a-command","name":"Running generators from a command","title":"Running generators from a command","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"A command that builds docs can follow what `generate` does:\n\n```js displayName=\"packages/cli/bin/commands/my-command.mjs\"\nimport createGenerator from '@doc-kit/core';\nimport {\n  assertRunnableOptions,\n  setConfig,\n} from '@doc-kit/core/utils/configuration/index.mjs';\nimport { Command, Option } from 'commander';\n\nimport { errorWrap } from '../utils.mjs';\n\nconst { runGenerators } = createGenerator();\n\nexport default new Command('my-command')\n  .description('Builds the docs, then does something useful')\n  .addOption(new Option('--config-file <path>', 'Config file'))\n  .addOption(\n    new Option('-i, --input <patterns...>', 'Input file patterns (glob)')\n  )\n  .addOption(new Option('-t, --target <generator...>', 'Target generator(s)'))\n  .action(\n    errorWrap(async opts => {\n      const config = await setConfig(opts);\n      assertRunnableOptions(config);\n\n      await runGenerators(config);\n    })\n  );\n```\n\n`setConfig` loads the\n[configuration file](../configuration.html#where-configuration-lives)\n(`opts.configFile`, or the first one it finds), merges in the command-line\noptions, and fills in the generators' defaults. `assertRunnableOptions` then\nthrows when no `target` or `input` is set.\n\n`setConfig` reads options by the names `generate` gives them (`configFile`,\n`input`, `target`, `output`, `gitRef`, and so on), so declare the flags your\ncommand needs with the same names. A flag you don't declare can't be passed,\nand its value comes only from the configuration file. To turn a new option\ninto configuration, map it in `createConfigFromCLIOptions`\n(`packages/core/src/utils/configuration/index.mjs`).","summary":"A command that builds docs can follow what `generate` does:","examples":[{"language":"js","displayName":"packages/cli/bin/commands/my-command.mjs","code":"import createGenerator from '@doc-kit/core';\nimport {\n  assertRunnableOptions,\n  setConfig,\n} from '@doc-kit/core/utils/configuration/index.mjs';\nimport { Command, Option } from 'commander';\n\nimport { errorWrap } from '../utils.mjs';\n\nconst { runGenerators } = createGenerator();\n\nexport default new Command('my-command')\n  .description('Builds the docs, then does something useful')\n  .addOption(new Option('--config-file <path>', 'Config file'))\n  .addOption(\n    new Option('-i, --input <patterns...>', 'Input file patterns (glob)')\n  )\n  .addOption(new Option('-t, --target <generator...>', 'Target generator(s)'))\n  .action(\n    errorWrap(async opts => {\n      const config = await setConfig(opts);\n      assertRunnableOptions(config);\n\n      await runGenerators(config);\n    })\n  );"}],"children":[]},{"kind":"section","id":"before-you-open-a-pull-request","name":"Before you open a pull request","title":"Before you open a pull request","scope":"module","overloadOf":null,"stability":null,"added":[],"deprecated":[],"removed":[],"napiVersion":[],"changes":[],"description":"* **Try it** from the repository root with\n  `node --run run -- my-command --help`, which runs\n  `node packages/cli/bin/cli.mjs`.\n* **Keep the command thin.** `@doc-kit/cli` publishes only `bin/` (see\n  `files` in `packages/cli/package.json`), and `node --run test` only picks up\n  tests under `packages/*/src/` and `scripts/`, never `bin/`. Put logic worth\n  testing in `@doc-kit/core`, with its tests in a `__tests__` directory next\n  to it.\n* **Document it** with a section on the [CLI page](../cli.html) (`docs/cli.md`),\n  using the same option-list format as the existing commands, and in\n  `packages/cli/README.md` if it changes how people use the package. If it\n  adds a flag that sets configuration, also add the flag to the\n  [CLI flags](../configuration.html#cli-flags) table.\n* **Add a changeset** with `node --run changeset`, since `@doc-kit/cli` is\n  published. See\n  [Adding a changeset](https://github.com/nodejs/doc-kit/blob/main/CONTRIBUTING.md#adding-a-changeset).","summary":"","examples":[],"children":[]}]}