low-level configurability · Issue #103 · mmkal/trpc-cli · GitHub
Skip to content

low-level configurability #103

Open
Open

Description

@mmkal

at this point there are a fair few decisions that trpc-cli makes for you when deciding how to create options, negations etc.

it'd be overkill to provide explicit levers to pull to change all of that, but maybe the commander module could be injectable so that people can just do their own little hacks like

import * as commander from 'commander'

class Command extends commander.Command {
  addOption(option: Option) {
    if (this.name() === 'say-hello' && option.name() === 'foo') {
      option.description(`${option.description()} - btw this has very verbose output so mebe use jq or whatever`)
    }
    if (this.name() === 'say-goodbye' && option.name() === 'bar') {
      // router comes from another module we can't add `.meta` to for some reason
      option.flags = `${option.flags}, -b`
    }
    super.addOption(option)
  }
}

const cli = createCli({
  router: myRouter,
  commander: {....commander, Command}
})
cli.run()

This is theoretically possible now via buildProgram() but it's (even more) inconvenient.

Or opt-out of the booleans-get-negated-automatically functionality of #102:

import * as commander from 'commander'

class Command extends commander.Command {
  addOption(option: Option) {
    if (option.flags.includes('--no-') return
    super.addOption(option)
  }
}

const cli = createCli({
  router: myRouter,
  commander: {....commander, Command}
})
cli.run()

Activity

  1. mmkal commented on Jun 11, 2025

    @mmkal
    OwnerAuthor

    Another possible API:

    const cli = createCli({
      router,
      modify: modifier => {
        modifier(router.deeply.nested.fooProcedure, command => {
          command.addHelpText(`be careful with this, it's destructive`)
        })
        modifier(router.some.other.barProcedure, command => {
          const abcOption = command.options.find(o => o.flags.includes('--abc')
          abcOption?.hideHelp()
        })
      }
    })
    
    cli.run()

    or something like that


    devil's advocate to myself, here's how you'd do that today:

    const cli = createCli({router})
    const program = cli.buildProgram() as import('commander').Command
    
    const findSubcommand = (...path: string[]) => {
      let command = program as typeof program | undefined
      for (const p of path) command = command?.commands.find(c => c.name === p)
      if (!command) throw new Error(`command at path ${path.join('.')} not found`)
      return command
    }
    
    findSubcommand('deeply', 'nested', 'foo-procedure').addHelpText(`be careful with this, it's destructive`)
    findSubcommand('some', 'other', 'bar-procedure').options.find(o => o.flags.includes('--abc')?.hideHelp()
    
    cli.run({}, program)

    which is really not so bad, not all that much more verbose and more obvious what it's doing. but not type safe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions