Skip to content

feat(cli): split cli into sub commands #250

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 19, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 71 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -29,24 +29,79 @@ Local invocation:
$ npx api-docs-tooling --help
```

```sh
Usage: api-docs-tooling [options]
```
Usage: api-docs-tooling [options] [command]

CLI tool to generate API documentation of a Node.js project.
CLI tool to generate and lint Node.js API documentation

Options:
-i, --input [patterns...] Specify input file patterns using glob syntax
--ignore [patterns...] Specify which input files to ignore using glob syntax
-o, --output <path> Specify the relative or absolute output directory
-v, --version <semver> Specify the target version of Node.js, semver compliant (default: "v22.11.0")
-c, --changelog <url> Specify the path (file: or https://) to the CHANGELOG.md file (default:
"https://raw.githubusercontent.com/nodejs/node/HEAD/CHANGELOG.md")
-t, --target [mode...] Set the processing target modes (choices: "json-simple", "legacy-html", "legacy-html-all",
"man-page", "legacy-json", "legacy-json-all", "addon-verify", "api-links", "orama-db")
--disable-rule [rule...] Disable a specific linter rule (choices: "invalid-change-version",
"missing-change-version", "missing-introduced-in", default: [])
--lint-dry-run Run linter in dry-run mode (default: false)
--git-ref A git ref/commit URL pointing to Node.js
-r, --reporter [reporter] Specify the linter reporter (choices: "console", "github", default: "console")
-h, --help display help for command

Commands:
generate [options] Generate API docs
lint [options] Run linter independently
interactive Launch guided CLI wizard
list <types> List the given type
help [command] display help for command
```

### `generate`

```
Usage: api-docs-tooling generate [options]

Generate API docs

Options:
-i, --input <patterns...> Input file patterns (glob)
--ignore [patterns...] Ignore patterns (comma-separated)
-o, --output <dir> Output directory
-p, --threads <number> (default: "12")
-v, --version <semver> Target Node.js version (default: "v22.14.0")
-c, --changelog <url> Changelog URL or path (default: "https://raw.githubusercontent.com/nodejs/node/HEAD/CHANGELOG.md")
--git-ref <url> Git ref/commit URL (default: "https://github.com/nodejs/node/tree/HEAD")
-t, --target [modes...] Target generator modes (choices: "json-simple", "legacy-html", "legacy-html-all", "man-page", "legacy-json", "legacy-json-all", "addon-verify", "api-links", "orama-db")
--no-lint Skip lint before generate
-h, --help display help for command
```

### `lint`

```
Usage: api-docs-tooling lint [options]

Run linter independently

Options:
-i, --input <patterns...> Input file patterns (glob)
--ignore [patterns...] Ignore patterns (comma-separated)
--disable-rule [rules...] Disable linter rules (choices: "duplicate-stability-nodes", "invalid-change-version", "missing-introduced-in")
--dry-run Dry run mode (default: false)
-r, --reporter <reporter> Linter reporter to use
-h, --help display help for command
```

### `interactive`

```
Usage: api-docs-tooling interactive [options]

Launch guided CLI wizard

Options:
-h, --help display help for command
```

### `list`

```
Usage: api-docs-tooling list [options] <types>

List the given type

Arguments:
types The type to list (choices: "generators", "rules", "reporters")

Options:
-h, --help display help for command
```
184 changes: 38 additions & 146 deletions bin/cli.mjs
Original file line number Diff line number Diff line change
@@ -1,161 +1,53 @@
#!/usr/bin/env node

import { resolve } from 'node:path';
import process from 'node:process';
import { cpus } from 'node:os';
import { Argument, Command, Option } from 'commander';

import { Command, Option } from 'commander';
import interactive from './commands/interactive.mjs';
import list, { types } from './commands/list.mjs';
import commands from './commands/index.mjs';

import { coerce } from 'semver';
import { DOC_NODE_CHANGELOG_URL, DOC_NODE_VERSION } from '../src/constants.mjs';
import createGenerator from '../src/generators.mjs';
import { publicGenerators } from '../src/generators/index.mjs';
import createLinter from '../src/linter/index.mjs';
import reporters from '../src/linter/reporters/index.mjs';
import rules from '../src/linter/rules/index.mjs';
import createMarkdownLoader from '../src/loaders/markdown.mjs';
import createMarkdownParser from '../src/parsers/markdown.mjs';
import createNodeReleases from '../src/releases.mjs';

const availableGenerators = Object.keys(publicGenerators);

const program = new Command();

program
const program = new Command()
.name('api-docs-tooling')
.description('CLI tool to generate API documentation of a Node.js project.')
.addOption(
new Option(
'-i, --input [patterns...]',
'Specify input file patterns using glob syntax'
).makeOptionMandatory()
)
.addOption(
new Option(
'--ignore [patterns...]',
'Specify which input files to ignore using glob syntax'
)
)
.addOption(
new Option(
'-o, --output <path>',
'Specify the relative or absolute output directory'
)
)
.addOption(
new Option(
'-v, --version <semver>',
'Specify the target version of Node.js, semver compliant'
).default(DOC_NODE_VERSION)
)
.addOption(
new Option(
'-c, --changelog <url>',
'Specify the path (file: or https://) to the CHANGELOG.md file'
).default(DOC_NODE_CHANGELOG_URL)
)
.addOption(
new Option(
'-t, --target [mode...]',
'Set the processing target modes'
).choices(availableGenerators)
)
.addOption(
new Option('--disable-rule [rule...]', 'Disable a specific linter rule')
.choices(Object.keys(rules))
.default([])
)
.addOption(
new Option('--lint-dry-run', 'Run linter in dry-run mode').default(false)
)
.addOption(
new Option('--git-ref', 'A git ref/commit URL pointing to Node.js').default(
'https://github.com/nodejs/node/tree/HEAD'
)
)
.addOption(
new Option('-r, --reporter [reporter]', 'Specify the linter reporter')
.choices(Object.keys(reporters))
.default('console')
)
.addOption(
new Option(
'-p, --threads <number>',
'The maximum number of threads to use. Set to 1 to disable parallelism'
).default(Math.max(1, cpus().length - 1))
)
.parse(process.argv);

/**
* @typedef {keyof publicGenerators} Target A list of the available generator names.
*
* @typedef {Object} Options
* @property {Array<string>|string} input Specifies the glob/path for input files.
* @property {string} output Specifies the directory where output files will be saved.
* @property {Target[]} target Specifies the generator target mode.
* @property {string} version Specifies the target Node.js version.
* @property {string} changelog Specifies the path to the Node.js CHANGELOG.md file.
* @property {string[]} disableRule Specifies the linter rules to disable.
* @property {boolean} lintDryRun Specifies whether the linter should run in dry-run mode.
* @property {boolean} useGit Specifies whether the parser should execute optional git commands. (Should only be used within a git repo)
* @property {keyof reporters} reporter Specifies the linter reporter.
*
* @name ProgramOptions
* @type {Options}
* @description The return type for values sent to the program from the CLI.
*/
const {
input,
ignore,
output,
target = [],
version,
changelog,
disableRule,
lintDryRun,
gitRef,
reporter,
threads,
} = program.opts();
.description('CLI tool to generate and lint Node.js API documentation');

const linter = createLinter(lintDryRun, disableRule);
// Registering generate and lint commands
commands.forEach(({ name, description, options, action }) => {
const cmd = program.command(name).description(description);

const { loadFiles } = createMarkdownLoader();
const { parseApiDocs } = createMarkdownParser();
// Add options to the command
Object.values(options).forEach(({ flags, desc, prompt }) => {
const option = new Option(flags.join(', '), desc).default(
prompt.initialValue
);

const apiDocFiles = await loadFiles(input, ignore);
if (prompt.required) {
option.makeOptionMandatory();
}

const parsedApiDocs = await parseApiDocs(apiDocFiles);
if (prompt.type === 'multiselect') {
option.choices(prompt.options.map(({ value }) => value));
}

const { runGenerators } = createGenerator(parsedApiDocs);

// Retrieves Node.js release metadata from a given Node.js version and CHANGELOG.md file
const { getAllMajors } = createNodeReleases(changelog);
cmd.addOption(option);
});

// Runs the Linter on the parsed API docs
linter.lintAll(parsedApiDocs);
// Set the action for the command
cmd.action(action);
});

if (target) {
await runGenerators({
// A list of target modes for the API docs parser
generators: target,
// Resolved `input` to be used
input: input,
// Resolved `output` path to be used
output: output && resolve(output),
// Resolved SemVer of current Node.js version
version: coerce(version),
// A list of all Node.js major versions with LTS status
releases: await getAllMajors(),
// An URL containing a git ref URL pointing to the commit or ref that was used
// to generate the API docs. This is used to link to the source code of the
gitRef,
// How many threads should be used
threads,
});
}
// Register the interactive command
program
.command('interactive')
.description('Launch guided CLI wizard')
.action(interactive);

// Reports Lint Content
linter.report(reporter);
// Register the list command
program
.command('list')
.addArgument(new Argument('<types>', 'The type to list').choices(types))
.description('List the given type')
.action(list);

process.exitCode = Number(linter.hasError());
// Parse and execute command-line arguments
program.parse(process.argv);
Loading
Oops, something went wrong.
Loading
Oops, something went wrong.