UNPKG

4.65 kBPlain TextView Raw
1import * as os from 'os';
2import { CliBaseCommandDefinition, CliCommandDefinition, CliCommandDefinitionOption, ParsedCliCommand } from './types';
3import { dashCase } from './utils';
4
5const EOL = os.EOL;
6const GAP = 4;
7const __ = ' ';
8
9// @see https://stackoverflow.com/a/18914855/3577474
10export const sentences = (str = '') => {
11 return str.replace(/([.?!])\s*(?=[A-Z])/g, '$1|').split('|');
12};
13
14const noop = (s: string): string => {
15 return s;
16};
17
18const maxLen = (arr: string[]): number => {
19 let c = 0,
20 d = 0,
21 l = 0,
22 i = arr.length;
23 if (i) {
24 while (i--) {
25 d = arr[i].length;
26 if (d > c) {
27 l = i;
28 c = d;
29 }
30 }
31 }
32 return arr[l].length;
33};
34
35const formatTable = (arr: string[][]): any => {
36 if (!arr.length) {
37 return '';
38 }
39 if (Array.isArray(arr)) {
40 const len = maxLen(arr.map(x => x[0])) + GAP;
41 const join = a => a[0] + ' '.repeat(len - a[0].length) + a[1] + (a[2] == null ? '' : ` ${a[2]}`);
42 return arr.map(join);
43 }
44};
45
46export const section = (str: string, content: string[] | string, fn?: (s: string) => string) => {
47 const arr = Array.isArray(content) ? content : [content];
48 fn = fn || noop;
49 if (!arr || !arr.length) {
50 return '';
51 }
52 let i = 0,
53 out = '';
54 out += EOL + __ + str;
55 for (; i < arr.length; i++) {
56 out += EOL + __ + __ + fn(arr[i]);
57 }
58 return out + EOL;
59};
60
61const buildUsage = (parsed: ParsedCliCommand): string => {
62 let args = [];
63 if (parsed.parsedCommandName) {
64 args = (parsed.command.arguments || []).map(a => {
65 return `${a.isOptional ? '[' : '<'}${a.name}${a.isOptional ? ']' : '>'}`;
66 });
67 return `${parsed.parsedCommandName}${args.length > 0 ? ` ${args.join(' ')}` : ''} [options]`;
68 } else {
69 return '<command> [args] [options]';
70 }
71};
72
73export const buildUsageSection = (parsed: ParsedCliCommand): string => {
74 const pfx = `$ ${parsed.program.name}`;
75 const prefix = s => `${pfx} ${s}`;
76 return section('Usage', `${buildUsage(parsed)}`, prefix);
77};
78
79export const helpFormatter = (parsed: ParsedCliCommand): string => {
80 const rootDef = parsed.program;
81 const cmdDefs = rootDef.commands;
82 let out = '';
83 const pfx = `$ ${rootDef.name}`;
84 const prefix = s => `${pfx} ${s}`;
85
86 // Description Section
87 out += section('Description', parsed.parsedCommandName ? parsed.command.description : rootDef.description);
88
89 // Usage Section
90 out += buildUsageSection(parsed);
91
92 let argumentsContent: string[][] = [];
93
94 if (!parsed.parsedCommandName) {
95 // Global Command List Section
96 const cmdAndDesc = cmdDefs.map(def => [def.name, def.description || '']);
97 out += section('Available Commands', formatTable(cmdAndDesc));
98 out += EOL + __ + 'For more info, run any command with the `--help` flag';
99 cmdDefs.forEach(def => {
100 out += EOL + __ + __ + `${pfx} ${def.name} --help`;
101 });
102 out += EOL;
103
104 argumentsContent = [
105 [
106 'command',
107 `Command Name${rootDef.defaultCommandName ? ` (default: '${rootDef.defaultCommandName}')` : ''}`,
108 ],
109 ];
110 } else {
111 // Global Command List Section
112 argumentsContent = parsed.command.arguments.map(a => [
113 `${a.name} ${a.isOptional ? '(optional)' : '(required)'}`,
114 `${a.description || ''}${a.default ? ` (default: '${a.default}')` : ''}`,
115 ]);
116 }
117
118 const cmdDef: CliBaseCommandDefinition = parsed.parsedCommandName ? parsed.command : rootDef;
119
120 // Arguments Section
121 out += section('Arguments', formatTable(argumentsContent));
122
123 // Global Options Section
124 const globalOptionsContent = rootDef.options.map(o => [
125 `--${dashCase(o.name)}${o.flag ? `, -${o.flag}` : ''}`,
126 o.description || '',
127 ]);
128 out += section('Global Options', formatTable(globalOptionsContent));
129
130 // Command Options Section
131 if (parsed.parsedCommandName) {
132 const cmdOptionsContent = parsed.command.options.map(o => [
133 `--${dashCase(o.name)}${o.flag ? `, -${o.flag}` : ''}`,
134 o.description || '',
135 ]);
136 out += section(`${parsed.command.name} Command Options`, formatTable(cmdOptionsContent));
137 }
138
139 // Examples Section
140 out += section('Examples', (cmdDef.examples || []).map(prefix));
141
142 return out;
143};
144
\No newline at end of file