Repository: oclif/core
Pull request #521 (2022-10-13 15:11:47)

feat: support flag aliases
Support flag aliases

#### Usage

```typescript
static flags = {
    name: Flags.string({
      char: 'n',
      summary: 'Name to print.',
      required: true,
      aliases: ['username', 'user-name'],
    }),
  }
```

[skip-validate-pr]


Patch:
diff --git a/src/config/config.ts b/src/config/config.ts
index 3d032b8..afbc99d 100644
--- a/src/config/config.ts
+++ b/src/config/config.ts
@@ -752,6 +752,7 @@ export async function toCached(c: Command.Class, plugin?: IPlugin): Promise<Comm
         allowNo: flag.allowNo,
         dependsOn: flag.dependsOn,
         exclusive: flag.exclusive,
+        aliases: flag.aliases,
       }
     } else {
       flags[name] = {
@@ -771,6 +772,7 @@ export async function toCached(c: Command.Class, plugin?: IPlugin): Promise<Comm
         relationships: flag.relationships,
         exclusive: flag.exclusive,
         default: await defaultToCached(flag),
+        aliases: flag.aliases,
       }
       // a command-level placeholder in the manifest so that oclif knows it should regenerate the command during help-time
       if (typeof flag.defaultHelp === 'function') {
diff --git a/src/interfaces/parser.ts b/src/interfaces/parser.ts
index 92b2b5f..e944eda 100644
--- a/src/interfaces/parser.ts
+++ b/src/interfaces/parser.ts
@@ -143,6 +143,10 @@ export type FlagProps = {
    * Define complex relationships between flags.
    */
   relationships?: Relationship[];
+  /**
+   * Alternate names that can be used for this flag.
+   */
+  aliases?: string[];
 }
 
 export type BooleanFlagProps = FlagProps & {
diff --git a/src/parser/parse.ts b/src/parser/parse.ts
index bee68e1..c58df74 100644
--- a/src/parser/parse.ts
+++ b/src/parser/parse.ts
@@ -39,6 +39,7 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
   private readonly raw: ParsingToken[] = []
 
   private readonly booleanFlags: { [k: string]: BooleanFlag<any> }
+  private readonly flagAliases: { [k: string]: BooleanFlag<any> | OptionFlag<any> }
 
   private readonly context: any
 
@@ -52,6 +53,10 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
     this.argv = [...input.argv]
     this._setNames()
     this.booleanFlags = pickBy(input.flags, f => f.type === 'boolean') as any
+    this.flagAliases = Object.fromEntries(Object.values(input.flags).flatMap(flag => {
+      return (flag.aliases ?? []).map(a => [a, flag])
+    }))
+
     this.metaData = {}
   }
 
@@ -64,6 +69,10 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
         return name
       }
 
+      if (this.flagAliases[name]) {
+        return this.flagAliases[name].name
+      }
+
       if (arg.startsWith('--no-')) {
         const flag = this.booleanFlags[arg.slice(5)]
         if (flag && flag.allowNo) return flag.name


Test patch:
diff --git a/test/integration/plugins.e2e.ts b/test/integration/plugins.e2e.ts
index 3313199..5ae240f 100644
--- a/test/integration/plugins.e2e.ts
+++ b/test/integration/plugins.e2e.ts
@@ -193,7 +193,7 @@ describe('oclif plugins', () => {
 
     describe('installing a plugin by name', () => {
       it('should install the plugin', async () => {
-        const result = await executor.executeCommand('plugins:install @oclif/plugin-warn-if-update-available')
+        const result = await executor.executeCommand('plugins:install @oclif/plugin-warn-if-update-available 2>&1')
         expect(result.code).to.equal(0)
         expect(result.output).to.include('@oclif/plugin-warn-if-update-available... installed v')
 
@@ -205,7 +205,7 @@ describe('oclif plugins', () => {
 
     describe('installing a plugin by github url', () => {
       after(async () => {
-        await executor.executeCommand('plugins:uninstall @oclif/plugin-warn-if-update-available')
+        await executor.executeCommand('plugins:uninstall @oclif/plugin-warn-if-update-available 2>&1')
       })
 
       it('should install the plugin', async () => {
@@ -220,7 +220,7 @@ describe('oclif plugins', () => {
 
     describe('forcefully installing a plugin', () => {
       it('should install the plugin', async () => {
-        const result = await executor.executeCommand('plugins:install @oclif/plugin-warn-if-update-available --force')
+        const result = await executor.executeCommand('plugins:install @oclif/plugin-warn-if-update-available --force 2>&1')
         expect(result.code).to.equal(0)
         expect(result.output).to.include('@oclif/plugin-warn-if-update-available... installed v')
 
@@ -236,7 +236,7 @@ describe('oclif plugins', () => {
       })
 
       it('should uninstall the plugin', async () => {
-        const result = await executor.executeCommand('plugins:uninstall @oclif/plugin-warn-if-update-available')
+        const result = await executor.executeCommand('plugins:uninstall @oclif/plugin-warn-if-update-available 2>&1')
         expect(result.code).to.equal(0)
         expect(result.output).to.include('success Uninstalled packages.Uninstalling @oclif/plugin-warn-if-update-available... done\n')
 
diff --git a/test/integration/util.ts b/test/integration/util.ts
index 7292b21..f7f7a05 100644
--- a/test/integration/util.ts
+++ b/test/integration/util.ts
@@ -39,7 +39,7 @@ export class Executor {
 
   public executeCommand(cmd: string): Promise<Result> {
     const executable = process.platform === 'win32' ? path.join('bin', 'run.cmd') : path.join('bin', 'run')
-    return this.executeInTestDir(`${executable} ${cmd} 2>&1`)
+    return this.executeInTestDir(`${executable} ${cmd}`)
   }
 
   public exec(cmd: string, cwd = process.cwd(), silent = true): Promise<Result> {
diff --git a/test/parser/parse.test.ts b/test/parser/parse.test.ts
index 48a27b5..1cd6058 100644
--- a/test/parser/parse.test.ts
+++ b/test/parser/parse.test.ts
@@ -1338,4 +1338,28 @@ See more help with --help`)
       })
     })
   })
+
+  describe('flag aliases', () => {
+    it('works with defined name', async () => {
+      const out = await parse(['--foo'], {
+        flags: {
+          foo: flags.boolean({
+            aliases: ['bar'],
+          }),
+        },
+      })
+      expect(out.flags.foo).to.equal(true)
+    })
+
+    it('works with aliased name', async () => {
+      const out = await parse(['--bar'], {
+        flags: {
+          foo: flags.boolean({
+            aliases: ['bar'],
+          }),
+        },
+      })
+      expect(out.flags.foo).to.equal(true)
+    })
+  })
 })