diff --git a/docs/advanced.md b/docs/advanced.md index 336bdff58..0b033fca1 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -169,6 +169,7 @@ simply needs to export: * `exports.describe`: string used as the description for the command in help text, use `false` for a hidden command * `exports.builder`: object declaring the options the command accepts, or a function accepting and returning a yargs instance * `exports.handler`: a function which will be passed the parsed argv. +* `exports.deprecated`: a boolean (or string) to show deprecation notice. ```js // my-module.js diff --git a/lib/command.js b/lib/command.js index 96bdb45c3..00bd18666 100644 --- a/lib/command.js +++ b/lib/command.js @@ -19,7 +19,7 @@ module.exports = function command (yargs, usage, validation, globalMiddleware) { let defaultCommand globalMiddleware = globalMiddleware || [] - self.addHandler = function addHandler (cmd, description, builder, handler, commandMiddleware) { + self.addHandler = function addHandler (cmd, description, builder, handler, commandMiddleware, deprecated) { let aliases = [] const middlewares = commandMiddlewareFactory(commandMiddleware) handler = handler || (() => {}) @@ -30,13 +30,13 @@ module.exports = function command (yargs, usage, validation, globalMiddleware) { } else if (typeof cmd === 'object') { let command = (Array.isArray(cmd.command) || typeof cmd.command === 'string') ? cmd.command : moduleName(cmd) if (cmd.aliases) command = [].concat(command).concat(cmd.aliases) - self.addHandler(command, extractDesc(cmd), cmd.builder, cmd.handler, cmd.middlewares) + self.addHandler(command, extractDesc(cmd), cmd.builder, cmd.handler, cmd.middlewares, cmd.deprecated) return } // allow a module to be provided instead of separate builder and handler if (typeof builder === 'object' && builder.builder && typeof builder.handler === 'function') { - self.addHandler([cmd].concat(aliases), description, builder.builder, builder.handler, builder.middlewares) + self.addHandler([cmd].concat(aliases), description, builder.builder, builder.handler, builder.middlewares, builder.deprecated) return } @@ -72,7 +72,7 @@ module.exports = function command (yargs, usage, validation, globalMiddleware) { }) if (description !== false) { - usage.command(cmd, description, isDefault, aliases) + usage.command(cmd, description, isDefault, aliases, deprecated) } handlers[parsedCommand.cmd] = { @@ -81,6 +81,7 @@ module.exports = function command (yargs, usage, validation, globalMiddleware) { handler, builder: builder || {}, middlewares, + deprecated, demanded: parsedCommand.demanded, optional: parsedCommand.optional } diff --git a/lib/types/frozen-usage-instance.ts b/lib/types/frozen-usage-instance.ts index a12ea9381..d384ad3d4 100644 --- a/lib/types/frozen-usage-instance.ts +++ b/lib/types/frozen-usage-instance.ts @@ -7,6 +7,6 @@ export interface FrozenUsageInstance { usageDisabled: boolean epilogs: string[] examples: [string, string][] - commands: [string, string, boolean, string[]][] + commands: [string, string, boolean, string[], boolean][] descriptions: Dictionary } diff --git a/lib/types/usage-instance.ts b/lib/types/usage-instance.ts index 287d7e55b..c2cbf4136 100644 --- a/lib/types/usage-instance.ts +++ b/lib/types/usage-instance.ts @@ -6,7 +6,7 @@ import { YError } from '../yerror' export interface UsageInstance { cacheHelpMessage (): void clearCachedHelpMessage (): void - command (cmd: string, description: string | undefined, isDefault: boolean, aliases: string[]): void + command (cmd: string, description: string | undefined, isDefault: boolean, aliases: string[], deprecated: boolean): void deferY18nLookup (str: string): string describe (key: string, desc?: string): void describe (keys: Dictionary): void @@ -16,7 +16,7 @@ export interface UsageInstance { failFn (f: FailureFunction): void freeze (): void functionDescription (fn: { name?: string }): string - getCommands (): [string, string, boolean, string[]][] + getCommands (): [string, string, boolean, string[], boolean][] getDescriptions (): Dictionary getPositionalGroupName (): string getUsage (): [string, string][] diff --git a/lib/usage.ts b/lib/usage.ts index 90c400339..a9e38b756 100644 --- a/lib/usage.ts +++ b/lib/usage.ts @@ -96,8 +96,8 @@ export function usage (yargs: YargsInstance, y18n: Y18N) { examples.push([cmd, description || '']) } - let commands: [string, string, boolean, string[]][] = [] - self.command = function command (cmd, description, isDefault, aliases) { + let commands: [string, string, boolean, string[], boolean][] = [] + self.command = function command (cmd, description, isDefault, aliases, deprecated = false) { // the last default wins, so cancel out any previously set default if (isDefault) { commands = commands.map((cmdArray) => { @@ -105,7 +105,7 @@ export function usage (yargs: YargsInstance, y18n: Y18N) { return cmdArray }) } - commands.push([cmd, description || '', isDefault, aliases]) + commands.push([cmd, description || '', isDefault, aliases, deprecated]) } self.getCommands = () => commands @@ -224,6 +224,13 @@ export function usage (yargs: YargsInstance, y18n: Y18N) { if (command[3] && command[3].length) { hints.push(`[${__('aliases:')} ${command[3].join(', ')}]`) } + if (command[4]) { + if (typeof command[4] === 'string') { + hints.push(`[${__('deprecated: %s', command[4])}]`) + } else { + hints.push(`[${__('deprecated')}]`) + } + } if (hints.length) { ui.div({ text: hints.join(' '), padding: [0, 0, 0, 2], align: 'right' }) } else { diff --git a/test/command.js b/test/command.js index eab449d72..f65c1c1c8 100644 --- a/test/command.js +++ b/test/command.js @@ -276,10 +276,11 @@ describe('Command', () => { const desc = 'i\'m not feeling very creative at the moment' const isDefault = false const aliases = [] + const deprecated = false const y = yargs([]).command(cmd, desc) const commands = y.getUsageInstance().getCommands() - commands[0].should.deep.equal([cmd, desc, isDefault, aliases]) + commands[0].should.deep.equal([cmd, desc, isDefault, aliases, deprecated]) }) it('accepts array, string as first 2 arguments', () => { @@ -287,10 +288,11 @@ describe('Command', () => { const cmd = 'foo ' const desc = 'i\'m not feeling very creative at the moment' const isDefault = false + const deprecated = false const y = yargs([]).command([cmd].concat(aliases), desc) const usageCommands = y.getUsageInstance().getCommands() - usageCommands[0].should.deep.equal([cmd, desc, isDefault, aliases]) + usageCommands[0].should.deep.equal([cmd, desc, isDefault, aliases, deprecated]) const cmdCommands = y.getCommandInstance().getCommands() cmdCommands.should.deep.equal(['foo', 'bar', 'baz']) }) @@ -381,6 +383,7 @@ describe('Command', () => { } const isDefault = false const aliases = [] + const deprecated = false const y = yargs([]).command(module) const handlers = y.getCommandInstance().getCommandHandlers() @@ -388,7 +391,7 @@ describe('Command', () => { handlers.foo.builder.should.equal(module.builder) handlers.foo.handler.should.equal(module.handler) const commands = y.getUsageInstance().getCommands() - commands[0].should.deep.equal([module.command, module.describe, isDefault, aliases]) + commands[0].should.deep.equal([module.command, module.describe, isDefault, aliases, deprecated]) }) it('accepts module (description key, builder function) as 1st argument', () => { @@ -400,6 +403,7 @@ describe('Command', () => { } const isDefault = false const aliases = [] + const deprecated = false const y = yargs([]).command(module) const handlers = y.getCommandInstance().getCommandHandlers() @@ -407,7 +411,7 @@ describe('Command', () => { handlers.foo.builder.should.equal(module.builder) handlers.foo.handler.should.equal(module.handler) const commands = y.getUsageInstance().getCommands() - commands[0].should.deep.equal([module.command, module.description, isDefault, aliases]) + commands[0].should.deep.equal([module.command, module.description, isDefault, aliases, deprecated]) }) it('accepts module (desc key, builder function) as 1st argument', () => { @@ -419,6 +423,7 @@ describe('Command', () => { } const isDefault = false const aliases = [] + const deprecated = false const y = yargs([]).command(module) const handlers = y.getCommandInstance().getCommandHandlers() @@ -426,7 +431,7 @@ describe('Command', () => { handlers.foo.builder.should.equal(module.builder) handlers.foo.handler.should.equal(module.handler) const commands = y.getUsageInstance().getCommands() - commands[0].should.deep.equal([module.command, module.desc, isDefault, aliases]) + commands[0].should.deep.equal([module.command, module.desc, isDefault, aliases, deprecated]) }) it('accepts module (false describe, builder function) as 1st argument', () => { @@ -475,6 +480,7 @@ describe('Command', () => { } const isDefault = false const aliases = [] + const deprecated = false const y = yargs([]).command(module) const handlers = y.getCommandInstance().getCommandHandlers() @@ -482,7 +488,7 @@ describe('Command', () => { handlers.foo.builder.should.equal(module.builder) handlers.foo.handler.should.equal(module.handler) const commands = y.getUsageInstance().getCommands() - commands[0].should.deep.equal([module.command, module.describe, isDefault, aliases]) + commands[0].should.deep.equal([module.command, module.describe, isDefault, aliases, deprecated]) }) it('accepts module (missing handler function) as 1st argument', () => { @@ -497,6 +503,7 @@ describe('Command', () => { } const isDefault = false const aliases = [] + const deprecated = false const y = yargs([]).command(module) const handlers = y.getCommandInstance().getCommandHandlers() @@ -504,7 +511,7 @@ describe('Command', () => { handlers.foo.builder.should.equal(module.builder) expect(typeof handlers.foo.handler).to.equal('function') const commands = y.getUsageInstance().getCommands() - commands[0].should.deep.equal([module.command, module.describe, isDefault, aliases]) + commands[0].should.deep.equal([module.command, module.describe, isDefault, aliases, deprecated]) }) it('accepts module (with command array) as 1st argument', () => { @@ -515,6 +522,7 @@ describe('Command', () => { handler (argv) {} } const isDefault = false + const deprecated = false const y = yargs([]).command(module) const handlers = y.getCommandInstance().getCommandHandlers() @@ -522,7 +530,7 @@ describe('Command', () => { handlers.foo.builder.should.equal(module.builder) handlers.foo.handler.should.equal(module.handler) const usageCommands = y.getUsageInstance().getCommands() - usageCommands[0].should.deep.equal([module.command[0], module.describe, isDefault, ['bar', 'baz']]) + usageCommands[0].should.deep.equal([module.command[0], module.describe, isDefault, ['bar', 'baz'], deprecated]) const cmdCommands = y.getCommandInstance().getCommands() cmdCommands.should.deep.equal(['foo', 'bar', 'baz']) }) @@ -536,6 +544,7 @@ describe('Command', () => { handler (argv) {} } const isDefault = false + const deprecated = false const y = yargs([]).command(module) const handlers = y.getCommandInstance().getCommandHandlers() @@ -543,7 +552,7 @@ describe('Command', () => { handlers.foo.builder.should.equal(module.builder) handlers.foo.handler.should.equal(module.handler) const usageCommands = y.getUsageInstance().getCommands() - usageCommands[0].should.deep.equal([module.command, module.describe, isDefault, module.aliases]) + usageCommands[0].should.deep.equal([module.command, module.describe, isDefault, module.aliases, deprecated]) const cmdCommands = y.getCommandInstance().getCommands() cmdCommands.should.deep.equal(['foo', 'bar', 'baz']) }) @@ -557,6 +566,7 @@ describe('Command', () => { handler (argv) {} } const isDefault = false + const deprecated = false const y = yargs([]).command(module) const handlers = y.getCommandInstance().getCommandHandlers() @@ -564,7 +574,7 @@ describe('Command', () => { handlers.foo.builder.should.equal(module.builder) handlers.foo.handler.should.equal(module.handler) const usageCommands = y.getUsageInstance().getCommands() - usageCommands[0].should.deep.equal([module.command[0], module.describe, isDefault, ['bar', 'baz', 'nat']]) + usageCommands[0].should.deep.equal([module.command[0], module.describe, isDefault, ['bar', 'baz', 'nat'], deprecated]) const cmdCommands = y.getCommandInstance().getCommands() cmdCommands.should.deep.equal(['foo', 'bar', 'baz', 'nat']) }) @@ -578,6 +588,7 @@ describe('Command', () => { handler (argv) {} } const isDefault = false + const deprecated = false const y = yargs([]).command(module) const handlers = y.getCommandInstance().getCommandHandlers() @@ -585,10 +596,21 @@ describe('Command', () => { handlers.foo.builder.should.equal(module.builder) handlers.foo.handler.should.equal(module.handler) const usageCommands = y.getUsageInstance().getCommands() - usageCommands[0].should.deep.equal([module.command, module.describe, isDefault, ['bar']]) + usageCommands[0].should.deep.equal([module.command, module.describe, isDefault, ['bar'], deprecated]) const cmdCommands = y.getCommandInstance().getCommands() cmdCommands.should.deep.equal(['foo', 'bar']) }) + + it('accepts deprecated as 5th argument', () => { + const command = 'command' + const description = 'description' + const isDefault = false + const aliases = [] + const deprecated = false + const y = yargs([]).command(command, description, {}, () => {}, [], deprecated) + const usageCommands = y.getUsageInstance().getCommands() + usageCommands[0].should.deep.equal([command, description, isDefault, aliases, deprecated]) + }) }) describe('commandDir', () => { @@ -1396,6 +1418,57 @@ describe('Command', () => { }) }) + describe('deprecated command', () => { + describe('using arg', () => { + it('shows deprecated notice with boolean', () => { + const command = 'command' + const description = 'description' + const deprecated = true + const r = checkOutput(() => { + yargs('--help') + .command(command, description, {}, () => {}, [], deprecated) + .parse() + }) + r.logs.should.match(/\[deprecated\]/) + }) + it('shows deprecated notice with string', () => { + const command = 'command' + const description = 'description' + const deprecated = 'deprecated' + const r = checkOutput(() => { + yargs('--help') + .command(command, description, {}, () => {}, [], deprecated) + .parse() + }) + r.logs.should.match(/\[deprecated: deprecated\]/) + }) + }) + describe('using module', () => { + it('shows deprecated notice with boolean', () => { + const command = 'command' + const description = 'description' + const deprecated = true + const r = checkOutput(() => { + yargs('--help') + .command({ command, description, deprecated }) + .parse() + }) + r.logs.should.match(/\[deprecated\]/) + }) + it('shows deprecated notice with string', () => { + const command = 'command' + const description = 'description' + const deprecated = 'deprecated' + const r = checkOutput(() => { + yargs('--help') + .command({ command, description, deprecated }) + .parse() + }) + r.logs.should.match(/\[deprecated: deprecated\]/) + }) + }) + }) + // addresses: https://github.com/yargs/yargs/issues/819 it('should kick along [demand] configuration to commands', () => { let called = false diff --git a/yargs.js b/yargs.js index 4e1705a92..81ab52ed9 100644 --- a/yargs.js +++ b/yargs.js @@ -389,9 +389,9 @@ function Yargs (processArgs, cwd, parentRequire) { return self } - self.command = function (cmd, description, builder, handler, middlewares) { - argsert(' [string|boolean] [function|object] [function] [array]', [cmd, description, builder, handler, middlewares], arguments.length) - command.addHandler(cmd, description, builder, handler, middlewares) + self.command = function (cmd, description, builder, handler, middlewares, deprecated) { + argsert(' [string|boolean] [function|object] [function] [array] [boolean|string]', [cmd, description, builder, handler, middlewares, deprecated], arguments.length) + command.addHandler(cmd, description, builder, handler, middlewares, deprecated) return self }