This repository has been archived by the owner on Aug 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 44
/
Copy pathcommand.ts
219 lines (176 loc) · 6.33 KB
/
command.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
const pjson = require('../package.json')
import * as Config from '@oclif/config'
import * as Errors from '@oclif/errors'
import * as Parser from '@oclif/parser'
import {HelpBase, getHelpClass} from '@oclif/help'
import {format, inspect} from 'util'
import * as flags from './flags'
import {sortBy, uniqBy} from './util'
import {PrettyPrintableError} from '@oclif/errors'
/**
* swallows stdout epipe errors
* this occurs when stdout closes such as when piping to head
*/
process.stdout.on('error', err => {
if (err && err.code === 'EPIPE')
return
throw err
})
/**
* An abstract class which acts as the base for each command
* in your project.
*/
export default abstract class Command {
static _base = `${pjson.name}@${pjson.version}`
/** A command ID, used mostly in error or verbose reporting */
static id: string
// to-do: Confirm unused?
static title: string | undefined
/**
* The tweet-sized description for your class, used in a parent-commands
* sub-command listing and as the header for the command help
*/
static description: string | undefined
/** hide the command from help? */
static hidden: boolean
/** An override string (or strings) for the default usage documentation */
static usage: string | string[] | undefined
static help: string | undefined
/** An array of aliases for this command */
static aliases: string[] = []
/** When set to false, allows a variable amount of arguments */
static strict = true
static parse = true
/** A hash of flags for the command */
static flags?: flags.Input<any>
/** An order-dependent array of arguments for the command */
static args?: Parser.args.Input
static plugin: Config.IPlugin | undefined
/** An array of example strings to show at the end of the command's help */
static examples: string[] | undefined
static parserOptions = {}
/**
* instantiate and run the command
* @param {Config.Command.Class} this Class
* @param {string[]} argv argv
* @param {Config.LoadOptions} opts options
* @returns Promise<any>
*/
static run: Config.Command.Class['run'] = async function (this: Config.Command.Class, argv?: string[], opts?) {
if (!argv) argv = process.argv.slice(2)
const config = await Config.load(opts || (module.parent && module.parent.parent && module.parent.parent.filename) || __dirname)
const cmd = new this(argv, config)
return cmd._run(argv)
}
id: string | undefined
protected debug: (...args: any[]) => void
constructor(public argv: string[], public config: Config.IConfig) {
this.id = this.ctor.id
try {
this.debug = require('debug')(this.id ? `${this.config.bin}:${this.id}` : this.config.bin)
} catch {
this.debug = () => {}
}
}
get ctor(): typeof Command {
return this.constructor as typeof Command
}
async _run<T>(): Promise<T | undefined> {
let err: Error | undefined
try {
// remove redirected env var to allow subsessions to run autoupdated client
delete process.env[this.config.scopedEnvVarKey('REDIRECTED')]
await this.init()
return await this.run()
} catch (error) {
err = error
await this.catch(error)
} finally {
await this.finally(err)
}
}
exit(code = 0) {
return Errors.exit(code)
}
warn(input: string | Error) {
Errors.warn(input)
}
error(input: string | Error, options: {code?: string; exit: false} & PrettyPrintableError): void
error(input: string | Error, options?: {code?: string; exit?: number} & PrettyPrintableError): never
error(input: string | Error, options: {code?: string; exit?: number | false} & PrettyPrintableError = {}) {
return Errors.error(input, options as any)
}
log(message = '', ...args: any[]) {
// tslint:disable-next-line strict-type-predicates
message = typeof message === 'string' ? message : inspect(message)
process.stdout.write(format(message, ...args) + '\n')
}
/**
* actual command run code goes here
*/
abstract run(): PromiseLike<any>
protected async init(): Promise<any> {
this.debug('init version: %s argv: %o', this.ctor._base, this.argv)
if (this.config.debug) Errors.config.debug = true
if (this.config.errlog) Errors.config.errlog = this.config.errlog
// global['cli-ux'].context = global['cli-ux'].context || {
// command: compact([this.id, ...this.argv]).join(' '),
// version: this.config.userAgent,
// }
const g: any = global
g['http-call'] = g['http-call'] || {}
g['http-call']!.userAgent = this.config.userAgent
if (this._helpOverride()) return this._help()
}
protected parse<F, A extends {[name: string]: any}>(options?: Parser.Input<F>, argv = this.argv): Parser.Output<F, A> {
if (!options) options = this.constructor as any
return require('@oclif/parser').parse(argv, {context: this, ...options})
}
protected async catch(err: any): Promise<any> {
if (!err.message) throw err
if (err.message.match(/Unexpected arguments?: (-h|--help|help)(,|\n)/)) {
return this._help()
}
if (err.message.match(/Unexpected arguments?: (-v|--version|version)(,|\n)/)) {
return this._version()
}
try {
const {cli} = require('cli-ux')
const chalk = require('chalk') // eslint-disable-line node/no-extraneous-require
cli.action.stop(chalk.bold.red('!'))
} catch {}
throw err
}
protected async finally(_: Error | undefined): Promise<any> {
try {
const config = require('@oclif/errors').config
if (config.errorLogger) await config.errorLogger.flush()
// tslint:disable-next-line no-console
} catch (error) {
console.error(error)
}
}
protected _help() {
const HelpClass = getHelpClass(this.config)
const help: HelpBase = new HelpClass(this.config)
const cmd = Config.Command.toCached(this.ctor as any as Config.Command.Class)
if (!cmd.id) cmd.id = ''
let topics = this.config.topics
topics = topics.filter((t: any) => !t.hidden)
topics = sortBy(topics, (t: any) => t.name)
topics = uniqBy(topics, (t: any) => t.name)
help.showCommandHelp(cmd, topics)
return this.exit(0)
}
protected _helpOverride(): boolean {
for (const arg of this.argv) {
if (arg === '--help') return true
if (arg === '--') return false
}
return false
}
protected _version() {
this.log(this.config.userAgent)
return this.exit(0)
}
}