@jsreport/jsreport-cli 3.0.0 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +13 -1
  2. package/cli.js +3 -3
  3. package/index.js +0 -2
  4. package/lib/cliExtension.js +2 -2
  5. package/lib/{createCommandParser.js → commander/createCommandParser.js} +2 -3
  6. package/lib/commander/createCustomCommandBuilder.js +150 -0
  7. package/lib/commander/createLogger.js +51 -0
  8. package/lib/commander/executeCommand.js +37 -0
  9. package/lib/commander/getCommandEventName.js +4 -0
  10. package/lib/commander/index.js +279 -0
  11. package/lib/commander/jsreportInstance.js +98 -0
  12. package/lib/commander/registerCommand.js +91 -0
  13. package/lib/commander/startCommand.js +234 -0
  14. package/lib/commander/startProcessing.js +208 -0
  15. package/lib/commands/_initializeApp.js +0 -2
  16. package/lib/commands/configure.js +1 -3
  17. package/lib/commands/help.js +7 -8
  18. package/lib/commands/init.js +4 -5
  19. package/lib/commands/kill.js +4 -5
  20. package/lib/commands/render.js +37 -35
  21. package/lib/commands/repair.js +4 -5
  22. package/lib/commands/start.js +0 -2
  23. package/lib/commands/win-install.js +0 -2
  24. package/lib/commands/win-uninstall.js +0 -2
  25. package/lib/daemonHandler.js +2 -2
  26. package/lib/daemonInstance.js +5 -5
  27. package/lib/{registerExtensionsCommands.js → detectAndRegisterExtensionsCommands.js} +1 -1
  28. package/lib/keepAliveProcess.js +3 -3
  29. package/lib/{errorUtils.js → utils/error.js} +23 -1
  30. package/lib/{getTempPaths.js → utils/getTempPaths.js} +0 -0
  31. package/lib/{normalizePathOptionOrArg.js → utils/normalizePathOptionOrArg.js} +0 -0
  32. package/lib/{normalizeSocketPath.js → utils/normalizeSocketPath.js} +0 -0
  33. package/lib/{startSocketServer.js → utils/startSocketServer.js} +1 -1
  34. package/package.json +12 -12
  35. package/lib/commander.js +0 -1108
@@ -0,0 +1,98 @@
1
+ const instanceHandler = require('../instanceHandler')
2
+ const detectAndRegisterExtensionsCommands = require('../detectAndRegisterExtensionsCommands')
3
+
4
+ module.exports.getInstance = function getInstance (commander, prevInstance, log, cwd) {
5
+ const args = Array.prototype.slice.call(arguments)
6
+
7
+ if (args.length === 3) {
8
+ return _getInstance_.bind(undefined, commander, prevInstance, log)
9
+ }
10
+
11
+ return _getInstance_(commander, prevInstance, log, cwd)
12
+
13
+ function _getInstance_ (commander, prevInstance, log, cwd) {
14
+ if (prevInstance) {
15
+ log('using jsreport instance passed from options')
16
+
17
+ return Promise.resolve(prevInstance)
18
+ }
19
+
20
+ commander.emit('instance.lookup')
21
+
22
+ if (cwd == null) {
23
+ cwd = commander.cwd
24
+ }
25
+
26
+ return (
27
+ instanceHandler
28
+ .find(cwd)
29
+ .then((instanceInfo) => {
30
+ if (instanceInfo.isDefault) {
31
+ commander.emit('instance.default', instanceInfo.instance)
32
+
33
+ log(
34
+ 'no entry point was found, creating a default instance ' +
35
+ 'using: require("' + instanceInfo.from + '")()'
36
+ )
37
+ } else {
38
+ commander.emit('instance.found', instanceInfo.instance)
39
+
40
+ log('using jsreport instance found in: ' + instanceInfo.entryPoint)
41
+ }
42
+
43
+ return instanceInfo.instance
44
+ })
45
+ )
46
+ }
47
+ }
48
+
49
+ module.exports.initInstance = function initInstance (commander, verbose, instance) {
50
+ const args = Array.prototype.slice.call(arguments)
51
+
52
+ if (args.length === 2) {
53
+ return _initInstance_.bind(undefined, commander, verbose)
54
+ }
55
+
56
+ return _initInstance_(commander, verbose, instance)
57
+
58
+ function _initInstance_ (commander, verbose, instance, forceVerbose) {
59
+ let verboseMode = verbose
60
+
61
+ commander.emit('instance.initializing')
62
+
63
+ if (forceVerbose === true) {
64
+ verboseMode = forceVerbose
65
+ }
66
+
67
+ return (
68
+ instanceHandler.initialize(instance, verboseMode)
69
+ .then((result) => {
70
+ commander.jsreportInstanceInitiated = instance
71
+
72
+ commander.emit('instance.initialized', result)
73
+
74
+ return result
75
+ })
76
+ )
77
+ }
78
+ }
79
+
80
+ module.exports.findAndLoadExtensionsCommands = async function findAndLoadExtensionsCommands (instance, commander, verbose) {
81
+ if (!instance || !instance.extensionsLoad) {
82
+ return
83
+ }
84
+
85
+ if (!verbose) {
86
+ if (instance.options.logger) {
87
+ instance.options.logger.silent = true
88
+ } else {
89
+ instance.options.logger = {
90
+ silent: true
91
+ }
92
+ }
93
+ }
94
+
95
+ await instance.extensionsLoad({ onlyLocation: true })
96
+
97
+ await detectAndRegisterExtensionsCommands(instance.extensionsManager.extensions, commander)
98
+ }
@@ -0,0 +1,91 @@
1
+ const createCustomCommandBuilder = require('./createCustomCommandBuilder')
2
+ const executeCommand = require('./executeCommand')
3
+
4
+ module.exports = function registerCommand (commander, rawCommandModule) {
5
+ const globalOptions = commander._globalOptions
6
+ const commandBuilder = rawCommandModule.builder || ((yargs) => (yargs))
7
+ const commandConfiguration = Object.assign({}, rawCommandModule.configuration)
8
+ let commandGlobalOptions = Object.assign([], commandConfiguration.globalOptions)
9
+
10
+ if (typeof rawCommandModule.command !== 'string' || !rawCommandModule.command) {
11
+ throw new Error('command module must have a .command property of type string')
12
+ }
13
+
14
+ // we do this because .command can include positional arguments like "command <requiredArg> [optionalArg]"
15
+ const commandName = rawCommandModule.command.split(' ')[0].trim()
16
+
17
+ if (typeof rawCommandModule.description !== 'string') {
18
+ throw new Error('command module must have a .description property of type string')
19
+ }
20
+
21
+ if (typeof rawCommandModule.handler !== 'function') {
22
+ throw new Error('command module must have a .handler property of type function')
23
+ }
24
+
25
+ if (commandBuilder != null && typeof commandBuilder !== 'function') {
26
+ throw new Error('command module .builder property must be a function')
27
+ }
28
+
29
+ if (commander._disabledCommands.indexOf(commandName) !== -1) {
30
+ return
31
+ }
32
+
33
+ if (commandGlobalOptions) {
34
+ commandGlobalOptions = commandGlobalOptions.map((opt) => {
35
+ if (globalOptions.options.indexOf(opt) === -1) {
36
+ return null
37
+ }
38
+
39
+ return opt
40
+ })
41
+
42
+ // removing invalid options
43
+ commandGlobalOptions.filter(Boolean)
44
+
45
+ // always add some options to global
46
+ globalOptions.alwaysGlobal.forEach((opt) => {
47
+ if (commandGlobalOptions.indexOf(opt) === -1) {
48
+ commandGlobalOptions.unshift(opt)
49
+ }
50
+ })
51
+ } else {
52
+ // always add some options to global
53
+ commandGlobalOptions = globalOptions.alwaysGlobal
54
+ }
55
+
56
+ commandConfiguration.globalOptions = commandGlobalOptions
57
+
58
+ const bindedHandler = executeCommand.bind(null, commander, commandName)
59
+
60
+ commander._commandsConfig[commandName] = commandConfiguration
61
+
62
+ const customCommandBuilder = createCustomCommandBuilder(commander, {
63
+ commandName,
64
+ commandDescription: rawCommandModule.description,
65
+ commandBuilder,
66
+ commandConfig: commandConfiguration,
67
+ globalOptions
68
+ })
69
+
70
+ const commandModule = Object.assign({}, rawCommandModule, {
71
+ builder: customCommandBuilder,
72
+ handler: async (argv) => {
73
+ const disableExit = commandConfiguration.disableProcessExit === true
74
+
75
+ try {
76
+ await bindedHandler(argv)
77
+ } catch (err) {
78
+ err.disableExit = disableExit
79
+ throw err
80
+ }
81
+ }
82
+ })
83
+
84
+ commander._cli.command(commandModule)
85
+
86
+ commander._commands[commandName] = rawCommandModule
87
+ commander._commandModules[commandName] = commandModule
88
+ commander._commandNames.push(commandName)
89
+
90
+ commander.emit('command.register', commandName, rawCommandModule)
91
+ }
@@ -0,0 +1,234 @@
1
+ const Yargs = require('yargs/yargs')
2
+ const { nanoid } = require('nanoid')
3
+ const createCommandParser = require('./createCommandParser')
4
+ const jsreportInstance = require('./jsreportInstance')
5
+
6
+ // check the command to see if we should handle jsreport instance
7
+ // initialization first or just delegate the command to cli handler
8
+ module.exports = async function startCommand (commander, commandName, args, options, onBeforeCLIParse) {
9
+ const logger = options.logger
10
+ const verbose = options.verbose
11
+ // creating a new context based on properties of commander's context
12
+ const context = Object.assign({}, commander.context)
13
+ const startImmediately = options.startImmediately === true || commander._commands[commandName] != null
14
+
15
+ // we need to handle the help option directly since there is a conflict
16
+ // in yargs when a custom command is called the same that the option used in .help()
17
+ // (that option registers an implicit command with the same name that the option passed)
18
+ if (commandName === 'help') {
19
+ // disable the implicit help command when the main command to execute is "help",
20
+ // this allows our custom command to run
21
+ commander._cli.help(false)
22
+ }
23
+
24
+ context.logger = logger
25
+
26
+ context.getCommandHelp = getCommandHelp(commander, commandName, verbose, context)
27
+
28
+ if (commander._daemonExecPath || commander._daemonExecArgs || commander._daemonExecOpts || commander._daemonExecScriptPath) {
29
+ context.daemonExec = {}
30
+
31
+ if (commander._daemonExecPath) {
32
+ context.daemonExec.path = commander._daemonExecPath
33
+ }
34
+
35
+ if (commander._daemonExecArgs) {
36
+ context.daemonExec.args = commander._daemonExecArgs
37
+ }
38
+
39
+ if (commander._daemonExecOpts) {
40
+ context.daemonExec.opts = commander._daemonExecOpts
41
+ }
42
+
43
+ if (commander._daemonExecScriptPath) {
44
+ context.daemonExec.scriptPath = commander._daemonExecScriptPath
45
+ }
46
+ }
47
+
48
+ if (startImmediately) {
49
+ // passing getInstance and initInstance as context
50
+ // to commands when they should ignore the entry point
51
+ context.getInstance = jsreportInstance.getInstance(commander, commander._jsreportInstance, logger.debug)
52
+ context.initInstance = jsreportInstance.initInstance(commander, verbose)
53
+
54
+ throwIfCommandIsNotValid(commander, commandName, onBeforeCLIParse)
55
+
56
+ const commandConfig = commander._commandsConfig[commandName]
57
+
58
+ if (commandConfig && commandConfig.globalOptions) {
59
+ commandConfig.globalOptions.forEach((optName) => {
60
+ commander._cli.global(optName)
61
+ })
62
+ }
63
+
64
+ onBeforeCLIParse()
65
+
66
+ // delegating the command to the CLI and activating it
67
+ await startCLI(logger, commander, commandName, args, context)
68
+
69
+ return
70
+ }
71
+
72
+ // at this point command was not found so we need to get jsreport instance and
73
+ // read extensions and look if any of those define the command
74
+ try {
75
+ logger.debug(`Searching for command "${commandName}" in extensions`)
76
+
77
+ let getInstancePromise
78
+
79
+ if (commander._jsreportInstance) {
80
+ getInstancePromise = Promise.resolve(commander._jsreportInstance)
81
+ } else {
82
+ getInstancePromise = jsreportInstance.getInstance(commander, null, logger.debug, commander.cwd)
83
+ }
84
+
85
+ const instanceOrFn = await getInstancePromise
86
+ const instance = typeof instanceOrFn === 'function' ? instanceOrFn() : instanceOrFn
87
+
88
+ context.getInstance = jsreportInstance.getInstance(commander, instance, logger.debug)
89
+ context.initInstance = jsreportInstance.initInstance(commander, verbose)
90
+
91
+ await jsreportInstance.findAndLoadExtensionsCommands(instance, commander, verbose)
92
+
93
+ throwIfCommandIsNotValid(commander, commandName, onBeforeCLIParse)
94
+
95
+ const commandConfig = commander._commandsConfig[commandName]
96
+
97
+ if (commandConfig && commandConfig.globalOptions) {
98
+ commandConfig.globalOptions.forEach((optName) => {
99
+ commander._cli.global(optName)
100
+ })
101
+ }
102
+
103
+ onBeforeCLIParse()
104
+ } catch (e) {
105
+ onBeforeCLIParse(e)
106
+ throw e
107
+ }
108
+
109
+ await startCLI(logger, commander, commandName, args, context)
110
+ }
111
+
112
+ async function startCLI (logger, commander, commandName, args, context) {
113
+ const cli = commander._cli
114
+
115
+ commander.emit('parsing', args, context)
116
+
117
+ let error
118
+ let output
119
+
120
+ try {
121
+ const result = await parseCLI(cli, args, context)
122
+ error = result.error
123
+ output = result.output
124
+ } catch (e) {
125
+ commander.emit('parsed', e, args, context)
126
+
127
+ const error = new Error(`An error ocurred while trying to execute "${commandName}" command`)
128
+ error.originalError = e
129
+
130
+ throw error
131
+ }
132
+
133
+ if (error) {
134
+ commander.emit('parsed', error, args, context)
135
+
136
+ if (output != null && output !== '') {
137
+ logger.error(output)
138
+ }
139
+
140
+ error.exitDirectly = true
141
+
142
+ throw error
143
+ }
144
+
145
+ commander.emit('parsed', null, args, context)
146
+
147
+ if (output != null && output !== '') {
148
+ logger.info(output)
149
+ }
150
+ }
151
+
152
+ async function parseCLI (cli, args, context) {
153
+ let error
154
+ let output
155
+
156
+ await cli.parseAsync(args, { context }, (_error, _argv, resultOutput) => {
157
+ output = resultOutput
158
+
159
+ if (_error) {
160
+ error = _error
161
+ }
162
+ })
163
+
164
+ return { error, output }
165
+ }
166
+
167
+ function getCommandHelp (commander, originCommandName, verbose, context) {
168
+ return async (command) => {
169
+ let customYargs
170
+ let out
171
+
172
+ if (!commander._commands[command]) {
173
+ let instance
174
+
175
+ try {
176
+ const instanceOrFn = await context.getInstance()
177
+ instance = typeof instanceOrFn === 'function' ? instanceOrFn() : instanceOrFn
178
+ } catch (e) {}
179
+
180
+ if (instance) {
181
+ await jsreportInstance.findAndLoadExtensionsCommands(instance, commander, verbose)
182
+ }
183
+ }
184
+
185
+ const commandModule = commander._commandModules[command]
186
+
187
+ if (commandModule) {
188
+ customYargs = createCommandParser(Yargs([]), commander.cliName).command(commandModule)
189
+ }
190
+
191
+ if (customYargs) {
192
+ let helpArg = '-h'
193
+
194
+ if (originCommandName === 'help') {
195
+ const randomHelpArg = nanoid(5)
196
+ customYargs.help(randomHelpArg).hide(randomHelpArg)
197
+ helpArg = randomHelpArg
198
+ }
199
+
200
+ const { error, output } = await parseCLI(customYargs, [command, helpArg], context)
201
+
202
+ if (error) {
203
+ throw error
204
+ }
205
+
206
+ out = output
207
+ } else {
208
+ const error = new Error(`"${command}" command not available to inspect information, to get the list of commands supported on your installation run "jsreport -h" and try again with a supported command`)
209
+ error.cleanState = true
210
+ error.notFound = true
211
+ throw error
212
+ }
213
+
214
+ return out
215
+ }
216
+ }
217
+
218
+ function throwIfCommandIsNotValid (commander, commandName, onBeforeThrow) {
219
+ if (!commander._commands[commandName]) {
220
+ const error = new Error(
221
+ '"' + commandName + '" command not found in this installation, ' +
222
+ 'check that you are writing the command correctly or check if the command ' +
223
+ 'is available in your installation, use "jsreport -h" to see the list of available commands'
224
+ )
225
+
226
+ error.cleanState = true
227
+
228
+ if (onBeforeThrow) {
229
+ onBeforeThrow(error)
230
+ }
231
+
232
+ throw error
233
+ }
234
+ }
@@ -0,0 +1,208 @@
1
+ const yargs = require('yargs')
2
+ const prompt = require('prompt')
3
+ const startCommand = require('./startCommand')
4
+ const jsreportInstance = require('./jsreportInstance')
5
+ const cliPackageJson = require('../../package.json')
6
+
7
+ module.exports = async function startProcessing (commander, logger, args) {
8
+ process.env.JSREPORT_CLI = true
9
+
10
+ if (!Array.isArray(args) && typeof args !== 'string') {
11
+ throw new Error('args must be an array or string')
12
+ }
13
+
14
+ commander.emit('starting')
15
+
16
+ const userArgv = yargs([]).help(false).version(false).parse(args)
17
+
18
+ const versionRequired = userArgv.version || userArgv.v
19
+ const helpRequired = userArgv.help || userArgv.h
20
+ const needsPassword = userArgv.password || userArgv.p
21
+ const verboseMode = userArgv.verbose || userArgv.b
22
+
23
+ if (userArgv._.length === 0) {
24
+ const willShowHelpExplicitly = commander._showHelpWhenNoCommand && !versionRequired && !helpRequired
25
+
26
+ commander.emit('started', null, { handled: versionRequired || helpRequired || willShowHelpExplicitly, mainCommand: null })
27
+
28
+ commander.emit('parsing', args, commander.context)
29
+
30
+ let instance
31
+
32
+ try {
33
+ instance = await jsreportInstance.getInstance(commander, commander._jsreportInstance, () => {}, commander.cwd)
34
+ } catch (e) {}
35
+
36
+ if (instance && (helpRequired || willShowHelpExplicitly)) {
37
+ await jsreportInstance.findAndLoadExtensionsCommands(instance, commander, verboseMode)
38
+ }
39
+
40
+ let parsed = false
41
+ let output
42
+
43
+ // activating CLI
44
+ try {
45
+ try {
46
+ // we run the cli for it to handle args validation and handle help arg, and we can extract the output
47
+ // from the run directly
48
+ await commander._cli.parseAsync(args, (_error, _argv, resultOutput) => {
49
+ // we need to use callback to be able to capture output (important for the default help)
50
+ // unfortunately using the callback just pass the validation arg error here in the callback
51
+ // for does not propagate to the parseAsync and does not make it to fail
52
+ if (_error) {
53
+ if (resultOutput != null && resultOutput !== '') {
54
+ logger.error(resultOutput)
55
+ }
56
+
57
+ _error.exitDirectly = true
58
+
59
+ throw _error
60
+ }
61
+
62
+ output = resultOutput
63
+ })
64
+ } catch (error) {
65
+ error.exitDirectly = true
66
+ throw error
67
+ }
68
+
69
+ commander.emit('parsed', null, args, commander.context)
70
+
71
+ parsed = true
72
+
73
+ if (versionRequired) {
74
+ let instanceToEvaluate
75
+
76
+ if (commander._jsreportVersion) {
77
+ instanceToEvaluate = {
78
+ version: commander._jsreportVersion
79
+ }
80
+ } else {
81
+ if (typeof instance === 'function') {
82
+ instanceToEvaluate = instance()
83
+ } else {
84
+ instanceToEvaluate = instance
85
+ }
86
+ }
87
+
88
+ return handleVersionOption(instanceToEvaluate, logger)
89
+ }
90
+
91
+ if (
92
+ willShowHelpExplicitly &&
93
+ !args.includes('--help') &&
94
+ !args.includes('-h')
95
+ ) {
96
+ output = await commander._cli.getHelp()
97
+ }
98
+
99
+ if (output != null && output !== '') {
100
+ logger.info(output)
101
+ }
102
+ } catch (e) {
103
+ if (!parsed) {
104
+ commander.emit('parsed', e, args, commander.context)
105
+ }
106
+
107
+ throw e
108
+ }
109
+
110
+ return
111
+ }
112
+
113
+ const mainCommandReceived = userArgv._[0]
114
+
115
+ const startImmediately = (
116
+ // if command is built-in and version or help options is activated
117
+ (commander._builtInCommandNames.indexOf(mainCommandReceived) !== -1 && (versionRequired || helpRequired))
118
+ )
119
+
120
+ const optionsForStart = {
121
+ startImmediately,
122
+ logger,
123
+ verbose: verboseMode
124
+ }
125
+
126
+ const commandConfiguration = commander._commandsConfig[mainCommandReceived] || {}
127
+
128
+ let beforeCLIParseCbCalled = false
129
+
130
+ const onBeforeCLIParse = (err) => {
131
+ if (beforeCLIParseCbCalled) {
132
+ return
133
+ }
134
+
135
+ beforeCLIParseCbCalled = true
136
+
137
+ const isSupportedCommand = commander._commandNames.indexOf(mainCommandReceived) !== -1
138
+
139
+ if (err) {
140
+ return commander.emit('started', err, null)
141
+ }
142
+
143
+ commander.emit('started', null, {
144
+ handled: isSupportedCommand || versionRequired || (isSupportedCommand && helpRequired),
145
+ mainCommand: mainCommandReceived
146
+ })
147
+ }
148
+
149
+ if (
150
+ needsPassword &&
151
+ commandConfiguration.globalOptions &&
152
+ commandConfiguration.globalOptions.indexOf('password') !== -1
153
+ ) {
154
+ if (typeof needsPassword === 'string') {
155
+ // we add middleware to pre-define the "p/password" option to
156
+ commander._cli.middleware((argv) => {
157
+ argv.p = needsPassword
158
+ argv.password = needsPassword
159
+ }, true)
160
+ } else {
161
+ await new Promise((resolve, reject) => {
162
+ prompt.start()
163
+
164
+ prompt.message = ''
165
+
166
+ prompt.get([{
167
+ name: 'password',
168
+ description: 'Password',
169
+ message: 'Password can\'t be empty',
170
+ type: 'string',
171
+ hidden: true,
172
+ required: true
173
+ }], (err, result) => {
174
+ if (err) {
175
+ const errorToReject = new Error('No value for password option')
176
+ errorToReject.cleanState = true
177
+
178
+ commander.emit('started', errorToReject, null)
179
+
180
+ return reject(errorToReject)
181
+ }
182
+
183
+ // we add middleware to pre-define the "p/password" option to
184
+ commander._cli.middleware((argv) => {
185
+ argv.p = result.password
186
+ argv.password = result.password
187
+ }, true)
188
+
189
+ resolve()
190
+ })
191
+ })
192
+ }
193
+ }
194
+
195
+ await startCommand(commander, mainCommandReceived, args, optionsForStart, onBeforeCLIParse)
196
+
197
+ return { command: mainCommandReceived }
198
+ }
199
+
200
+ function handleVersionOption (instance, logger) {
201
+ let versionOutput = `cli version: ${cliPackageJson.version}`
202
+
203
+ if (instance) {
204
+ versionOutput = `jsreport version: ${instance.version}\n${versionOutput}`
205
+ }
206
+
207
+ logger.info(versionOutput)
208
+ }
@@ -1,5 +1,3 @@
1
- 'use strict'
2
-
3
1
  const path = require('path')
4
2
  const fs = require('fs')
5
3
  const install = require('npm-install-package')
@@ -1,8 +1,6 @@
1
- 'use strict'
2
-
3
1
  const path = require('path')
4
2
  const fs = require('fs')
5
- const nanoid = require('nanoid')
3
+ const { nanoid } = require('nanoid')
6
4
  const inquirer = require('inquirer')
7
5
 
8
6
  const description = 'Generates a jsreport configuration file (*.config.json) based on some questions'
@@ -1,13 +1,12 @@
1
- 'use strict'
2
-
3
1
  const cliui = require('cliui')
4
2
  const chalk = require('chalk')
5
3
  const omit = require('lodash.omit')
6
4
 
7
5
  const description = 'Prints information about a command or topic'
8
6
  const command = 'help'
7
+ const positionalArgs = '[commandOrTopic]'
9
8
 
10
- exports.command = command
9
+ exports.command = `${command} ${positionalArgs}`
11
10
  exports.description = description
12
11
 
13
12
  function getExamples (command) {
@@ -55,7 +54,7 @@ exports.builder = (yargs) => {
55
54
  return true
56
55
  }
57
56
 
58
- if (!argv || !argv._[1]) {
57
+ if (!argv || !argv.commandOrTopic) {
59
58
  throw new Error('"commandOrTopic" argument is required')
60
59
  }
61
60
 
@@ -70,7 +69,7 @@ exports.handler = async (argv) => {
70
69
  const logger = context.logger
71
70
  const getInstance = context.getInstance
72
71
  const initInstance = context.initInstance
73
- let commandOrTopic = argv._[1]
72
+ let commandOrTopic = argv.commandOrTopic
74
73
  let helpResult
75
74
 
76
75
  const getCommandHelp = context.getCommandHelp
@@ -135,8 +134,8 @@ exports.handler = async (argv) => {
135
134
  }
136
135
  }
137
136
 
138
- if (helpResult) {
139
- helpResult = { output: helpResult.output }
137
+ if (helpResult != null && typeof helpResult === 'string') {
138
+ helpResult = { output: helpResult }
140
139
  }
141
140
  }
142
141
 
@@ -442,7 +441,7 @@ function printProperties (ui, props, {
442
441
  required: schema.items.required
443
442
  })
444
443
 
445
- ui.div({ text: '{', padding: getPadding(level + 1) })
444
+ ui.div({ text: '}', padding: getPadding(level + 1) })
446
445
  ui.div({ text: `]${!isLastKey ? ',' : ''}`, padding: content.padding })
447
446
  } else if (schema.type === 'array' && Array.isArray(schema.items)) {
448
447
  printProperties(ui, schema.items.map((s, idx) => {