oclif / core

Node.js Open CLI Framework. Built by Salesforce.

Home Page:https://oclif.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

feat: allow renaming of --json

jpshack-at-palomar opened this issue · comments

Problem

In our cli we have options for both input and output as JSON. The functionality provided by --json is great, but the flag name for us is ambiguous and we want to be able to override it. Furthermore we need --json as a modifier flag for commands that offer output options (e.g. --yaml vs. --json).

We've worked around this but our solution is quite hacky. A variation of the same problem is if one wanted to add additional information the definition of --json flag. That definition is hard-coded in aggregate-flags.ts and used in both the init time caching of commands (used by the help system) and during flag parsing.

Describe the solution you'd like

Open to many different ways of handling this:

a. Config parameter in package.json that allows overriding of the json flag name
b. Have aggregateFlags use a function rather than use its own constant to build the flag and allow commands to expose that function as a static method on the command class or in set in a hook.
c. Separate the runtime checks done against enableJsonFlag for error handling and logging from the definition of the flag itself and allow commands to manage this in their own init method.
d. remove the --jsonFlag if a command implements its own enableJsonFlag() method. (I think something like this worked some time back: #467)

Describe alternatives you've considered

Currently we are resorting to an ugly hack of setting our enableJsonFlag false on our base command and then flipping it to true in init. This ensures that the help-plugin doesn't list the --json flag but that error handling and output are handled correctly. This suggests to me that the framework is trying to do a little too much for me and that something along the lines of (c.) is likely better direction for oclif.

Additional context

I'd be happy to help with a PR if you'll indicate an acceptable direction.

@jpshack-at-palomar Let me make sure I understand your requirements:

  • You want automatic json formatting for commands using a different flag that --json
  • You want a --json flag that doesn't trigger automatic json output

If that's the case, I'd suggest that this is something that you can achieve quite easily without any changes in oclif/core

import {Args, Command, Flags, Interfaces} from '@oclif/core'

export type Flags<T extends typeof Command> = Interfaces.InferredFlags<(typeof BaseCommand)['baseFlags'] & T['flags']>
export type Args<T extends typeof Command> = Interfaces.InferredArgs<T['args']>

abstract class BaseCommand<T extends typeof Command> extends Command {
  static baseFlags = {
    jsonOutput: Flags.boolean({
      description: 'Output response in JSON format',
      helpGroup: 'GLOBAL',
    }),
  }

  protected args!: Args<T>
  protected flags!: Flags<T>

  public async init(): Promise<void> {
    await super.init()
    const {args, flags} = await this.parse({
      args: this.ctor.args,
      baseFlags: (super.ctor as typeof BaseCommand).baseFlags,
      enableJsonFlag: this.ctor.enableJsonFlag,
      flags: this.ctor.flags,
      strict: this.ctor.strict,
    })
    this.flags = flags as Flags<T>
    this.args = args as Args<T>
  }

  public jsonEnabled(): boolean {
    // if --jsonOutput is set, return true
    if (this.flags.jsonOutput) return true
    // If the <CLI>_CONTENT_TYPE env var is set to json, return true
    if (this.config.scopedEnvVar?.('CONTENT_TYPE')?.toLowerCase() === 'json') return true

    return false
  }
}

export default class Hello extends BaseCommand<typeof Hello> {
  static args = {
    person: Args.string({description: 'Person to say hello to', required: true}),
  }

  static flags = {
    csv: Flags.boolean({description: 'Input CSV data'}),
    from: Flags.string({char: 'f', description: 'Who is saying hello', required: true}),
    json: Flags.boolean({description: 'Input JSON data'}),
    yaml: Flags.boolean({description: 'Input YAML data'}),
  }

  async run(): Promise<{args: Args<typeof Hello>; flags: Flags<typeof Hello>}> {
    this.log(`hello ${this.args.person} from ${this.flags.from}! (./src/commands/hello/index.ts)`)
    return {args: this.args, flags: this.flags}
  }
}
USAGE
  $ gh1105 hello PERSON -f <value> [--jsonOutput] [--csv] [--json] [--yaml]

ARGUMENTS
  PERSON  Person to say hello to

FLAGS
  -f, --from=<value>  (required) Who is saying hello
      --csv           Input CSV data
      --json          Input JSON data
      --yaml          Input YAML data

GLOBAL FLAGS
  --jsonOutput  Output response in JSON format

COMMANDS
  hello world  Say hello world

By overriding the isJsonEnabled method, you're able to get automatic json output using whatever flag name you want. You can see that you're now free to the --json flag for other purposes.

Does that meet your requirements?

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.