UNPKG

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