diff --git a/Readme.md b/Readme.md index 14dba192a..c3241a476 100644 --- a/Readme.md +++ b/Readme.md @@ -37,7 +37,7 @@ Read this in other languages: English | [简体中文](./Readme_zh-CN.md) - [.usage](#usage) - [.description and .summary](#description-and-summary) - [.helpOption(flags, description)](#helpoptionflags-description) - - [.addHelpCommand()](#addhelpcommand) + - [.helpCommand()](#helpcommand) - [More configuration](#more-configuration-2) - [Custom event listeners](#custom-event-listeners) - [Bits and pieces](#bits-and-pieces) @@ -904,16 +904,18 @@ program .helpOption('-e, --HELP', 'read more information'); ``` -### .addHelpCommand() +### .helpCommand() -A help command is added by default if your command has subcommands. You can explicitly turn on or off the implicit help command with `.addHelpCommand()` and `.addHelpCommand(false)`. +A help command is added by default if your command has subcommands. You can explicitly turn on or off the implicit help command with `.helpCommand(true)` and `.helpCommand(false)`. You can both turn on and customise the help command by supplying the name and description: ```js -program.addHelpCommand('assist [command]', 'show assistance'); +program.helpCommand('assist [command]', 'show assistance'); ``` +(Or use `.addHelpCommand()` to add a command you construct yourself.) + ### More configuration The built-in help is formatted using the Help class. diff --git a/docs/deprecated.md b/docs/deprecated.md index 74ffb16d6..7dc5bad23 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -15,6 +15,7 @@ They are currently still available for backwards compatibility, but should not b - [Short option flag longer than a single character](#short-option-flag-longer-than-a-single-character) - [Import from `commander/esm.mjs`](#import-from-commanderesmmjs) - [cmd.\_args](#cmd_args) + - [.addHelpCommand(string|boolean|undefined)](#addhelpcommandstringbooleanundefined) - [Removed](#removed) - [Default import of global Command object](#default-import-of-global-command-object) @@ -205,6 +206,27 @@ const registeredArguments = program.registeredArguments; Deprecated from Commander v11. +### .addHelpCommand(string|boolean|undefined) + +This was originally used with a variety of parameters, but not by passing a Command object despite the "add" name. + +```js +program.addHelpCommand('assist [command]'); +program.addHelpCommand('assist', 'show assistance'); +program.addHelpCommand(false); + +``` + +In new code you configure the help command with `.helpCommand()`. Or use `.addHelpCommand()` which now takes a Command object, like `.addCommand()`. + +```js +program.helpCommand('assist [command]'); +program.helpCommand('assist', 'show assistance'); +program.helpCommand(false); + +program.addHelpCommand(new Command('assist').argument('[command]').description('show assistance')); + +``` ## Removed ### Default import of global Command object diff --git a/lib/command.js b/lib/command.js index 8f2d30071..7e568df38 100644 --- a/lib/command.js +++ b/lib/command.js @@ -71,10 +71,9 @@ class Command extends EventEmitter { this._helpDescription = 'display help for command'; this._helpShortFlag = '-h'; this._helpLongFlag = '--help'; - this._addImplicitHelpCommand = undefined; // Deliberately undefined, not decided whether true or false - this._helpCommandName = 'help'; - this._helpCommandnameAndArgs = 'help [command]'; - this._helpCommandDescription = 'display help for command'; + this._addImplicitHelpCommand = undefined; // undecided whether true or false yet, not inherited + /** @type {Command} */ + this._helpCommand = undefined; // lazy initialised, inherited this._helpConfiguration = {}; } @@ -93,9 +92,7 @@ class Command extends EventEmitter { this._helpDescription = sourceCommand._helpDescription; this._helpShortFlag = sourceCommand._helpShortFlag; this._helpLongFlag = sourceCommand._helpLongFlag; - this._helpCommandName = sourceCommand._helpCommandName; - this._helpCommandnameAndArgs = sourceCommand._helpCommandnameAndArgs; - this._helpCommandDescription = sourceCommand._helpCommandDescription; + this._helpCommand = sourceCommand._helpCommand; this._helpConfiguration = sourceCommand._helpConfiguration; this._exitCallback = sourceCommand._exitCallback; this._storeOptionsAsProperties = sourceCommand._storeOptionsAsProperties; @@ -369,39 +366,76 @@ class Command extends EventEmitter { } /** - * Override default decision whether to add implicit help command. + * Customise or override default help command. By default a help command is automatically added if your command has subcommands. * - * addHelpCommand() // force on - * addHelpCommand(false); // force off - * addHelpCommand('help [cmd]', 'display help for [cmd]'); // force on with custom details + * program.helpCommand('help [cmd]'); + * program.helpCommand('help [cmd]', 'show help'); + * program.helpCommand(false); // suppress default help command + * program.helpCommand(true); // add help command even if no subcommands * + * @param {string|boolean} enableOrNameAndArgs - enable with custom name and/or arguments, or boolean to override whether added + * @param {string} [description] - custom description * @return {Command} `this` command for chaining */ - addHelpCommand(enableOrNameAndArgs, description) { - if (enableOrNameAndArgs === false) { - this._addImplicitHelpCommand = false; - } else { - this._addImplicitHelpCommand = true; - if (typeof enableOrNameAndArgs === 'string') { - this._helpCommandName = enableOrNameAndArgs.split(' ')[0]; - this._helpCommandnameAndArgs = enableOrNameAndArgs; - } - this._helpCommandDescription = description || this._helpCommandDescription; + helpCommand(enableOrNameAndArgs, description) { + if (typeof enableOrNameAndArgs === 'boolean') { + this._addImplicitHelpCommand = enableOrNameAndArgs; + return this; } + + enableOrNameAndArgs = enableOrNameAndArgs ?? 'help [command]'; + const [, helpName, helpArgs] = enableOrNameAndArgs.match(/([^ ]+) *(.*)/); + const helpDescription = description ?? 'display help for command'; + + const helpCommand = this.createCommand(helpName); + helpCommand.helpOption(false); + if (helpArgs) helpCommand.arguments(helpArgs); + if (helpDescription) helpCommand.description(helpDescription); + + this._addImplicitHelpCommand = true; + this._helpCommand = helpCommand; + return this; } /** - * @return {boolean} - * @package internal use only + * Add prepared custom help command. + * + * @param {(Command|string|boolean)} helpCommand - custom help command, or deprecated enableOrNameAndArgs as for `.helpCommand()` + * @param {string} [deprecatedDescription] - deprecated custom description used with custom name only + * @return {Command} `this` command for chaining */ + addHelpCommand(helpCommand, deprecatedDescription) { + // If not passed an object, call through to helpCommand for backwards compatibility, + // as addHelpCommand was originally used like helpCommand is now. + if (typeof helpCommand !== 'object') { + this.helpCommand(helpCommand, deprecatedDescription); + return this; + } + + this._addImplicitHelpCommand = true; + this._helpCommand = helpCommand; + return this; + } - _hasImplicitHelpCommand() { - if (this._addImplicitHelpCommand === undefined) { - return this.commands.length && !this._actionHandler && !this._findCommand('help'); + /** + * Lazy create help command. + * + * @return {(Command|null)} + * @package + */ + _getHelpCommand() { + const hasImplicitHelpCommand = this._addImplicitHelpCommand ?? + (this.commands.length && !this._actionHandler && !this._findCommand('help')); + + if (hasImplicitHelpCommand) { + if (this._helpCommand === undefined) { + this.helpCommand(undefined, undefined); // use default name and description + } + return this._helpCommand; } - return this._addImplicitHelpCommand; + return null; } /** @@ -1315,7 +1349,7 @@ Expecting one of '${allowedValues.join("', '")}'`); if (operands && this._findCommand(operands[0])) { return this._dispatchSubcommand(operands[0], operands.slice(1), unknown); } - if (this._hasImplicitHelpCommand() && operands[0] === this._helpCommandName) { + if (this._getHelpCommand() && operands[0] === this._getHelpCommand().name()) { return this._dispatchHelpCommand(operands[1]); } if (this._defaultCommandName) { @@ -1572,7 +1606,7 @@ Expecting one of '${allowedValues.join("', '")}'`); operands.push(arg); if (args.length > 0) unknown.push(...args); break; - } else if (arg === this._helpCommandName && this._hasImplicitHelpCommand()) { + } else if (this._getHelpCommand() && arg === this._getHelpCommand().name()) { operands.push(arg); if (args.length > 0) operands.push(...args); break; diff --git a/lib/help.js b/lib/help.js index e2441ed87..586bad8d2 100644 --- a/lib/help.js +++ b/lib/help.js @@ -26,13 +26,8 @@ class Help { visibleCommands(cmd) { const visibleCommands = cmd.commands.filter(cmd => !cmd._hidden); - if (cmd._hasImplicitHelpCommand()) { - // Create a command matching the implicit help command. - const [, helpName, helpArgs] = cmd._helpCommandnameAndArgs.match(/([^ ]+) *(.*)/); - const helpCommand = cmd.createCommand(helpName) - .helpOption(false); - helpCommand.description(cmd._helpCommandDescription); - if (helpArgs) helpCommand.arguments(helpArgs); + const helpCommand = cmd._getHelpCommand(); + if (helpCommand && !helpCommand._hidden) { visibleCommands.push(helpCommand); } if (this.sortSubcommands) { diff --git a/package-lock.json b/package-lock.json index a29166316..94e1aa3fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -725,9 +725,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -748,9 +748,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", - "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1382,9 +1382,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.6", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.6.tgz", - "integrity": "sha512-/t9NnzkOpXb4Nfvg17ieHE6EeSjDS2SGSpNYfoLbUAeL/EOueU/RSdOWFpfQTXBEM7BguYW1XQ0EbM+6RlIh6w==", + "version": "29.5.8", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.8.tgz", + "integrity": "sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -1410,9 +1410,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.8.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz", - "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==", + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -1452,16 +1452,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.0.tgz", - "integrity": "sha512-lgX7F0azQwRPB7t7WAyeHWVfW1YJ9NIgd9mvGhfQpRY56X6AVf8mwM8Wol+0z4liE7XX3QOt8MN1rUKCfSjRIA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", + "integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.9.0", - "@typescript-eslint/type-utils": "6.9.0", - "@typescript-eslint/utils": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/type-utils": "6.10.0", + "@typescript-eslint/utils": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1487,15 +1487,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.0.tgz", - "integrity": "sha512-GZmjMh4AJ/5gaH4XF2eXA8tMnHWP+Pm1mjQR2QN4Iz+j/zO04b9TOvJYOX2sCNIQHtRStKTxRY1FX7LhpJT4Gw==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", + "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.9.0", - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/typescript-estree": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", "debug": "^4.3.4" }, "engines": { @@ -1515,13 +1515,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", - "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", + "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0" + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1532,9 +1532,9 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", - "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", + "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1545,13 +1545,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", - "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", + "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1572,12 +1572,12 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", - "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", + "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/types": "6.10.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1589,13 +1589,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", - "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", + "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0" + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1606,13 +1606,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.0.tgz", - "integrity": "sha512-XXeahmfbpuhVbhSOROIzJ+b13krFmgtc4GlEuu1WBT+RpyGPIA4Y/eGnXzjbDj5gZLzpAXO/sj+IF/x2GtTMjQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz", + "integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.9.0", - "@typescript-eslint/utils": "6.9.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/utils": "6.10.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1633,9 +1633,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", - "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", + "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1646,13 +1646,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", - "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", + "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/visitor-keys": "6.9.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1673,17 +1673,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.0.tgz", - "integrity": "sha512-5Wf+Jsqya7WcCO8me504FBigeQKVLAMPmUzYgDbWchINNh1KJbxCgVya3EQ2MjvJMVeXl3pofRmprqX6mfQkjQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", + "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.9.0", - "@typescript-eslint/types": "6.9.0", - "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", "semver": "^7.5.4" }, "engines": { @@ -1698,12 +1698,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", - "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", + "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/types": "6.10.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1721,9 +1721,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2168,6 +2168,18 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/builtins": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", @@ -2687,15 +2699,15 @@ } }, "node_modules/eslint": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", - "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.52.0", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -3084,9 +3096,9 @@ } }, "node_modules/eslint-plugin-n": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.2.0.tgz", - "integrity": "sha512-AQER2jEyQOt1LG6JkGJCCIFotzmlcCZFur2wdKrp1JX2cNotC7Ae0BcD/4lLv3lUAArM9uNS8z/fsvXTd0L71g==", + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.3.1.tgz", + "integrity": "sha512-w46eDIkxQ2FaTHcey7G40eD+FhTXOdKudDXPUO2n9WNcslze/i/HT2qJ3GXjHngYSGDISIgPNhwGtgoix4zeOw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", @@ -3094,6 +3106,7 @@ "eslint-plugin-es-x": "^7.1.0", "get-tsconfig": "^4.7.0", "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", "is-core-module": "^2.12.1", "minimatch": "^3.1.2", "resolve": "^1.22.2", @@ -3945,6 +3958,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -5611,9 +5639,9 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" diff --git a/tests/command.chain.test.js b/tests/command.chain.test.js index 3f907c869..58d8c4af2 100644 --- a/tests/command.chain.test.js +++ b/tests/command.chain.test.js @@ -34,12 +34,54 @@ describe('Command methods that should return this for chaining', () => { expect(result).toBe(program); }); - test('when call .addHelpCommand() then returns this', () => { + test('when call .addHelpCommand(cmd) then returns this', () => { const program = new Command(); - const result = program.addHelpCommand(false); + const result = program.addHelpCommand(new Command('assist')); expect(result).toBe(program); }); + test('when call deprecated .addHelpCommand() then returns this', () => { + const program = new Command(); + const result = program.addHelpCommand(); + expect(result).toBe(program); + }); + + test('when call deprecated .addHelpCommand(boolean) then returns this', () => { + const program = new Command(); + const result1 = program.addHelpCommand(true); + expect(result1).toBe(program); + const result2 = program.addHelpCommand(false); + expect(result2).toBe(program); + }); + + test('when call deprecated .addHelpCommand(string[, string]) then returns this', () => { + const program = new Command(); + const result1 = program.addHelpCommand('assist'); + expect(result1).toBe(program); + const result2 = program.addHelpCommand('assist', 'assist description'); + expect(result2).toBe(program); + }); + + test('when call .helpCommand(name) then returns this', () => { + const program = new Command(); + const result = program.helpCommand(); + expect(result).toBe(program); + }); + + test('when call .helpCommand(name, description) then returns this', () => { + const program = new Command(); + const result1 = program.helpCommand('assist', 'assist description'); + expect(result1).toBe(program); + }); + + test('when call .helpCommand(boolean) then returns this', () => { + const program = new Command(); + const result1 = program.helpCommand(true); + expect(result1).toBe(program); + const result2 = program.helpCommand(false); + expect(result2).toBe(program); + }); + test('when call .exitOverride() then returns this', () => { const program = new Command(); const result = program.exitOverride(() => { }); diff --git a/tests/command.copySettings.test.js b/tests/command.copySettings.test.js index 79722d78b..de06914d7 100644 --- a/tests/command.copySettings.test.js +++ b/tests/command.copySettings.test.js @@ -50,15 +50,28 @@ describe('copyInheritedSettings property tests', () => { expect(cmd._helpLongFlag).toBe('--zz'); }); - test('when copyInheritedSettings then copies addHelpCommand(name, description)', () => { + test('when copyInheritedSettings then copies custom help command', () => { const source = new commander.Command(); const cmd = new commander.Command(); - source.addHelpCommand('HELP [cmd]', 'ddd'); + source.helpCommand('HELP [cmd]', 'ddd'); cmd.copyInheritedSettings(source); - expect(cmd._helpCommandName).toBe('HELP'); - expect(cmd._helpCommandnameAndArgs).toBe('HELP [cmd]'); - expect(cmd._helpCommandDescription).toBe('ddd'); + cmd.helpCommand(true); // force enable + const helpCommand = cmd._getHelpCommand(); + expect(helpCommand).toBeTruthy(); + expect(helpCommand.name()).toBe('HELP'); + expect(helpCommand.description()).toBe('ddd'); + }); + + test('when copyInheritedSettings then does not copy help enable override', () => { + const source = new commander.Command(); + const cmd = new commander.Command(); + + // Existing behaviour, force enable/disable does not inherit, + // largely so (probably redundant) program.helpCommand(true) does not inherit to leaf subcommands. + source.helpCommand(true); + cmd.copyInheritedSettings(source); + expect(cmd._addHelpOption).toBeUndefined(); }); test('when copyInheritedSettings then copies configureHelp(config)', () => { diff --git a/typings/index.d.ts b/typings/index.d.ts index e77cb1bf3..7ca2c49c9 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -419,18 +419,27 @@ export class Command { arguments(names: string): this; /** - * Override default decision whether to add implicit help command. + * Customise or override default help command. By default a help command is automatically added if your command has subcommands. * * @example + * ```ts + * program.helpCommand('help [cmd]'); + * program.helpCommand('help [cmd]', 'show help'); + * program.helpCommand(false); // suppress default help command + * program.helpCommand(true); // add help command even if no subcommands * ``` - * addHelpCommand() // force on - * addHelpCommand(false); // force off - * addHelpCommand('help [cmd]', 'display help for [cmd]'); // force on with custom details - * ``` - * - * @returns `this` command for chaining */ - addHelpCommand(enableOrNameAndArgs?: string | boolean, description?: string): this; + helpCommand(nameAndArgs: string, description?: string): this; + helpCommand(enable: boolean): this; + + /** + * Add prepared custom help command. + */ + addHelpCommand(cmd: Command): this; + /** @deprecated since v12, instead use helpCommand */ + addHelpCommand(nameAndArgs: string, description?: string): this; + /** @deprecated since v12, instead use helpCommand */ + addHelpCommand(enable?: boolean): this; /** * Add hook for life cycle event. diff --git a/typings/index.test-d.ts b/typings/index.test-d.ts index 22706982b..2a4864ad7 100644 --- a/typings/index.test-d.ts +++ b/typings/index.test-d.ts @@ -59,11 +59,19 @@ expectType(program.argument('[value]', 'description', parseFl expectType(program.arguments(' [env]')); // addHelpCommand +expectType(program.addHelpCommand(new commander.Command('assist'))); +// Deprecated uses expectType(program.addHelpCommand()); expectType(program.addHelpCommand(false)); expectType(program.addHelpCommand(true)); -expectType(program.addHelpCommand('compress ')); -expectType(program.addHelpCommand('compress ', 'compress target file')); +expectType(program.addHelpCommand('assist [cmd]')); +expectType(program.addHelpCommand('assist [file]', 'display help')); + +// helpCommand +expectType(program.helpCommand(false)); +expectType(program.helpCommand(true)); +expectType(program.helpCommand('assist [cmd]')); +expectType(program.helpCommand('assist [file]', 'display help')); // exitOverride expectType(program.exitOverride());