|
| 1 | +> a video showcasing how pnpm autocompletions works on a test cli command like `my-cli` |
| 2 | +
|
| 3 | +# tab |
| 4 | + |
| 5 | +> instant feedback when hitting [TAB] in your cli tool |
| 6 | +
|
| 7 | +as cli tooling authors, if we can spare our users a second or two by not checking the documentation or writing the `-h` option, we're doing them a huge favor. the unconscious loves hitting the [TAB] key. it always expects feedback. so it feels dissappointing when hitting that key in the terminal but then nothing happens. that frustration is apparent across the whole javascript cli tooling ecosystem. |
| 8 | + |
| 9 | +autocompletions are the solution to not break the user's flow. the issue is they're not simple to add. `zsh` expects them in a way, and `bash` in another way. then where do we provide them so the shell environment parses them? too many headaches to ease the user's experience. whether it's worth it or not is out of the question. because tab is the solution to this complexity. |
| 10 | + |
| 11 | +`my-cli.ts`: |
| 12 | +```typescript |
| 13 | +import t from '@bombsh/tab' |
| 14 | + |
| 15 | +t.name('my-cli') |
| 16 | + |
| 17 | +t.command('start', 'start the development server') |
| 18 | + |
| 19 | +if (process.argv[2] === 'complete') { |
| 20 | + const [shell, ...args] = process.argv.slice(3) |
| 21 | + if (shell === '--') { |
| 22 | + t.parse(args) |
| 23 | + } else { |
| 24 | + t.setup(shell, x) |
| 25 | + } |
| 26 | +} |
| 27 | +``` |
| 28 | + |
| 29 | +this `my-cli.ts` would be equipped with all the tools required to provide autocompletions. |
| 30 | + |
| 31 | +```bash |
| 32 | +node my-cli.ts complete -- "st" |
| 33 | +``` |
| 34 | +``` |
| 35 | +start start the development server |
| 36 | +:0 |
| 37 | +``` |
| 38 | + |
| 39 | +this output was generated by the `t.parse` method to autocomplete "st" to "start". |
| 40 | + |
| 41 | +obviously, the user won't be running that command directly in their terminal. they'd be running something like this. |
| 42 | + |
| 43 | +```bash |
| 44 | +source <(node my-cli.ts complete zsh) |
| 45 | +``` |
| 46 | + |
| 47 | +now whenever the shell sees `my-cli`, it would bring the autocompletions we wrote for this cli tool. the `node my-cli.ts complete zsh` part would output the zsh script that loads the autocompletions provided by `t.parse` which then would be executed using `source`. |
| 48 | + |
| 49 | +the autocompletions are only lived through the current session. to set them up across all of terminal sessions, the autocompletion script should be injected in the `.zshrc` file. |
| 50 | + |
| 51 | +```bash |
| 52 | +my-cli complete zsh > ~/completion-for-my-cli.zsh && echo 'source ~/completion-for-my-cli.zsh' >> ~/.zshrc |
| 53 | +``` |
| 54 | + |
| 55 | +this is an example of autocompletions on a global cli command that is usually installed using the `-g` flag (e.g. `npm add -g my-cli`) which is available across the computer. |
| 56 | + |
| 57 | +--- |
| 58 | + |
| 59 | +while working on tab, we came to the realization that most javascript clis are not global cli commands but rather, per-project dependencies. |
| 60 | + |
| 61 | +for instance, vite won't be installed globally and instead it'd be always installed on a project. here's an example usage: |
| 62 | + |
| 63 | +```bash |
| 64 | +pnpm vite -h |
| 65 | +``` |
| 66 | + |
| 67 | +so in this case, a computer might have hundreds of vite instances each installed separately and potentially from different versions on different projects. |
| 68 | + |
| 69 | +we were looking for a fluid strategy that would be able to load the autocompletions from each of these dependencies on a per-project basis. |
| 70 | + |
| 71 | +and that made us develop our own autocompletion abstraction over npm, pnpm and yarn. this would help tab identify which binaries are avaialble in a project and which of these binaries provide autocompletions. so the user would not have to `source` anything or inject any script in their `.zshrc`. |
| 72 | + |
| 73 | +they'd only have to run this command once and inject it in their shell config. |
| 74 | + |
| 75 | +```bash |
| 76 | +echo 'eval "$(npx --prefer-offline @bombsh/tab pnpm zsh)"' >> ~/.zshrc |
| 77 | +``` |
| 78 | + |
| 79 | +--- |
| 80 | + |
| 81 | +```typescript |
| 82 | +import t from '@bombsh/tab' |
| 83 | + |
| 84 | +t.option('help', 'list available commands') // Command (Root) |
| 85 | + |
| 86 | +t.command('start', 'start the development server') // Command ('start') |
| 87 | + .option('port', 'specify the port number') // Command ('port') |
| 88 | + |
| 89 | +t.parse(process.argv.slice(3)) |
| 90 | + |
| 91 | +t.setup(process.argv[2], x) |
| 92 | +``` |
0 commit comments