Skip to content
This repository has been archived by the owner on Apr 23, 2020. It is now read-only.

CLI tool #11

Open
jasonkuhrt opened this issue Dec 19, 2019 · 1 comment
Open

CLI tool #11

jasonkuhrt opened this issue Dec 19, 2019 · 1 comment

Comments

@jasonkuhrt
Copy link
Member

jasonkuhrt commented Dec 19, 2019

Proposal:

Goals

  • a routing tree
    • permits visualizing the layout of the api commands
    • permits painless hierarchical refactors
  • robust automatic help system
  • automatic bash/zsh/fish completion generation and installation including support for running behind npm / yarn
  • automatic man page generation and installation (note: this does not seem useful for CLIs that are intended to be used local to a project)
  • extensible (e.g. how hub seamlessly extends git)
  • high-fi developer experience
    • remove as much boilerplate as possible (e.g. singletons, globals, ...)
    • convention over configuration
  • 100% type safety

How the api might look like:

A combinator api manifested as chaining. Under the hood all this is doing is building up a plain javascript object. This makes snapshot testing and debugging super easy. Also opens up the potential for powerful plugins.

command('foo')
  .add(
    params
      .string('bebe')
      .synopsis('...')
      .description('... ... ... ... ... ... ... ... ...  .. ')
      .validate(/A-Za-z/),
    params
      .int('abba')
      .short('a')
      .synopsis('...')
      .description('... ...')
      .require()
  )
  .def(async (parent, args) => {
    await doSomeWork()
    console.log('Ok!')
  })

param statics might turn out to need context. Not sure yet. If so, that might manifest like so:

command('foo')
  .params(par => {
    par
      .string('bebe')
      .synopsis('...')
      .description('... ... ... ... ... ... ... ... ...  .. ')
      .validate(/A-Za-z/)
    par
      .int('abba')
      .short('a')
      .synopsis('...')
      .description('... ...')
      .require()
  })
  .runs(async (parent, args) => {
    await doSomeWork()
    console.log('Ok!')
  })

Commands could be brought together around a "routing tree":

const a = command('a') //...
const b1 = command('b1') //...
const b2 = command('b2') //...
const b2 = command('b3') //...
const c = command('c') //...

cli(
  a,
  b.group(
    b1,
    b2,
    b3,
  ),
  c,
) 

// statics approach

cli(
  a,
  cmd.group(b)(
    b1,
    b2,
    b3,
  )
  c,
)

Example use:

$ my-cli a
$ my-cli b 
$ my-cli b b3

An important feature would be how ancestor commands contribute params to descendent commands. Example:

const a = command('a').add(param.string('febe').optional()) //...
const b = command('b').runs(args => {
	args.febe // undefined | string
})

cli(
  a.group(b)
)

...

more to come!

Ecosystem:

  • oclif

    problems:
    - workflow issues, linking doesn't work, weird magic with TypeScript at runtime...
    - confusing ish mocha-coupled testing
    - ...

  • yargs

    problems:
    - expressing CLI layouts seems to fall apart at one point
    - ...

  • commander

    problems:
    - expressing CLI layouts seems to fall apart at one point
    - ...

@jasonkuhrt
Copy link
Member Author

spec changes

Developing an API for the tree. I find the POJO awkward, the keys ambiguous in meaning given commands co-locate their names.

  • cmd instead of command
  • cmd.group varg func that accepts multiple commands
  • cli just a special instance of cmd.group that permits not having a parent (because its root)
  • buildCLI to just cli, var ident should be name of cli e.g. const santa = cli(...)
  const a = cmd('a').add(param.string('febe').optional()) //...
  const b = cmd('b').runs(args => {
  	args.febe // undefined | string
  })
  
- buildCLI({
-   a: {
- 	  b,
- 	  $default: a
-   }
- })

+ cli(
+   a.group(b)
+ )
- buildCLI({
-   a,
-   b: {
-     b1,
-     b2,
-     b3,
-     $default: b2
-   },
-   c,
- })
+ cli(
+   a,
+   b.group(
+     b1,
+     b2,
+     b3,
+   ),
+   c,
+ ) 
+
+ statics approach (we can support both static+chaining)
+
+ cli(
+   a,
+   cmd.group(b)(
+     b1,
+     b2,
+     b3,
+   )
+   c,
+ )
+

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant