cli-api 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +22 -25
  2. package/dist/index.d.mts +394 -0
  3. package/dist/index.mjs +3 -0
  4. package/dist/interfaces-COq24bNI.mjs +391 -0
  5. package/dist/run-C903J5ca.mjs +1137 -0
  6. package/package.json +37 -37
  7. package/.hgignore +0 -12
  8. package/.idea/$CACHE_FILE$ +0 -6
  9. package/.idea/clap.iml +0 -8
  10. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  11. package/.idea/deployment.xml +0 -63
  12. package/.idea/inspectionProfiles/Project_Default.xml +0 -10
  13. package/.idea/misc.xml +0 -6
  14. package/.idea/modules.xml +0 -8
  15. package/.idea/vcs.xml +0 -6
  16. package/Makefile +0 -14
  17. package/babel.config.json +0 -33
  18. package/dist/cjs/index.js +0 -588
  19. package/dist/cjs/index.js.map +0 -1
  20. package/dist/es/index.mjs +0 -578
  21. package/dist/es/index.mjs.map +0 -1
  22. package/dist/types/app-help.d.ts +0 -3
  23. package/dist/types/commands/command-help.d.ts +0 -2
  24. package/dist/types/commands/version.d.ts +0 -2
  25. package/dist/types/constants.d.ts +0 -4
  26. package/dist/types/index.d.ts +0 -3
  27. package/dist/types/interfaces.d.ts +0 -79
  28. package/dist/types/options.d.ts +0 -7
  29. package/dist/types/print-command-help.d.ts +0 -2
  30. package/dist/types/run.d.ts +0 -2
  31. package/dist/types/utils.d.ts +0 -17
  32. package/rollup.config.js +0 -44
  33. package/src/app-help.ts +0 -34
  34. package/src/commands/command-help.ts +0 -28
  35. package/src/commands/version.ts +0 -11
  36. package/src/constants.ts +0 -4
  37. package/src/index.ts +0 -3
  38. package/src/interfaces.ts +0 -89
  39. package/src/options.ts +0 -266
  40. package/src/print-command-help.ts +0 -78
  41. package/src/run.ts +0 -45
  42. package/src/utils.ts +0 -86
  43. package/tsconfig.json +0 -32
@@ -1,4 +0,0 @@
1
- export declare const EMPTY_ARRAY: ReadonlyArray<any>;
2
- export declare const EMPTY_OBJECT: Record<string, any>;
3
- export declare const TRUE_VALUES: Set<string>;
4
- export declare const FALSE_VALUES: Set<string>;
@@ -1,3 +0,0 @@
1
- export type { App, Command, Option } from "./interfaces";
2
- export { OptType } from './interfaces';
3
- export { default as default } from './run';
@@ -1,79 +0,0 @@
1
- export interface Command {
2
- name: string;
3
- alias?: string | string[];
4
- description?: string;
5
- longDescription?: string;
6
- flags?: Flag[];
7
- options?: Option[];
8
- arguments?: Argument[];
9
- /**
10
- * Executed when the command matches.
11
- *
12
- * @param options Named arguments, options, and flags.
13
- * @param args Positional arguments.
14
- * @param app Entire app config.
15
- */
16
- execute(options: Record<string, any>, args: string[], app: App): Promise<number | void>;
17
- }
18
- export declare enum OptType {
19
- STRING = 0,
20
- BOOL = 1,
21
- INT = 2,
22
- FLOAT = 3,
23
- /** A string, truncated and converted to lowercase. */
24
- ENUM = 4,
25
- /** File must be readable. Single dash will be converted to STDIN. */
26
- INPUT_FILE = 5,
27
- /** Directory must be readable. */
28
- INPUT_DIRECTORY = 6,
29
- /** File's directory must exist and be writeable. Single dash will be converted to STDOUT. */
30
- OUTPUT_FILE = 7,
31
- OUTPUT_DIRECTORY = 8,
32
- /** An empty or non-existent directory. */
33
- EMPTY_DIRECTORY = 9
34
- }
35
- interface ArgumentOrOptionOrFlag {
36
- /** Name of the option to display in help. */
37
- name: string;
38
- /** Alternative name for this option. */
39
- alias?: string | string[];
40
- /** Description of the option. */
41
- description?: string;
42
- /** Default value if not provided. */
43
- defaultValue?: any | ((value: string) => any);
44
- /** Default value to display in help. */
45
- defaultValueText?: string;
46
- /** Property name to use in `execute()` options. */
47
- key?: string;
48
- }
49
- export declare type AnyOptType = OptType | string[];
50
- export interface ArgumentOrOption extends ArgumentOrOptionOrFlag {
51
- /** Type to coerce the option value to. */
52
- type?: AnyOptType;
53
- /** Option is repeatable by specifying the flag again. Value will be an array. */
54
- repeatable?: boolean;
55
- /** Option is required. */
56
- required?: boolean;
57
- }
58
- /** Boolean flag. */
59
- export interface Flag extends ArgumentOrOptionOrFlag, OptionOrFlag {
60
- }
61
- /** Positional argument. */
62
- export interface Argument extends ArgumentOrOption {
63
- }
64
- interface OptionOrFlag {
65
- valueNotRequired?: boolean;
66
- }
67
- /** Option with value. */
68
- export interface Option extends ArgumentOrOption, OptionOrFlag {
69
- /** Placeholder value to use in help. */
70
- valuePlaceholder?: string;
71
- }
72
- export interface App {
73
- name: string;
74
- argv0?: string;
75
- version?: string;
76
- commands: Command[];
77
- globalOptions?: Option[];
78
- }
79
- export {};
@@ -1,7 +0,0 @@
1
- import { App, Command, Option } from './interfaces';
2
- export declare function formatOption(opt: Option): [string, string];
3
- export declare function getValuePlaceholder(opt: Option): string;
4
- export declare function getOptions(cmd: Command): Option[];
5
- export declare function parseArgs(cmd: Command, argv: string[]): [any[], Record<string, any>];
6
- export declare function getOptName(opt: Option): string;
7
- export declare function getCommand(name: string, app: App): Command;
@@ -1,2 +0,0 @@
1
- import { App, Command } from './interfaces';
2
- export declare function printCommandHelp(app: App, cmd: Command): void;
@@ -1,2 +0,0 @@
1
- import { App } from './interfaces';
2
- export default function run(app: App): void;
@@ -1,17 +0,0 @@
1
- /// <reference types="node" />
2
- import { App } from './interfaces';
3
- import FileSys from 'fs';
4
- export declare const print: {
5
- (buffer: string | Uint8Array, cb?: ((err?: Error | undefined) => void) | undefined): boolean;
6
- (str: string | Uint8Array, encoding?: string | undefined, cb?: ((err?: Error | undefined) => void) | undefined): boolean;
7
- };
8
- export declare const printLn: (message?: any, ...optionalParams: any[]) => void;
9
- export declare function abort(message: string, code?: number): never;
10
- export declare function toArray<T>(x: T | T[]): readonly T[];
11
- export declare function resolve<T>(x: any): T;
12
- export declare function toBool(str: string | boolean): boolean;
13
- export declare function space(len: number, str?: string): string;
14
- export declare function getProcName(app: App): string;
15
- export declare function includes(needle: string, haystack: string | string[] | undefined): boolean;
16
- export declare function statSync(path: string): FileSys.Stats | null;
17
- export declare function sortBy<T>(arr: T[], cmp: (x: T) => string): T[];
package/rollup.config.js DELETED
@@ -1,44 +0,0 @@
1
- import babel from '@rollup/plugin-babel';
2
- import nodeResolve from '@rollup/plugin-node-resolve';
3
- import nodeExternals from 'rollup-plugin-node-externals'
4
- import * as tsconfig from './tsconfig.json';
5
- import * as pkg from './package.json'
6
- import json from '@rollup/plugin-json';
7
- const extensions = ['.ts'];
8
-
9
- export default {
10
- input: tsconfig.files,
11
- plugins: [
12
- nodeResolve({
13
- extensions,
14
- }),
15
- nodeExternals({
16
- builtins: true,
17
- deps: true,
18
- devDeps: false,
19
- peerDeps: true,
20
- optDeps: true,
21
- }),
22
- json(),
23
- // TODO: change to typescript2 like node-mysql3c
24
- babel({
25
- exclude: 'node_modules/**',
26
- extensions,
27
- comments: false,
28
- babelHelpers: 'bundled',
29
- }),
30
- ],
31
- output: [
32
- {
33
- file: pkg.main,
34
- format: 'cjs',
35
- sourcemap: true,
36
- exports: 'named',
37
- },
38
- {
39
- file: pkg.module,
40
- format: 'es',
41
- sourcemap: true,
42
- },
43
- ],
44
- }
package/src/app-help.ts DELETED
@@ -1,34 +0,0 @@
1
- import {App} from './interfaces'
2
- import {getProcName, print, printLn, space} from './utils'
3
- import Chalk from 'chalk'
4
- import stringWidth from 'string-width'
5
-
6
- export function printHelp(app: App) {
7
- print(Chalk.green(app.name))
8
- if (app.version) {
9
- print(` version ${Chalk.yellow(app.version)}`)
10
- }
11
- print('\n\n')
12
- printLn(Chalk.yellow("Usage:"))
13
- printLn(` ${Chalk.cyan(getProcName(app))} ${Chalk.gray('<')}command${Chalk.gray('>')} ${Chalk.gray(`[options] [arguments]`)}\n`)
14
-
15
- if (app.globalOptions) {
16
- printLn("TODO")
17
- }
18
-
19
- printAvailableCommands(app)
20
- }
21
-
22
- export function printAvailableCommands(app: App) {
23
- printLn(Chalk.yellow("Available commands:"))
24
- const width = Math.max(...app.commands.map(c => stringWidth(c.name))) + 2
25
- for (const cmd of app.commands) {
26
- print(` ${Chalk.green(cmd.name)}`)
27
- if (cmd.description) {
28
- print(`${space(width, cmd.name)}${cmd.description}`)
29
- }
30
- printLn()
31
- }
32
-
33
- // printLn()
34
- }
@@ -1,28 +0,0 @@
1
- import {App, Command} from '../interfaces'
2
- import {getCommand} from '../options'
3
- import {printCommandHelp} from '../print-command-help'
4
- import {printAvailableCommands} from '../app-help'
5
- import {printLn} from '../utils'
6
-
7
- export const helpCommand: Command = {
8
- name: 'help',
9
- // alias: '--help',
10
- description: "Displays help for a command",
11
- arguments: [
12
- {
13
- name: "command",
14
- description: "The command name.",
15
- required: false,
16
- }
17
- ],
18
- async execute(options: Record<string, string>, [commandName]: string[], app: App) {
19
- if(commandName) {
20
- printCommandHelp(app, getCommand(commandName, app))
21
- } else {
22
- printCommandHelp(app, getCommand('help', app))
23
- printLn()
24
- printAvailableCommands(app)
25
- }
26
- }
27
- }
28
-
@@ -1,11 +0,0 @@
1
- import {Command} from '../interfaces'
2
- import {printLn} from '../utils'
3
-
4
- export const versionCommand: Command = {
5
- name: 'version',
6
- // alias: '--version',
7
- description: "Displays current version",
8
- async execute(opts, args, app) {
9
- printLn(app.version)
10
- }
11
- }
package/src/constants.ts DELETED
@@ -1,4 +0,0 @@
1
- export const EMPTY_ARRAY: ReadonlyArray<any> = Object.freeze([])
2
- export const EMPTY_OBJECT: Record<string,any> = Object.freeze(Object.create({__proto__:null}))
3
- export const TRUE_VALUES = new Set(['y', 'yes', 't', 'true', '1', 'on'])
4
- export const FALSE_VALUES = new Set(['n', 'no', 'f', 'false', '0', 'off'])
package/src/index.ts DELETED
@@ -1,3 +0,0 @@
1
- export type {App, Command, Option} from "./interfaces"
2
- export {OptType} from './interfaces'
3
- export {default as default} from './run'
package/src/interfaces.ts DELETED
@@ -1,89 +0,0 @@
1
- export interface Command {
2
- name: string
3
- alias?: string|string[]
4
- description?: string
5
- longDescription?: string
6
- flags?: Flag[]
7
- options?: Option[]
8
- arguments?: Argument[]
9
- /**
10
- * Executed when the command matches.
11
- *
12
- * @param options Named arguments, options, and flags.
13
- * @param args Positional arguments.
14
- * @param app Entire app config.
15
- */
16
- execute(options: Record<string,any>, args: string[], app: App): Promise<number|void>
17
- }
18
-
19
- export enum OptType {
20
- STRING,
21
- BOOL,
22
- INT,
23
- FLOAT,
24
- /** A string, truncated and converted to lowercase. */
25
- ENUM,
26
- /** File must be readable. Single dash will be converted to STDIN. */
27
- INPUT_FILE,
28
- /** Directory must be readable. */
29
- INPUT_DIRECTORY,
30
- /** File's directory must exist and be writeable. Single dash will be converted to STDOUT. */
31
- OUTPUT_FILE,
32
- OUTPUT_DIRECTORY,
33
- /** An empty or non-existent directory. */
34
- EMPTY_DIRECTORY,
35
- }
36
-
37
- interface ArgumentOrOptionOrFlag {
38
- /** Name of the option to display in help. */
39
- name: string
40
- /** Alternative name for this option. */
41
- alias?: string|string[]
42
- /** Description of the option. */
43
- description?: string
44
- /** Default value if not provided. */
45
- defaultValue?: any|((value:string)=>any)
46
- /** Default value to display in help. */
47
- defaultValueText?: string
48
- /** Property name to use in `execute()` options. */
49
- key?: string
50
- }
51
-
52
- export type AnyOptType = OptType | string[] // | ((value:string)=>any)
53
-
54
- export interface ArgumentOrOption extends ArgumentOrOptionOrFlag {
55
- /** Type to coerce the option value to. */
56
- type?: AnyOptType
57
- /** Option is repeatable by specifying the flag again. Value will be an array. */
58
- repeatable?: boolean
59
- /** Option is required. */
60
- required?: boolean
61
- }
62
-
63
- /** Boolean flag. */
64
- export interface Flag extends ArgumentOrOptionOrFlag,OptionOrFlag {
65
-
66
- }
67
-
68
- /** Positional argument. */
69
- export interface Argument extends ArgumentOrOption {
70
-
71
- }
72
-
73
- interface OptionOrFlag {
74
- valueNotRequired?: boolean
75
- }
76
-
77
- /** Option with value. */
78
- export interface Option extends ArgumentOrOption,OptionOrFlag {
79
- /** Placeholder value to use in help. */
80
- valuePlaceholder?: string
81
- }
82
-
83
- export interface App {
84
- name: string
85
- argv0?: string
86
- version?: string
87
- commands: Command[]
88
- globalOptions?: Option[]
89
- }
package/src/options.ts DELETED
@@ -1,266 +0,0 @@
1
- import {AnyOptType, App, Command, Option, OptType} from './interfaces'
2
- import {abort, includes, resolve, statSync, toArray, toBool} from './utils'
3
- import Chalk from 'chalk'
4
- import Path from 'path'
5
- import FileSys from 'fs'
6
-
7
- export function formatOption(opt: Option): [string, string] {
8
- const aliases: string[] = []
9
- if (opt.alias) {
10
- if (Array.isArray(opt.alias)) {
11
- aliases.push(...opt.alias)
12
- } else {
13
- aliases.push(opt.alias)
14
- }
15
- }
16
- aliases.push(opt.name)
17
- let flags = aliases.map(a => Chalk.green(a.length === 1 ? `-${a}` : `--${a}`)).join(', ')
18
- let valuePlaceholder = getValuePlaceholder(opt)
19
- if (opt.type !== OptType.BOOL) {
20
- flags += `=${valuePlaceholder}`
21
- }
22
- let desc = opt.description ?? ''
23
- let defaultValueText = opt.defaultValueText
24
- if (defaultValueText === undefined && opt.defaultValue !== undefined) {
25
- defaultValueText = JSON.stringify(resolve(opt.defaultValue))
26
- }
27
- if (defaultValueText !== undefined) {
28
- desc += Chalk.yellow(` [default: ${defaultValueText}]`)
29
- }
30
- return [flags, desc]
31
- }
32
-
33
- export function getValuePlaceholder(opt: Option): string {
34
- if (opt.valuePlaceholder !== undefined) {
35
- return opt.valuePlaceholder
36
- }
37
- if (Array.isArray(opt.type)) {
38
- return opt.type.join('|')
39
- } else if (opt.type == OptType.BOOL) {
40
- return JSON.stringify(!resolve(opt.defaultValue))
41
- } else if (opt.type === OptType.INT || opt.type === OptType.FLOAT) {
42
- return '#'
43
- } else if (opt.type === OptType.INPUT_FILE || opt.type === OptType.OUTPUT_FILE) {
44
- return 'FILE'
45
- } else if (opt.type === OptType.INPUT_DIRECTORY || opt.type === OptType.OUTPUT_DIRECTORY || opt.type === OptType.EMPTY_DIRECTORY) {
46
- return 'DIR'
47
- } else {
48
- return opt.name
49
- }
50
- }
51
-
52
- export function getOptions(cmd: Command): Option[] {
53
- return [
54
- ...toArray(cmd.options),
55
- ...toArray(cmd.flags).map(f => ({
56
- ...f,
57
- valueNotRequired: true,
58
- type: OptType.BOOL,
59
-
60
- })),
61
- // {
62
- // name: 'help',
63
- // description: "Print help for this command",
64
- // valueNotRequired: true,
65
- // type: OptType.BOOL,
66
- // }
67
- ] as Option[]
68
- }
69
-
70
- export function parseArgs(cmd: Command, argv: string[]): [any[], Record<string, any>] {
71
- const args: any[] = []
72
- const opts: Record<string, any> = Object.create(null)
73
- let parseFlags = true
74
- // TODO: initialize all repeatables to empty arrays
75
-
76
- const allOptions = getOptions(cmd)
77
-
78
- let argIdx = 0
79
- for (let i = 0; i < argv.length; ++i) {
80
- let arg = argv[i]
81
-
82
- if (parseFlags && arg === '--') {
83
- parseFlags = false
84
- continue
85
- }
86
-
87
- if (parseFlags && arg.length >= 2 && arg.startsWith('-')) {
88
- let name: string
89
- let value: any
90
- if (arg.includes('=')) {
91
- [arg, value] = arg.split('=', 2)
92
- } /*else if(i < argv.length - 2 && argv[i+1] === '=') {
93
- value = argv[i+2]
94
- i += 2
95
- }*/
96
- if (arg.startsWith('--')) {
97
- name = arg.slice(2)
98
- } else {
99
- if (arg.length > 2) {
100
- if (value !== undefined) {
101
- abort(`Malformed option "${arg}"`)
102
- }
103
- value = arg.slice(2)
104
- arg = arg.slice(0, 2)
105
- }
106
- name = arg.slice(1)
107
- // TODO: parse multiple single-char flags
108
- }
109
-
110
- const opt = allOptions.find(opt => opt.name === name || includes(name, opt.alias))
111
- if (!opt) {
112
- abort(`"${cmd.name}" command does not have option "${name}".`)
113
- }
114
- if (value === undefined) {
115
- if (opt.valueNotRequired) {
116
- value = !resolve(opt.defaultValue)
117
- } else if (i < argv.length - 1) {
118
- value = argv[++i]
119
- } else {
120
- abort(`Missing required value for option "${arg}"`)
121
- }
122
- }
123
- if (opt.type != null) {
124
- value = coerceType(value, opt.type)
125
- }
126
- opts[opt.key ?? opt.name] = value
127
- } else {
128
- // TODO: examine cmd.arguments
129
- let value: any = arg
130
-
131
- if (cmd.arguments && cmd.arguments.length > argIdx) {
132
- const cmdArg = cmd.arguments[argIdx]
133
- if (cmdArg.type != null) {
134
- value = coerceType(value, cmdArg.type)
135
- }
136
- if (cmdArg.key) {
137
- opts[cmdArg.key] = value
138
- }
139
- }
140
- args.push(value)
141
- ++argIdx
142
- }
143
- }
144
-
145
- if (allOptions.length) {
146
- for (const opt of allOptions) {
147
- const k = opt.key ?? opt.name
148
- if (opts[k] === undefined) {
149
- if (opt.defaultValue !== undefined) {
150
- opts[k] = resolve(opt.defaultValue)
151
- } else if (opt.required) {
152
- throw new Error(`"${getOptName(opt)}" option is required`)
153
- } else {
154
- // TODO: should we fill in undefined options? with `null` or `undefined`?
155
- }
156
- }
157
- }
158
- }
159
-
160
- if (cmd.arguments?.length) {
161
- for (let i = 0; i < cmd.arguments.length; ++i) {
162
- if (cmd.arguments[i].required && argIdx <= i) {
163
- throw new Error(`"${cmd.arguments[i].name}" argument is required`)
164
- }
165
- }
166
- }
167
-
168
- // TODO: fill global options into opts
169
- // TODO: copy named arguments into opts too
170
- return [args, opts]
171
- }
172
-
173
- function coerceType(value: string, type: AnyOptType) {
174
- if (Array.isArray(type)) {
175
- // TODO: search for closest match of value in type or throw error
176
- return String(value).trim().toLowerCase()
177
- }
178
- switch (type) {
179
- case OptType.BOOL:
180
- return toBool(value)
181
- case OptType.INT:
182
- return Math.trunc(Number(value))
183
- case OptType.FLOAT:
184
- return Number(value)
185
- case OptType.ENUM:
186
- return String(value).trim().toLowerCase()
187
- case OptType.STRING:
188
- return String(value)
189
- case OptType.INPUT_FILE: {
190
- if (value === '-') return '/dev/stdin' // TODO: support windows
191
- const file = Path.normalize(value)
192
- const fullPath = Path.resolve(file)
193
- const stat = statSync(file)
194
- if (!stat) {
195
- throw new Error(`File ${Chalk.underline(fullPath)} does not exist`)
196
- }
197
- if (!stat.isFile()) {
198
- throw new Error(`${Chalk.underline(fullPath)} is not a file`)
199
- }
200
- try {
201
- FileSys.accessSync(file, FileSys.constants.R_OK)
202
- } catch (err) {
203
- throw new Error(`${Chalk.underline(fullPath)} is not readable`)
204
- }
205
- return file
206
- }
207
- case OptType.INPUT_DIRECTORY:
208
- const dir = Path.normalize(value)
209
- FileSys.accessSync(dir, FileSys.constants.X_OK)
210
- return dir
211
- case OptType.OUTPUT_FILE: {
212
- if (value === '-') return '/dev/stdout' // TODO: support windows
213
- const file = Path.normalize(value)
214
- const stat = statSync(file)
215
- if (stat) {
216
- if (!stat.isFile()) {
217
- throw new Error(`'${file}' is not a file`)
218
- }
219
- // if((stat.mode & 0x222) === 0) { // TODO: does this work?
220
- // throw new Error(`'${value}' is not writeable`);
221
- // }
222
- FileSys.accessSync(file, FileSys.constants.W_OK)
223
- } else {
224
- FileSys.accessSync(Path.dirname(file), FileSys.constants.W_OK)
225
- }
226
- return file
227
- }
228
- case OptType.OUTPUT_DIRECTORY: {
229
- FileSys.accessSync(value, FileSys.constants.W_OK)
230
- return Path.normalize(value)
231
- }
232
- case OptType.EMPTY_DIRECTORY: {
233
- const dir = Path.normalize(value)
234
- let files = []
235
- try {
236
- files = FileSys.readdirSync(dir)
237
- } catch (err) {
238
- if (err.code === 'ENOENT') {
239
- FileSys.accessSync(Path.dirname(dir), FileSys.constants.W_OK)
240
- } else {
241
- throw err
242
- }
243
- }
244
- if (files.length) {
245
- throw new Error(`${Chalk.underline(dir)} is not empty`)
246
- }
247
- return dir
248
- }
249
- }
250
- return value
251
- }
252
-
253
-
254
- export function getOptName(opt: Option) {
255
- return (opt.name.length > 1 ? '--' : '-') + opt.name
256
- }
257
-
258
- export function getCommand(name: string, app: App): Command {
259
- const cmdName = String(name).trim().replace(/^-{1,2}/,'').toLowerCase()
260
- const cmd = app.commands.find(c => c.name === cmdName || includes(cmdName, c.alias))
261
- if (cmd === undefined) {
262
- // TODO: levenshtein search for closest match? "Did you mean...?"
263
- throw new Error(`Command "${name}" does not exist.`)
264
- }
265
- return cmd
266
- }
@@ -1,78 +0,0 @@
1
- import {App, Command, OptType} from './interfaces'
2
- import {getProcName, print, printLn, space, toArray} from './utils'
3
- import Chalk from 'chalk'
4
- import {formatOption, getOptions, getOptName, getValuePlaceholder} from './options'
5
- import stringWidth from 'string-width'
6
-
7
-
8
- export function printCommandHelp(app: App, cmd: Command) {
9
- if (cmd.description) {
10
- printLn(cmd.description)
11
- printLn()
12
- }
13
-
14
- printLn(Chalk.yellow("Usage:"))
15
- print(` ${Chalk.cyan(getProcName(app))} ${cmd.name}`)
16
-
17
- const allOptions = getOptions(cmd)
18
-
19
- if (allOptions.length) {
20
- let otherOptions = 0
21
- for (let opt of allOptions) {
22
- if (opt.required) {
23
- print(` ${getOptName(opt)}`)
24
- if (opt.type !== OptType.BOOL) {
25
- print(`=${getValuePlaceholder(opt)}`)
26
- }
27
- } else {
28
- ++otherOptions
29
- }
30
- }
31
- if (otherOptions) {
32
- print(` ${Chalk.gray('[')}options${Chalk.gray(']')}`)
33
- }
34
- }
35
- if (cmd.arguments?.length) {
36
- print(` ${Chalk.grey('[')}--${Chalk.grey(']')}`)
37
- for (const arg of cmd.arguments) {
38
- print(' ')
39
- print(Chalk.grey(arg.required ? '<' : '['))
40
- if (arg.repeatable) {
41
- print(Chalk.grey('...'))
42
- }
43
- print(arg.name)
44
- print(Chalk.grey(arg.required ? '>' : ']'))
45
- }
46
- }
47
- printLn()
48
-
49
- if (allOptions.length) {
50
- printLn(Chalk.yellow("\nOptions:"))
51
- const lines = allOptions.map(formatOption)
52
- const width = Math.max(...lines.map(l => stringWidth(l[0])))
53
- for (const line of lines) {
54
- printLn(' ' + line[0] + space(width + 2, line[0]) + line[1])
55
- }
56
- }
57
-
58
- if (cmd.arguments?.length) {
59
- printLn(Chalk.yellow("\nArguments:"))
60
- const width = Math.max(...cmd.arguments.map(a => stringWidth(a.name)))
61
- for (const arg of cmd.arguments) {
62
- print(' ' + Chalk.green(arg.name))
63
- if (arg.description) {
64
- print(space(width + 2, arg.name) + arg.description)
65
- }
66
- printLn()
67
- }
68
- }
69
-
70
- if (cmd.alias) {
71
- const alaises = toArray(cmd.alias)
72
- printLn(Chalk.yellow(`\nAlias${alaises.length !== 1 ? 'es' : ''}: `) + toArray(cmd.alias).join(Chalk.gray(', ')))
73
- }
74
- if (cmd.longDescription) {
75
- printLn(Chalk.yellow("\nDescription:"))
76
- printLn(' ' + cmd.longDescription)
77
- }
78
- }