Skip to content

Commit 5369852

Browse files
committed
update help usage
1 parent 989f689 commit 5369852

2 files changed

Lines changed: 89 additions & 28 deletions

File tree

src/index.ts

Lines changed: 77 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
#!/usr/bin/env node
22
import chalk from "chalk"
33
import merge from "lodash/merge"
4-
import { clearLine } from "node:readline"
54
import path from "path"
6-
import { color, COLOR_CODE_LEN, wrap } from "./utils"
5+
import { ArrayOr, asArray, color, COLOR_CODE_LEN, wrap } from "./utils"
76

87
export function massarg() {
98
return new Massarg()
@@ -21,7 +20,7 @@ export interface OptionDef<Options, Value> {
2120
description?: string
2221
defaultValue?: Value
2322
boolean?: boolean
24-
command?: string
23+
commands?: ArrayOr<string>
2524
parse?(value: string, options: Options): Value
2625
}
2726

@@ -35,9 +34,10 @@ export interface CommandDef<T> {
3534
export interface HelpDef {
3635
printWidth?: number
3736
binName?: string
38-
normalColor?: keyof typeof chalk
39-
highlightColor?: keyof typeof chalk
40-
titleColor?: keyof typeof chalk
37+
normalColors?: ArrayOr<keyof typeof chalk>
38+
highlightColors?: ArrayOr<keyof typeof chalk>
39+
titleColors?: ArrayOr<keyof typeof chalk>
40+
subtitleColors?: ArrayOr<keyof typeof chalk>
4141
header?: string
4242
footer?: string
4343
commandNameSeparator?: string
@@ -55,9 +55,10 @@ export class Massarg<Options extends OptionsBase = OptionsBase> {
5555

5656
private _help: Required<HelpDef> = {
5757
binName: undefined as any,
58-
normalColor: "dim",
59-
highlightColor: "yellow",
60-
titleColor: "white",
58+
normalColors: "dim",
59+
highlightColors: "yellow",
60+
titleColors: "white",
61+
subtitleColors: "dim",
6162
printWidth: 80,
6263
header: "",
6364
footer: "",
@@ -99,33 +100,31 @@ export class Massarg<Options extends OptionsBase = OptionsBase> {
99100
public printHelp(args?: string[]): void {
100101
this.parseArgs(args)
101102

102-
const { highlightColor, normalColor, titleColor, binName } = this._help
103+
const { highlightColors, normalColors, titleColors, binName } = this._help
103104

104105
console.log(
105-
color(titleColor, chalk.bold`Usage:`),
106-
color(highlightColor, binName ?? path.basename(process.argv[1])),
107-
color(normalColor, "[command] [options]")
106+
color(titleColors, chalk.bold`Usage:`),
107+
color(highlightColors, binName ?? path.basename(process.argv[1])),
108+
color(normalColors, "[command] [options]")
108109
)
109110

110111
if (this._help.header) {
111112
console.log()
112-
console.log(color(titleColor, this._help.header))
113+
console.log(color(titleColors, this._help.header))
113114
}
114115

115116
if (this._commands.length) {
116117
console.log("")
117-
console.log(color(titleColor, chalk.bold`Commands:`))
118+
console.log(color(titleColors, chalk.bold`Commands:`))
118119
this._printCommands()
119120
}
120121

121122
console.log()
122-
123-
console.log(color(titleColor, chalk.bold`Options:`))
124123
this._printOptions()
125124

126125
if (this._help.footer) {
127126
console.log()
128-
console.log(color(titleColor, this._help.footer))
127+
console.log(color(titleColors, this._help.footer))
129128
}
130129
}
131130

@@ -203,6 +202,7 @@ export class Massarg<Options extends OptionsBase = OptionsBase> {
203202
}
204203

205204
private _getWrappedLines(list: Array<{ name: string; description?: string }>): string[] {
205+
const { normalColors, highlightColors } = this._help
206206
const lines: string[] = []
207207

208208
let maxNameLen = this._help.useGlobalColumns ? this._maxNameLen ?? 0 : 0
@@ -221,8 +221,11 @@ export class Massarg<Options extends OptionsBase = OptionsBase> {
221221
const nameFullSize = maxNameLen + ARG_SPACE_LEN + INDENT_LEN
222222

223223
for (const item of list) {
224-
const cmdName = chalk.yellow(`${item.name}`).padEnd(nameFullSize + (COLOR_COUNT - 1) * COLOR_CODE_LEN, " ")
225-
const cmdDescr = chalk.dim(item.description ?? "")
224+
const cmdName = color(highlightColors, `${item.name}`).padEnd(
225+
nameFullSize + (COLOR_COUNT - 1) * COLOR_CODE_LEN,
226+
" "
227+
)
228+
const cmdDescr = color(normalColors, item.description ?? "")
226229

227230
for (const line of wrap(cmdName + cmdDescr, {
228231
indent: nameFullSize + INDENT_LEN,
@@ -248,21 +251,69 @@ export class Massarg<Options extends OptionsBase = OptionsBase> {
248251
}
249252

250253
private _printOptions() {
251-
for (const line of this._getWrappedLines(
252-
this._options.map((c) => ({ name: this._fullOptName(c), description: c.description }))
253-
)) {
254-
if (line.trim().length) {
255-
console.log(line)
254+
let printedTitle = false
255+
const { titleColors, subtitleColors } = this._help
256+
257+
const commandOpts: string[] = []
258+
259+
for (const cmd of this._commands) {
260+
const opts = this._commandOptions(cmd)
261+
if (opts.length) {
262+
commandOpts.push(color(subtitleColors, `${cmd.name}:`))
263+
for (const line of this._getWrappedLines(
264+
opts.map((c) => ({ name: this._fullOptName(c), description: c.description }))
265+
)) {
266+
if (line.trim().length) {
267+
commandOpts.push(line)
268+
}
269+
}
270+
}
271+
}
272+
273+
console.log(color(titleColors, chalk.bold`${commandOpts.length ? "Command Options" : "Options"}:`))
274+
// if (commandOpts.length) {
275+
console.log()
276+
// }
277+
278+
for (const line of commandOpts) {
279+
console.log(line)
280+
}
281+
282+
const globalOpts = this._globalOptions()
283+
if (globalOpts.length) {
284+
if (commandOpts.length) {
285+
console.log(chalk.bold`Global Options:`)
286+
console.log()
287+
}
288+
for (const line of this._getWrappedLines(
289+
globalOpts.map((c) => ({ name: this._fullOptName(c), description: c.description }))
290+
)) {
291+
if (line.trim().length) {
292+
console.log(line)
293+
}
256294
}
257295
}
258296
}
259297

260298
private _fullCmdName(cmd: CommandDef<Options>) {
261299
return [cmd.name, ...(cmd.aliases ?? [])].join(this._help.commandNameSeparator)
262300
}
301+
263302
private _fullOptName(opt: OptionDef<Options, any>) {
264303
return [`--${opt.name}`, ...(opt.aliases ?? []).map((a) => `-${a}`)].join(this._help.optionNameSeparator)
265304
}
305+
306+
private _commandOptions(cmd: CommandDef<Options>): OptionDef<Options, any>[] {
307+
return this._options.filter(
308+
(o) =>
309+
(asArray(o.commands).length && asArray(o.commands).includes(cmd.name)) ||
310+
cmd.aliases?.some((a) => asArray(o.commands).includes(a))
311+
)
312+
}
313+
314+
private _globalOptions(): OptionDef<Options, any>[] {
315+
return this._options.filter((o) => !o.commands)
316+
}
266317
}
267318

268319
massarg()
@@ -285,6 +336,7 @@ massarg()
285336
aliases: ["n"],
286337
description: "This is a number arg, if you include this option, you must supply it with a value.",
287338
defaultValue: 0,
339+
commands: ["do"],
288340
parse: (v) => parseInt(v),
289341
})
290342
.command({

src/utils.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ import chalk from "chalk"
33
import repeat from "lodash/repeat"
44
import merge from "lodash/merge"
55

6-
export function color(color: keyof typeof chalk, ...text: any[]): string {
7-
return chalk.reset((chalk[color] as typeof chalk.dim)(...text))
6+
export function color(color: ArrayOr<keyof typeof chalk>, ...text: any[]): string {
7+
let output: string = undefined as any
8+
for (const c of asArray(color)) {
9+
output = (chalk[c as keyof typeof chalk] as typeof chalk.dim)(...(output ? [output] : text))
10+
}
11+
return chalk.reset(output)
812
}
913

1014
export interface WrapOptions {
@@ -54,7 +58,7 @@ export function wrap(text: string, options?: WrapOptions): string[] {
5458
return lines
5559
}
5660

57-
export const COLOR_CODE_LEN = chalk.yellow` `.length - 1
61+
export const COLOR_CODE_LEN = color("yellow", " ").length - 1
5862

5963
function chunk(text: string, len: number): string[] {
6064
const arr = text.split(" ")
@@ -74,3 +78,8 @@ function chunk(text: string, len: number): string[] {
7478
}
7579
return result
7680
}
81+
82+
export type ArrayOr<T> = T | T[]
83+
export function asArray<T>(obj: T | T[]): T[] {
84+
return Array.isArray(obj) ? obj ?? [] : obj ? [obj] : []
85+
}

0 commit comments

Comments
 (0)