spawn-rx 3.0.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.prettierrc +4 -0
- package/eslint.config.mjs +88 -0
- package/lib/src/index.d.ts +1 -1
- package/lib/src/index.js +82 -52
- package/lib/src/index.js.map +1 -1
- package/package.json +26 -20
- package/src/ambient.d.ts +1 -1
- package/src/index.ts +200 -129
- package/test/asserttest.ts +5 -4
- package/test/spawn.ts +86 -49
- package/test/support.ts +4 -3
- package/tsconfig.json +2 -6
- package/.travis.yml +0 -24
- package/appveyor.yml +0 -21
- package/tslint.json +0 -40
package/src/index.ts
CHANGED
@@ -1,16 +1,18 @@
|
|
1
|
-
|
2
|
-
import * as
|
3
|
-
import * as
|
4
|
-
import * as
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2
|
+
import * as path from "path";
|
3
|
+
import * as net from "net";
|
4
|
+
import * as sfs from "fs";
|
5
|
+
import * as assign from "lodash.assign";
|
5
6
|
|
6
|
-
import {
|
7
|
-
import {
|
8
|
-
import
|
7
|
+
import type { Observer, Subject } from "rxjs";
|
8
|
+
import { Observable, Subscription, AsyncSubject, of, merge } from "rxjs";
|
9
|
+
import { map, reduce } from "rxjs/operators";
|
10
|
+
import { spawn as spawnOg } from "child_process";
|
11
|
+
import Debug from "debug";
|
9
12
|
|
10
|
-
const
|
11
|
-
const isWindows = process.platform === 'win32';
|
13
|
+
const isWindows = process.platform === "win32";
|
12
14
|
|
13
|
-
const d =
|
15
|
+
const d = Debug("spawn-rx"); // tslint:disable-line:no-var-requires
|
14
16
|
|
15
17
|
/**
|
16
18
|
* stat a file but don't throw if it doesn't exist
|
@@ -23,7 +25,7 @@ const d = require('debug')('spawn-rx'); //tslint:disable-line:no-var-requires
|
|
23
25
|
function statSyncNoException(file: string): sfs.Stats | null {
|
24
26
|
try {
|
25
27
|
return sfs.statSync(file);
|
26
|
-
} catch
|
28
|
+
} catch {
|
27
29
|
return null;
|
28
30
|
}
|
29
31
|
}
|
@@ -42,26 +44,26 @@ function runDownPath(exe: string): string {
|
|
42
44
|
// Posix does
|
43
45
|
|
44
46
|
// Files with any directory path don't get this applied
|
45
|
-
if (exe.match(/[
|
46
|
-
d(
|
47
|
+
if (exe.match(/[\\/]/)) {
|
48
|
+
d("Path has slash in directory, bailing");
|
47
49
|
return exe;
|
48
50
|
}
|
49
51
|
|
50
|
-
|
52
|
+
const target = path.join(".", exe);
|
51
53
|
if (statSyncNoException(target)) {
|
52
54
|
d(`Found executable in currect directory: ${target}`);
|
53
55
|
return target;
|
54
56
|
}
|
55
57
|
|
56
|
-
|
57
|
-
for (
|
58
|
-
|
58
|
+
const haystack = process.env.PATH!.split(isWindows ? ";" : ":");
|
59
|
+
for (const p of haystack) {
|
60
|
+
const needle = path.join(p, exe);
|
59
61
|
if (statSyncNoException(needle)) {
|
60
62
|
return needle;
|
61
63
|
}
|
62
64
|
}
|
63
65
|
|
64
|
-
d(
|
66
|
+
d("Failed to find executable anywhere in path");
|
65
67
|
return exe;
|
66
68
|
}
|
67
69
|
|
@@ -81,12 +83,15 @@ function runDownPath(exe: string): string {
|
|
81
83
|
* @property {string} cmd The command to pass to spawn
|
82
84
|
* @property {Array<string>} args The arguments to pass to spawn
|
83
85
|
*/
|
84
|
-
export function findActualExecutable(
|
86
|
+
export function findActualExecutable(
|
87
|
+
exe: string,
|
88
|
+
args: Array<string>,
|
89
|
+
): {
|
85
90
|
cmd: string;
|
86
|
-
args: Array<string
|
91
|
+
args: Array<string>;
|
87
92
|
} {
|
88
93
|
// POSIX can just execute scripts directly, no need for silly goosery
|
89
|
-
if (process.platform !==
|
94
|
+
if (process.platform !== "win32") {
|
90
95
|
return { cmd: runDownPath(exe), args: args };
|
91
96
|
}
|
92
97
|
|
@@ -94,9 +99,9 @@ export function findActualExecutable(exe: string, args: Array<string>): {
|
|
94
99
|
// NB: When you write something like `surf-client ... -- surf-build` on Windows,
|
95
100
|
// a shell would normally convert that to surf-build.cmd, but since it's passed
|
96
101
|
// in as an argument, it doesn't happen
|
97
|
-
const possibleExts = [
|
98
|
-
for (
|
99
|
-
|
102
|
+
const possibleExts = [".exe", ".bat", ".cmd", ".ps1"];
|
103
|
+
for (const ext of possibleExts) {
|
104
|
+
const possibleFullPath = runDownPath(`${exe}${ext}`);
|
100
105
|
|
101
106
|
if (sfs.existsSync(possibleFullPath)) {
|
102
107
|
return findActualExecutable(possibleFullPath, args);
|
@@ -105,22 +110,35 @@ export function findActualExecutable(exe: string, args: Array<string>): {
|
|
105
110
|
}
|
106
111
|
|
107
112
|
if (exe.match(/\.ps1$/i)) {
|
108
|
-
|
109
|
-
|
113
|
+
const cmd = path.join(
|
114
|
+
process.env.SYSTEMROOT!,
|
115
|
+
"System32",
|
116
|
+
"WindowsPowerShell",
|
117
|
+
"v1.0",
|
118
|
+
"PowerShell.exe",
|
119
|
+
);
|
120
|
+
const psargs = [
|
121
|
+
"-ExecutionPolicy",
|
122
|
+
"Unrestricted",
|
123
|
+
"-NoLogo",
|
124
|
+
"-NonInteractive",
|
125
|
+
"-File",
|
126
|
+
exe,
|
127
|
+
];
|
110
128
|
|
111
129
|
return { cmd: cmd, args: psargs.concat(args) };
|
112
130
|
}
|
113
131
|
|
114
132
|
if (exe.match(/\.(bat|cmd)$/i)) {
|
115
|
-
|
116
|
-
|
133
|
+
const cmd = path.join(process.env.SYSTEMROOT!, "System32", "cmd.exe");
|
134
|
+
const cmdArgs = ["/C", exe, ...args];
|
117
135
|
|
118
136
|
return { cmd: cmd, args: cmdArgs };
|
119
137
|
}
|
120
138
|
|
121
139
|
if (exe.match(/\.(js)$/i)) {
|
122
|
-
|
123
|
-
|
140
|
+
const cmd = process.execPath;
|
141
|
+
const nodeArgs = [exe];
|
124
142
|
|
125
143
|
return { cmd: cmd, args: nodeArgs.concat(args) };
|
126
144
|
}
|
@@ -146,7 +164,11 @@ export function findActualExecutable(exe: string, args: Array<string>): {
|
|
146
164
|
* process terminates with a non-zero value,
|
147
165
|
* the Observable will terminate with onError.
|
148
166
|
*/
|
149
|
-
export function spawnDetached(
|
167
|
+
export function spawnDetached(
|
168
|
+
exe: string,
|
169
|
+
params: Array<string>,
|
170
|
+
opts: any = null,
|
171
|
+
): Observable<string> {
|
150
172
|
const { cmd, args } = findActualExecutable(exe, params);
|
151
173
|
|
152
174
|
if (!isWindows) {
|
@@ -155,8 +177,15 @@ export function spawnDetached(exe: string, params: Array<string>, opts: any = nu
|
|
155
177
|
|
156
178
|
const newParams = [cmd].concat(args);
|
157
179
|
|
158
|
-
|
159
|
-
|
180
|
+
const target = path.join(
|
181
|
+
__dirname,
|
182
|
+
"..",
|
183
|
+
"..",
|
184
|
+
"vendor",
|
185
|
+
"jobber",
|
186
|
+
"Jobber.exe",
|
187
|
+
);
|
188
|
+
const options = assign({}, opts || {}, { detached: true, jobber: true });
|
160
189
|
|
161
190
|
d(`spawnDetached: ${target}, ${newParams}`);
|
162
191
|
return spawn(target, newParams, options);
|
@@ -178,123 +207,157 @@ export function spawnDetached(exe: string, params: Array<string>, opts: any = nu
|
|
178
207
|
* the Observable will terminate with onError.
|
179
208
|
*/
|
180
209
|
|
181
|
-
export function spawn<T = string>(
|
210
|
+
export function spawn<T = string>(
|
211
|
+
exe: string,
|
212
|
+
params: Array<string> = [],
|
213
|
+
opts: any = null,
|
214
|
+
): Observable<T> {
|
182
215
|
opts = opts || {};
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
if (b.length < 1) {
|
202
|
-
return;
|
216
|
+
const spawnObs = Observable.create(
|
217
|
+
(
|
218
|
+
subj: Observer<{
|
219
|
+
source: any;
|
220
|
+
text: any;
|
221
|
+
}>,
|
222
|
+
) => {
|
223
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
224
|
+
const { _, ...optsWithoutStdIn } = opts;
|
225
|
+
const { cmd, args } = findActualExecutable(exe, params);
|
226
|
+
d(
|
227
|
+
`spawning process: ${cmd} ${args.join()}, ${JSON.stringify(
|
228
|
+
optsWithoutStdIn,
|
229
|
+
)}`,
|
230
|
+
);
|
231
|
+
const origOpts = assign({}, optsWithoutStdIn);
|
232
|
+
if ("jobber" in origOpts) {
|
233
|
+
delete origOpts.jobber;
|
203
234
|
}
|
204
|
-
|
205
|
-
|
206
|
-
if (typeof b === 'string') {
|
207
|
-
chunk = b.toString();
|
208
|
-
} else {
|
209
|
-
chunk = b.toString(origOpts.encoding || 'utf8');
|
210
|
-
}
|
211
|
-
} catch (e) {
|
212
|
-
chunk = `<< Lost chunk of process output for ${exe} - length was ${b.length}>>`;
|
235
|
+
if ("split" in origOpts) {
|
236
|
+
delete origOpts.split;
|
213
237
|
}
|
214
238
|
|
215
|
-
|
216
|
-
};
|
217
|
-
|
218
|
-
let ret = new Subscription();
|
219
|
-
|
220
|
-
if (opts.stdin) {
|
221
|
-
if (proc.stdin) {
|
222
|
-
ret.add(opts.stdin.subscribe(
|
223
|
-
(x: any) => proc.stdin.write(x),
|
224
|
-
subj.error.bind(subj),
|
225
|
-
() => proc.stdin.end()
|
226
|
-
));
|
227
|
-
} else {
|
228
|
-
subj.error(new Error(`opts.stdio conflicts with provided spawn opts.stdin observable, 'pipe' is required`));
|
229
|
-
}
|
230
|
-
}
|
231
|
-
|
232
|
-
let stderrCompleted: Subject<boolean> | Observable<boolean> | null = null;
|
233
|
-
let stdoutCompleted: Subject<boolean> | Observable<boolean> | null = null;
|
234
|
-
let noClose = false;
|
235
|
-
|
236
|
-
if (proc.stdout) {
|
237
|
-
stdoutCompleted = new AsyncSubject<boolean>();
|
238
|
-
proc.stdout.on('data', bufHandler('stdout'));
|
239
|
-
proc.stdout.on('close', () => { (stdoutCompleted! as Subject<boolean>).next(true); (stdoutCompleted! as Subject<boolean>).complete(); });
|
240
|
-
} else {
|
241
|
-
stdoutCompleted = of(true);
|
242
|
-
}
|
239
|
+
const proc = spawnOg(cmd, args, origOpts);
|
243
240
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
241
|
+
const bufHandler = (source: string) => (b: string | Buffer) => {
|
242
|
+
if (b.length < 1) {
|
243
|
+
return;
|
244
|
+
}
|
245
|
+
let chunk = "<< String sent back was too long >>";
|
246
|
+
try {
|
247
|
+
if (typeof b === "string") {
|
248
|
+
chunk = b.toString();
|
249
|
+
} else {
|
250
|
+
chunk = b.toString(origOpts.encoding || "utf8");
|
251
|
+
}
|
252
|
+
} catch {
|
253
|
+
chunk = `<< Lost chunk of process output for ${exe} - length was ${b.length}>>`;
|
254
|
+
}
|
251
255
|
|
252
|
-
|
253
|
-
|
254
|
-
subj.error(e);
|
255
|
-
});
|
256
|
+
subj.next({ source: source, text: chunk });
|
257
|
+
};
|
256
258
|
|
257
|
-
|
258
|
-
noClose = true;
|
259
|
-
let pipesClosed = merge(stdoutCompleted!, stderrCompleted!)
|
260
|
-
.pipe(reduce((acc) => acc, true));
|
259
|
+
const ret = new Subscription();
|
261
260
|
|
262
|
-
if (
|
263
|
-
|
264
|
-
|
265
|
-
|
261
|
+
if (opts.stdin) {
|
262
|
+
if (proc.stdin) {
|
263
|
+
ret.add(
|
264
|
+
opts.stdin.subscribe(
|
265
|
+
(x: any) => proc.stdin.write(x),
|
266
|
+
subj.error.bind(subj),
|
267
|
+
() => proc.stdin.end(),
|
268
|
+
),
|
269
|
+
);
|
270
|
+
} else {
|
271
|
+
subj.error(
|
272
|
+
new Error(
|
273
|
+
`opts.stdio conflicts with provided spawn opts.stdin observable, 'pipe' is required`,
|
274
|
+
),
|
275
|
+
);
|
276
|
+
}
|
266
277
|
}
|
267
|
-
});
|
268
278
|
|
269
|
-
|
270
|
-
|
271
|
-
|
279
|
+
let stderrCompleted: Subject<boolean> | Observable<boolean> | null = null;
|
280
|
+
let stdoutCompleted: Subject<boolean> | Observable<boolean> | null = null;
|
281
|
+
let noClose = false;
|
282
|
+
|
283
|
+
if (proc.stdout) {
|
284
|
+
stdoutCompleted = new AsyncSubject<boolean>();
|
285
|
+
proc.stdout.on("data", bufHandler("stdout"));
|
286
|
+
proc.stdout.on("close", () => {
|
287
|
+
(stdoutCompleted! as Subject<boolean>).next(true);
|
288
|
+
(stdoutCompleted! as Subject<boolean>).complete();
|
289
|
+
});
|
290
|
+
} else {
|
291
|
+
stdoutCompleted = of(true);
|
272
292
|
}
|
273
293
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
294
|
+
if (proc.stderr) {
|
295
|
+
stderrCompleted = new AsyncSubject<boolean>();
|
296
|
+
proc.stderr.on("data", bufHandler("stderr"));
|
297
|
+
proc.stderr.on("close", () => {
|
298
|
+
(stderrCompleted! as Subject<boolean>).next(true);
|
299
|
+
(stderrCompleted! as Subject<boolean>).complete();
|
300
|
+
});
|
279
301
|
} else {
|
280
|
-
|
302
|
+
stderrCompleted = of(true);
|
281
303
|
}
|
282
|
-
}));
|
283
304
|
|
284
|
-
|
285
|
-
|
305
|
+
proc.on("error", (e: Error) => {
|
306
|
+
noClose = true;
|
307
|
+
subj.error(e);
|
308
|
+
});
|
309
|
+
|
310
|
+
proc.on("close", (code: number) => {
|
311
|
+
noClose = true;
|
312
|
+
const pipesClosed = merge(stdoutCompleted!, stderrCompleted!).pipe(
|
313
|
+
reduce((acc) => acc, true),
|
314
|
+
);
|
286
315
|
|
287
|
-
|
316
|
+
if (code === 0) {
|
317
|
+
pipesClosed.subscribe(() => subj.complete());
|
318
|
+
} else {
|
319
|
+
pipesClosed.subscribe(() => {
|
320
|
+
const e: any = new Error(`Failed with exit code: ${code}`);
|
321
|
+
e.exitCode = code;
|
322
|
+
|
323
|
+
subj.error(e);
|
324
|
+
});
|
325
|
+
}
|
326
|
+
});
|
327
|
+
|
328
|
+
ret.add(
|
329
|
+
new Subscription(() => {
|
330
|
+
if (noClose) {
|
331
|
+
return;
|
332
|
+
}
|
333
|
+
|
334
|
+
d(`Killing process: ${cmd} ${args.join()}`);
|
335
|
+
if (opts.jobber) {
|
336
|
+
// NB: Connecting to Jobber's named pipe will kill it
|
337
|
+
net.connect(`\\\\.\\pipe\\jobber-${proc.pid}`);
|
338
|
+
setTimeout(() => proc.kill(), 5 * 1000);
|
339
|
+
} else {
|
340
|
+
proc.kill();
|
341
|
+
}
|
342
|
+
}),
|
343
|
+
);
|
344
|
+
|
345
|
+
return ret;
|
346
|
+
},
|
347
|
+
);
|
348
|
+
|
349
|
+
return opts.split ? spawnObs : spawnObs.pipe(map((x: any) => x?.text));
|
288
350
|
}
|
289
351
|
|
290
352
|
function wrapObservableInPromise<T>(obs: Observable<T>) {
|
291
353
|
return new Promise<string>((res, rej) => {
|
292
|
-
let out =
|
354
|
+
let out = "";
|
293
355
|
|
294
356
|
obs.subscribe(
|
295
|
-
(x) => out += x,
|
357
|
+
(x) => (out += x),
|
296
358
|
(e) => rej(new Error(`${out}\n${e.message}`)),
|
297
|
-
() => res(out)
|
359
|
+
() => res(out),
|
360
|
+
);
|
298
361
|
});
|
299
362
|
}
|
300
363
|
|
@@ -312,7 +375,11 @@ function wrapObservableInPromise<T>(obs: Observable<T>) {
|
|
312
375
|
* non-zero value, the Promise will resolve with
|
313
376
|
* an Error.
|
314
377
|
*/
|
315
|
-
export function spawnDetachedPromise(
|
378
|
+
export function spawnDetachedPromise(
|
379
|
+
exe: string,
|
380
|
+
params: Array<string>,
|
381
|
+
opts: any = null,
|
382
|
+
): Promise<string> {
|
316
383
|
return wrapObservableInPromise<string>(spawnDetached(exe, params, opts));
|
317
384
|
}
|
318
385
|
|
@@ -329,6 +396,10 @@ export function spawnDetachedPromise(exe: string, params: Array<string>, opts: a
|
|
329
396
|
* non-zero value, the Promise will resolve with
|
330
397
|
* an Error.
|
331
398
|
*/
|
332
|
-
export function spawnPromise(
|
399
|
+
export function spawnPromise(
|
400
|
+
exe: string,
|
401
|
+
params: Array<string>,
|
402
|
+
opts: any = null,
|
403
|
+
): Promise<string> {
|
333
404
|
return wrapObservableInPromise<string>(spawn(exe, params, opts));
|
334
405
|
}
|
package/test/asserttest.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
|
2
|
-
import
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
2
|
+
import { expect } from "chai";
|
3
|
+
import "./support";
|
3
4
|
|
4
5
|
function delay(ms: number) {
|
5
6
|
return new Promise((resolve) => {
|
@@ -7,8 +8,8 @@ function delay(ms: number) {
|
|
7
8
|
});
|
8
9
|
}
|
9
10
|
|
10
|
-
describe(
|
11
|
-
it(
|
11
|
+
describe("The test runner", function () {
|
12
|
+
it("should pass this test", async function () {
|
12
13
|
await delay(1000);
|
13
14
|
expect(true).to.be.ok;
|
14
15
|
});
|
package/test/spawn.ts
CHANGED
@@ -1,102 +1,139 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
2
|
+
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
3
|
+
import { expect } from "chai";
|
4
|
+
import "./support";
|
3
5
|
|
4
|
-
import { spawn, spawnPromise, spawnDetachedPromise } from
|
6
|
+
import { spawn, spawnPromise, spawnDetachedPromise } from "../src/index";
|
5
7
|
|
6
|
-
import { Observable
|
8
|
+
import type { Observable } from "rxjs";
|
9
|
+
import { of } from "rxjs";
|
7
10
|
|
8
|
-
const uuidRegex =
|
11
|
+
const uuidRegex =
|
12
|
+
/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
9
13
|
|
10
|
-
describe(
|
11
|
-
it(
|
14
|
+
describe("The spawnPromise method", function () {
|
15
|
+
it("should return a uuid when we call uuid", async function () {
|
12
16
|
// NB: Since we get run via npm run test, we know that npm bins are in our
|
13
17
|
// PATH.
|
14
|
-
|
18
|
+
const result = await spawnPromise("uuid", []);
|
15
19
|
expect(result.match(uuidRegex)).to.be.ok;
|
16
20
|
});
|
17
21
|
});
|
18
22
|
|
19
|
-
describe(
|
20
|
-
it(
|
23
|
+
describe("The spawnDetachedPromise method", function () {
|
24
|
+
it("should return a uuid when we call uuid", async function () {
|
21
25
|
// NB: Since we get run via npm run test, we know that npm bins are in our
|
22
26
|
// PATH.
|
23
|
-
|
27
|
+
const result = await spawnDetachedPromise("uuid", ["--help"]);
|
24
28
|
expect(result.length > 10).to.be.ok;
|
25
29
|
});
|
26
30
|
});
|
27
31
|
|
28
|
-
function wrapSplitObservableInPromise(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
function wrapSplitObservableInPromise(
|
33
|
+
obs: Observable<{
|
34
|
+
source: any;
|
35
|
+
text: any;
|
36
|
+
}>,
|
37
|
+
): Promise<{
|
38
|
+
stderr: string;
|
39
|
+
stdout: string;
|
40
|
+
error: Error | undefined;
|
35
41
|
}> {
|
36
42
|
return new Promise((res) => {
|
37
|
-
|
43
|
+
const out = { stderr: "", stdout: "", error: undefined };
|
38
44
|
|
39
45
|
obs.subscribe(
|
40
46
|
(x) => {
|
41
|
-
if (x.source ===
|
47
|
+
if (x.source === "stdout") {
|
42
48
|
out.stdout += x.text;
|
43
49
|
} else {
|
44
50
|
out.stderr += x.text;
|
45
51
|
}
|
46
52
|
},
|
47
|
-
(e) => {
|
48
|
-
|
53
|
+
(e) => {
|
54
|
+
out.error = e;
|
55
|
+
res(out);
|
56
|
+
},
|
57
|
+
() => res(out),
|
58
|
+
);
|
49
59
|
});
|
50
60
|
}
|
51
61
|
|
52
|
-
describe(
|
53
|
-
it(
|
62
|
+
describe("The spawn method", function () {
|
63
|
+
it("should return a disposable subscription", async function () {
|
54
64
|
// this only check the unsubscribe goes w/o error, not that the spawned process is killed
|
55
65
|
// (difficult to do that, maybe iterate through child processes and check ?)
|
56
|
-
spawn(
|
66
|
+
spawn("sleep", ["2"]).subscribe().unsubscribe();
|
57
67
|
});
|
58
68
|
|
59
|
-
it(
|
69
|
+
it("should return split stderr in a inner tag when called with split", async function () {
|
60
70
|
// provide an invalid param to uuid so it complains on stderr
|
61
|
-
|
62
|
-
|
71
|
+
const rxSpawn: Observable<{ source: any; text: any }> = spawn(
|
72
|
+
"uuid",
|
73
|
+
["foo"],
|
74
|
+
{ split: true },
|
75
|
+
) as any;
|
76
|
+
const result = await wrapSplitObservableInPromise(rxSpawn);
|
63
77
|
expect(result.stderr.length > 10).to.be.ok;
|
64
78
|
expect(result.stdout).to.be.empty;
|
65
|
-
expect(result.error).to.be.an(
|
79
|
+
expect(result.error).to.be.an("error");
|
66
80
|
});
|
67
81
|
|
68
|
-
it(
|
69
|
-
|
70
|
-
|
82
|
+
it("should return split stdout in a inner tag when called with split", async function () {
|
83
|
+
const rxSpawn: Observable<{ source: any; text: any }> = spawn("uuid", [], {
|
84
|
+
split: true,
|
85
|
+
});
|
86
|
+
const result = await wrapSplitObservableInPromise(rxSpawn);
|
71
87
|
expect(result.stdout.match(uuidRegex)).to.be.ok;
|
72
88
|
expect(result.stderr).to.be.empty;
|
73
89
|
expect(result.error).to.be.undefined;
|
74
90
|
});
|
75
91
|
|
76
|
-
it(
|
77
|
-
|
78
|
-
|
92
|
+
it("should ignore stderr if options.stdio = ignore", async function () {
|
93
|
+
const rxSpawn: Observable<{ source: any; text: any }> = spawn(
|
94
|
+
"uuid",
|
95
|
+
["foo"],
|
96
|
+
{ split: true, stdio: [null, null, "ignore"] },
|
97
|
+
);
|
98
|
+
const result = await wrapSplitObservableInPromise(rxSpawn);
|
79
99
|
expect(result.stderr).to.be.empty;
|
80
100
|
});
|
81
101
|
|
82
|
-
it(
|
83
|
-
|
84
|
-
|
102
|
+
it("should ignore stdout if options.stdio = inherit", async function () {
|
103
|
+
const rxSpawn: Observable<{ source: any; text: any }> = spawn("uuid", [], {
|
104
|
+
split: true,
|
105
|
+
stdio: [null, "inherit", null],
|
106
|
+
});
|
107
|
+
const result = await wrapSplitObservableInPromise(rxSpawn);
|
85
108
|
expect(result.stdout).to.be.empty;
|
86
109
|
});
|
87
110
|
|
88
|
-
it(
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
111
|
+
it("should croak if stdin is provided but stdio.stdin is disabled", async function () {
|
112
|
+
const stdin = of("a");
|
113
|
+
const rxSpawn: Observable<{ source: any; text: any }> = spawn(
|
114
|
+
"marked",
|
115
|
+
[],
|
116
|
+
{
|
117
|
+
split: true,
|
118
|
+
stdin: stdin,
|
119
|
+
stdio: ["ignore", null, null],
|
120
|
+
},
|
121
|
+
);
|
122
|
+
const result = await wrapSplitObservableInPromise(rxSpawn);
|
123
|
+
expect(result.error).to.be.an("error");
|
93
124
|
});
|
94
125
|
|
95
|
-
it(
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
126
|
+
it("should subscribe to provided stdin", async function () {
|
127
|
+
const stdin = of("a");
|
128
|
+
const rxSpawn: Observable<{ source: any; text: any }> = spawn(
|
129
|
+
"marked",
|
130
|
+
[],
|
131
|
+
{
|
132
|
+
split: true,
|
133
|
+
stdin: stdin,
|
134
|
+
},
|
135
|
+
);
|
136
|
+
const result = await wrapSplitObservableInPromise(rxSpawn);
|
137
|
+
expect(result.stdout.trim()).to.be.equal("<p>a</p>");
|
100
138
|
});
|
101
|
-
|
102
139
|
});
|