1 | import * as os from 'os';
|
2 | import { CliBaseCommandDefinition, CliCommandDefinition, CliCommandDefinitionOption, ParsedCliCommand } from './types';
|
3 | import { dashCase } from './utils';
|
4 |
|
5 | const EOL = os.EOL;
|
6 | const GAP = 4;
|
7 | const __ = ' ';
|
8 |
|
9 |
|
10 | export const sentences = (str = '') => {
|
11 | return str.replace(/([.?!])\s*(?=[A-Z])/g, '$1|').split('|');
|
12 | };
|
13 |
|
14 | const noop = (s: string): string => {
|
15 | return s;
|
16 | };
|
17 |
|
18 | const maxLen = (arr: string[]): number => {
|
19 | let c = 0;
|
20 | let d = 0;
|
21 | let l = 0;
|
22 | let 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 |
|
35 | const 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 |
|
46 | export 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 | let out = '';
|
54 | out += EOL + __ + str;
|
55 | for (; i < arr.length; i++) {
|
56 | out += EOL + __ + __ + fn(arr[i]);
|
57 | }
|
58 | return out + EOL;
|
59 | };
|
60 |
|
61 | const 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 |
|
73 | export 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 |
|
79 | export 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 |