concurrently 9.2.1 → 10.0.0
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 +25 -14
- package/dist/bin/{concurrently.js → index.js} +23 -62
- package/dist/bin/index.spec.d.ts +1 -0
- package/dist/bin/index.spec.js +368 -0
- package/dist/bin/normalize-cli-command.d.ts +1 -0
- package/dist/bin/normalize-cli-command.js +15 -0
- package/dist/bin/normalize-cli-command.spec.d.ts +1 -0
- package/dist/bin/normalize-cli-command.spec.js +36 -0
- package/dist/bin/read-package-json.d.ts +4 -0
- package/dist/bin/read-package-json.js +17 -0
- package/dist/lib/__fixtures__/create-mock-instance.d.ts +2 -0
- package/dist/lib/__fixtures__/create-mock-instance.js +5 -0
- package/dist/lib/__fixtures__/fake-command.d.ts +6 -0
- package/dist/lib/__fixtures__/fake-command.js +37 -0
- package/dist/lib/assert.d.ts +10 -0
- package/dist/lib/assert.js +24 -0
- package/dist/lib/assert.spec.d.ts +1 -0
- package/dist/lib/assert.spec.js +41 -0
- package/dist/{src → lib}/command-parser/expand-arguments.d.ts +2 -2
- package/dist/{src → lib}/command-parser/expand-arguments.js +7 -12
- package/dist/lib/command-parser/expand-arguments.spec.d.ts +1 -0
- package/dist/lib/command-parser/expand-arguments.spec.js +57 -0
- package/dist/{src → lib}/command-parser/expand-shortcut.d.ts +2 -2
- package/dist/{src → lib}/command-parser/expand-shortcut.js +1 -5
- package/dist/lib/command-parser/expand-shortcut.spec.d.ts +1 -0
- package/dist/lib/command-parser/expand-shortcut.spec.js +36 -0
- package/dist/{src → lib}/command-parser/expand-wildcard.d.ts +2 -2
- package/dist/{src → lib}/command-parser/expand-wildcard.js +21 -28
- package/dist/lib/command-parser/expand-wildcard.spec.d.ts +1 -0
- package/dist/lib/command-parser/expand-wildcard.spec.js +288 -0
- package/dist/{src → lib}/command.d.ts +5 -4
- package/dist/{src → lib}/command.js +3 -39
- package/dist/lib/command.spec.d.ts +1 -0
- package/dist/lib/command.spec.js +369 -0
- package/dist/{src → lib}/completion-listener.d.ts +2 -2
- package/dist/{src → lib}/completion-listener.js +9 -46
- package/dist/lib/completion-listener.spec.d.ts +1 -0
- package/dist/lib/completion-listener.spec.js +229 -0
- package/dist/{src → lib}/concurrently.d.ts +10 -10
- package/dist/{src → lib}/concurrently.js +29 -40
- package/dist/lib/concurrently.spec.d.ts +1 -0
- package/dist/lib/concurrently.spec.js +320 -0
- package/dist/{src → lib}/date-format.d.ts +2 -2
- package/dist/{src → lib}/date-format.js +97 -76
- package/dist/lib/date-format.spec.d.ts +1 -0
- package/dist/lib/date-format.spec.js +480 -0
- package/dist/{src → lib}/defaults.d.ts +2 -6
- package/dist/{src → lib}/defaults.js +16 -23
- package/dist/{src → lib}/flow-control/input-handler.d.ts +4 -4
- package/dist/{src → lib}/flow-control/input-handler.js +8 -44
- package/dist/lib/flow-control/input-handler.spec.d.ts +1 -0
- package/dist/lib/flow-control/input-handler.spec.js +116 -0
- package/dist/{src → lib}/flow-control/kill-on-signal.d.ts +3 -3
- package/dist/{src → lib}/flow-control/kill-on-signal.js +3 -7
- package/dist/lib/flow-control/kill-on-signal.spec.d.ts +1 -0
- package/dist/lib/flow-control/kill-on-signal.spec.js +63 -0
- package/dist/{src → lib}/flow-control/kill-others.d.ts +3 -3
- package/dist/{src → lib}/flow-control/kill-others.js +8 -12
- package/dist/lib/flow-control/kill-others.spec.d.ts +1 -0
- package/dist/lib/flow-control/kill-others.spec.js +98 -0
- package/dist/{src → lib}/flow-control/log-error.d.ts +3 -3
- package/dist/{src → lib}/flow-control/log-error.js +1 -5
- package/dist/lib/flow-control/log-error.spec.d.ts +1 -0
- package/dist/lib/flow-control/log-error.spec.js +33 -0
- package/dist/{src → lib}/flow-control/log-exit.d.ts +3 -3
- package/dist/{src → lib}/flow-control/log-exit.js +1 -5
- package/dist/lib/flow-control/log-exit.spec.d.ts +1 -0
- package/dist/lib/flow-control/log-exit.spec.js +24 -0
- package/dist/{src → lib}/flow-control/log-output.d.ts +3 -3
- package/dist/{src → lib}/flow-control/log-output.js +1 -5
- package/dist/lib/flow-control/log-output.spec.d.ts +1 -0
- package/dist/lib/flow-control/log-output.spec.js +33 -0
- package/dist/{src → lib}/flow-control/log-timings.d.ts +3 -3
- package/dist/{src → lib}/flow-control/log-timings.js +8 -45
- package/dist/lib/flow-control/log-timings.spec.d.ts +1 -0
- package/dist/lib/flow-control/log-timings.spec.js +89 -0
- package/dist/{src → lib}/flow-control/logger-padding.d.ts +3 -3
- package/dist/{src → lib}/flow-control/logger-padding.js +7 -7
- package/dist/lib/flow-control/logger-padding.spec.d.ts +1 -0
- package/dist/lib/flow-control/logger-padding.spec.js +60 -0
- package/dist/{src → lib}/flow-control/output-error-handler.d.ts +4 -4
- package/dist/{src → lib}/flow-control/output-error-handler.js +3 -7
- package/dist/lib/flow-control/output-error-handler.spec.d.ts +1 -0
- package/dist/lib/flow-control/output-error-handler.spec.js +41 -0
- package/dist/{src → lib}/flow-control/restart-process.d.ts +4 -4
- package/dist/lib/flow-control/restart-process.js +61 -0
- package/dist/lib/flow-control/restart-process.spec.d.ts +1 -0
- package/dist/lib/flow-control/restart-process.spec.js +127 -0
- package/dist/{src → lib}/flow-control/teardown.d.ts +4 -5
- package/dist/lib/flow-control/teardown.js +45 -0
- package/dist/lib/flow-control/teardown.spec.d.ts +1 -0
- package/dist/lib/flow-control/teardown.spec.js +93 -0
- package/dist/{src → lib}/index.d.ts +21 -19
- package/dist/lib/index.js +98 -0
- package/dist/{src → lib}/jsonc.js +3 -8
- package/dist/lib/jsonc.spec.d.ts +1 -0
- package/dist/lib/jsonc.spec.js +73 -0
- package/dist/{src → lib}/logger.d.ts +3 -2
- package/dist/{src → lib}/logger.js +109 -65
- package/dist/lib/logger.spec.d.ts +1 -0
- package/dist/lib/logger.spec.js +507 -0
- package/dist/{src → lib}/observables.d.ts +1 -1
- package/dist/{src → lib}/observables.js +3 -6
- package/dist/lib/observables.spec.d.ts +1 -0
- package/dist/lib/observables.spec.js +29 -0
- package/dist/{src → lib}/output-writer.d.ts +3 -3
- package/dist/{src → lib}/output-writer.js +4 -41
- package/dist/lib/output-writer.spec.d.ts +1 -0
- package/dist/lib/output-writer.spec.js +96 -0
- package/dist/lib/prefix-color-selector.d.ts +21 -0
- package/dist/{src → lib}/prefix-color-selector.js +14 -31
- package/dist/lib/prefix-color-selector.spec.d.ts +1 -0
- package/dist/lib/prefix-color-selector.spec.js +159 -0
- package/dist/{src → lib}/spawn.d.ts +14 -5
- package/dist/lib/spawn.js +105 -0
- package/dist/lib/spawn.spec.d.ts +1 -0
- package/dist/lib/spawn.spec.js +100 -0
- package/dist/lib/utils.d.ts +25 -0
- package/dist/lib/utils.js +52 -0
- package/dist/lib/utils.spec.d.ts +1 -0
- package/dist/lib/utils.spec.js +58 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/README.md +6 -0
- package/docs/cli/prefixing.md +44 -4
- package/docs/shell-resolution.md +48 -0
- package/package.json +59 -75
- package/dist/bin/read-package.d.ts +0 -6
- package/dist/bin/read-package.js +0 -56
- package/dist/src/assert.d.ts +0 -5
- package/dist/src/assert.js +0 -15
- package/dist/src/command-parser/command-parser.d.ts +0 -19
- package/dist/src/command-parser/command-parser.js +0 -2
- package/dist/src/command-parser/strip-quotes.d.ts +0 -16
- package/dist/src/command-parser/strip-quotes.js +0 -17
- package/dist/src/flow-control/flow-controller.d.ts +0 -13
- package/dist/src/flow-control/flow-controller.js +0 -2
- package/dist/src/flow-control/restart-process.js +0 -98
- package/dist/src/flow-control/teardown.js +0 -82
- package/dist/src/index.js +0 -96
- package/dist/src/prefix-color-selector.d.ts +0 -11
- package/dist/src/spawn.js +0 -49
- package/dist/src/utils.d.ts +0 -10
- package/dist/src/utils.js +0 -16
- package/index.d.mts +0 -7
- package/index.d.ts +0 -11
- package/index.js +0 -14
- package/index.mjs +0 -9
- /package/dist/bin/{concurrently.d.ts → index.d.ts} +0 -0
- /package/dist/{src → lib}/jsonc.d.ts +0 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { getEventListeners } from 'node:events';
|
|
2
|
+
import { TestScheduler } from 'rxjs/testing';
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { createFakeCloseEvent, FakeCommand } from './__fixtures__/fake-command.js';
|
|
5
|
+
import { CompletionListener } from './completion-listener.js';
|
|
6
|
+
let commands;
|
|
7
|
+
let scheduler;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
commands = [
|
|
10
|
+
new FakeCommand('foo', 'echo', 0),
|
|
11
|
+
new FakeCommand('bar', 'echo', 1),
|
|
12
|
+
new FakeCommand('baz', 'echo', 2),
|
|
13
|
+
];
|
|
14
|
+
scheduler = new TestScheduler(() => true);
|
|
15
|
+
});
|
|
16
|
+
const createController = (successCondition) => new CompletionListener({
|
|
17
|
+
successCondition,
|
|
18
|
+
scheduler,
|
|
19
|
+
});
|
|
20
|
+
const emitFakeCloseEvent = (command, event) => {
|
|
21
|
+
const fakeEvent = createFakeCloseEvent({ ...event, command, index: command.index });
|
|
22
|
+
command.state = 'exited';
|
|
23
|
+
command.close.next(fakeEvent);
|
|
24
|
+
return fakeEvent;
|
|
25
|
+
};
|
|
26
|
+
const flushPromises = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
27
|
+
describe('listen', () => {
|
|
28
|
+
it('resolves when there are no commands', async () => {
|
|
29
|
+
const result = createController().listen([]);
|
|
30
|
+
await expect(result).resolves.toHaveLength(0);
|
|
31
|
+
});
|
|
32
|
+
it('completes only when commands emit a close event, returns close event', async () => {
|
|
33
|
+
const abortCtrl = new AbortController();
|
|
34
|
+
const result = createController('all').listen(commands, abortCtrl.signal);
|
|
35
|
+
commands[0].state = 'started';
|
|
36
|
+
abortCtrl.abort();
|
|
37
|
+
const event = emitFakeCloseEvent(commands[0]);
|
|
38
|
+
scheduler.flush();
|
|
39
|
+
await expect(result).resolves.toHaveLength(1);
|
|
40
|
+
await expect(result).resolves.toEqual([event]);
|
|
41
|
+
});
|
|
42
|
+
it('completes when abort signal is received and command is stopped, returns nothing', async () => {
|
|
43
|
+
const abortCtrl = new AbortController();
|
|
44
|
+
// Use success condition = first to test index access when there are no close events
|
|
45
|
+
const result = createController('first').listen([new FakeCommand()], abortCtrl.signal);
|
|
46
|
+
abortCtrl.abort();
|
|
47
|
+
scheduler.flush();
|
|
48
|
+
await expect(result).resolves.toHaveLength(0);
|
|
49
|
+
});
|
|
50
|
+
it('does not leak memory when listening for abort signals', () => {
|
|
51
|
+
const abortCtrl = new AbortController();
|
|
52
|
+
createController().listen(Array.from({ length: 10 }, () => new FakeCommand()), abortCtrl.signal);
|
|
53
|
+
expect(getEventListeners(abortCtrl.signal, 'abort')).toHaveLength(1);
|
|
54
|
+
});
|
|
55
|
+
it('check for success once all commands have emitted at least a single close event', async () => {
|
|
56
|
+
const finallyCallback = vi.fn();
|
|
57
|
+
const result = createController().listen(commands).finally(finallyCallback);
|
|
58
|
+
// Emitting multiple close events to mimic calling command `kill/start` APIs.
|
|
59
|
+
emitFakeCloseEvent(commands[0]);
|
|
60
|
+
emitFakeCloseEvent(commands[0]);
|
|
61
|
+
emitFakeCloseEvent(commands[0]);
|
|
62
|
+
scheduler.flush();
|
|
63
|
+
// A broken implementation will have called finallyCallback only after flushing promises
|
|
64
|
+
await flushPromises();
|
|
65
|
+
expect(finallyCallback).not.toHaveBeenCalled();
|
|
66
|
+
emitFakeCloseEvent(commands[1]);
|
|
67
|
+
emitFakeCloseEvent(commands[2]);
|
|
68
|
+
scheduler.flush();
|
|
69
|
+
await expect(result).resolves.toEqual(expect.anything());
|
|
70
|
+
expect(finallyCallback).toHaveBeenCalled();
|
|
71
|
+
});
|
|
72
|
+
it('takes last event emitted from each command', async () => {
|
|
73
|
+
const result = createController().listen(commands);
|
|
74
|
+
emitFakeCloseEvent(commands[0], { exitCode: 0 });
|
|
75
|
+
emitFakeCloseEvent(commands[0], { exitCode: 1 });
|
|
76
|
+
emitFakeCloseEvent(commands[1], { exitCode: 0 });
|
|
77
|
+
emitFakeCloseEvent(commands[2], { exitCode: 0 });
|
|
78
|
+
scheduler.flush();
|
|
79
|
+
await expect(result).rejects.toEqual(expect.anything());
|
|
80
|
+
});
|
|
81
|
+
it('waits for manually restarted events to close', async () => {
|
|
82
|
+
const finallyCallback = vi.fn();
|
|
83
|
+
const result = createController().listen(commands).finally(finallyCallback);
|
|
84
|
+
emitFakeCloseEvent(commands[0]);
|
|
85
|
+
commands[0].state = 'started';
|
|
86
|
+
emitFakeCloseEvent(commands[1]);
|
|
87
|
+
emitFakeCloseEvent(commands[2]);
|
|
88
|
+
scheduler.flush();
|
|
89
|
+
// A broken implementation will have called finallyCallback only after flushing promises
|
|
90
|
+
await flushPromises();
|
|
91
|
+
expect(finallyCallback).not.toHaveBeenCalled();
|
|
92
|
+
commands[0].state = 'exited';
|
|
93
|
+
emitFakeCloseEvent(commands[0]);
|
|
94
|
+
scheduler.flush();
|
|
95
|
+
await expect(result).resolves.toEqual(expect.anything());
|
|
96
|
+
expect(finallyCallback).toHaveBeenCalled();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('detect commands exit conditions', () => {
|
|
100
|
+
describe('with default success condition set', () => {
|
|
101
|
+
it('succeeds if all processes exited with code 0', () => {
|
|
102
|
+
const result = createController().listen(commands);
|
|
103
|
+
commands[0].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
104
|
+
commands[1].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
105
|
+
commands[2].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
106
|
+
scheduler.flush();
|
|
107
|
+
return expect(result).resolves.toEqual(expect.anything());
|
|
108
|
+
});
|
|
109
|
+
it('fails if one of the processes exited with non-0 code', () => {
|
|
110
|
+
const result = createController().listen(commands);
|
|
111
|
+
commands[0].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
112
|
+
commands[1].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
113
|
+
commands[2].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
114
|
+
scheduler.flush();
|
|
115
|
+
return expect(result).rejects.toEqual(expect.anything());
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('with success condition set to first', () => {
|
|
119
|
+
it('succeeds if first process to exit has code 0', () => {
|
|
120
|
+
const result = createController('first').listen(commands);
|
|
121
|
+
commands[1].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
122
|
+
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
123
|
+
commands[2].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
124
|
+
scheduler.flush();
|
|
125
|
+
return expect(result).resolves.toEqual(expect.anything());
|
|
126
|
+
});
|
|
127
|
+
it('fails if first process to exit has non-0 code', () => {
|
|
128
|
+
const result = createController('first').listen(commands);
|
|
129
|
+
commands[1].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
130
|
+
commands[0].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
131
|
+
commands[2].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
132
|
+
scheduler.flush();
|
|
133
|
+
return expect(result).rejects.toEqual(expect.anything());
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe('with success condition set to last', () => {
|
|
137
|
+
it('succeeds if last process to exit has code 0', () => {
|
|
138
|
+
const result = createController('last').listen(commands);
|
|
139
|
+
commands[1].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
140
|
+
commands[0].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
141
|
+
commands[2].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
142
|
+
scheduler.flush();
|
|
143
|
+
return expect(result).resolves.toEqual(expect.anything());
|
|
144
|
+
});
|
|
145
|
+
it('fails if last process to exit has non-0 code', () => {
|
|
146
|
+
const result = createController('last').listen(commands);
|
|
147
|
+
commands[1].close.next(createFakeCloseEvent({ exitCode: 0 }));
|
|
148
|
+
commands[0].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
149
|
+
commands[2].close.next(createFakeCloseEvent({ exitCode: 1 }));
|
|
150
|
+
scheduler.flush();
|
|
151
|
+
return expect(result).rejects.toEqual(expect.anything());
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
describe.each([
|
|
155
|
+
// Use the middle command for both cases to make it more difficult to make a mess up
|
|
156
|
+
// in the implementation cause false passes.
|
|
157
|
+
['command-bar', 'bar'],
|
|
158
|
+
['command-1', 1],
|
|
159
|
+
])('with success condition set to %s', (condition, nameOrIndex) => {
|
|
160
|
+
it(`succeeds if command ${nameOrIndex} exits with code 0`, () => {
|
|
161
|
+
const result = createController(condition).listen(commands);
|
|
162
|
+
emitFakeCloseEvent(commands[0], { exitCode: 1 });
|
|
163
|
+
emitFakeCloseEvent(commands[1], { exitCode: 0 });
|
|
164
|
+
emitFakeCloseEvent(commands[2], { exitCode: 1 });
|
|
165
|
+
scheduler.flush();
|
|
166
|
+
return expect(result).resolves.toEqual(expect.anything());
|
|
167
|
+
});
|
|
168
|
+
it(`succeeds if all commands ${nameOrIndex} exit with code 0`, () => {
|
|
169
|
+
commands = [commands[0], commands[1], commands[1]];
|
|
170
|
+
const result = createController(condition).listen(commands);
|
|
171
|
+
emitFakeCloseEvent(commands[0], { exitCode: 1 });
|
|
172
|
+
emitFakeCloseEvent(commands[1], { exitCode: 0 });
|
|
173
|
+
emitFakeCloseEvent(commands[2], { exitCode: 0 });
|
|
174
|
+
scheduler.flush();
|
|
175
|
+
return expect(result).resolves.toEqual(expect.anything());
|
|
176
|
+
});
|
|
177
|
+
it(`fails if command ${nameOrIndex} exits with non-0 code`, () => {
|
|
178
|
+
const result = createController(condition).listen(commands);
|
|
179
|
+
emitFakeCloseEvent(commands[0], { exitCode: 0 });
|
|
180
|
+
emitFakeCloseEvent(commands[1], { exitCode: 1 });
|
|
181
|
+
emitFakeCloseEvent(commands[2], { exitCode: 0 });
|
|
182
|
+
scheduler.flush();
|
|
183
|
+
return expect(result).rejects.toEqual(expect.anything());
|
|
184
|
+
});
|
|
185
|
+
it(`succeeds if command ${nameOrIndex} exits with code 0 even when others fail`, () => {
|
|
186
|
+
const result = createController(condition).listen(commands);
|
|
187
|
+
emitFakeCloseEvent(commands[0], { exitCode: 1 });
|
|
188
|
+
emitFakeCloseEvent(commands[1], { exitCode: 0 });
|
|
189
|
+
emitFakeCloseEvent(commands[2], { exitCode: 1 });
|
|
190
|
+
scheduler.flush();
|
|
191
|
+
return expect(result).resolves.toEqual(expect.anything());
|
|
192
|
+
});
|
|
193
|
+
it(`fails if command ${nameOrIndex} doesn't exist`, () => {
|
|
194
|
+
const result = createController(condition).listen([commands[0]]);
|
|
195
|
+
emitFakeCloseEvent(commands[0], { exitCode: 0 });
|
|
196
|
+
scheduler.flush();
|
|
197
|
+
return expect(result).rejects.toEqual(expect.anything());
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
describe.each([
|
|
201
|
+
// Use the middle command for both cases to make it more difficult to make a mess up
|
|
202
|
+
// in the implementation cause false passes.
|
|
203
|
+
['!command-bar', 'bar'],
|
|
204
|
+
['!command-1', 1],
|
|
205
|
+
])('with success condition set to %s', (condition, nameOrIndex) => {
|
|
206
|
+
it(`succeeds if all commands but ${nameOrIndex} exit with code 0`, () => {
|
|
207
|
+
const result = createController(condition).listen(commands);
|
|
208
|
+
emitFakeCloseEvent(commands[0], { exitCode: 0 });
|
|
209
|
+
emitFakeCloseEvent(commands[1], { exitCode: 1 });
|
|
210
|
+
emitFakeCloseEvent(commands[2], { exitCode: 0 });
|
|
211
|
+
scheduler.flush();
|
|
212
|
+
return expect(result).resolves.toEqual(expect.anything());
|
|
213
|
+
});
|
|
214
|
+
it(`fails if any commands but ${nameOrIndex} exit with non-0 code`, () => {
|
|
215
|
+
const result = createController(condition).listen(commands);
|
|
216
|
+
emitFakeCloseEvent(commands[0], { exitCode: 1 });
|
|
217
|
+
emitFakeCloseEvent(commands[1], { exitCode: 1 });
|
|
218
|
+
emitFakeCloseEvent(commands[2], { exitCode: 0 });
|
|
219
|
+
scheduler.flush();
|
|
220
|
+
return expect(result).rejects.toEqual(expect.anything());
|
|
221
|
+
});
|
|
222
|
+
it(`succeeds if command ${nameOrIndex} doesn't exist`, () => {
|
|
223
|
+
const result = createController(condition).listen([commands[0]]);
|
|
224
|
+
emitFakeCloseEvent(commands[0], { exitCode: 0 });
|
|
225
|
+
scheduler.flush();
|
|
226
|
+
return expect(result).resolves.toEqual(expect.anything());
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Writable } from 'stream';
|
|
2
|
-
import { CloseEvent, Command, CommandIdentifier, CommandInfo, KillProcess, SpawnCommand } from './command';
|
|
3
|
-
import { SuccessCondition } from './completion-listener';
|
|
4
|
-
import { FlowController } from './flow-control/flow-controller';
|
|
5
|
-
import { Logger } from './logger';
|
|
1
|
+
import { Writable } from 'node:stream';
|
|
2
|
+
import { CloseEvent, Command, CommandIdentifier, CommandInfo, KillProcess, SpawnCommand } from './command.js';
|
|
3
|
+
import { SuccessCondition } from './completion-listener.js';
|
|
4
|
+
import { FlowController } from './flow-control/flow-controller.js';
|
|
5
|
+
import { Logger } from './logger.js';
|
|
6
6
|
/**
|
|
7
7
|
* A command that is to be passed into `concurrently()`.
|
|
8
8
|
* If value is a string, then that's the command's command line.
|
|
@@ -11,7 +11,7 @@ import { Logger } from './logger';
|
|
|
11
11
|
export type ConcurrentlyCommandInput = string | ({
|
|
12
12
|
command: string;
|
|
13
13
|
} & Partial<CommandInfo>);
|
|
14
|
-
export
|
|
14
|
+
export interface ConcurrentlyResult {
|
|
15
15
|
/**
|
|
16
16
|
* All commands created and ran by concurrently.
|
|
17
17
|
*/
|
|
@@ -24,8 +24,8 @@ export type ConcurrentlyResult = {
|
|
|
24
24
|
* spawned; commands that didn't spawn are filtered out.
|
|
25
25
|
*/
|
|
26
26
|
result: Promise<CloseEvent[]>;
|
|
27
|
-
}
|
|
28
|
-
export
|
|
27
|
+
}
|
|
28
|
+
export interface ConcurrentlyOptions {
|
|
29
29
|
logger?: Logger;
|
|
30
30
|
/**
|
|
31
31
|
* Which stream should the commands output be written to.
|
|
@@ -36,7 +36,7 @@ export type ConcurrentlyOptions = {
|
|
|
36
36
|
*/
|
|
37
37
|
group?: boolean;
|
|
38
38
|
/**
|
|
39
|
-
* A comma-separated list of
|
|
39
|
+
* A comma-separated list of Chalk colors or a string for available styles listed below to use on prefixes.
|
|
40
40
|
* If there are more commands than colors, the last color will be repeated.
|
|
41
41
|
*
|
|
42
42
|
* Available modifiers:
|
|
@@ -106,7 +106,7 @@ export type ConcurrentlyOptions = {
|
|
|
106
106
|
* @see ExpandArguments
|
|
107
107
|
*/
|
|
108
108
|
additionalArguments?: string[];
|
|
109
|
-
}
|
|
109
|
+
}
|
|
110
110
|
/**
|
|
111
111
|
* Core concurrently functionality -- spawns the given commands concurrently and
|
|
112
112
|
* returns the commands themselves + the result according to the specified success condition.
|
|
@@ -1,26 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const expand_wildcard_1 = require("./command-parser/expand-wildcard");
|
|
15
|
-
const strip_quotes_1 = require("./command-parser/strip-quotes");
|
|
16
|
-
const completion_listener_1 = require("./completion-listener");
|
|
17
|
-
const output_writer_1 = require("./output-writer");
|
|
18
|
-
const prefix_color_selector_1 = require("./prefix-color-selector");
|
|
19
|
-
const spawn_1 = require("./spawn");
|
|
20
|
-
const utils_1 = require("./utils");
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import { takeUntil } from 'rxjs';
|
|
4
|
+
import treeKill from 'tree-kill';
|
|
5
|
+
import { Command, } from './command.js';
|
|
6
|
+
import { ExpandArguments } from './command-parser/expand-arguments.js';
|
|
7
|
+
import { ExpandShortcut } from './command-parser/expand-shortcut.js';
|
|
8
|
+
import { ExpandWildcard } from './command-parser/expand-wildcard.js';
|
|
9
|
+
import { CompletionListener } from './completion-listener.js';
|
|
10
|
+
import { OutputWriter } from './output-writer.js';
|
|
11
|
+
import { PrefixColorSelector } from './prefix-color-selector.js';
|
|
12
|
+
import { createSpawn, getSpawnOpts } from './spawn.js';
|
|
13
|
+
import { castArray } from './utils.js';
|
|
21
14
|
const defaults = {
|
|
22
|
-
spawn:
|
|
23
|
-
kill:
|
|
15
|
+
spawn: createSpawn(),
|
|
16
|
+
kill: treeKill,
|
|
24
17
|
raw: false,
|
|
25
18
|
controllers: [],
|
|
26
19
|
cwd: undefined,
|
|
@@ -31,18 +24,14 @@ const defaults = {
|
|
|
31
24
|
*
|
|
32
25
|
* @see CompletionListener
|
|
33
26
|
*/
|
|
34
|
-
function concurrently(baseCommands, baseOptions) {
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
export function concurrently(baseCommands, baseOptions) {
|
|
28
|
+
assert.ok(Array.isArray(baseCommands), '[concurrently] commands should be an array');
|
|
29
|
+
assert.notStrictEqual(baseCommands.length, 0, '[concurrently] no commands provided');
|
|
37
30
|
const options = { ...defaults, ...baseOptions };
|
|
38
|
-
const prefixColorSelector = new
|
|
39
|
-
const commandParsers = [
|
|
40
|
-
new strip_quotes_1.StripQuotes(),
|
|
41
|
-
new expand_shortcut_1.ExpandShortcut(),
|
|
42
|
-
new expand_wildcard_1.ExpandWildcard(),
|
|
43
|
-
];
|
|
31
|
+
const prefixColorSelector = new PrefixColorSelector(options.prefixColors || []);
|
|
32
|
+
const commandParsers = [new ExpandShortcut(), new ExpandWildcard()];
|
|
44
33
|
if (options.additionalArguments) {
|
|
45
|
-
commandParsers.push(new
|
|
34
|
+
commandParsers.push(new ExpandArguments(options.additionalArguments));
|
|
46
35
|
}
|
|
47
36
|
const hide = (options.hide || []).map(String);
|
|
48
37
|
let commands = baseCommands
|
|
@@ -50,11 +39,11 @@ function concurrently(baseCommands, baseOptions) {
|
|
|
50
39
|
.flatMap((command) => parseCommand(command, commandParsers))
|
|
51
40
|
.map((command, index) => {
|
|
52
41
|
const hidden = hide.includes(command.name) || hide.includes(String(index));
|
|
53
|
-
return new
|
|
42
|
+
return new Command({
|
|
54
43
|
index,
|
|
55
44
|
prefixColor: prefixColorSelector.getNextColor(),
|
|
56
45
|
...command,
|
|
57
|
-
},
|
|
46
|
+
}, getSpawnOpts({
|
|
58
47
|
ipc: command.ipc,
|
|
59
48
|
stdio: hidden ? 'hidden' : (command.raw ?? options.raw) ? 'raw' : 'normal',
|
|
60
49
|
env: command.env,
|
|
@@ -70,24 +59,24 @@ function concurrently(baseCommands, baseOptions) {
|
|
|
70
59
|
}, { commands, onFinishCallbacks: [] });
|
|
71
60
|
commands = handleResult.commands;
|
|
72
61
|
if (options.logger && options.outputStream) {
|
|
73
|
-
const outputWriter = new
|
|
62
|
+
const outputWriter = new OutputWriter({
|
|
74
63
|
outputStream: options.outputStream,
|
|
75
64
|
group: !!options.group,
|
|
76
65
|
commands,
|
|
77
66
|
});
|
|
78
67
|
options.logger.output
|
|
79
68
|
// Stop trying to write after there's been an error.
|
|
80
|
-
.pipe(
|
|
69
|
+
.pipe(takeUntil(outputWriter.error))
|
|
81
70
|
.subscribe(({ command, text }) => outputWriter.write(command, text));
|
|
82
71
|
}
|
|
83
72
|
const commandsLeft = commands.slice();
|
|
84
73
|
const maxProcesses = Math.max(1, (typeof options.maxProcesses === 'string' && options.maxProcesses.endsWith('%')
|
|
85
|
-
? Math.round((
|
|
74
|
+
? Math.round((os.cpus().length * Number(options.maxProcesses.slice(0, -1))) / 100)
|
|
86
75
|
: Number(options.maxProcesses)) || commandsLeft.length);
|
|
87
76
|
for (let i = 0; i < maxProcesses; i++) {
|
|
88
77
|
maybeRunMore(commandsLeft, options.abortSignal);
|
|
89
78
|
}
|
|
90
|
-
const result = new
|
|
79
|
+
const result = new CompletionListener({ successCondition: options.successCondition })
|
|
91
80
|
.listen(commands, options.abortSignal)
|
|
92
81
|
.finally(() => Promise.all(handleResult.onFinishCallbacks.map((onFinish) => onFinish())));
|
|
93
82
|
return {
|
|
@@ -99,7 +88,7 @@ function mapToCommandInfo(command) {
|
|
|
99
88
|
if (typeof command === 'string') {
|
|
100
89
|
return mapToCommandInfo({ command });
|
|
101
90
|
}
|
|
102
|
-
|
|
91
|
+
assert.ok(command.command, '[concurrently] command cannot be empty');
|
|
103
92
|
return {
|
|
104
93
|
command: command.command,
|
|
105
94
|
name: command.name || '',
|
|
@@ -119,7 +108,7 @@ function mapToCommandInfo(command) {
|
|
|
119
108
|
};
|
|
120
109
|
}
|
|
121
110
|
function parseCommand(command, parsers) {
|
|
122
|
-
return parsers.reduce((commands, parser) => commands.flatMap((command) => parser.parse(command)),
|
|
111
|
+
return parsers.reduce((commands, parser) => commands.flatMap((command) => parser.parse(command)), castArray(command));
|
|
123
112
|
}
|
|
124
113
|
function maybeRunMore(commandsLeft, abortSignal) {
|
|
125
114
|
const command = commandsLeft.shift();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|