@jsreport/jsreport-cli 3.0.0-beta.2 → 3.1.1

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 (41) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +16 -12
  3. package/cli.js +93 -93
  4. package/example.config.json +43 -43
  5. package/example.server.js +14 -14
  6. package/index.js +20 -22
  7. package/jsreport.config.js +8 -8
  8. package/lib/cliExtension.js +59 -59
  9. package/lib/{createCommandParser.js → commander/createCommandParser.js} +44 -45
  10. package/lib/commander/createCustomCommandBuilder.js +150 -0
  11. package/lib/commander/createLogger.js +51 -0
  12. package/lib/commander/executeCommand.js +37 -0
  13. package/lib/commander/getCommandEventName.js +4 -0
  14. package/lib/commander/index.js +279 -0
  15. package/lib/commander/jsreportInstance.js +98 -0
  16. package/lib/commander/registerCommand.js +91 -0
  17. package/lib/commander/startCommand.js +234 -0
  18. package/lib/commander/startProcessing.js +208 -0
  19. package/lib/commands/_initializeApp.js +206 -208
  20. package/lib/commands/configure.js +393 -395
  21. package/lib/commands/help.js +453 -454
  22. package/lib/commands/init.js +76 -77
  23. package/lib/commands/kill.js +85 -86
  24. package/lib/commands/render.js +490 -488
  25. package/lib/commands/repair.js +55 -56
  26. package/lib/commands/start.js +72 -74
  27. package/lib/commands/win-install.js +124 -126
  28. package/lib/commands/win-uninstall.js +100 -102
  29. package/lib/daemonHandler.js +164 -164
  30. package/lib/daemonInstance.js +225 -225
  31. package/lib/{registerExtensionsCommands.js → detectAndRegisterExtensionsCommands.js} +74 -74
  32. package/lib/instanceHandler.js +316 -316
  33. package/lib/keepAliveProcess.js +205 -205
  34. package/lib/{errorUtils.js → utils/error.js} +149 -127
  35. package/lib/{getTempPaths.js → utils/getTempPaths.js} +39 -39
  36. package/lib/{normalizePathOptionOrArg.js → utils/normalizePathOptionOrArg.js} +41 -41
  37. package/lib/{normalizeSocketPath.js → utils/normalizeSocketPath.js} +9 -9
  38. package/lib/{startSocketServer.js → utils/startSocketServer.js} +53 -53
  39. package/package.json +105 -106
  40. package/test/testUtils.js +197 -198
  41. package/lib/commander.js +0 -1108
@@ -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-tmp')
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
+ }