UNPKG

7.81 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const errors_1 = require("@oclif/errors");
4const chalk = require("chalk");
5const indent = require("indent-string");
6const stripAnsi = require("strip-ansi");
7const command_1 = require("./command");
8const list_1 = require("./list");
9const root_1 = require("./root");
10const screen_1 = require("./screen");
11const util_1 = require("./util");
12const util_2 = require("./util");
13exports.getHelpClass = util_2.getHelpClass;
14const wrap = require('wrap-ansi');
15const { bold, } = chalk;
16const ROOT_INDEX_CMD_ID = '';
17function getHelpSubject(args) {
18 for (const arg of args) {
19 if (arg === '--')
20 return;
21 if (arg === 'help' || arg === '--help' || arg === '-h')
22 continue;
23 if (arg.startsWith('-'))
24 return;
25 return arg;
26 }
27}
28class HelpBase {
29 constructor(config, opts = {}) {
30 this.config = config;
31 this.opts = Object.assign({ maxWidth: screen_1.stdtermwidth }, opts);
32 }
33}
34exports.HelpBase = HelpBase;
35class Help extends HelpBase {
36 constructor(config, opts = {}) {
37 super(config, opts);
38 this.render = util_1.template(this);
39 }
40 /*
41 * _topics is to work around Config.topics mistakenly including commands that do
42 * not have children, as well as topics. A topic has children, either commands or other topics. When
43 * this is fixed upstream config.topics should return *only* topics with children,
44 * and this can be removed.
45 */
46 get _topics() {
47 // since this.config.topics is a getter that does non-trivial work, cache it outside the filter loop for
48 // performance benefits in the presence of large numbers of topics
49 const topics = this.config.topics;
50 return topics.filter((topic) => {
51 // it is assumed a topic has a child if it has children
52 const hasChild = topics.some(subTopic => subTopic.name.includes(`${topic.name}:`));
53 return hasChild;
54 });
55 }
56 get sortedCommands() {
57 let commands = this.config.commands;
58 commands = commands.filter(c => this.opts.all || !c.hidden);
59 commands = util_1.sortBy(commands, c => c.id);
60 commands = util_1.uniqBy(commands, c => c.id);
61 return commands;
62 }
63 get sortedTopics() {
64 let topics = this._topics;
65 topics = topics.filter(t => this.opts.all || !t.hidden);
66 topics = util_1.sortBy(topics, t => t.name);
67 topics = util_1.uniqBy(topics, t => t.name);
68 return topics;
69 }
70 showHelp(argv) {
71 const subject = getHelpSubject(argv);
72 if (!subject) {
73 const rootCmd = this.config.findCommand(ROOT_INDEX_CMD_ID);
74 if (rootCmd)
75 this.showCommandHelp(rootCmd);
76 this.showRootHelp();
77 return;
78 }
79 const command = this.config.findCommand(subject);
80 if (command) {
81 this.showCommandHelp(command);
82 return;
83 }
84 const topic = this.config.findTopic(subject);
85 if (topic) {
86 this.showTopicHelp(topic);
87 return;
88 }
89 errors_1.error(`command ${subject} not found`);
90 }
91 showCommandHelp(command) {
92 const name = command.id;
93 const depth = name.split(':').length;
94 const subTopics = this.sortedTopics.filter(t => t.name.startsWith(name + ':') && t.name.split(':').length === depth + 1);
95 const subCommands = this.sortedCommands.filter(c => c.id.startsWith(name + ':') && c.id.split(':').length === depth + 1);
96 const title = command.description && this.render(command.description).split('\n')[0];
97 if (title)
98 console.log(title + '\n');
99 console.log(this.formatCommand(command));
100 console.log('');
101 if (subTopics.length > 0) {
102 console.log(this.formatTopics(subTopics));
103 console.log('');
104 }
105 if (subCommands.length > 0) {
106 console.log(this.formatCommands(subCommands));
107 console.log('');
108 }
109 }
110 showRootHelp() {
111 let rootTopics = this.sortedTopics;
112 let rootCommands = this.sortedCommands;
113 console.log(this.formatRoot());
114 console.log('');
115 if (!this.opts.all) {
116 rootTopics = rootTopics.filter(t => !t.name.includes(':'));
117 rootCommands = rootCommands.filter(c => !c.id.includes(':'));
118 }
119 if (rootTopics.length > 0) {
120 console.log(this.formatTopics(rootTopics));
121 console.log('');
122 }
123 if (rootCommands.length > 0) {
124 rootCommands = rootCommands.filter(c => c.id);
125 console.log(this.formatCommands(rootCommands));
126 console.log('');
127 }
128 }
129 showTopicHelp(topic) {
130 const name = topic.name;
131 const depth = name.split(':').length;
132 const subTopics = this.sortedTopics.filter(t => t.name.startsWith(name + ':') && t.name.split(':').length === depth + 1);
133 const commands = this.sortedCommands.filter(c => c.id.startsWith(name + ':') && c.id.split(':').length === depth + 1);
134 console.log(this.formatTopic(topic));
135 if (subTopics.length > 0) {
136 console.log(this.formatTopics(subTopics));
137 console.log('');
138 }
139 if (commands.length > 0) {
140 console.log(this.formatCommands(commands));
141 console.log('');
142 }
143 }
144 formatRoot() {
145 const help = new root_1.default(this.config, this.opts);
146 return help.root();
147 }
148 formatCommand(command) {
149 const help = new command_1.default(command, this.config, this.opts);
150 return help.generate();
151 }
152 formatCommands(commands) {
153 if (commands.length === 0)
154 return '';
155 const body = list_1.renderList(commands.map(c => [
156 c.id,
157 c.description && this.render(c.description.split('\n')[0]),
158 ]), {
159 spacer: '\n',
160 stripAnsi: this.opts.stripAnsi,
161 maxWidth: this.opts.maxWidth - 2,
162 });
163 return [
164 bold('COMMANDS'),
165 indent(body, 2),
166 ].join('\n');
167 }
168 formatTopic(topic) {
169 let description = this.render(topic.description || '');
170 const title = description.split('\n')[0];
171 description = description.split('\n').slice(1).join('\n');
172 let output = util_1.compact([
173 title,
174 [
175 bold('USAGE'),
176 indent(wrap(`$ ${this.config.bin} ${topic.name}:COMMAND`, this.opts.maxWidth - 2, { trim: false, hard: true }), 2),
177 ].join('\n'),
178 description && ([
179 bold('DESCRIPTION'),
180 indent(wrap(description, this.opts.maxWidth - 2, { trim: false, hard: true }), 2),
181 ].join('\n')),
182 ]).join('\n\n');
183 if (this.opts.stripAnsi)
184 output = stripAnsi(output);
185 return output + '\n';
186 }
187 formatTopics(topics) {
188 if (topics.length === 0)
189 return '';
190 const body = list_1.renderList(topics.map(c => [
191 c.name,
192 c.description && this.render(c.description.split('\n')[0]),
193 ]), {
194 spacer: '\n',
195 stripAnsi: this.opts.stripAnsi,
196 maxWidth: this.opts.maxWidth - 2,
197 });
198 return [
199 bold('TOPICS'),
200 indent(body, 2),
201 ].join('\n');
202 }
203 /**
204 * @deprecated used for readme generation
205 * @param {object} command The command to generate readme help for
206 * @return {string} the readme help string for the given command
207 */
208 command(command) {
209 return this.formatCommand(command);
210 }
211}
212exports.default = Help;
213exports.Help = Help;