Skip to content

Commit

Permalink
feat: warn about alias deprecations (#540)
Browse files Browse the repository at this point in the history
* feat: warn about alias deprecations

* fix: make new props optional

* chore: code review

* fix: handle edge cases

* chore: remove .only
  • Loading branch information
mdonnalley committed Oct 28, 2022
1 parent 00c6118 commit aed5db3
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 10 deletions.
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -114,4 +114,4 @@
"pretest": "yarn build --noEmit && tsc -p test --noEmit --skipLibCheck"
},
"types": "lib/index.d.ts"
}
}
24 changes: 23 additions & 1 deletion src/command.ts
Expand Up @@ -9,7 +9,7 @@ import {PrettyPrintableError} from './errors'
import * as Parser from './parser'
import * as Flags from './flags'
import {Deprecation} from './interfaces/parser'
import {formatCommandDeprecationWarning, formatFlagDeprecationWarning} from './help/util'
import {formatCommandDeprecationWarning, formatFlagDeprecationWarning, normalizeArgv} from './help/util'

const pjson = require('../package.json')

Expand Down Expand Up @@ -62,6 +62,11 @@ export default abstract class Command {

static deprecationOptions?: Deprecation;

/**
* Emit deprecation warning when a command alias is used
*/
static deprecateAliases?: boolean

/**
* An override string (or strings) for the default usage documentation.
*/
Expand Down Expand Up @@ -255,10 +260,27 @@ export default abstract class Command {
if (deprecated) {
this.warn(formatFlagDeprecationWarning(flag, deprecated))
}

const deprecateAliases = this.ctor.flags[flag]?.deprecateAliases
const aliases = (this.ctor.flags[flag]?.aliases ?? []).map(a => a.length === 1 ? `-${a}` : `--${a}`)
if (deprecateAliases && aliases.length > 0) {
const foundAliases = this.argv.filter(a => aliases.includes(a))
for (const alias of foundAliases) {
this.warn(formatFlagDeprecationWarning(alias, {to: this.ctor.flags[flag]?.name}))
}
}
}
}

protected warnIfCommandDeprecated(): void {
const [id] = normalizeArgv(this.config)

if (this.ctor.deprecateAliases && this.ctor.aliases.includes(id)) {
const cmdName = toConfiguredId(this.ctor.id, this.config)
const aliasName = toConfiguredId(id, this.config)
this.warn(formatCommandDeprecationWarning(aliasName, {to: cmdName}))
}

if (this.ctor.state === 'deprecated') {
const cmdName = toConfiguredId(this.ctor.id, this.config)
this.warn(formatCommandDeprecationWarning(cmdName, this.ctor.deprecationOptions))
Expand Down
3 changes: 3 additions & 0 deletions src/config/config.ts
Expand Up @@ -754,6 +754,7 @@ export async function toCached(c: Command.Class, plugin?: IPlugin): Promise<Comm
relationships: flag.relationships,
exclusive: flag.exclusive,
deprecated: flag.deprecated,
deprecateAliases: c.deprecateAliases,
aliases: flag.aliases,
}
} else {
Expand All @@ -775,6 +776,7 @@ export async function toCached(c: Command.Class, plugin?: IPlugin): Promise<Comm
exclusive: flag.exclusive,
default: await defaultToCached(flag),
deprecated: flag.deprecated,
deprecateAliases: c.deprecateAliases,
aliases: flag.aliases,
}
// a command-level placeholder in the manifest so that oclif knows it should regenerate the command during help-time
Expand Down Expand Up @@ -809,6 +811,7 @@ export async function toCached(c: Command.Class, plugin?: IPlugin): Promise<Comm
aliases: c.aliases || [],
examples: c.examples || (c as any).example,
deprecationOptions: c.deprecationOptions,
deprecateAliases: c.deprecateAliases,
flags,
args,
}
Expand Down
2 changes: 1 addition & 1 deletion src/flags.ts
Expand Up @@ -37,7 +37,7 @@ export const help = (opts: Partial<BooleanFlag<boolean>> = {}) => {
description: 'Show CLI help.',
...opts,
parse: async (_: any, cmd: Command) => {
new Help(cmd.config).showHelp(cmd.argv)
new Help(cmd.config).showHelp(cmd.id ? [cmd.id, ...cmd.argv] : cmd.argv)
cmd.exit(0)
},
})
Expand Down
2 changes: 1 addition & 1 deletion src/help/index.ts
Expand Up @@ -9,7 +9,7 @@ import {formatCommandDeprecationWarning, getHelpFlagAdditions, standardizeIDFrom
import {HelpFormatter} from './formatter'
import {toCached} from '../config/config'
export {CommandHelp} from './command'
export {standardizeIDFromArgv, loadHelpClass, getHelpFlagAdditions} from './util'
export {standardizeIDFromArgv, loadHelpClass, getHelpFlagAdditions, normalizeArgv} from './util'

function getHelpSubject(args: string[], config: Interfaces.Config): string | undefined {
// for each help flag that starts with '--' create a new flag with same name sans '--'
Expand Down
7 changes: 6 additions & 1 deletion src/help/util.ts
Expand Up @@ -108,7 +108,7 @@ export function formatFlagDeprecationWarning(flag: string, opts: true | Deprecat
message += ` and will be removed in version ${opts.version}`
}

message += opts.to ? `. Use "${opts.to}" instead.` : '.'
message += opts.to ? `. Use "--${opts.to}" instead.` : '.'

return message
}
Expand All @@ -127,3 +127,8 @@ export function formatCommandDeprecationWarning(command: string, opts?: Deprecat

return message
}

export function normalizeArgv(config: IConfig, argv = process.argv.slice(2)): string[] {
if (config.topicSeparator !== ':' && !argv[0]?.includes(':')) argv = standardizeIDFromArgv(argv, config)
return argv
}
5 changes: 5 additions & 0 deletions src/interfaces/command.ts
Expand Up @@ -22,6 +22,11 @@ export interface CommandProps {
*/
deprecationOptions?: Deprecation;

/**
* Emit a deprecation warning when a command alias is used.
*/
deprecateAliases?: boolean

/** An array of aliases for this command. */
aliases: string[];

Expand Down
4 changes: 4 additions & 0 deletions src/interfaces/parser.ts
Expand Up @@ -157,6 +157,10 @@ export type FlagProps = {
* Alternate names that can be used for this flag.
*/
aliases?: string[];
/**
* Emit deprecation warning when a flag alias is provided
*/
deprecateAliases?: boolean
}

export type BooleanFlagProps = FlagProps & {
Expand Down
5 changes: 2 additions & 3 deletions src/main.ts
Expand Up @@ -5,7 +5,7 @@ import {format, inspect} from 'util'
import * as Interfaces from './interfaces'
import {URL} from 'url'
import {Config} from './config'
import {getHelpFlagAdditions, loadHelpClass, standardizeIDFromArgv} from './help'
import {getHelpFlagAdditions, loadHelpClass, normalizeArgv} from './help'

const log = (message = '', ...args: any[]) => {
// tslint:disable-next-line strict-type-predicates
Expand Down Expand Up @@ -41,8 +41,7 @@ export async function run(argv = process.argv.slice(2), options?: Interfaces.Loa
// return Main.run(argv, options)
const config = await Config.load(options || (module.parent && module.parent.parent && module.parent.parent.filename) || __dirname) as Config

if (config.topicSeparator !== ':' && !argv[0]?.includes(':')) argv = standardizeIDFromArgv(argv, config)
let [id, ...argvSlice] = argv
let [id, ...argvSlice] = normalizeArgv(config, argv)
// run init hook
await config.runHook('init', {id, argv: argvSlice})

Expand Down
8 changes: 6 additions & 2 deletions src/parser/parse.ts
Expand Up @@ -79,8 +79,12 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
}
}

const findShortFlag = (arg: string) => {
return Object.keys(this.input.flags).find(k => this.input.flags[k].char === arg[1])
const findShortFlag = ([_, char]: string) => {
if (this.flagAliases[char]) {
return this.flagAliases[char].name
}

return Object.keys(this.input.flags).find(k => this.input.flags[k].char === char)
}

const parseFlag = (arg: string): boolean => {
Expand Down
59 changes: 59 additions & 0 deletions test/command/command.test.ts
Expand Up @@ -233,6 +233,65 @@ describe('command', () => {
.it('uses util.format()')
})

describe('flags with deprecated aliases', () => {
class CMD extends Command {
static flags = {
name: Flags.string({
aliases: ['username', 'target-user', 'u'],
deprecateAliases: true,
}),
other: Flags.string(),
}

async run() {
await this.parse(CMD)
this.log('running command')
}
}

fancy
.stdout()
.stderr()
.do(async () => CMD.run(['--username', 'astro']))
.do(ctx => expect(ctx.stderr).to.include('Warning: The "--username" flag has been deprecated'))
.it('shows warning for deprecated flag alias')

fancy
.stdout()
.stderr()
.do(async () => CMD.run(['--target-user', 'astro']))
.do(ctx => expect(ctx.stderr).to.include('Warning: The "--target-user" flag has been deprecated'))
.it('shows warning for deprecated flag alias')

fancy
.stdout()
.stderr()
.do(async () => CMD.run(['-u', 'astro']))
.do(ctx => expect(ctx.stderr).to.include('Warning: The "-u" flag has been deprecated'))
.it('shows warning for deprecated short char flag alias')

fancy
.stdout()
.stderr()
.do(async () => CMD.run(['--name', 'username']))
.do(ctx => expect(ctx.stderr).to.be.empty)
.it('shows no warning when using proper flag name with a value that matches a flag alias')

fancy
.stdout()
.stderr()
.do(async () => CMD.run(['--other', 'target-user']))
.do(ctx => expect(ctx.stderr).to.be.empty)
.it('shows no warning when using another flag with a value that matches a deprecated flag alias')

fancy
.stdout()
.stderr()
.do(async () => CMD.run(['--name', 'u']))
.do(ctx => expect(ctx.stderr).to.be.empty)
.it('shows no warning when proper flag name with a value that matches a short char flag alias')
})

describe('deprecated flags', () => {
fancy
.stdout()
Expand Down

0 comments on commit aed5db3

Please sign in to comment.