concurrently 6.5.0 → 7.1.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 +82 -22
- package/dist/bin/concurrently.d.ts +2 -0
- package/dist/bin/concurrently.js +193 -0
- package/dist/bin/epilogue.d.ts +1 -0
- package/dist/bin/epilogue.js +69 -0
- package/dist/src/command-parser/command-parser.d.ts +19 -0
- package/dist/src/command-parser/command-parser.js +2 -0
- package/dist/src/command-parser/expand-npm-shortcut.d.ts +8 -0
- package/{src → dist/src}/command-parser/expand-npm-shortcut.js +10 -3
- package/dist/src/command-parser/expand-npm-wildcard.d.ts +13 -0
- package/dist/src/command-parser/expand-npm-wildcard.js +82 -0
- package/dist/src/command-parser/strip-quotes.d.ts +10 -0
- package/{src → dist/src}/command-parser/strip-quotes.js +10 -4
- package/dist/src/command.d.ts +101 -0
- package/{src → dist/src}/command.js +53 -29
- package/dist/src/completion-listener.d.ts +37 -0
- package/dist/src/completion-listener.js +60 -0
- package/dist/src/concurrently.d.ts +77 -0
- package/dist/src/concurrently.js +120 -0
- package/dist/src/defaults.d.ts +52 -0
- package/dist/src/defaults.js +58 -0
- package/dist/src/flow-control/flow-controller.d.ts +13 -0
- package/dist/src/flow-control/flow-controller.js +2 -0
- package/dist/src/flow-control/input-handler.d.ts +33 -0
- package/dist/src/flow-control/input-handler.js +73 -0
- package/dist/src/flow-control/kill-on-signal.d.ts +17 -0
- package/{src → dist/src}/flow-control/kill-on-signal.js +13 -11
- package/dist/src/flow-control/kill-others.d.ts +18 -0
- package/dist/src/flow-control/kill-others.js +35 -0
- package/dist/src/flow-control/log-error.d.ts +15 -0
- package/dist/src/flow-control/log-error.js +21 -0
- package/dist/src/flow-control/log-exit.d.ts +15 -0
- package/dist/src/flow-control/log-exit.js +19 -0
- package/dist/src/flow-control/log-output.d.ts +15 -0
- package/{src → dist/src}/flow-control/log-output.js +13 -5
- package/dist/src/flow-control/log-timings.d.ts +27 -0
- package/dist/src/flow-control/log-timings.js +88 -0
- package/dist/src/flow-control/restart-process.d.ts +22 -0
- package/dist/src/flow-control/restart-process.js +71 -0
- package/dist/src/get-spawn-opts.d.ts +30 -0
- package/dist/src/get-spawn-opts.js +11 -0
- package/dist/src/index.d.ts +69 -0
- package/dist/src/index.js +69 -0
- package/dist/src/logger.d.ts +72 -0
- package/{src → dist/src}/logger.js +77 -58
- package/dist/src/output-writer.d.ts +19 -0
- package/dist/src/output-writer.js +69 -0
- package/index.js +6 -68
- package/index.mjs +9 -0
- package/package.json +38 -11
- package/bin/concurrently.js +0 -186
- package/bin/concurrently.spec.js +0 -428
- package/bin/epilogue.txt +0 -46
- package/src/command-parser/expand-npm-shortcut.spec.js +0 -36
- package/src/command-parser/expand-npm-wildcard.js +0 -43
- package/src/command-parser/expand-npm-wildcard.spec.js +0 -87
- package/src/command-parser/strip-quotes.spec.js +0 -20
- package/src/command.spec.js +0 -275
- package/src/completion-listener.js +0 -39
- package/src/completion-listener.spec.js +0 -89
- package/src/concurrently.js +0 -116
- package/src/concurrently.spec.js +0 -199
- package/src/defaults.js +0 -35
- package/src/flow-control/base-handler.js +0 -16
- package/src/flow-control/base-handler.spec.js +0 -22
- package/src/flow-control/input-handler.js +0 -50
- package/src/flow-control/input-handler.spec.js +0 -113
- package/src/flow-control/kill-on-signal.spec.js +0 -79
- package/src/flow-control/kill-others.js +0 -38
- package/src/flow-control/kill-others.spec.js +0 -66
- package/src/flow-control/log-error.js +0 -18
- package/src/flow-control/log-error.spec.js +0 -40
- package/src/flow-control/log-exit.js +0 -11
- package/src/flow-control/log-exit.spec.js +0 -36
- package/src/flow-control/log-output.spec.js +0 -41
- package/src/flow-control/log-timings.js +0 -64
- package/src/flow-control/log-timings.spec.js +0 -137
- package/src/flow-control/restart-process.js +0 -56
- package/src/flow-control/restart-process.spec.js +0 -139
- package/src/get-spawn-opts.js +0 -16
- package/src/get-spawn-opts.spec.js +0 -30
- package/src/logger.spec.js +0 -318
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
module.exports = class ExpandNpmWildcard {
|
|
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) {
|
|
15
|
-
this.readPackage = readPackage;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
parse(commandInfo) {
|
|
19
|
-
const [, npmCmd, cmdName, args] = commandInfo.command.match(/(npm|yarn|pnpm) run (\S+)([^&]*)/) || [];
|
|
20
|
-
const wildcardPosition = (cmdName || '').indexOf('*');
|
|
21
|
-
|
|
22
|
-
// If the regex didn't match an npm script, or it has no wildcard,
|
|
23
|
-
// then we have nothing to do here
|
|
24
|
-
if (!cmdName || wildcardPosition === -1) {
|
|
25
|
-
return commandInfo;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (!this.scripts) {
|
|
29
|
-
this.scripts = Object.keys(this.readPackage().scripts || {});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const preWildcard = _.escapeRegExp(cmdName.substr(0, wildcardPosition));
|
|
33
|
-
const postWildcard = _.escapeRegExp(cmdName.substr(wildcardPosition + 1));
|
|
34
|
-
const wildcardRegex = new RegExp(`^${preWildcard}(.*?)${postWildcard}$`);
|
|
35
|
-
|
|
36
|
-
return this.scripts
|
|
37
|
-
.filter(script => wildcardRegex.test(script))
|
|
38
|
-
.map(script => Object.assign({}, commandInfo, {
|
|
39
|
-
command: `${npmCmd} run ${script}${args}`,
|
|
40
|
-
name: script
|
|
41
|
-
}));
|
|
42
|
-
}
|
|
43
|
-
};
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
const ExpandNpmWildcard = require('./expand-npm-wildcard');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
|
|
4
|
-
let parser, readPkg;
|
|
5
|
-
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
readPkg = jest.fn();
|
|
8
|
-
parser = new ExpandNpmWildcard(readPkg);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
describe('ExpandNpmWildcard#readPackage', () => {
|
|
12
|
-
it('can read package', () => {
|
|
13
|
-
const expectedPackage = {
|
|
14
|
-
'name': 'concurrently',
|
|
15
|
-
'version': '6.4.0',
|
|
16
|
-
};
|
|
17
|
-
jest.spyOn(fs, 'readFileSync').mockImplementation((path, options) => {
|
|
18
|
-
if (path === 'package.json') {
|
|
19
|
-
return JSON.stringify(expectedPackage);
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const actualReadPackage = ExpandNpmWildcard.readPackage();
|
|
25
|
-
expect(actualReadPackage).toEqual(expectedPackage);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('can handle errors reading package', () => {
|
|
29
|
-
jest.spyOn(fs, 'readFileSync').mockImplementation(() => {
|
|
30
|
-
throw new Error('Error reading package');
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
expect(() => ExpandNpmWildcard.readPackage()).not.toThrow();
|
|
34
|
-
expect(ExpandNpmWildcard.readPackage()).toEqual({});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('returns same command if not an npm run command', () => {
|
|
40
|
-
const commandInfo = {
|
|
41
|
-
command: 'npm test'
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
expect(readPkg).not.toHaveBeenCalled();
|
|
45
|
-
expect(parser.parse(commandInfo)).toBe(commandInfo);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('returns same command if no wildcard present', () => {
|
|
49
|
-
const commandInfo = {
|
|
50
|
-
command: 'npm run foo bar'
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
expect(readPkg).not.toHaveBeenCalled();
|
|
54
|
-
expect(parser.parse(commandInfo)).toBe(commandInfo);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('expands to nothing if no scripts exist in package.json', () => {
|
|
58
|
-
readPkg.mockReturnValue({});
|
|
59
|
-
|
|
60
|
-
expect(parser.parse({ command: 'npm run foo-*-baz qux' })).toEqual([]);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
for (const npmCmd of ['npm', 'yarn', 'pnpm']) {
|
|
64
|
-
describe(`with an ${npmCmd}: prefix`, () => {
|
|
65
|
-
it('expands to all scripts matching pattern', () => {
|
|
66
|
-
readPkg.mockReturnValue({
|
|
67
|
-
scripts: {
|
|
68
|
-
'foo-bar-baz': '',
|
|
69
|
-
'foo--baz': '',
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
expect(parser.parse({ command: `${npmCmd} run foo-*-baz qux` })).toEqual([
|
|
74
|
-
{ name: 'foo-bar-baz', command: `${npmCmd} run foo-bar-baz qux` },
|
|
75
|
-
{ name: 'foo--baz', command: `${npmCmd} run foo--baz qux` },
|
|
76
|
-
]);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('caches scripts upon calls', () => {
|
|
80
|
-
readPkg.mockReturnValue({});
|
|
81
|
-
parser.parse({ command: `${npmCmd} run foo-*-baz qux` });
|
|
82
|
-
parser.parse({ command: `${npmCmd} run foo-*-baz qux` });
|
|
83
|
-
|
|
84
|
-
expect(readPkg).toHaveBeenCalledTimes(1);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
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.spec.js
DELETED
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
const EventEmitter = require('events');
|
|
2
|
-
const process = require('process');
|
|
3
|
-
const Command = require('./command');
|
|
4
|
-
|
|
5
|
-
const createProcess = () => {
|
|
6
|
-
const process = new EventEmitter();
|
|
7
|
-
process.pid = 1;
|
|
8
|
-
return process;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const createProcessWithIO = () => {
|
|
12
|
-
const process = createProcess();
|
|
13
|
-
return Object.assign(process, {
|
|
14
|
-
stdout: new EventEmitter(),
|
|
15
|
-
stderr: new EventEmitter(),
|
|
16
|
-
stdin: new EventEmitter()
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
describe('#start()', () => {
|
|
21
|
-
it('spawns process with given command and options', () => {
|
|
22
|
-
const spawn = jest.fn().mockReturnValue(createProcess());
|
|
23
|
-
const command = new Command({
|
|
24
|
-
spawn,
|
|
25
|
-
spawnOpts: { bla: true },
|
|
26
|
-
command: 'echo foo',
|
|
27
|
-
});
|
|
28
|
-
command.start();
|
|
29
|
-
|
|
30
|
-
expect(spawn).toHaveBeenCalledTimes(1);
|
|
31
|
-
expect(spawn).toHaveBeenCalledWith(command.command, { bla: true });
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('sets stdin, process and PID', () => {
|
|
35
|
-
const process = createProcessWithIO();
|
|
36
|
-
const command = new Command({ spawn: () => process });
|
|
37
|
-
|
|
38
|
-
command.start();
|
|
39
|
-
expect(command.process).toBe(process);
|
|
40
|
-
expect(command.pid).toBe(process.pid);
|
|
41
|
-
expect(command.stdin).toBe(process.stdin);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('shares errors to the error stream', done => {
|
|
45
|
-
const process = createProcess();
|
|
46
|
-
const command = new Command({ spawn: () => process });
|
|
47
|
-
|
|
48
|
-
command.error.subscribe(data => {
|
|
49
|
-
expect(data).toBe('foo');
|
|
50
|
-
expect(command.process).toBeUndefined();
|
|
51
|
-
done();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
command.start();
|
|
55
|
-
process.emit('error', 'foo');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('shares start and close timing events to the timing stream', done => {
|
|
59
|
-
const process = createProcess();
|
|
60
|
-
const command = new Command({ spawn: () => process });
|
|
61
|
-
|
|
62
|
-
const startDate = new Date();
|
|
63
|
-
const endDate = new Date(startDate.getTime() + 1000);
|
|
64
|
-
jest.spyOn(Date, 'now')
|
|
65
|
-
.mockReturnValueOnce(startDate.getTime())
|
|
66
|
-
.mockReturnValueOnce(endDate.getTime());
|
|
67
|
-
|
|
68
|
-
let callCount = 0;
|
|
69
|
-
command.timer.subscribe(({startDate: actualStartDate, endDate: actualEndDate}) => {
|
|
70
|
-
switch (callCount) {
|
|
71
|
-
case 0:
|
|
72
|
-
expect(actualStartDate).toStrictEqual(startDate);
|
|
73
|
-
expect(actualEndDate).toBeUndefined();
|
|
74
|
-
break;
|
|
75
|
-
case 1:
|
|
76
|
-
expect(actualStartDate).toStrictEqual(startDate);
|
|
77
|
-
expect(actualEndDate).toStrictEqual(endDate);
|
|
78
|
-
done();
|
|
79
|
-
break;
|
|
80
|
-
default:
|
|
81
|
-
throw new Error('Unexpected call count');
|
|
82
|
-
}
|
|
83
|
-
callCount++;
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
command.start();
|
|
87
|
-
process.emit('close', 0, null);
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('shares start and error timing events to the timing stream', done => {
|
|
92
|
-
const process = createProcess();
|
|
93
|
-
const command = new Command({ spawn: () => process });
|
|
94
|
-
|
|
95
|
-
const startDate = new Date();
|
|
96
|
-
const endDate = new Date(startDate.getTime() + 1000);
|
|
97
|
-
jest.spyOn(Date, 'now')
|
|
98
|
-
.mockReturnValueOnce(startDate.getTime())
|
|
99
|
-
.mockReturnValueOnce(endDate.getTime());
|
|
100
|
-
|
|
101
|
-
let callCount = 0;
|
|
102
|
-
command.timer.subscribe(({startDate: actualStartDate, endDate: actualEndDate}) => {
|
|
103
|
-
switch (callCount) {
|
|
104
|
-
case 0:
|
|
105
|
-
expect(actualStartDate).toStrictEqual(startDate);
|
|
106
|
-
expect(actualEndDate).toBeUndefined();
|
|
107
|
-
break;
|
|
108
|
-
case 1:
|
|
109
|
-
expect(actualStartDate).toStrictEqual(startDate);
|
|
110
|
-
expect(actualEndDate).toStrictEqual(endDate);
|
|
111
|
-
done();
|
|
112
|
-
break;
|
|
113
|
-
default:
|
|
114
|
-
throw new Error('Unexpected call count');
|
|
115
|
-
}
|
|
116
|
-
callCount++;
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
command.start();
|
|
120
|
-
process.emit('error', 0, null);
|
|
121
|
-
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('shares closes to the close stream with exit code', done => {
|
|
125
|
-
const process = createProcess();
|
|
126
|
-
const command = new Command({ spawn: () => process });
|
|
127
|
-
|
|
128
|
-
command.close.subscribe(data => {
|
|
129
|
-
expect(data.exitCode).toBe(0);
|
|
130
|
-
expect(data.killed).toBe(false);
|
|
131
|
-
expect(command.process).toBeUndefined();
|
|
132
|
-
done();
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
command.start();
|
|
136
|
-
process.emit('close', 0, null);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('shares closes to the close stream with signal', done => {
|
|
140
|
-
const process = createProcess();
|
|
141
|
-
const command = new Command({ spawn: () => process });
|
|
142
|
-
|
|
143
|
-
command.close.subscribe(data => {
|
|
144
|
-
expect(data.exitCode).toBe('SIGKILL');
|
|
145
|
-
expect(data.killed).toBe(false);
|
|
146
|
-
done();
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
command.start();
|
|
150
|
-
process.emit('close', null, 'SIGKILL');
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('shares closes to the close stream with timing information', done => {
|
|
154
|
-
const process1 = createProcess();
|
|
155
|
-
const command = new Command({ spawn: () => process1 });
|
|
156
|
-
|
|
157
|
-
const startDate = new Date();
|
|
158
|
-
const endDate = new Date(startDate.getTime() + 1000);
|
|
159
|
-
jest.spyOn(Date, 'now')
|
|
160
|
-
.mockReturnValueOnce(startDate.getTime())
|
|
161
|
-
.mockReturnValueOnce(endDate.getTime());
|
|
162
|
-
|
|
163
|
-
jest.spyOn(process, 'hrtime')
|
|
164
|
-
.mockReturnValueOnce([0, 0])
|
|
165
|
-
.mockReturnValueOnce([1, 1e8]);
|
|
166
|
-
|
|
167
|
-
command.close.subscribe(data => {
|
|
168
|
-
expect(data.timings.startDate).toStrictEqual(startDate);
|
|
169
|
-
expect(data.timings.endDate).toStrictEqual(endDate);
|
|
170
|
-
expect(data.timings.durationSeconds).toBe(1.1);
|
|
171
|
-
done();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
command.start();
|
|
175
|
-
process1.emit('close', null, 'SIGKILL');
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('shares closes to the close stream with command info and index', done => {
|
|
179
|
-
const process = createProcess();
|
|
180
|
-
const commandInfo = {
|
|
181
|
-
command: 'cmd',
|
|
182
|
-
name: 'name',
|
|
183
|
-
prefixColor: 'green',
|
|
184
|
-
env: { VAR: 'yes' },
|
|
185
|
-
};
|
|
186
|
-
const command = new Command(
|
|
187
|
-
Object.assign({
|
|
188
|
-
index: 1,
|
|
189
|
-
spawn: () => process
|
|
190
|
-
}, commandInfo)
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
command.close.subscribe(data => {
|
|
194
|
-
expect(data.command).toEqual(commandInfo);
|
|
195
|
-
expect(data.killed).toBe(false);
|
|
196
|
-
expect(data.index).toBe(1);
|
|
197
|
-
done();
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
command.start();
|
|
201
|
-
process.emit('close', 0, null);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('shares stdout to the stdout stream', done => {
|
|
205
|
-
const process = createProcessWithIO();
|
|
206
|
-
const command = new Command({ spawn: () => process });
|
|
207
|
-
|
|
208
|
-
command.stdout.subscribe(data => {
|
|
209
|
-
expect(data.toString()).toBe('hello');
|
|
210
|
-
done();
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
command.start();
|
|
214
|
-
process.stdout.emit('data', Buffer.from('hello'));
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('shares stderr to the stdout stream', done => {
|
|
218
|
-
const process = createProcessWithIO();
|
|
219
|
-
const command = new Command({ spawn: () => process });
|
|
220
|
-
|
|
221
|
-
command.stderr.subscribe(data => {
|
|
222
|
-
expect(data.toString()).toBe('dang');
|
|
223
|
-
done();
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
command.start();
|
|
227
|
-
process.stderr.emit('data', Buffer.from('dang'));
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
describe('#kill()', () => {
|
|
232
|
-
let process, killProcess, command;
|
|
233
|
-
beforeEach(() => {
|
|
234
|
-
process = createProcess();
|
|
235
|
-
killProcess = jest.fn();
|
|
236
|
-
command = new Command({ spawn: () => process, killProcess });
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
it('kills process', () => {
|
|
240
|
-
command.start();
|
|
241
|
-
command.kill();
|
|
242
|
-
|
|
243
|
-
expect(killProcess).toHaveBeenCalledTimes(1);
|
|
244
|
-
expect(killProcess).toHaveBeenCalledWith(command.pid, undefined);
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
it('kills process with some signal', () => {
|
|
248
|
-
command.start();
|
|
249
|
-
command.kill('SIGKILL');
|
|
250
|
-
|
|
251
|
-
expect(killProcess).toHaveBeenCalledTimes(1);
|
|
252
|
-
expect(killProcess).toHaveBeenCalledWith(command.pid, 'SIGKILL');
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it('does not try to kill inexistent process', () => {
|
|
256
|
-
command.start();
|
|
257
|
-
process.emit('error');
|
|
258
|
-
command.kill();
|
|
259
|
-
|
|
260
|
-
expect(killProcess).not.toHaveBeenCalled();
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it('marks the command as killed', done => {
|
|
264
|
-
command.start();
|
|
265
|
-
|
|
266
|
-
command.close.subscribe(data => {
|
|
267
|
-
expect(data.exitCode).toBe(1);
|
|
268
|
-
expect(data.killed).toBe(true);
|
|
269
|
-
done();
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
command.kill();
|
|
273
|
-
process.emit('close', 1, null);
|
|
274
|
-
});
|
|
275
|
-
});
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
const Rx = require('rxjs');
|
|
2
|
-
const { bufferCount, switchMap, take } = require('rxjs/operators');
|
|
3
|
-
|
|
4
|
-
module.exports = class CompletionListener {
|
|
5
|
-
constructor({ successCondition, scheduler }) {
|
|
6
|
-
this.successCondition = successCondition;
|
|
7
|
-
this.scheduler = scheduler;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
isSuccess(exitCodes) {
|
|
11
|
-
switch (this.successCondition) {
|
|
12
|
-
/* eslint-disable indent */
|
|
13
|
-
case 'first':
|
|
14
|
-
return exitCodes[0] === 0;
|
|
15
|
-
|
|
16
|
-
case 'last':
|
|
17
|
-
return exitCodes[exitCodes.length - 1] === 0;
|
|
18
|
-
|
|
19
|
-
default:
|
|
20
|
-
return exitCodes.every(exitCode => exitCode === 0);
|
|
21
|
-
/* eslint-enable indent */
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
listen(commands) {
|
|
26
|
-
const closeStreams = commands.map(command => command.close);
|
|
27
|
-
return Rx.merge(...closeStreams)
|
|
28
|
-
.pipe(
|
|
29
|
-
bufferCount(closeStreams.length),
|
|
30
|
-
switchMap(exitInfos =>
|
|
31
|
-
this.isSuccess(exitInfos.map(({ exitCode }) => exitCode))
|
|
32
|
-
? Rx.of(exitInfos, this.scheduler)
|
|
33
|
-
: Rx.throwError(exitInfos, this.scheduler)
|
|
34
|
-
),
|
|
35
|
-
take(1),
|
|
36
|
-
)
|
|
37
|
-
.toPromise();
|
|
38
|
-
}
|
|
39
|
-
};
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
const { TestScheduler } = require('rxjs/testing');
|
|
2
|
-
|
|
3
|
-
const createFakeCommand = require('./flow-control/fixtures/fake-command');
|
|
4
|
-
const CompletionListener = require('./completion-listener');
|
|
5
|
-
|
|
6
|
-
let commands, scheduler;
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
commands = [createFakeCommand('foo'), createFakeCommand('bar')];
|
|
9
|
-
scheduler = new TestScheduler();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
const createController = successCondition =>
|
|
13
|
-
new CompletionListener({
|
|
14
|
-
successCondition,
|
|
15
|
-
scheduler
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe('with default success condition set', () => {
|
|
19
|
-
it('succeeds if all processes exited with code 0', () => {
|
|
20
|
-
const result = createController().listen(commands);
|
|
21
|
-
|
|
22
|
-
commands[0].close.next({ exitCode: 0 });
|
|
23
|
-
commands[1].close.next({ exitCode: 0 });
|
|
24
|
-
|
|
25
|
-
scheduler.flush();
|
|
26
|
-
|
|
27
|
-
return expect(result).resolves.toEqual([{ exitCode: 0 }, { exitCode: 0 }]);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('fails if one of the processes exited with non-0 code', () => {
|
|
31
|
-
const result = createController().listen(commands);
|
|
32
|
-
|
|
33
|
-
commands[0].close.next({ exitCode: 0 });
|
|
34
|
-
commands[1].close.next({ exitCode: 1 });
|
|
35
|
-
|
|
36
|
-
scheduler.flush();
|
|
37
|
-
|
|
38
|
-
expect(result).rejects.toEqual([{ exitCode: 0 }, { exitCode: 1 }]);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe('with success condition set to first', () => {
|
|
43
|
-
it('succeeds if first process to exit has code 0', () => {
|
|
44
|
-
const result = createController('first').listen(commands);
|
|
45
|
-
|
|
46
|
-
commands[1].close.next({ exitCode: 0 });
|
|
47
|
-
commands[0].close.next({ exitCode: 1 });
|
|
48
|
-
|
|
49
|
-
scheduler.flush();
|
|
50
|
-
|
|
51
|
-
return expect(result).resolves.toEqual([{ exitCode: 0 }, { exitCode: 1 }]);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('fails if first process to exit has non-0 code', () => {
|
|
55
|
-
const result = createController('first').listen(commands);
|
|
56
|
-
|
|
57
|
-
commands[1].close.next({ exitCode: 1 });
|
|
58
|
-
commands[0].close.next({ exitCode: 0 });
|
|
59
|
-
|
|
60
|
-
scheduler.flush();
|
|
61
|
-
|
|
62
|
-
return expect(result).rejects.toEqual([{ exitCode: 1 }, { exitCode: 0 }]);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe('with success condition set to last', () => {
|
|
67
|
-
it('succeeds if last process to exit has code 0', () => {
|
|
68
|
-
const result = createController('last').listen(commands);
|
|
69
|
-
|
|
70
|
-
commands[1].close.next({ exitCode: 1 });
|
|
71
|
-
commands[0].close.next({ exitCode: 0 });
|
|
72
|
-
|
|
73
|
-
scheduler.flush();
|
|
74
|
-
|
|
75
|
-
return expect(result).resolves.toEqual([{ exitCode: 1 }, { exitCode: 0 }]);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('fails if last process to exit has non-0 code', () => {
|
|
79
|
-
const result = createController('last').listen(commands);
|
|
80
|
-
|
|
81
|
-
commands[1].close.next({ exitCode: 0 });
|
|
82
|
-
commands[0].close.next({ exitCode: 1 });
|
|
83
|
-
|
|
84
|
-
scheduler.flush();
|
|
85
|
-
|
|
86
|
-
return expect(result).rejects.toEqual([{ exitCode: 0 }, { exitCode: 1 }]);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
});
|
package/src/concurrently.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
const assert = require('assert');
|
|
2
|
-
const _ = require('lodash');
|
|
3
|
-
const spawn = require('spawn-command');
|
|
4
|
-
const treeKill = require('tree-kill');
|
|
5
|
-
|
|
6
|
-
const StripQuotes = require('./command-parser/strip-quotes');
|
|
7
|
-
const ExpandNpmShortcut = require('./command-parser/expand-npm-shortcut');
|
|
8
|
-
const ExpandNpmWildcard = require('./command-parser/expand-npm-wildcard');
|
|
9
|
-
|
|
10
|
-
const CompletionListener = require('./completion-listener');
|
|
11
|
-
|
|
12
|
-
const getSpawnOpts = require('./get-spawn-opts');
|
|
13
|
-
const Command = require('./command');
|
|
14
|
-
|
|
15
|
-
const defaults = {
|
|
16
|
-
spawn,
|
|
17
|
-
kill: treeKill,
|
|
18
|
-
raw: false,
|
|
19
|
-
controllers: [],
|
|
20
|
-
cwd: undefined,
|
|
21
|
-
timings: false
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
module.exports = (commands, options) => {
|
|
25
|
-
assert.ok(Array.isArray(commands), '[concurrently] commands should be an array');
|
|
26
|
-
assert.notStrictEqual(commands.length, 0, '[concurrently] no commands provided');
|
|
27
|
-
|
|
28
|
-
options = _.defaults(options, defaults);
|
|
29
|
-
|
|
30
|
-
const commandParsers = [
|
|
31
|
-
new StripQuotes(),
|
|
32
|
-
new ExpandNpmShortcut(),
|
|
33
|
-
new ExpandNpmWildcard()
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
let lastColor = '';
|
|
37
|
-
commands = _(commands)
|
|
38
|
-
.map(mapToCommandInfo)
|
|
39
|
-
.flatMap(command => parseCommand(command, commandParsers))
|
|
40
|
-
.map((command, index) => {
|
|
41
|
-
// Use documented behaviour of repeating last color when specifying more commands than colors
|
|
42
|
-
lastColor = options.prefixColors && options.prefixColors[index] || lastColor;
|
|
43
|
-
return new Command(
|
|
44
|
-
Object.assign({
|
|
45
|
-
index,
|
|
46
|
-
spawnOpts: getSpawnOpts({
|
|
47
|
-
raw: options.raw,
|
|
48
|
-
env: command.env,
|
|
49
|
-
cwd: command.cwd || options.cwd,
|
|
50
|
-
}),
|
|
51
|
-
prefixColor: lastColor,
|
|
52
|
-
killProcess: options.kill,
|
|
53
|
-
spawn: options.spawn,
|
|
54
|
-
timings: options.timings,
|
|
55
|
-
}, command)
|
|
56
|
-
);
|
|
57
|
-
})
|
|
58
|
-
.value();
|
|
59
|
-
|
|
60
|
-
const handleResult = options.controllers.reduce(
|
|
61
|
-
({ commands: prevCommands, onFinishCallbacks }, controller) => {
|
|
62
|
-
const { commands, onFinish } = controller.handle(prevCommands);
|
|
63
|
-
return {
|
|
64
|
-
commands,
|
|
65
|
-
onFinishCallbacks: _.concat(onFinishCallbacks, onFinish ? [onFinish] : [])
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
{ commands, onFinishCallbacks: [] }
|
|
69
|
-
);
|
|
70
|
-
commands = handleResult.commands;
|
|
71
|
-
|
|
72
|
-
const commandsLeft = commands.slice();
|
|
73
|
-
const maxProcesses = Math.max(1, Number(options.maxProcesses) || commandsLeft.length);
|
|
74
|
-
for (let i = 0; i < maxProcesses; i++) {
|
|
75
|
-
maybeRunMore(commandsLeft);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return new CompletionListener({
|
|
79
|
-
successCondition: options.successCondition,
|
|
80
|
-
})
|
|
81
|
-
.listen(commands)
|
|
82
|
-
.finally(() => {
|
|
83
|
-
handleResult.onFinishCallbacks.forEach((onFinish) => onFinish());
|
|
84
|
-
});
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
function mapToCommandInfo(command) {
|
|
88
|
-
return Object.assign({
|
|
89
|
-
command: command.command || command,
|
|
90
|
-
name: command.name || '',
|
|
91
|
-
env: command.env || {},
|
|
92
|
-
cwd: command.cwd || '',
|
|
93
|
-
|
|
94
|
-
}, command.prefixColor ? {
|
|
95
|
-
prefixColor: command.prefixColor,
|
|
96
|
-
} : {});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function parseCommand(command, parsers) {
|
|
100
|
-
return parsers.reduce(
|
|
101
|
-
(commands, parser) => _.flatMap(commands, command => parser.parse(command)),
|
|
102
|
-
_.castArray(command)
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function maybeRunMore(commandsLeft) {
|
|
107
|
-
const command = commandsLeft.shift();
|
|
108
|
-
if (!command) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
command.start();
|
|
113
|
-
command.close.subscribe(() => {
|
|
114
|
-
maybeRunMore(commandsLeft);
|
|
115
|
-
});
|
|
116
|
-
}
|