Skip to content
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

feat: new option to display brief with custom usage #34

Merged
merged 3 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export const root = buildCommand({
docs: {
brief: "Example for live playground with boolean flag",
customUsage: [
"--quiet",
"--quiet=yes",
"--noQuiet",
{ input: "--quiet", brief: "Flag with no value" },
{ input: "--quiet=yes", brief: "Flag with explicit value" },
{ input: "--noQuiet", brief: "Negated flag" },
],
},
});
5 changes: 4 additions & 1 deletion docs/docs/features/command-routing/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ The parameters object is a specification of all of the parameters (arguments and

A base level of documentation is required, and more is always appreciated. All commands must specify a value for `brief` that contains a short line of text to be used when referring to this command throughout the help text of the application.

Optionally, you can further customize the help text for this specific command by including `fullDescription` or `customUsage`. The former will override `brief` in the command's help text and can contain multiple lines of text rather than just one. The `customUsage` property will replace the auto-generated usage lines, and is useful when there's some [additional validation of user inputs](../out-of-scope.mdx#cross-argument-validation) that isn't represented natively by Stricli.
Optionally, you can further customize the help text for this specific command by including `fullDescription` or `customUsage`. The former will override `brief` in the command's help text and can contain multiple lines of text rather than just one. The `customUsage` property will replace the auto-generated usage lines, and is useful when there's some [additional validation of user inputs](../out-of-scope.mdx#cross-argument-validation) that isn't represented natively by Stricli. You can also provide an object with `input` and `brief` to print a description after the usage line.

```ts
// output-next-line
Expand All @@ -84,6 +84,7 @@ buildCommand({
customUsage: [
"-a -b",
"-c",
{ input: "-d", brief: "Brief description of this use case" },
],
},
...
Expand All @@ -98,6 +99,8 @@ run --help
USAGE
run -a -b
run -c
run -d
Brief description of this use case
run --help

This is the full description of the command.
Expand Down
16 changes: 13 additions & 3 deletions packages/core/src/routing/command/documentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import { formatDocumentationForPositionalParameters } from "../../parameter/posi
import type { CommandParameters } from "../../parameter/types";
import type { HelpFormattingArguments } from "../types";

export interface CustomUsage {
readonly input: string;
readonly brief: string;
}

export interface CommandDocumentation {
/**
* In-line documentation for this command.
Expand All @@ -18,7 +23,7 @@ export interface CommandDocumentation {
/**
* Sample usage to replace the generated usage lines.
*/
readonly customUsage?: readonly string[];
readonly customUsage?: readonly (string | CustomUsage)[];
}

/**
Expand All @@ -34,8 +39,13 @@ export function* generateCommandHelpLines(
const prefix = args.prefix.join(" ");
yield args.ansiColor ? `\x1B[1m${headers.usage}\x1B[22m` : headers.usage;
if (customUsage) {
for (const customUsageLine of customUsage) {
yield ` ${prefix} ${customUsageLine}`;
for (const usage of customUsage) {
if (typeof usage === "string") {
yield ` ${prefix} ${usage}`;
} else {
const brief = args.ansiColor ? `\x1B[3m${usage.brief}\x1B[23m` : usage.brief;
yield ` ${prefix} ${usage.input}\n ${brief}`;
}
}
} else {
yield ` ${formatUsageLineForParameters(parameters, args)}`;
Expand Down
65 changes: 64 additions & 1 deletion packages/core/tests/application.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,11 @@ describe("Application", () => {
},
docs: {
brief: "basic command",
customUsage: ["custom usage 1", "custom usage 2", "custom usage 3"],
customUsage: [
"custom usage 1",
{ input: "custom-two", brief: "enhanced custom usage 2" },
"custom usage 3",
],
},
});
const appWithAlternateUsage = buildApplication(commandWithAlternateUsage, {
Expand Down Expand Up @@ -1526,6 +1530,65 @@ describe("Application", () => {
compareToBaseline(this, ApplicationRunResultBaselineFormat, result);
});
});

describe("nested basic route map with hidden routes, always show help-all (alias)", () => {
// GIVEN
const rootRouteMap = buildRouteMapForFakeContext({
routes: {
sub: buildBasicRouteMap("sub"),
subHidden: buildBasicRouteMap("subHidden"),
},
docs: {
brief: "root route map",
hideRoute: {
subHidden: true,
},
},
});
const app = buildApplication(rootRouteMap, {
name: "cli",
documentation: {
alwaysShowHelpAllFlag: true,
useAliasInUsageLine: true,
},
});

// WHEN
it("display help text for root (implicit)", async function () {
const result = await runWithInputs(app, []);
compareToBaseline(this, ApplicationRunResultBaselineFormat, result);
});

it("display help text for root", async function () {
const result = await runWithInputs(app, ["--help"]);
compareToBaseline(this, ApplicationRunResultBaselineFormat, result);
});

it("display help text for root including hidden", async function () {
const result = await runWithInputs(app, ["--help-all"]);
compareToBaseline(this, ApplicationRunResultBaselineFormat, result);
});

it("fails for undefined route", async function () {
const result = await runWithInputs(app, ["undefined"]);
compareToBaseline(this, ApplicationRunResultBaselineFormat, result);
});

it("fails for undefined flag", async function () {
const result = await runWithInputs(app, ["--undefined"]);
compareToBaseline(this, ApplicationRunResultBaselineFormat, result);
});

it("displays help text for nested hidden route map (implicit)", async function () {
const result = await runWithInputs(app, ["subHidden"]);
compareToBaseline(this, ApplicationRunResultBaselineFormat, result);
});

it("displays help text for nested hidden route map", async function () {
const result = await runWithInputs(app, ["subHidden", "--help"]);
compareToBaseline(this, ApplicationRunResultBaselineFormat, result);
});
});
});

describe("proposeCompletions", () => {
Expand Down
114 changes: 113 additions & 1 deletion packages/core/tests/baselines/reference/application.txt
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@ ExitCode=Success
:: STDOUT
USAGE
cli custom usage 1
cli custom usage 2
cli custom-two
enhanced custom usage 2
cli custom usage 3
cli --help

Expand Down Expand Up @@ -1085,6 +1086,117 @@ ExitCode=UnknownCommand
:: STDERR
No command registered for `undefined`

:::: Application / run / nested basic route map with hidden routes, always show help-all (alias) / display help text for root
ExitCode=Success
:: STDOUT
USAGE
cli sub command ...
cli -h
cli -H

root route map

FLAGS
-h --help Print help information and exit
-H --helpAll Print help information (including hidden commands/flags) and exit

COMMANDS
sub sub

:: STDERR

:::: Application / run / nested basic route map with hidden routes, always show help-all (alias) / display help text for root (implicit)
ExitCode=Success
:: STDOUT
USAGE
cli sub command ...
cli -h
cli -H

root route map

FLAGS
-h --help Print help information and exit
-H --helpAll Print help information (including hidden commands/flags) and exit

COMMANDS
sub sub

:: STDERR

:::: Application / run / nested basic route map with hidden routes, always show help-all (alias) / display help text for root including hidden
ExitCode=Success
:: STDOUT
USAGE
cli sub command ...
cli subHidden command ...
cli -h
cli -H

root route map

FLAGS
-h --help Print help information and exit
-H --helpAll Print help information (including hidden commands/flags) and exit

COMMANDS
sub sub
subHidden subHidden

:: STDERR

:::: Application / run / nested basic route map with hidden routes, always show help-all (alias) / displays help text for nested hidden route map
ExitCode=Success
:: STDOUT
USAGE
cli subHidden command
cli subHidden -h
cli subHidden -H

subHidden

FLAGS
-h --help Print help information and exit
-H --helpAll Print help information (including hidden commands/flags) and exit

COMMANDS
command basic command

:: STDERR

:::: Application / run / nested basic route map with hidden routes, always show help-all (alias) / displays help text for nested hidden route map (implicit)
ExitCode=Success
:: STDOUT
USAGE
cli subHidden command
cli subHidden -h
cli subHidden -H

subHidden

FLAGS
-h --help Print help information and exit
-H --helpAll Print help information (including hidden commands/flags) and exit

COMMANDS
command basic command

:: STDERR

:::: Application / run / nested basic route map with hidden routes, always show help-all (alias) / fails for undefined flag
ExitCode=UnknownCommand
:: STDOUT

:: STDERR
No command registered for `--undefined`

:::: Application / run / nested basic route map with hidden routes, always show help-all (alias) / fails for undefined route
ExitCode=UnknownCommand
:: STDOUT

:: STDERR
No command registered for `undefined`

:::: Application / run / nested basic route map with hidden routes, always show help-all / display help text for root
ExitCode=Success
:: STDOUT
Expand Down
75 changes: 75 additions & 0 deletions packages/core/tests/baselines/reference/routing/command.txt
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,26 @@ USAGE

brief

FLAGS
-a --alpha alpha flag brief
--bravo... bravo flag brief
[--charlie] charlie flag brief
-d --delta/--noDelta delta flag brief
-h --help Print help information and exit

ARGUMENTS
args... string array brief

:::: Command / printHelp / mixed parameters, enhanced custom usage
USAGE
prefix -a 1
enhanced usage line #1
prefix -a 2 -d
enhanced usage line #2
prefix --help

brief

FLAGS
-a --alpha alpha flag brief
--bravo... bravo flag brief
Expand Down Expand Up @@ -307,6 +327,49 @@ USAGE

Longer description of this command's behavior, only printed during --help

FLAGS
-a --alpha alpha flag brief
--bravo... bravo flag brief
[--charlie] charlie flag brief
-d --delta/--noDelta delta flag brief
-h --help Print help information and exit

ARGUMENTS
args... string array brief

:::: Command / printHelp / mixed parameters, help all, force alias in usage line
USAGE
prefix (-a value) (--bravo value)... [--charlie c] (-d) <args>...
prefix -h
prefix -H

brief

FLAGS
-a --alpha alpha flag brief
--bravo... bravo flag brief
[--charlie] charlie flag brief
-d --delta/--noDelta delta flag brief
-h --help Print help information and exit
-H --helpAll Print help information (including hidden commands/flags) and exit

ARGUMENTS
args... string array brief

:::: Command / printHelp / mixed parameters, mixed custom usage
USAGE
prefix -a 1
enhanced usage line #1
prefix normal custom usage A
prefix normal custom usage B
prefix -a 2 -d
enhanced usage line #2
prefix normal custom usage C
prefix normal custom usage D
prefix --help

brief

FLAGS
-a --alpha alpha flag brief
--bravo... bravo flag brief
Expand Down Expand Up @@ -442,6 +505,18 @@ brief
FLAGS
-h --help Print help information and exit

:::: Command / printHelp / no parameters, help all, force alias in usage line
USAGE
prefix
prefix -h
prefix -H

brief

FLAGS
-h --help Print help information and exit
-H --helpAll Print help information (including hidden commands/flags) and exit

:::: Command / run / command function returns error / with custom exit code
ExitCode=Unknown(10)
:: STDOUT
Expand Down
Loading