concurrently 6.0.2 → 6.2.2
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/README.md +16 -10
- package/bin/concurrently.js +4 -2
- package/bin/concurrently.spec.js +365 -0
- package/bin/epilogue.txt +1 -1
- package/index.js +3 -2
- package/package.json +2 -3
- package/src/command-parser/expand-npm-shortcut.spec.js +36 -0
- package/src/command-parser/expand-npm-wildcard.js +11 -2
- package/src/command-parser/expand-npm-wildcard.spec.js +58 -0
- package/src/command-parser/strip-quotes.spec.js +20 -0
- package/src/command.js +3 -0
- package/src/command.spec.js +183 -0
- package/src/completion-listener.js +1 -1
- package/src/completion-listener.spec.js +88 -0
- package/src/concurrently.js +15 -4
- package/src/concurrently.spec.js +186 -0
- package/src/defaults.js +1 -1
- package/src/flow-control/base-handler.js +16 -0
- package/src/flow-control/input-handler.js +16 -5
- package/src/flow-control/input-handler.spec.js +113 -0
- package/src/flow-control/kill-on-signal.js +17 -12
- package/src/flow-control/kill-on-signal.spec.js +79 -0
- package/src/flow-control/kill-others.js +7 -4
- package/src/flow-control/kill-others.spec.js +66 -0
- package/src/flow-control/log-error.js +3 -5
- package/src/flow-control/log-error.spec.js +40 -0
- package/src/flow-control/log-exit.js +3 -5
- package/src/flow-control/log-exit.spec.js +36 -0
- package/src/flow-control/log-output.js +3 -5
- package/src/flow-control/log-output.spec.js +41 -0
- package/src/flow-control/restart-process.js +19 -14
- package/src/flow-control/restart-process.spec.js +131 -0
- package/src/get-spawn-opts.spec.js +30 -0
- package/src/logger.js +4 -3
- package/src/logger.spec.js +195 -0
- package/src/flow-control/fixtures/fake-command.js +0 -16
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Concurrently
|
|
2
2
|
|
|
3
|
-
[](https://github.com/open-cli-tools/concurrently/actions?workflow=Tests)
|
|
4
|
+
[](https://coveralls.io/github/open-cli-tools/concurrently?branch=master)
|
|
4
5
|
|
|
5
6
|
[](https://www.npmjs.com/package/concurrently)
|
|
6
7
|
|
|
@@ -20,7 +21,7 @@ Like `npm run watch-js & npm run watch-less` but better.
|
|
|
20
21
|
|
|
21
22
|
## Why
|
|
22
23
|
|
|
23
|
-
I like [task automation with npm](
|
|
24
|
+
I like [task automation with npm](https://github.com/substack/blog/blob/master/npm_run.markdown)
|
|
24
25
|
but the usual way to run multiple commands concurrently is
|
|
25
26
|
`npm run watch-js & npm run watch-css`. That's fine but it's hard to keep
|
|
26
27
|
on track of different outputs. Also if one process fails, others still keep running
|
|
@@ -147,7 +148,7 @@ Prefix styling
|
|
|
147
148
|
- Available background colors: bgBlack, bgRed,
|
|
148
149
|
bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite
|
|
149
150
|
See https://www.npmjs.com/package/chalk for more
|
|
150
|
-
information. [string] [default: "
|
|
151
|
+
information. [string] [default: "reset"]
|
|
151
152
|
-l, --prefix-length Limit how many characters of the command is displayed
|
|
152
153
|
in prefix. The option can be used to shorten the
|
|
153
154
|
prefix when it is set to "command"
|
|
@@ -169,9 +170,10 @@ Killing other processes
|
|
|
169
170
|
code [boolean]
|
|
170
171
|
|
|
171
172
|
Restarting
|
|
172
|
-
|
|
173
|
+
--restart-tries How many times a process that died should restart.
|
|
174
|
+
Negative numbers will make the process restart forever.
|
|
173
175
|
[number] [default: 0]
|
|
174
|
-
|
|
176
|
+
--restart-after Delay time to respawn the process, in milliseconds.
|
|
175
177
|
[number] [default: 0]
|
|
176
178
|
|
|
177
179
|
Options:
|
|
@@ -220,22 +222,26 @@ Examples:
|
|
|
220
222
|
|
|
221
223
|
$ concurrently npm:watch-*
|
|
222
224
|
|
|
223
|
-
For more details, visit https://github.com/
|
|
225
|
+
For more details, visit https://github.com/open-cli-tools/concurrently
|
|
224
226
|
```
|
|
225
227
|
|
|
226
228
|
## Programmatic Usage
|
|
227
229
|
concurrently can be used programmatically by using the API documented below:
|
|
228
230
|
|
|
229
231
|
### `concurrently(commands[, options])`
|
|
232
|
+
|
|
230
233
|
- `commands`: an array of either strings (containing the commands to run) or objects
|
|
231
234
|
with the shape `{ command, name, prefixColor, env, cwd }`.
|
|
235
|
+
|
|
232
236
|
- `options` (optional): an object containing any of the below:
|
|
233
237
|
- `cwd`: the working directory to be used by all commands. Can be overriden per command.
|
|
234
238
|
Default: `process.cwd()`.
|
|
235
239
|
- `defaultInputTarget`: the default input target when reading from `inputStream`.
|
|
236
240
|
Default: `0`.
|
|
241
|
+
- `handleInput`: when `true`, reads input from `process.stdin`.
|
|
237
242
|
- `inputStream`: a [`Readable` stream](https://nodejs.org/dist/latest-v10.x/docs/api/stream.html#stream_readable_streams)
|
|
238
|
-
to read the input from
|
|
243
|
+
to read the input from. Should only be used in the rare instance you would like to stream anything other than `process.stdin`. Overrides `handleInput`.
|
|
244
|
+
- `pauseInputStreamOnFinish`: by default, pauses the input stream (`process.stdin` when `handleInput` is enabled, or `inputStream` if provided) when all of the processes have finished. If you need to read from the input stream after `concurrently` has finished, set this to `false`. ([#252](https://github.com/kimmobrunfeldt/concurrently/issues/252)).
|
|
239
245
|
- `killOthers`: an array of exitting conditions that will cause a process to kill others.
|
|
240
246
|
Can contain any of `success` or `failure`.
|
|
241
247
|
- `maxProcesses`: how many processes should run at once.
|
|
@@ -257,9 +263,9 @@ concurrently can be used programmatically by using the API documented below:
|
|
|
257
263
|
|
|
258
264
|
> Returns: a `Promise` that resolves if the run was successful (according to `successCondition` option),
|
|
259
265
|
> or rejects, containing an array of objects with information for each command that has been run, in the order
|
|
260
|
-
> that the commands terminated. The objects have the shape `{ command, index, exitCode }`, where `command` is the object
|
|
261
|
-
> passed in the `commands` array
|
|
262
|
-
> the fields that were not specified.
|
|
266
|
+
> that the commands terminated. The objects have the shape `{ command, index, exitCode, killed }`, where `command` is the object
|
|
267
|
+
> passed in the `commands` array, `index` its index there and `killed` indicates if the process was killed as a result of
|
|
268
|
+
> `killOthers`. Default values (empty strings or objects) are returned for the fields that were not specified.
|
|
263
269
|
|
|
264
270
|
Example:
|
|
265
271
|
|
package/bin/concurrently.js
CHANGED
|
@@ -107,7 +107,9 @@ const args = yargs
|
|
|
107
107
|
|
|
108
108
|
// Restarting
|
|
109
109
|
'restart-tries': {
|
|
110
|
-
describe:
|
|
110
|
+
describe:
|
|
111
|
+
'How many times a process that died should restart.\n' +
|
|
112
|
+
'Negative numbers will make the process restart forever.',
|
|
111
113
|
default: defaults.restartTries,
|
|
112
114
|
type: 'number'
|
|
113
115
|
},
|
|
@@ -155,7 +157,7 @@ concurrently(args._.map((command, index) => {
|
|
|
155
157
|
name: names[index]
|
|
156
158
|
};
|
|
157
159
|
}), {
|
|
158
|
-
|
|
160
|
+
handleInput: args.handleInput,
|
|
159
161
|
defaultInputTarget: args.defaultInputTarget,
|
|
160
162
|
killOthers: args.killOthers
|
|
161
163
|
? ['success', 'failure']
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
const Rx = require('rxjs');
|
|
4
|
+
const { buffer, map } = require('rxjs/operators');
|
|
5
|
+
const spawn = require('spawn-command');
|
|
6
|
+
|
|
7
|
+
const isWindows = process.platform === 'win32';
|
|
8
|
+
const createKillMessage = prefix => new RegExp(
|
|
9
|
+
_.escapeRegExp(prefix) +
|
|
10
|
+
' exited with code ' +
|
|
11
|
+
(isWindows ? 1 : '(SIGTERM|143)')
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const run = args => {
|
|
15
|
+
const child = spawn('node ./concurrently.js ' + args, {
|
|
16
|
+
cwd: __dirname,
|
|
17
|
+
env: Object.assign({}, process.env, {
|
|
18
|
+
// When upgrading from jest 23 -> 24, colors started printing in the test output.
|
|
19
|
+
// They are forcibly disabled here
|
|
20
|
+
FORCE_COLOR: 0
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const stdout = readline.createInterface({
|
|
25
|
+
input: child.stdout,
|
|
26
|
+
output: null
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const stderr = readline.createInterface({
|
|
30
|
+
input: child.stderr,
|
|
31
|
+
output: null
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const close = Rx.fromEvent(child, 'close');
|
|
35
|
+
const log = Rx.merge(
|
|
36
|
+
Rx.fromEvent(stdout, 'line'),
|
|
37
|
+
Rx.fromEvent(stderr, 'line')
|
|
38
|
+
).pipe(map(data => data.toString()));
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
close,
|
|
42
|
+
log,
|
|
43
|
+
stdin: child.stdin,
|
|
44
|
+
pid: child.pid
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
it('has help command', done => {
|
|
49
|
+
run('--help').close.subscribe(event => {
|
|
50
|
+
expect(event[0]).toBe(0);
|
|
51
|
+
done();
|
|
52
|
+
}, done);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('has version command', done => {
|
|
56
|
+
Rx.combineLatest(
|
|
57
|
+
run('--version').close,
|
|
58
|
+
run('-V').close,
|
|
59
|
+
run('-v').close
|
|
60
|
+
).subscribe(events => {
|
|
61
|
+
expect(events[0][0]).toBe(0);
|
|
62
|
+
expect(events[1][0]).toBe(0);
|
|
63
|
+
expect(events[2][0]).toBe(0);
|
|
64
|
+
done();
|
|
65
|
+
}, done);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('exiting conditions', () => {
|
|
69
|
+
it('is of success by default when running successful commands', done => {
|
|
70
|
+
run('"echo foo" "echo bar"')
|
|
71
|
+
.close
|
|
72
|
+
.subscribe(exit => {
|
|
73
|
+
expect(exit[0]).toBe(0);
|
|
74
|
+
done();
|
|
75
|
+
}, done);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('is of failure by default when one of the command fails', done => {
|
|
79
|
+
run('"echo foo" "exit 1"')
|
|
80
|
+
.close
|
|
81
|
+
.subscribe(exit => {
|
|
82
|
+
expect(exit[0]).toBeGreaterThan(0);
|
|
83
|
+
done();
|
|
84
|
+
}, done);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('is of success when --success=first and first command to exit succeeds', done => {
|
|
88
|
+
run('--success=first "echo foo" "sleep 0.5 && exit 1"')
|
|
89
|
+
.close
|
|
90
|
+
.subscribe(exit => {
|
|
91
|
+
expect(exit[0]).toBe(0);
|
|
92
|
+
done();
|
|
93
|
+
}, done);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('is of failure when --success=first and first command to exit fails', done => {
|
|
97
|
+
run('--success=first "exit 1" "sleep 0.5 && echo foo"')
|
|
98
|
+
.close
|
|
99
|
+
.subscribe(exit => {
|
|
100
|
+
expect(exit[0]).toBeGreaterThan(0);
|
|
101
|
+
done();
|
|
102
|
+
}, done);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('is of success when --success=last and last command to exit succeeds', done => {
|
|
106
|
+
run('--success=last "exit 1" "sleep 0.5 && echo foo"')
|
|
107
|
+
.close
|
|
108
|
+
.subscribe(exit => {
|
|
109
|
+
expect(exit[0]).toBe(0);
|
|
110
|
+
done();
|
|
111
|
+
}, done);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('is of failure when --success=last and last command to exit fails', done => {
|
|
115
|
+
run('--success=last "echo foo" "sleep 0.5 && exit 1"')
|
|
116
|
+
.close
|
|
117
|
+
.subscribe(exit => {
|
|
118
|
+
expect(exit[0]).toBeGreaterThan(0);
|
|
119
|
+
done();
|
|
120
|
+
}, done);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it.skip('is of success when a SIGINT is sent', done => {
|
|
124
|
+
const child = run('"node fixtures/read-echo.js"');
|
|
125
|
+
child.close.subscribe(exit => {
|
|
126
|
+
// TODO This is null within Node, but should be 0 outside (eg from real terminal)
|
|
127
|
+
expect(exit[0]).toBe(0);
|
|
128
|
+
done();
|
|
129
|
+
}, done);
|
|
130
|
+
|
|
131
|
+
process.kill(child.pid, 'SIGINT');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('is aliased to -s', done => {
|
|
135
|
+
run('-s last "exit 1" "sleep 0.5 && echo foo"')
|
|
136
|
+
.close
|
|
137
|
+
.subscribe(exit => {
|
|
138
|
+
expect(exit[0]).toBe(0);
|
|
139
|
+
done();
|
|
140
|
+
}, done);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('--raw', () => {
|
|
145
|
+
it('is aliased to -r', done => {
|
|
146
|
+
const child = run('-r "echo foo" "echo bar"');
|
|
147
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
148
|
+
expect(lines).toHaveLength(2);
|
|
149
|
+
expect(lines).toContainEqual(expect.stringContaining('foo'));
|
|
150
|
+
expect(lines).toContainEqual(expect.stringContaining('bar'));
|
|
151
|
+
done();
|
|
152
|
+
}, done);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('does not log any extra output', done => {
|
|
156
|
+
const child = run('--raw "echo foo" "echo bar"');
|
|
157
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
158
|
+
expect(lines).toHaveLength(2);
|
|
159
|
+
expect(lines).toContainEqual(expect.stringContaining('foo'));
|
|
160
|
+
expect(lines).toContainEqual(expect.stringContaining('bar'));
|
|
161
|
+
done();
|
|
162
|
+
}, done);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('--names', () => {
|
|
167
|
+
it('is aliased to -n', done => {
|
|
168
|
+
const child = run('-n foo,bar "echo foo" "echo bar"');
|
|
169
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
170
|
+
expect(lines).toContainEqual(expect.stringContaining('[foo] foo'));
|
|
171
|
+
expect(lines).toContainEqual(expect.stringContaining('[bar] bar'));
|
|
172
|
+
done();
|
|
173
|
+
}, done);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('prefixes with names', done => {
|
|
177
|
+
const child = run('--names foo,bar "echo foo" "echo bar"');
|
|
178
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
179
|
+
expect(lines).toContainEqual(expect.stringContaining('[foo] foo'));
|
|
180
|
+
expect(lines).toContainEqual(expect.stringContaining('[bar] bar'));
|
|
181
|
+
done();
|
|
182
|
+
}, done);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('is split using --name-separator arg', done => {
|
|
186
|
+
const child = run('--names "foo|bar" --name-separator "|" "echo foo" "echo bar"');
|
|
187
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
188
|
+
expect(lines).toContainEqual(expect.stringContaining('[foo] foo'));
|
|
189
|
+
expect(lines).toContainEqual(expect.stringContaining('[bar] bar'));
|
|
190
|
+
done();
|
|
191
|
+
}, done);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('--prefix', () => {
|
|
196
|
+
it('is aliased to -p', done => {
|
|
197
|
+
const child = run('-p command "echo foo" "echo bar"');
|
|
198
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
199
|
+
expect(lines).toContainEqual(expect.stringContaining('[echo foo] foo'));
|
|
200
|
+
expect(lines).toContainEqual(expect.stringContaining('[echo bar] bar'));
|
|
201
|
+
done();
|
|
202
|
+
}, done);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('specifies custom prefix', done => {
|
|
206
|
+
const child = run('--prefix command "echo foo" "echo bar"');
|
|
207
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
208
|
+
expect(lines).toContainEqual(expect.stringContaining('[echo foo] foo'));
|
|
209
|
+
expect(lines).toContainEqual(expect.stringContaining('[echo bar] bar'));
|
|
210
|
+
done();
|
|
211
|
+
}, done);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
describe('--prefix-length', () => {
|
|
216
|
+
it('is aliased to -l', done => {
|
|
217
|
+
const child = run('-p command -l 5 "echo foo" "echo bar"');
|
|
218
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
219
|
+
expect(lines).toContainEqual(expect.stringContaining('[ec..o] foo'));
|
|
220
|
+
expect(lines).toContainEqual(expect.stringContaining('[ec..r] bar'));
|
|
221
|
+
done();
|
|
222
|
+
}, done);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('specifies custom prefix length', done => {
|
|
226
|
+
const child = run('--prefix command --prefix-length 5 "echo foo" "echo bar"');
|
|
227
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
228
|
+
expect(lines).toContainEqual(expect.stringContaining('[ec..o] foo'));
|
|
229
|
+
expect(lines).toContainEqual(expect.stringContaining('[ec..r] bar'));
|
|
230
|
+
done();
|
|
231
|
+
}, done);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe('--restart-tries', () => {
|
|
236
|
+
it('changes how many times a command will restart', done => {
|
|
237
|
+
const child = run('--restart-tries 1 "exit 1"');
|
|
238
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
239
|
+
expect(lines).toEqual([
|
|
240
|
+
expect.stringContaining('[0] exit 1 exited with code 1'),
|
|
241
|
+
expect.stringContaining('[0] exit 1 restarted'),
|
|
242
|
+
expect.stringContaining('[0] exit 1 exited with code 1'),
|
|
243
|
+
]);
|
|
244
|
+
done();
|
|
245
|
+
}, done);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe('--kill-others', () => {
|
|
250
|
+
it('is aliased to -k', done => {
|
|
251
|
+
const child = run('-k "sleep 10" "exit 0"');
|
|
252
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
253
|
+
expect(lines).toContainEqual(expect.stringContaining('[1] exit 0 exited with code 0'));
|
|
254
|
+
expect(lines).toContainEqual(expect.stringContaining('Sending SIGTERM to other processes'));
|
|
255
|
+
expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] sleep 10')));
|
|
256
|
+
done();
|
|
257
|
+
}, done);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('kills on success', done => {
|
|
261
|
+
const child = run('--kill-others "sleep 10" "exit 0"');
|
|
262
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
263
|
+
expect(lines).toContainEqual(expect.stringContaining('[1] exit 0 exited with code 0'));
|
|
264
|
+
expect(lines).toContainEqual(expect.stringContaining('Sending SIGTERM to other processes'));
|
|
265
|
+
expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] sleep 10')));
|
|
266
|
+
done();
|
|
267
|
+
}, done);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('kills on failure', done => {
|
|
271
|
+
const child = run('--kill-others "sleep 10" "exit 1"');
|
|
272
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
273
|
+
expect(lines).toContainEqual(expect.stringContaining('[1] exit 1 exited with code 1'));
|
|
274
|
+
expect(lines).toContainEqual(expect.stringContaining('Sending SIGTERM to other processes'));
|
|
275
|
+
expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] sleep 10')));
|
|
276
|
+
done();
|
|
277
|
+
}, done);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
describe('--kill-others-on-fail', () => {
|
|
282
|
+
it('does not kill on success', done => {
|
|
283
|
+
const child = run('--kill-others-on-fail "sleep 0.5" "exit 0"');
|
|
284
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
285
|
+
expect(lines).toContainEqual(expect.stringContaining('[1] exit 0 exited with code 0'));
|
|
286
|
+
expect(lines).toContainEqual(expect.stringContaining('[0] sleep 0.5 exited with code 0'));
|
|
287
|
+
done();
|
|
288
|
+
}, done);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('kills on failure', done => {
|
|
292
|
+
const child = run('--kill-others-on-fail "sleep 10" "exit 1"');
|
|
293
|
+
child.log.pipe(buffer(child.close)).subscribe(lines => {
|
|
294
|
+
expect(lines).toContainEqual(expect.stringContaining('[1] exit 1 exited with code 1'));
|
|
295
|
+
expect(lines).toContainEqual(expect.stringContaining('Sending SIGTERM to other processes'));
|
|
296
|
+
expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] sleep 10')));
|
|
297
|
+
done();
|
|
298
|
+
}, done);
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe('--handle-input', () => {
|
|
303
|
+
it('is aliased to -i', done => {
|
|
304
|
+
const child = run('-i "node fixtures/read-echo.js"');
|
|
305
|
+
child.log.subscribe(line => {
|
|
306
|
+
if (/READING/.test(line)) {
|
|
307
|
+
child.stdin.write('stop\n');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (/\[0\] stop/.test(line)) {
|
|
311
|
+
done();
|
|
312
|
+
}
|
|
313
|
+
}, done);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('forwards input to first process by default', done => {
|
|
317
|
+
const child = run('--handle-input "node fixtures/read-echo.js"');
|
|
318
|
+
child.log.subscribe(line => {
|
|
319
|
+
if (/READING/.test(line)) {
|
|
320
|
+
child.stdin.write('stop\n');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (/\[0\] stop/.test(line)) {
|
|
324
|
+
done();
|
|
325
|
+
}
|
|
326
|
+
}, done);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
it('forwards input to process --default-input-target', done => {
|
|
331
|
+
const lines = [];
|
|
332
|
+
const child = run('-ki --default-input-target 1 "node fixtures/read-echo.js" "node fixtures/read-echo.js"');
|
|
333
|
+
child.log.subscribe(line => {
|
|
334
|
+
lines.push(line);
|
|
335
|
+
if (/\[1\] READING/.test(line)) {
|
|
336
|
+
child.stdin.write('stop\n');
|
|
337
|
+
}
|
|
338
|
+
}, done);
|
|
339
|
+
|
|
340
|
+
child.close.subscribe(exit => {
|
|
341
|
+
expect(exit[0]).toBeGreaterThan(0);
|
|
342
|
+
expect(lines).toContainEqual(expect.stringContaining('[1] stop'));
|
|
343
|
+
expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] node fixtures/read-echo.js')));
|
|
344
|
+
done();
|
|
345
|
+
}, done);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('forwards input to specified process', done => {
|
|
349
|
+
const lines = [];
|
|
350
|
+
const child = run('-ki "node fixtures/read-echo.js" "node fixtures/read-echo.js"');
|
|
351
|
+
child.log.subscribe(line => {
|
|
352
|
+
lines.push(line);
|
|
353
|
+
if (/\[1\] READING/.test(line)) {
|
|
354
|
+
child.stdin.write('1:stop\n');
|
|
355
|
+
}
|
|
356
|
+
}, done);
|
|
357
|
+
|
|
358
|
+
child.close.subscribe(exit => {
|
|
359
|
+
expect(exit[0]).toBeGreaterThan(0);
|
|
360
|
+
expect(lines).toContainEqual(expect.stringContaining('[1] stop'));
|
|
361
|
+
expect(lines).toContainEqual(expect.stringMatching(createKillMessage('[0] node fixtures/read-echo.js')));
|
|
362
|
+
done();
|
|
363
|
+
}, done);
|
|
364
|
+
});
|
|
365
|
+
});
|
package/bin/epilogue.txt
CHANGED
package/index.js
CHANGED
|
@@ -9,7 +9,7 @@ const RestartProcess = require('./src/flow-control/restart-process');
|
|
|
9
9
|
const concurrently = require('./src/concurrently');
|
|
10
10
|
const Logger = require('./src/logger');
|
|
11
11
|
|
|
12
|
-
module.exports = (commands, options = {}) => {
|
|
12
|
+
module.exports = exports = (commands, options = {}) => {
|
|
13
13
|
const logger = new Logger({
|
|
14
14
|
outputStream: options.outputStream || process.stdout,
|
|
15
15
|
prefixFormat: options.prefix,
|
|
@@ -30,7 +30,8 @@ module.exports = (commands, options = {}) => {
|
|
|
30
30
|
new InputHandler({
|
|
31
31
|
logger,
|
|
32
32
|
defaultInputTarget: options.defaultInputTarget,
|
|
33
|
-
inputStream: options.inputStream,
|
|
33
|
+
inputStream: options.inputStream || (options.handleInput && process.stdin),
|
|
34
|
+
pauseInputStreamOnFinish: options.pauseInputStreamOnFinish,
|
|
34
35
|
}),
|
|
35
36
|
new KillOnSignal({ process }),
|
|
36
37
|
new RestartProcess({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "concurrently",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.2",
|
|
4
4
|
"description": "Run commands concurrently",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "https://github.com/
|
|
19
|
+
"url": "https://github.com/open-cli-tools/concurrently.git"
|
|
20
20
|
},
|
|
21
21
|
"keywords": [
|
|
22
22
|
"bash",
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
"chalk": "^4.1.0",
|
|
33
33
|
"date-fns": "^2.16.1",
|
|
34
34
|
"lodash": "^4.17.21",
|
|
35
|
-
"read-pkg": "^5.2.0",
|
|
36
35
|
"rxjs": "^6.6.3",
|
|
37
36
|
"spawn-command": "^0.0.2-1",
|
|
38
37
|
"supports-color": "^8.1.0",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const ExpandNpmShortcut = require('./expand-npm-shortcut');
|
|
2
|
+
const parser = new ExpandNpmShortcut();
|
|
3
|
+
|
|
4
|
+
it('returns same command if no npm: prefix is present', () => {
|
|
5
|
+
const commandInfo = {
|
|
6
|
+
name: 'echo',
|
|
7
|
+
command: 'echo foo'
|
|
8
|
+
};
|
|
9
|
+
expect(parser.parse(commandInfo)).toBe(commandInfo);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
for (const npmCmd of ['npm', 'yarn', 'pnpm']) {
|
|
13
|
+
describe(`with ${npmCmd}: prefix`, () => {
|
|
14
|
+
it(`expands to "${npmCmd} run <script> <args>"`, () => {
|
|
15
|
+
const commandInfo = {
|
|
16
|
+
name: 'echo',
|
|
17
|
+
command: `${npmCmd}:foo -- bar`
|
|
18
|
+
};
|
|
19
|
+
expect(parser.parse(commandInfo)).toEqual({
|
|
20
|
+
name: 'echo',
|
|
21
|
+
command: `${npmCmd} run foo -- bar`
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('sets name to script name if none', () => {
|
|
26
|
+
const commandInfo = {
|
|
27
|
+
command: `${npmCmd}:foo -- bar`
|
|
28
|
+
};
|
|
29
|
+
expect(parser.parse(commandInfo)).toEqual({
|
|
30
|
+
name: 'foo',
|
|
31
|
+
command: `${npmCmd} run foo -- bar`
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
|
-
const
|
|
2
|
+
const fs = require('fs');
|
|
3
3
|
|
|
4
4
|
module.exports = class ExpandNpmWildcard {
|
|
5
|
-
|
|
5
|
+
static readPackage() {
|
|
6
|
+
try {
|
|
7
|
+
const json = fs.readFileSync('package.json', { encoding: 'utf-8' });
|
|
8
|
+
return JSON.parse(json);
|
|
9
|
+
} catch (e) {
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
constructor(readPackage = ExpandNpmWildcard.readPackage) {
|
|
6
15
|
this.readPackage = readPackage;
|
|
7
16
|
}
|
|
8
17
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const ExpandNpmWildcard = require('./expand-npm-wildcard');
|
|
2
|
+
|
|
3
|
+
let parser, readPkg;
|
|
4
|
+
|
|
5
|
+
beforeEach(() => {
|
|
6
|
+
readPkg = jest.fn();
|
|
7
|
+
parser = new ExpandNpmWildcard(readPkg);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('returns same command if not an npm run command', () => {
|
|
11
|
+
const commandInfo = {
|
|
12
|
+
command: 'npm test'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
expect(readPkg).not.toHaveBeenCalled();
|
|
16
|
+
expect(parser.parse(commandInfo)).toBe(commandInfo);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('returns same command if no wildcard present', () => {
|
|
20
|
+
const commandInfo = {
|
|
21
|
+
command: 'npm run foo bar'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
expect(readPkg).not.toHaveBeenCalled();
|
|
25
|
+
expect(parser.parse(commandInfo)).toBe(commandInfo);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('expands to nothing if no scripts exist in package.json', () => {
|
|
29
|
+
readPkg.mockReturnValue({});
|
|
30
|
+
|
|
31
|
+
expect(parser.parse({ command: 'npm run foo-*-baz qux' })).toEqual([]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
for (const npmCmd of ['npm', 'yarn', 'pnpm']) {
|
|
35
|
+
describe(`with an ${npmCmd}: prefix`, () => {
|
|
36
|
+
it('expands to all scripts matching pattern', () => {
|
|
37
|
+
readPkg.mockReturnValue({
|
|
38
|
+
scripts: {
|
|
39
|
+
'foo-bar-baz': '',
|
|
40
|
+
'foo--baz': '',
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
expect(parser.parse({ command: `${npmCmd} run foo-*-baz qux` })).toEqual([
|
|
45
|
+
{ name: 'foo-bar-baz', command: `${npmCmd} run foo-bar-baz qux` },
|
|
46
|
+
{ name: 'foo--baz', command: `${npmCmd} run foo--baz qux` },
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('caches scripts upon calls', () => {
|
|
51
|
+
readPkg.mockReturnValue({});
|
|
52
|
+
parser.parse({ command: `${npmCmd} run foo-*-baz qux` });
|
|
53
|
+
parser.parse({ command: `${npmCmd} run foo-*-baz qux` });
|
|
54
|
+
|
|
55
|
+
expect(readPkg).toHaveBeenCalledTimes(1);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const StripQuotes = require('./strip-quotes');
|
|
2
|
+
const parser = new StripQuotes();
|
|
3
|
+
|
|
4
|
+
it('returns command as is if no single/double quote at the beginning', () => {
|
|
5
|
+
expect(parser.parse({ command: 'echo foo' })).toEqual({ command: 'echo foo' });
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it('strips single quotes', () => {
|
|
9
|
+
expect(parser.parse({ command: '\'echo foo\'' })).toEqual({ command: 'echo foo' });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('strips double quotes', () => {
|
|
13
|
+
expect(parser.parse({ command: '"echo foo"' })).toEqual({ command: 'echo foo' });
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('does not remove quotes if they are impaired', () => {
|
|
17
|
+
expect(parser.parse({ command: '"echo foo' })).toEqual({ command: '"echo foo' });
|
|
18
|
+
expect(parser.parse({ command: 'echo foo\'' })).toEqual({ command: 'echo foo\'' });
|
|
19
|
+
expect(parser.parse({ command: '"echo foo\'' })).toEqual({ command: '"echo foo\'' });
|
|
20
|
+
});
|
package/src/command.js
CHANGED
|
@@ -14,6 +14,7 @@ module.exports = class Command {
|
|
|
14
14
|
this.killProcess = killProcess;
|
|
15
15
|
this.spawn = spawn;
|
|
16
16
|
this.spawnOpts = spawnOpts;
|
|
17
|
+
this.killed = false;
|
|
17
18
|
|
|
18
19
|
this.error = new Rx.Subject();
|
|
19
20
|
this.close = new Rx.Subject();
|
|
@@ -41,6 +42,7 @@ module.exports = class Command {
|
|
|
41
42
|
},
|
|
42
43
|
index: this.index,
|
|
43
44
|
exitCode: exitCode === null ? signal : exitCode,
|
|
45
|
+
killed: this.killed,
|
|
44
46
|
});
|
|
45
47
|
});
|
|
46
48
|
child.stdout && pipeTo(Rx.fromEvent(child.stdout, 'data'), this.stdout);
|
|
@@ -50,6 +52,7 @@ module.exports = class Command {
|
|
|
50
52
|
|
|
51
53
|
kill(code) {
|
|
52
54
|
if (this.killable) {
|
|
55
|
+
this.killed = true;
|
|
53
56
|
this.killProcess(this.pid, code);
|
|
54
57
|
}
|
|
55
58
|
}
|