1 | const cp = require('child_process')
|
2 | const EOL = require('os').EOL
|
3 |
|
4 | function writeStdIn(proc, data, encoding) {
|
5 |
|
6 | proc.stdin.write(data, encoding)
|
7 | proc.stdin.write(EOL, encoding)
|
8 | }
|
9 |
|
10 | function close(proc) {
|
11 | return new Promise((resolve) => {
|
12 | proc.on('close', resolve)
|
13 | writeStdIn(proc, '-stay_open')
|
14 | writeStdIn(proc, 'false')
|
15 | })
|
16 | }
|
17 |
|
18 | function isString(s) {
|
19 | return (typeof s).toLowerCase() === 'string'
|
20 | }
|
21 |
|
22 |
|
23 |
|
24 |
|
25 | function getArgs(args, noSplit) {
|
26 | if(!(Array.isArray(args) && args.length)) {
|
27 | return []
|
28 | }
|
29 | return args
|
30 | .filter(isString)
|
31 | .map(arg => `-${arg}`)
|
32 | .reduce((acc, arg) =>
|
33 | [].concat(acc, noSplit ? [arg] : arg.split(/\s+/))
|
34 | , [])
|
35 | }
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | function execute(proc, command, commandNumber, args, noSplitArgs, encoding) {
|
47 | const extendedArgs = getArgs(args)
|
48 | const extendedArgsNoSplit = getArgs(noSplitArgs, true)
|
49 |
|
50 | command = command !== undefined ? command : '';
|
51 |
|
52 | [].concat(
|
53 | extendedArgsNoSplit,
|
54 | extendedArgs,
|
55 | ['-json', '-s'],
|
56 | [
|
57 | command,
|
58 | '-echo1',
|
59 | `{begin${commandNumber}}`,
|
60 | '-echo2',
|
61 | `{begin${commandNumber}}`,
|
62 | '-echo4',
|
63 | `{ready${commandNumber}}`,
|
64 | `-execute${commandNumber}`,
|
65 | ]
|
66 | )
|
67 | .forEach(arg => writeStdIn(proc, arg, encoding))
|
68 | }
|
69 |
|
70 | function genCommandNumber() {
|
71 | return String(Math.floor(Math.random() * 1000000))
|
72 | }
|
73 |
|
74 | function executeCommand(proc, stdoutRws, stderrRws, command, args, noSplitArgs, encoding) {
|
75 | const commandNumber = genCommandNumber()
|
76 |
|
77 | if (proc === process) {
|
78 | execute(proc, command, commandNumber, args, noSplitArgs, encoding)
|
79 | return Promise.resolve({ data: 'debug', error: null })
|
80 | }
|
81 |
|
82 | const dataPromise = new Promise(resolve => {
|
83 | stdoutRws.addToResolveMap(commandNumber, resolve)
|
84 | })
|
85 | const errPromise = new Promise(resolve =>
|
86 | stderrRws.addToResolveMap(commandNumber, resolve)
|
87 | )
|
88 |
|
89 | execute(proc, command, commandNumber, args, noSplitArgs, encoding)
|
90 |
|
91 | return Promise.all([
|
92 | dataPromise,
|
93 | errPromise,
|
94 | ])
|
95 | .then(res => ({
|
96 | data: res[0] ? JSON.parse(res[0]) : null,
|
97 | error: res[1] || null,
|
98 | }))
|
99 | }
|
100 |
|
101 | function spawn(bin) {
|
102 | return new Promise((resolve, reject) => {
|
103 | const echoString = Date.now().toString()
|
104 | const process = cp.spawn(bin, ['-echo2', echoString, '-stay_open', 'True', '-@', '-'])
|
105 | process.once('error', reject)
|
106 | const echoHandler = (data) => {
|
107 | const d = data.toString().trim()
|
108 |
|
109 | if (d === echoString) {
|
110 | resolve(process)
|
111 | } else {
|
112 | reject(new Error(`Unexpected string on start: ${d}`))
|
113 | }
|
114 | }
|
115 | process.stderr.once('data', echoHandler)
|
116 | })
|
117 | }
|
118 |
|
119 | function checkDataObject(data) {
|
120 | return data === Object(data) && !Array.isArray(data)
|
121 | }
|
122 |
|
123 | function mapDataToTagArray(data, array) {
|
124 | const res = Array.isArray(array) ? array : []
|
125 | Object
|
126 | .keys(data)
|
127 | .forEach(tag => {
|
128 | const value = data[tag]
|
129 | if (Array.isArray(value)) {
|
130 | value.forEach((v) => {
|
131 | const arg = `${tag}=${v}`
|
132 | res.push(arg)
|
133 | })
|
134 | } else {
|
135 | res.push(`${tag}=${value}`)
|
136 | }
|
137 | })
|
138 | return res
|
139 | }
|
140 |
|
141 | module.exports = {
|
142 | spawn,
|
143 | close,
|
144 | executeCommand,
|
145 | checkDataObject,
|
146 | mapDataToTagArray,
|
147 | getArgs,
|
148 | execute,
|
149 | isString,
|
150 | }
|