diff --git a/README.md b/README.md
index dcbb008..02d92c7 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
+
@@ -99,10 +99,10 @@ This allows us to organize and structure the logic nicely.
You can check the full [docker-based example](./examples/docker) for a more in-depth demo.
-
## Features
- [**Intl support**](./docs/features.md#intl-support): support for internationalized messages.
- [**Routing**](./docs/features.md#routing): routes are generated where command handlers are expected to be found.
+- [**Bash completion**](./docs/features.md#bash-completion): a command is created to generate the `bash-completions` script for the cli.
- [**Debug mode**](./docs/features.md#debug-mode): validate the definition and options, especially when upgrading to a new version.
- [**Typescript support**](./docs/features.md#typescript-support): build the cli with typescript.
diff --git a/docs/cli-options.md b/docs/cli-options.md
index fa74c9c..8875605 100644
--- a/docs/cli-options.md
+++ b/docs/cli-options.md
@@ -90,6 +90,15 @@ $ CLIER_DEBUG=1 node cli.js
**Default**: `process.env.CLIER_DEBUG`
+#### `completion`
+Configure bash-completion functionality
+##### `completion.enabled`
+Whether to create bash-completion command
+**Default**: `true`
+##### `completion.command`
+Name of the completion command
+**Default**: `generate-completions`
+
#### `messages`
Object containing the messages to be used in the Cli, to override the default ones defined by this library. This enables internationalization and customization of cli-native messages. You can see a use-case in this [intl-cli example](/examples/intl-cli)
**Default**: defined in [/src/cli-messages](/src/cli-messages.ts) and [/src/cli-errors](/src/cli-errors.ts)
diff --git a/docs/features.md b/docs/features.md
index 8931b87..38063b0 100644
--- a/docs/features.md
+++ b/docs/features.md
@@ -38,6 +38,11 @@ If the location is `["nms", "cmd"]` for an entryfile `cli.js`, the list of candi
5. `/index.js` - named import (`cmd`)
6. `/cli.js` - named import (`cmd`)
+## Bash completion
+`cli-er` includes a command to generate bash-completions for the cli. This can be configured through [`CliOptions.completion`](/docs/cli-options.md#completion), to change the name of such command, or to disable this behaviour.
+You can check [here](/examples/docker/completions.sh) the generated script for docker example.
+> The script was tested with `bash` version 3.2.57 and `zsh` version 5.9.
+
## Debug mode
When active, the library will generate debug logs warning about problems, deprecations or suggestions. Two types exist:
- `WARN`: to indicated misused or deprecated options.
diff --git a/examples/docker/README.md b/examples/docker/README.md
index 3f59781..daf050d 100644
--- a/examples/docker/README.md
+++ b/examples/docker/README.md
@@ -30,4 +30,7 @@ node docker.js builder prune --all --filter until=24 -f true --keep-storage 100
# Print error when unknown option is provided (since v0.5.0)
node docker.js builder prune --test
+
+# Generate bash-completion script (since v0.14.0)
+node docker.js generate-completions > completions.sh
```
diff --git a/examples/docker/completions.sh b/examples/docker/completions.sh
new file mode 100644
index 0000000..1e01bf8
--- /dev/null
+++ b/examples/docker/completions.sh
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+# Bash completion script for docker
+# This file is automatically generated by running `docker generate-completions`.
+# Created with cli-er@0.13.0 on 2023-11-25 09:40
+
+function indirect(){
+ if [[ -z $ZSH_VERSION ]]; then
+ echo ${!1}
+ else
+ echo ${(P)1}
+ fi
+}
+_docker() {
+ # declare nestings
+ local nestings=(
+ "builder=build,prune"
+ )
+ # declare options by location ("o_" represents root)
+ local opts_by_location=(
+ "o_=--debug,--help,--version,--nodebug,--no-debug"
+ "o_builder="
+ "o_build=--source,--add-host,--build-arg,--cache-from,--disable-content-trust,--file,--iidfile,--isolation,--label,--network,--no-cache,--output,--platform,--progress,--pull,--quiet,--secret,--ssh,--tag,--target"
+ "o_prune=--all,--filter,--force,--keep-storage"
+ )
+ # Calculate keys for all available commands/namespaces
+ local all_locations=($(echo "${opts_by_location[@]:1}" | sed 's/o_\([^=]*\)=[^ ]*/\1/g'))
+ # initialize top-level definitions
+ local top_defs=("${nestings[@]%%=*}")
+
+ for d in "${nestings[@]}";do declare "$d";done
+ for o in "${opts_by_location[@]}";do declare "$o";done
+
+ # Obtain the location by removing the cli-name from the list of words
+ local location=("${COMP_WORDS[@]:1:$COMP_CWORD-1}")
+ # Initialize options with global values
+ local opts=($(echo "${o_}" | tr "," "\n"))
+ local initialized=false includeopts=false
+ while [ ${#location[@]} -gt 0 ]; do
+ local curr="${location[@]:0:1}"
+ local ocurr="o_$curr"
+ # Check for valid command/namespace
+ if [[ " ${top_defs[@]} " =~ " ${curr} " ]] && [[ " ${all_locations[@]} " =~ " ${curr} " ]]; then
+ top_defs=($(echo "$(indirect $curr)" | tr "," "\n"))
+ location=(${location[@]:1})
+ opts+=($(echo "$(indirect $ocurr)" | tr "," "\n"))
+ initialized=true
+ # Check if element is a command to include options
+ [[ ! " ${nestings[@]%%=*} " =~ " ${curr} " ]] && includeopts=true || includeopts=false
+ else
+ # if not valid location was found, empty the list
+ [[ $initialized != true ]] && top_defs=()
+ break
+ fi
+ done
+
+ # Include options into top_defs
+ [[ $includeopts == true ]] && top_defs+=("${opts[@]}")
+ COMPREPLY=($(compgen -W "${top_defs[*]}" -- $2))
+}
+
+complete -F _docker docker
diff --git a/examples/docker/package.json b/examples/docker/package.json
index c6cef1f..b86b129 100644
--- a/examples/docker/package.json
+++ b/examples/docker/package.json
@@ -5,6 +5,6 @@
"main": "docker.js",
"author": "carloscortonc",
"dependencies": {
- "cli-er": "0.12.0"
+ "cli-er": "0.14.0"
}
}
diff --git a/src/bash-completion.ts b/src/bash-completion.ts
new file mode 100644
index 0000000..a7431ac
--- /dev/null
+++ b/src/bash-completion.ts
@@ -0,0 +1,119 @@
+import { DefinitionElement, isShortAlias } from "./cli-utils";
+import { CliOptions, Definition, Kind } from "./types";
+import { getClierVersion } from "./utils";
+
+export function generateCompletions({
+ definition,
+ cliOptions,
+}: {
+ definition: Definition;
+ cliOptions: CliOptions;
+}) {
+ const tab = (n: number = 1) => " ".repeat(2 * n);
+ const getOptionsAliases = (e: DefinitionElement, filterFn?: (e: DefinitionElement) => boolean) =>
+ Object.values(e.options || {})
+ .filter(filterFn || (() => true))
+ .reduce((acc, curr) => acc.concat(...curr.aliases!.filter((a) => !isShortAlias(a))), [] as string[])
+ .join(",");
+ // prettier-ignore
+ const nestings: string[] = [], optionsByLocation: string[] = [];
+ const populateLists = (def?: Definition, loc = "") => {
+ if (!def) return;
+ const _currOptions = getOptionsAliases({ options: def }, (e) => e.kind === Kind.OPTION);
+ optionsByLocation.push(`"o_${loc}=${_currOptions}"`);
+ for (const el of Object.values(def)) {
+ const currNesting = getOptionsAliases(el, (e) => [Kind.NAMESPACE, Kind.COMMAND].includes(e.kind as Kind));
+ if (el.kind === Kind.NAMESPACE) {
+ nestings.push(`"${el.key}=${currNesting}"`);
+ }
+ populateLists(el.options, el.key);
+ }
+ };
+ populateLists(definition);
+ const toFormattedList = (els: string[]) =>
+ [""]
+ .concat(...els)
+ .join(`\n${tab(2)}`)
+ .concat(`\n${tab(1)}`);
+ const str = Object.entries({
+ cliName: cliOptions.cliName,
+ cliVersion: cliOptions.cliVersion,
+ clierVersion: getClierVersion(),
+ date: getLocalDate("YYYY-MM-DD HH:mm"),
+ command: cliOptions.completion.command,
+ nestings: toFormattedList(nestings),
+ optionsByLocation: toFormattedList(optionsByLocation),
+ }).reduce((acc, [k, v]) => acc.replace(new RegExp(`{{${k}}}`, "g"), v), defaultTemplate);
+
+ process.stdout.write(str);
+}
+
+const getLocalDate = (template: string) => {
+ const d = new Date();
+ return [
+ { m: "getFullYear", n: "YYYY", p: 4 },
+ { m: "getMonth", n: "MM", o: 1 },
+ { m: "getDate", n: "DD" },
+ { m: "getHours", n: "HH" },
+ { m: "getMinutes", n: "mm" },
+ ]
+ .map((m) => ({ p: 2, o: 0, ...m }))
+ .map(({ m, p, n, o }) => ({ v: ((d as any)[m]() + o).toString().padStart(p, "0"), n }))
+ .reduce((acc, { v, n }) => acc.replace(new RegExp(n), v), template);
+};
+
+const defaultTemplate = `#!/usr/bin/env bash
+# Bash completion script for {{cliName}}
+# This file is automatically generated by running \`{{cliName}} {{command}}\`.
+# Created with cli-er@{{clierVersion}} on {{date}}
+
+function indirect(){
+ if [[ -z $ZSH_VERSION ]]; then
+ echo \${!1}
+ else
+ echo \${(P)1}
+ fi
+}
+_{{cliName}}() {
+ # declare nestings
+ local nestings=({{nestings}})
+ # declare options by location ("o_" represents root)
+ local opts_by_location=({{optionsByLocation}})
+ # Calculate keys for all available commands/namespaces
+ local all_locations=($(echo "\${opts_by_location[@]:1}" | sed 's/o_\\([^=]*\\)=[^ ]*/\\1/g'))
+ # initialize top-level definitions
+ local top_defs=("\${nestings[@]%%=*}")
+
+ for d in "\${nestings[@]}";do declare "$d";done
+ for o in "\${opts_by_location[@]}";do declare "$o";done
+
+ # Obtain the location by removing the cli-name from the list of words
+ local location=("\${COMP_WORDS[@]:1:$COMP_CWORD-1}")
+ # Initialize options with global values
+ local opts=($(echo "\${o_}" | tr "," "\\n"))
+ local initialized=false includeopts=false
+ while [ \${#location[@]} -gt 0 ]; do
+ local curr="\${location[@]:0:1}"
+ local ocurr="o_$curr"
+ # Check for valid command/namespace
+ if [[ " \${top_defs[@]} " =~ " \${curr} " ]] && [[ " \${all_locations[@]} " =~ " \${curr} " ]]; then
+ top_defs=($(echo "$(indirect $curr)" | tr "," "\\n"))
+ location=(\${location[@]:1})
+ opts+=($(echo "$(indirect $ocurr)" | tr "," "\\n"))
+ initialized=true
+ # Check if element is a command to include options
+ [[ ! " \${nestings[@]%%=*} " =~ " \${curr} " ]] && includeopts=true || includeopts=false
+ else
+ # if not valid location was found, empty the list
+ [[ $initialized != true ]] && top_defs=()
+ break
+ fi
+ done
+
+ # Include options into top_defs
+ [[ $includeopts == true ]] && top_defs+=("\${opts[@]}")
+ COMPREPLY=($(compgen -W "\${top_defs[*]}" -- $2))
+}
+
+complete -F _{{cliName}} {{cliName}}
+`;
diff --git a/src/cli-utils.ts b/src/cli-utils.ts
index 5db2c13..45f77aa 100644
--- a/src/cli-utils.ts
+++ b/src/cli-utils.ts
@@ -1,12 +1,13 @@
import path from "path";
import fs from "fs";
import url from "url";
-import { addLineBreaks, ColumnFormatter, debug, DEBUG_TYPE, deprecationWarning, logErrorAndExit } from "./utils";
+import { addLineBreaks, clone, ColumnFormatter, debug, DEBUG_TYPE, deprecationWarning, logErrorAndExit } from "./utils";
import { Kind, ParsingOutput, Definition, Type, CliOptions, Option, Namespace, Command } from "./types";
import parseOptionValue from "./cli-option-parser";
import { validatePositional } from "./definition-validations";
import flattenArguments from "./option-syntax";
import { closest } from "./edit-distance";
+import { generateCompletions } from "./bash-completion";
import Cli from ".";
/** Create a type containing all elements for better readability, as here is not necessary type-checking due to all methods being internal */
@@ -64,6 +65,13 @@ export const isShortAlias = (alias: string) => /^-.$/.test(alias);
/** Process definition and complete any missing fields */
export function completeDefinition(definition: Definition, cliOptions: CliOptions) {
+ // Include completion command
+ if (cliOptions.completion.enabled) {
+ definition[cliOptions.completion.command] = {
+ action: () => generateCompletions({ definition, cliOptions }),
+ hidden: true,
+ };
+ }
const { autoInclude: helpAutoInclude, template: _, ...helpOption } = cliOptions.help;
// Auto-include help option
if (helpAutoInclude) {
@@ -473,21 +481,23 @@ export function generateScopedHelp(
sections[HELP_SECTIONS.DESCRIPTION] = cliOptions.cliDescription.concat("\n");
}
// Add usage section
- const { existingKinds, hasOptions, positionalOptions } = Object.values(definitionRef || {}).reduce(
- (acc, curr) => {
- const { kind, positional, required, key } = curr;
- if (kind === Kind.OPTION) {
- acc.hasOptions = true;
- if (positional === true || typeof positional === "number") {
- acc.positionalOptions.push({ index: positional, key, required });
+ const { existingKinds, hasOptions, positionalOptions } = Object.values(definitionRef || {})
+ .filter((e) => !e.hidden)
+ .reduce(
+ (acc, curr) => {
+ const { kind, positional, required, key } = curr;
+ if (kind === Kind.OPTION) {
+ acc.hasOptions = true;
+ if (positional === true || typeof positional === "number") {
+ acc.positionalOptions.push({ index: positional, key, required });
+ }
+ } else if (acc.existingKinds.indexOf(kind as string) < 0) {
+ acc.existingKinds.push(kind as string);
}
- } else if (acc.existingKinds.indexOf(kind as string) < 0) {
- acc.existingKinds.push(kind as string);
- }
- return acc;
- },
- { existingKinds: [] as string[], hasOptions: false, positionalOptions: [] as any[] },
- );
+ return acc;
+ },
+ { existingKinds: [] as string[], hasOptions: false, positionalOptions: [] as any[] },
+ );
const formatKinds = (kinds: string[]) =>
kinds
@@ -610,7 +620,7 @@ export function getDefinitionElement(
rawLocation: string[],
cliOptions: CliOptions,
): DefinitionElement | undefined {
- let definitionRef = definition;
+ let definitionRef = clone(definition);
let inheritedOptions: Definition = {};
const getOptions = (d: Definition) =>
Object.entries(d)
diff --git a/src/index.ts b/src/index.ts
index f19677e..3ecabbc 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -67,6 +67,10 @@ export default class Cli {
cliVersion: packagejson.version || "-",
cliDescription: packagejson.description || "",
debug: false,
+ completion: {
+ enabled: true,
+ command: "generate-completions",
+ },
};
// Environment variables should have the highest priority
@@ -87,7 +91,10 @@ export default class Cli {
}
// Store back at process.env.CLIER_DEBUG the final value of CliOptions.debug, to be accesible without requiring CliOptions
process.env[CLIER_DEBUG_KEY] = this.options.debug ? "1" : "";
- this.definition = completeDefinition(clone(definition), this.options) as Definition;
+ // Do this so we can provide "completeDefinition" with a reference to "this.definition"
+ this.definition = clone(definition);
+ this.definition = completeDefinition(this.definition, this.options) as Definition;
+
return this;
}
/**
diff --git a/src/types.ts b/src/types.ts
index 73a6666..e37ff70 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -187,6 +187,17 @@ export type CliOptions = {
* @default `process.env.CLIER_DEBUG`
*/
debug: boolean;
+ /** bash-completion related config */
+ completion: {
+ /** Whether to generate the completion-command
+ * @default true
+ */
+ enabled: boolean;
+ /** The name of the completion command
+ * @default "generate-completions"
+ */
+ command: string;
+ };
/** Messages to be used, overriding the ones defined by this library
* This allows to include new translations, or tweak the current ones
*/
diff --git a/src/utils.ts b/src/utils.ts
index 5acafbd..279d655 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -130,6 +130,16 @@ export function findPackageJson(baseLocation: string) {
return undefined;
}
+/** Find the version of this library */
+export function getClierVersion() {
+ const location = path.join(__dirname, "..", "package.json");
+ try {
+ return JSON.parse(fs.readFileSync(location, "utf-8")).version;
+ } catch {
+ return undefined;
+ }
+}
+
export const isDebugActive = () => process.env[CLIER_DEBUG_KEY];
export enum DEBUG_TYPE {
diff --git a/test/__snapshots__/bash-completion.test.ts.snap b/test/__snapshots__/bash-completion.test.ts.snap
new file mode 100644
index 0000000..628d42f
--- /dev/null
+++ b/test/__snapshots__/bash-completion.test.ts.snap
@@ -0,0 +1,69 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`generateCompletions bash-script gets printed via process.stdout.write 1`] = `
+"#!/usr/bin/env bash
+# Bash completion script for cliName
+# This file is automatically generated by running \`cliName complete\`.
+# Created with cli-er@0.13.0 on 2022-08-04 00:00
+
+function indirect(){
+ if [[ -z $ZSH_VERSION ]]; then
+ echo \${!1}
+ else
+ echo \${(P)1}
+ fi
+}
+_cliName() {
+ # declare nestings
+ local nestings=(
+ \\"nms=cmd\\"
+ \\"nmsi=nested-nms\\"
+ \\"nested-nms=cmd\\"
+ )
+ # declare options by location (\\"o_\\" represents root)
+ local opts_by_location=(
+ \\"o_=--global,--help,--version\\"
+ \\"o_nms=\\"
+ \\"o_cmd=--opt\\"
+ \\"o_nmsi=--nmsi-o\\"
+ \\"o_nested-nms=--nested-nms-o\\"
+ )
+ # Calculate keys for all available commands/namespaces
+ local all_locations=($(echo \\"\${opts_by_location[@]:1}\\" | sed 's/o_\\\\([^=]*\\\\)=[^ ]*/\\\\1/g'))
+ # initialize top-level definitions
+ local top_defs=(\\"\${nestings[@]%%=*}\\")
+
+ for d in \\"\${nestings[@]}\\";do declare \\"$d\\";done
+ for o in \\"\${opts_by_location[@]}\\";do declare \\"$o\\";done
+
+ # Obtain the location by removing the cli-name from the list of words
+ local location=(\\"\${COMP_WORDS[@]:1:$COMP_CWORD-1}\\")
+ # Initialize options with global values
+ local opts=($(echo \\"\${o_}\\" | tr \\",\\" \\"\\\\n\\"))
+ local initialized=false includeopts=false
+ while [ \${#location[@]} -gt 0 ]; do
+ local curr=\\"\${location[@]:0:1}\\"
+ local ocurr=\\"o_$curr\\"
+ # Check for valid command/namespace
+ if [[ \\" \${top_defs[@]} \\" =~ \\" \${curr} \\" ]] && [[ \\" \${all_locations[@]} \\" =~ \\" \${curr} \\" ]]; then
+ top_defs=($(echo \\"$(indirect $curr)\\" | tr \\",\\" \\"\\\\n\\"))
+ location=(\${location[@]:1})
+ opts+=($(echo \\"$(indirect $ocurr)\\" | tr \\",\\" \\"\\\\n\\"))
+ initialized=true
+ # Check if element is a command to include options
+ [[ ! \\" \${nestings[@]%%=*} \\" =~ \\" \${curr} \\" ]] && includeopts=true || includeopts=false
+ else
+ # if not valid location was found, empty the list
+ [[ $initialized != true ]] && top_defs=()
+ break
+ fi
+ done
+
+ # Include options into top_defs
+ [[ $includeopts == true ]] && top_defs+=(\\"\${opts[@]}\\")
+ COMPREPLY=($(compgen -W \\"\${top_defs[*]}\\" -- $2))
+}
+
+complete -F _cliName cliName
+"
+`;
diff --git a/test/bash-completion.test.ts b/test/bash-completion.test.ts
new file mode 100644
index 0000000..7d9451e
--- /dev/null
+++ b/test/bash-completion.test.ts
@@ -0,0 +1,22 @@
+import { generateCompletions } from "../src/bash-completion";
+import d from "./data/definition.json";
+import Cli from "../src";
+
+describe("generateCompletions", () => {
+ beforeAll(() => {
+ const mockDate = new Date(2022, 7, 4);
+ jest.spyOn(global, "Date").mockImplementation((() => mockDate) as any);
+ });
+ const cliOptions: any = {
+ cliName: "cliName",
+ cliVersion: "cliVersion",
+ completion: { command: "complete" },
+ };
+ const definition = new Cli(d as any).definition;
+ it("bash-script gets printed via process.stdout.write", () => {
+ const writeSpy = jest.spyOn(process.stdout, "write").mockImplementationOnce((() => {}) as any);
+ generateCompletions({ definition, cliOptions });
+ expect(writeSpy.mock.lastCall[0]).toMatchSnapshot();
+ writeSpy.mockRestore();
+ });
+});
diff --git a/test/cli-utils.test.ts b/test/cli-utils.test.ts
index 781d3c8..bbd9904 100644
--- a/test/cli-utils.test.ts
+++ b/test/cli-utils.test.ts
@@ -65,6 +65,10 @@ describe("completeDefinition", () => {
cliVersion: "",
cliDescription: "",
debug: false,
+ completion: {
+ enabled: false,
+ command: "generate-completions",
+ },
};
it("Completes missing fields in definition with nested content ", () => {
const completedDefinition = completeDefinition(d, cliOptions);
@@ -705,7 +709,7 @@ Usage: cli-name NAMESPACE|COMMAND [OPTIONS]`),
},
});
expect(output).toStrictEqual(`
-Usage: cli-name NAMESPACE [OPTIONS]
+Usage: cli-name NAMESPACE
Namespaces:
nms -
@@ -720,7 +724,7 @@ This is a custom footer
bool: { type: "boolean", default: true, description: "boolean option" },
arg1: { positional: 0, required: true, description: "first positional mandatory option" },
arg2: { positional: 1, description: "second positional option" },
- arg3: { positional: true, hidden: true },
+ arg3: { positional: true, description: "catch-all positional option" },
});
generateScopedHelp(def, [], cliOptions);
expect(output).toStrictEqual(`
@@ -732,6 +736,7 @@ Options:
--bool boolean option (default: true)
--arg1 first positional mandatory option
--arg2 second positional option
+ --arg3 catch-all positional option
-h, --help Display global help, or scoped to a namespace/command
`);
diff --git a/test/index.test.ts b/test/index.test.ts
index eb3a2db..5379bad 100644
--- a/test/index.test.ts
+++ b/test/index.test.ts
@@ -21,7 +21,7 @@ beforeEach(() => {
});
describe("Cli.constructor", () => {
- it("Resulting definition contains only auto-included options when provided with empty definition", () => {
+ it("Resulting definition contains only auto-included options/commands when provided with empty definition", () => {
const c = new Cli({});
expect(c.definition).toStrictEqual({
help: expect.objectContaining({
@@ -34,6 +34,10 @@ describe("Cli.constructor", () => {
aliases: ["-v", "--version"],
description: "Display version",
}),
+ "generate-completions": expect.objectContaining({
+ aliases: ["generate-completions"],
+ hidden: true,
+ }),
});
});
it("Resulting definition calculates aliases dashes when not present", () => {
@@ -58,8 +62,11 @@ describe("Cli.constructor", () => {
}),
);
});
- it("Resulting definition is an empty object when provided with empty definition and all auto-included options are disabled", () => {
- const c = new Cli({}, { help: { autoInclude: false }, version: { autoInclude: false } });
+ it("Resulting definition is an empty object when provided with empty definition and all auto-included options/commands are disabled", () => {
+ const c = new Cli(
+ {},
+ { help: { autoInclude: false }, version: { autoInclude: false }, completion: { enabled: false } },
+ );
expect(c.definition).toStrictEqual({});
});
it("CliOptions are default when instantiating with no options", () => {
@@ -97,6 +104,10 @@ describe("Cli.constructor", () => {
cliVersion: "1.0.0",
cliDescription: "cli-description",
debug: false,
+ completion: {
+ enabled: true,
+ command: "generate-completions",
+ },
});
});
it("CliOptions are the result of merging default and provided options when instantiating with options", () => {
@@ -138,6 +149,10 @@ describe("Cli.constructor", () => {
cliVersion: "2.0.0",
cliDescription: "custom-description",
debug: false,
+ completion: {
+ enabled: true,
+ command: "generate-completions",
+ },
});
});
it("Overwrite default logger", () => {