@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
@@ -1,205 +1,205 @@
1
- 'use strict'
2
-
3
- const util = require('util')
4
- const path = require('path')
5
- const fs = require('fs')
6
- const spawn = require('silent-spawn')
7
- const lockfile = require('lockfile')
8
- const omit = require('lodash.omit')
9
- const { addStack } = require('./errorUtils')
10
- const getTempPaths = require('./getTempPaths')
11
- const daemonHandler = require('./daemonHandler')
12
- const startSocketServer = require('./startSocketServer')
13
-
14
- const lock = util.promisify(lockfile.lock.bind(lockfile))
15
- const unlock = util.promisify(lockfile.unlock.bind(lockfile))
16
-
17
- const IS_WINDOWS = process.platform === 'win32'
18
-
19
- module.exports = async function keepAliveProcess (options) {
20
- const daemonExec = options.daemonExec || {}
21
- const mainSockPath = options.mainSockPath
22
- const workerSockPath = options.workerSockPath
23
- const cwd = options.cwd
24
- const verbose = options.verbose
25
- const daemonInstancePath = daemonExec.scriptPath || path.join(__dirname, './daemonInstance.js')
26
- const lockfilePath = path.join(getTempPaths().cliPath, 'jsreport-keepAlive-start.lock')
27
-
28
- // we acquire a lock first to ensure that calls across multiple process executes just once at
29
- // a time and waits for the others to finish first
30
- await lock(lockfilePath, {
31
- stale: 16000,
32
- retries: 400,
33
- retryWait: 50
34
- })
35
-
36
- try {
37
- // check if the process was already created after the lock was released
38
- let processInfo = await daemonHandler.findProcessByCWD(workerSockPath, cwd)
39
-
40
- if (processInfo) {
41
- // if yes then just return the processInfo
42
- return processInfo
43
- }
44
-
45
- // activate env var JSREPORT_CLI_DAEMON_STD_FILES when you want to debug messages
46
- // in the child process
47
- const processStdToFiles = process.env.JSREPORT_CLI_DAEMON_STD_FILES === 'enabled'
48
- let targetStdio = 'ignore'
49
-
50
- if (processStdToFiles) {
51
- const stdStreams = await createStdFiles()
52
- targetStdio = ['ignore', stdStreams[0], stdStreams[1]]
53
- }
54
-
55
- processInfo = await new Promise((resolve, reject) => {
56
- let initOptions
57
- let child
58
- const daemonProcess = {}
59
-
60
- // we are starting a temporary server for communication with the child
61
- // that it's gonna be spawned below, we can't use an IPC channel between
62
- // the processes because we are using "silent-spawn" package
63
- // that uses a little hack to be able to have a detached process without
64
- // creating a new console in windows, an because of that the built-in IPC
65
- // will not work.
66
- const socketServer = startSocketServer({
67
- socketPath: mainSockPath,
68
- socketPrefix: 'c',
69
- protocol: daemonInstanceProtocol
70
- }, (err, serverInfo) => {
71
- if (err) {
72
- const error = new Error('Error while trying to start socket server')
73
- error.originalError = err
74
- return reject(error)
75
- }
76
-
77
- initOptions = {
78
- sockPath: workerSockPath,
79
- cwd: cwd,
80
- verbose: verbose
81
- }
82
-
83
- const socketFile = serverInfo.normalizedSocketFile
84
-
85
- if (daemonExec.path) {
86
- daemonProcess.path = daemonExec.path
87
- } else {
88
- daemonProcess.path = process.execPath
89
- }
90
-
91
- daemonProcess.args = [
92
- daemonInstancePath,
93
- socketFile
94
- ]
95
-
96
- if (Array.isArray(daemonExec.args)) {
97
- daemonProcess.args = daemonProcess.args.concat(daemonExec.args)
98
- } else if (typeof daemonExec.args === 'function') {
99
- const customArgs = daemonExec.args(daemonProcess.args)
100
-
101
- if (Array.isArray(customArgs)) {
102
- daemonProcess.args = customArgs
103
- }
104
- }
105
-
106
- // spacing arguments in windows
107
- if (IS_WINDOWS) {
108
- daemonProcess.args = daemonProcess.args.map((arg) => {
109
- return '"' + arg + '"'
110
- })
111
- }
112
-
113
- daemonProcess.opts = {
114
- stdio: targetStdio,
115
- detached: true,
116
- cwd: cwd
117
- }
118
-
119
- daemonProcess.opts.env = Object.assign({}, process.env, { JSREPORT_CLI: true, JSREPORT_CLI_DAEMON_PROCESS: true })
120
-
121
- if (daemonExec.opts) {
122
- daemonProcess.opts = Object.assign(daemonProcess.opts, daemonExec.opts)
123
- daemonProcess.opts.env = Object.assign(daemonProcess.opts.env, daemonProcess.opts.env)
124
- }
125
-
126
- // start a daemonized process
127
- child = spawn(
128
- daemonProcess.path,
129
- daemonProcess.args,
130
- daemonProcess.opts
131
- )
132
-
133
- child.on('exit', (code) => {
134
- reject(new Error('Child process died to soon with exit code ' + code))
135
- })
136
- })
137
-
138
- function daemonInstanceProtocol (socket) {
139
- socket.on('error', () => {
140
- socket.destroy()
141
- socketServer.close()
142
- })
143
-
144
- socket.dataOnce(['alive'], () => {
145
- socket.send(['init'], initOptions, (err) => {
146
- if (err) {
147
- return reject(err)
148
- }
149
- })
150
- })
151
-
152
- socket.dataOnce(['init'], (result) => {
153
- socket.end()
154
- socketServer.close()
155
-
156
- if (result.error) {
157
- const error = new Error(result.error)
158
-
159
- if (result.meta != null) {
160
- Object.assign(error, omit(result.meta, ['message', 'stack']))
161
- }
162
-
163
- if (Array.isArray(result.stacks)) {
164
- result.stacks.forEach((stk) => {
165
- addStack(error, stk, {
166
- stackPrefix: 'Remote Instance Error: ',
167
- stripMessage: true
168
- })
169
- })
170
- }
171
-
172
- error.message = `An error occurred while trying to start daemonized process: ${error.message}`
173
-
174
- reject(error)
175
- } else {
176
- // remove error prop from result
177
- delete result.error
178
-
179
- resolve(resolve(Object.assign({}, result, {
180
- // result.pid and child.pid can be different on windows, because the detached mode
181
- pid: result.pid,
182
- proc: child
183
- })))
184
- }
185
- })
186
- }
187
- })
188
-
189
- return processInfo
190
- } finally {
191
- await unlock(lockfilePath)
192
- }
193
- }
194
-
195
- async function createStdFiles () {
196
- return new Promise((resolve) => {
197
- const fileStream = fs.createWriteStream('jsreport-daemon-log.txt')
198
-
199
- function ready () {
200
- resolve([fileStream, fileStream])
201
- }
202
-
203
- fileStream.on('open', ready)
204
- })
205
- }
1
+ 'use strict'
2
+
3
+ const util = require('util')
4
+ const path = require('path')
5
+ const fs = require('fs')
6
+ const spawn = require('silent-spawn')
7
+ const lockfile = require('lockfile')
8
+ const omit = require('lodash.omit')
9
+ const { addStack } = require('./utils/error')
10
+ const getTempPaths = require('./utils/getTempPaths')
11
+ const startSocketServer = require('./utils/startSocketServer')
12
+ const daemonHandler = require('./daemonHandler')
13
+
14
+ const lock = util.promisify(lockfile.lock.bind(lockfile))
15
+ const unlock = util.promisify(lockfile.unlock.bind(lockfile))
16
+
17
+ const IS_WINDOWS = process.platform === 'win32'
18
+
19
+ module.exports = async function keepAliveProcess (options) {
20
+ const daemonExec = options.daemonExec || {}
21
+ const mainSockPath = options.mainSockPath
22
+ const workerSockPath = options.workerSockPath
23
+ const cwd = options.cwd
24
+ const verbose = options.verbose
25
+ const daemonInstancePath = daemonExec.scriptPath || path.join(__dirname, './daemonInstance.js')
26
+ const lockfilePath = path.join(getTempPaths().cliPath, 'jsreport-keepAlive-start.lock')
27
+
28
+ // we acquire a lock first to ensure that calls across multiple process executes just once at
29
+ // a time and waits for the others to finish first
30
+ await lock(lockfilePath, {
31
+ stale: 16000,
32
+ retries: 400,
33
+ retryWait: 50
34
+ })
35
+
36
+ try {
37
+ // check if the process was already created after the lock was released
38
+ let processInfo = await daemonHandler.findProcessByCWD(workerSockPath, cwd)
39
+
40
+ if (processInfo) {
41
+ // if yes then just return the processInfo
42
+ return processInfo
43
+ }
44
+
45
+ // activate env var JSREPORT_CLI_DAEMON_STD_FILES when you want to debug messages
46
+ // in the child process
47
+ const processStdToFiles = process.env.JSREPORT_CLI_DAEMON_STD_FILES === 'enabled'
48
+ let targetStdio = 'ignore'
49
+
50
+ if (processStdToFiles) {
51
+ const stdStreams = await createStdFiles()
52
+ targetStdio = ['ignore', stdStreams[0], stdStreams[1]]
53
+ }
54
+
55
+ processInfo = await new Promise((resolve, reject) => {
56
+ let initOptions
57
+ let child
58
+ const daemonProcess = {}
59
+
60
+ // we are starting a temporary server for communication with the child
61
+ // that it's gonna be spawned below, we can't use an IPC channel between
62
+ // the processes because we are using "silent-spawn" package
63
+ // that uses a little hack to be able to have a detached process without
64
+ // creating a new console in windows, an because of that the built-in IPC
65
+ // will not work.
66
+ const socketServer = startSocketServer({
67
+ socketPath: mainSockPath,
68
+ socketPrefix: 'c',
69
+ protocol: daemonInstanceProtocol
70
+ }, (err, serverInfo) => {
71
+ if (err) {
72
+ const error = new Error('Error while trying to start socket server')
73
+ error.originalError = err
74
+ return reject(error)
75
+ }
76
+
77
+ initOptions = {
78
+ sockPath: workerSockPath,
79
+ cwd: cwd,
80
+ verbose: verbose
81
+ }
82
+
83
+ const socketFile = serverInfo.normalizedSocketFile
84
+
85
+ if (daemonExec.path) {
86
+ daemonProcess.path = daemonExec.path
87
+ } else {
88
+ daemonProcess.path = process.execPath
89
+ }
90
+
91
+ daemonProcess.args = [
92
+ daemonInstancePath,
93
+ socketFile
94
+ ]
95
+
96
+ if (Array.isArray(daemonExec.args)) {
97
+ daemonProcess.args = daemonProcess.args.concat(daemonExec.args)
98
+ } else if (typeof daemonExec.args === 'function') {
99
+ const customArgs = daemonExec.args(daemonProcess.args)
100
+
101
+ if (Array.isArray(customArgs)) {
102
+ daemonProcess.args = customArgs
103
+ }
104
+ }
105
+
106
+ // spacing arguments in windows
107
+ if (IS_WINDOWS) {
108
+ daemonProcess.args = daemonProcess.args.map((arg) => {
109
+ return '"' + arg + '"'
110
+ })
111
+ }
112
+
113
+ daemonProcess.opts = {
114
+ stdio: targetStdio,
115
+ detached: true,
116
+ cwd: cwd
117
+ }
118
+
119
+ daemonProcess.opts.env = Object.assign({}, process.env, { JSREPORT_CLI: true, JSREPORT_CLI_DAEMON_PROCESS: true })
120
+
121
+ if (daemonExec.opts) {
122
+ daemonProcess.opts = Object.assign(daemonProcess.opts, daemonExec.opts)
123
+ daemonProcess.opts.env = Object.assign(daemonProcess.opts.env, daemonProcess.opts.env)
124
+ }
125
+
126
+ // start a daemonized process
127
+ child = spawn(
128
+ daemonProcess.path,
129
+ daemonProcess.args,
130
+ daemonProcess.opts
131
+ )
132
+
133
+ child.on('exit', (code) => {
134
+ reject(new Error('Child process died to soon with exit code ' + code))
135
+ })
136
+ })
137
+
138
+ function daemonInstanceProtocol (socket) {
139
+ socket.on('error', () => {
140
+ socket.destroy()
141
+ socketServer.close()
142
+ })
143
+
144
+ socket.dataOnce(['alive'], () => {
145
+ socket.send(['init'], initOptions, (err) => {
146
+ if (err) {
147
+ return reject(err)
148
+ }
149
+ })
150
+ })
151
+
152
+ socket.dataOnce(['init'], (result) => {
153
+ socket.end()
154
+ socketServer.close()
155
+
156
+ if (result.error) {
157
+ const error = new Error(result.error)
158
+
159
+ if (result.meta != null) {
160
+ Object.assign(error, omit(result.meta, ['message', 'stack']))
161
+ }
162
+
163
+ if (Array.isArray(result.stacks)) {
164
+ result.stacks.forEach((stk) => {
165
+ addStack(error, stk, {
166
+ stackPrefix: 'Remote Instance Error: ',
167
+ stripMessage: true
168
+ })
169
+ })
170
+ }
171
+
172
+ error.message = `An error occurred while trying to start daemonized process: ${error.message}`
173
+
174
+ reject(error)
175
+ } else {
176
+ // remove error prop from result
177
+ delete result.error
178
+
179
+ resolve(resolve(Object.assign({}, result, {
180
+ // result.pid and child.pid can be different on windows, because the detached mode
181
+ pid: result.pid,
182
+ proc: child
183
+ })))
184
+ }
185
+ })
186
+ }
187
+ })
188
+
189
+ return processInfo
190
+ } finally {
191
+ await unlock(lockfilePath)
192
+ }
193
+ }
194
+
195
+ async function createStdFiles () {
196
+ return new Promise((resolve) => {
197
+ const fileStream = fs.createWriteStream('jsreport-daemon-log.txt')
198
+
199
+ function ready () {
200
+ resolve([fileStream, fileStream])
201
+ }
202
+
203
+ fileStream.on('open', ready)
204
+ })
205
+ }