@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.
- package/LICENSE +21 -21
- package/README.md +16 -12
- package/cli.js +93 -93
- package/example.config.json +43 -43
- package/example.server.js +14 -14
- package/index.js +20 -22
- package/jsreport.config.js +8 -8
- package/lib/cliExtension.js +59 -59
- package/lib/{createCommandParser.js → commander/createCommandParser.js} +44 -45
- package/lib/commander/createCustomCommandBuilder.js +150 -0
- package/lib/commander/createLogger.js +51 -0
- package/lib/commander/executeCommand.js +37 -0
- package/lib/commander/getCommandEventName.js +4 -0
- package/lib/commander/index.js +279 -0
- package/lib/commander/jsreportInstance.js +98 -0
- package/lib/commander/registerCommand.js +91 -0
- package/lib/commander/startCommand.js +234 -0
- package/lib/commander/startProcessing.js +208 -0
- package/lib/commands/_initializeApp.js +206 -208
- package/lib/commands/configure.js +393 -395
- package/lib/commands/help.js +453 -454
- package/lib/commands/init.js +76 -77
- package/lib/commands/kill.js +85 -86
- package/lib/commands/render.js +490 -488
- package/lib/commands/repair.js +55 -56
- package/lib/commands/start.js +72 -74
- package/lib/commands/win-install.js +124 -126
- package/lib/commands/win-uninstall.js +100 -102
- package/lib/daemonHandler.js +164 -164
- package/lib/daemonInstance.js +225 -225
- package/lib/{registerExtensionsCommands.js → detectAndRegisterExtensionsCommands.js} +74 -74
- package/lib/instanceHandler.js +316 -316
- package/lib/keepAliveProcess.js +205 -205
- package/lib/{errorUtils.js → utils/error.js} +149 -127
- package/lib/{getTempPaths.js → utils/getTempPaths.js} +39 -39
- package/lib/{normalizePathOptionOrArg.js → utils/normalizePathOptionOrArg.js} +41 -41
- package/lib/{normalizeSocketPath.js → utils/normalizeSocketPath.js} +9 -9
- package/lib/{startSocketServer.js → utils/startSocketServer.js} +53 -53
- package/package.json +105 -106
- package/test/testUtils.js +197 -198
- package/lib/commander.js +0 -1108
package/lib/keepAliveProcess.js
CHANGED
|
@@ -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('./
|
|
10
|
-
const getTempPaths = require('./getTempPaths')
|
|
11
|
-
const
|
|
12
|
-
const
|
|
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
|
+
}
|