Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,17 @@ cli
```

This will let CAC set the default value of `config` to true, and you can use `--no-config` flag to set it to `false`.
This also results in the familiar `[no-]foo` syntax in `cli.help()` output:

```
Usage:
$ foo build [project]

Options:
--[no-]config <path> Disable config file/Use a custom config file
-h, --help Display this message
-v, --version Display version number
```

### Variadic Arguments

Expand Down
34 changes: 27 additions & 7 deletions src/Command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,22 +184,42 @@ class Command {
? globalOptions
: [...this.options, ...(globalOptions || [])]
if (options.length > 0) {
const longestOptionName = findLongest(
options.map((option) => option.rawName)
)
const negatedOpts: { [key: string]: Option } = {}
// O(n2), let's hope there aren't a zillion options
for (const o1 of options) {
if (!o1.negated) continue
for (const o2 of options) {
if (o1.name == o2.name) negatedOpts[o2.name] = o1
}
}
const longest = options
.map((o) => {
if (o.negated) return 0
const l = o.rawName.length
// '5' is the length of '[no-]'. As to the 1, I have no clue...
return negatedOpts[o.name] ? l + 5 + 1 : l
})
.reduce((acc, l) => Math.max(acc, l), 0)
sections.push({
title: 'Options',
body: options
.map((option) => {
return ` ${padRight(option.rawName, longestOptionName.length)} ${
option.description
} ${
if (option.negated) return
let desc = option.description
let name = option.rawName
if (negatedOpts[option.name]) {
const ndesc = negatedOpts[option.name].description
if (ndesc) desc = `${ndesc}/${desc}`
name = name.replace(/^(--?)/, '$1[no-]')
}

return ` ${padRight(name, longest)} ${desc} ${
option.config.default === undefined
? ''
: `(default: ${option.config.default})`
}`
})
.join('\n'),
.filter(Boolean).join('\n'),
})
}

Expand Down
28 changes: 28 additions & 0 deletions src/__test__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,34 @@ test('negated option validation', () => {
expect(options.config).toBe(false)
})

test('negated option help output', () => {
const cli = cac()

cli.option('--config <config>', 'Use custom config file')
cli.option('--no-config', 'Skip')

const saved = console.log
let output = ''
try {
console.log = (msg, more) => {
if (more) throw new Error('Unexpected multi-arg call to console.log')
output += msg
}
cli.outputHelp()
expect(output).toBe(
`

Usage:
$ <command> [options]

Options:
--[no-]config <config> Skip/Use custom config file `
)
} finally {
console.log = saved
}
})

test('array types without transformFunction', () => {
const cli = cac()

Expand Down
4 changes: 1 addition & 3 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ export const getMriOptions = (options: Option[]) => {
}

export const findLongest = (arr: string[]) => {
return arr.sort((a, b) => {
return a.length > b.length ? -1 : 1
})[0]
return arr.reduce((acc, a) => (a.length > acc.length ? a : acc))
}

export const padRight = (str: string, length: number) => {
Expand Down