concurrently 8.2.1 → 9.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 +35 -240
- package/dist/bin/concurrently.js +37 -22
- package/dist/bin/read-package.d.ts +6 -0
- package/dist/bin/read-package.js +47 -0
- package/dist/src/command-parser/expand-arguments.d.ts +1 -0
- package/dist/src/command-parser/expand-arguments.js +1 -0
- package/dist/src/command-parser/expand-npm-shortcut.d.ts +1 -1
- package/dist/src/command-parser/expand-npm-shortcut.js +4 -3
- package/dist/src/command-parser/expand-npm-wildcard.js +4 -2
- package/dist/src/command-parser/strip-quotes.d.ts +1 -0
- package/dist/src/command.d.ts +46 -5
- package/dist/src/command.js +91 -16
- package/dist/src/completion-listener.d.ts +4 -1
- package/dist/src/completion-listener.js +27 -6
- package/dist/src/concurrently.d.ts +30 -5
- package/dist/src/concurrently.js +15 -14
- package/dist/src/date-format.d.ts +19 -0
- package/dist/src/date-format.js +318 -0
- package/dist/src/defaults.d.ts +1 -1
- package/dist/src/defaults.js +1 -1
- package/dist/src/flow-control/flow-controller.d.ts +1 -1
- package/dist/src/flow-control/input-handler.js +4 -0
- package/dist/src/flow-control/kill-on-signal.d.ts +4 -1
- package/dist/src/flow-control/kill-on-signal.js +8 -1
- package/dist/src/flow-control/kill-others.d.ts +4 -1
- package/dist/src/flow-control/kill-others.js +7 -1
- package/dist/src/flow-control/log-error.js +1 -0
- package/dist/src/flow-control/log-exit.js +1 -0
- package/dist/src/flow-control/log-output.js +1 -0
- package/dist/src/flow-control/log-timings.d.ts +1 -1
- package/dist/src/flow-control/log-timings.js +6 -4
- package/dist/src/flow-control/logger-padding.d.ts +13 -0
- package/dist/src/flow-control/logger-padding.js +35 -0
- package/dist/src/flow-control/restart-process.d.ts +3 -2
- package/dist/src/flow-control/restart-process.js +14 -2
- package/dist/src/flow-control/teardown.d.ts +21 -0
- package/dist/src/flow-control/teardown.js +72 -0
- package/dist/src/index.d.ts +18 -8
- package/dist/src/index.js +28 -7
- package/dist/src/logger.d.ts +25 -10
- package/dist/src/logger.js +78 -39
- package/dist/src/output-writer.js +6 -2
- package/dist/src/prefix-color-selector.d.ts +1 -1
- package/dist/src/prefix-color-selector.js +3 -1
- package/dist/src/{get-spawn-opts.d.ts → spawn.d.ts} +20 -5
- package/dist/src/spawn.js +49 -0
- package/docs/README.md +13 -0
- package/docs/cli/configuration.md +11 -0
- package/docs/cli/input-handling.md +40 -0
- package/docs/cli/output-control.md +35 -0
- package/docs/cli/passthrough-arguments.md +80 -0
- package/docs/cli/prefixing.md +147 -0
- package/docs/cli/restarting.md +38 -0
- package/docs/cli/shortcuts.md +72 -0
- package/docs/demo.gif +0 -0
- package/index.d.mts +7 -0
- package/index.d.ts +11 -0
- package/index.js +6 -1
- package/index.mjs +2 -2
- package/package.json +41 -33
- package/dist/bin/epilogue.d.ts +0 -1
- package/dist/bin/epilogue.js +0 -90
- package/dist/src/get-spawn-opts.js +0 -18
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.Teardown = void 0;
|
|
27
|
+
const Rx = __importStar(require("rxjs"));
|
|
28
|
+
const spawn_1 = require("../spawn");
|
|
29
|
+
class Teardown {
|
|
30
|
+
logger;
|
|
31
|
+
spawn;
|
|
32
|
+
teardown;
|
|
33
|
+
constructor({ logger, spawn, commands, }) {
|
|
34
|
+
this.logger = logger;
|
|
35
|
+
this.spawn = spawn || spawn_1.spawn;
|
|
36
|
+
this.teardown = commands;
|
|
37
|
+
}
|
|
38
|
+
handle(commands) {
|
|
39
|
+
const { logger, teardown, spawn } = this;
|
|
40
|
+
const onFinish = async () => {
|
|
41
|
+
if (!teardown.length) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
for (const command of teardown) {
|
|
45
|
+
logger.logGlobalEvent(`Running teardown command "${command}"`);
|
|
46
|
+
const child = spawn(command, (0, spawn_1.getSpawnOpts)({ stdio: 'raw' }));
|
|
47
|
+
const error = Rx.fromEvent(child, 'error');
|
|
48
|
+
const close = Rx.fromEvent(child, 'close');
|
|
49
|
+
try {
|
|
50
|
+
const [exitCode, signal] = await Promise.race([
|
|
51
|
+
Rx.firstValueFrom(error).then((event) => {
|
|
52
|
+
throw event;
|
|
53
|
+
}),
|
|
54
|
+
Rx.firstValueFrom(close).then((event) => event),
|
|
55
|
+
]);
|
|
56
|
+
logger.logGlobalEvent(`Teardown command "${command}" exited with code ${exitCode ?? signal}`);
|
|
57
|
+
if (signal === 'SIGINT') {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const errorText = String(error instanceof Error ? error.stack || error : error);
|
|
63
|
+
logger.logGlobalEvent(`Teardown command "${command}" errored:`);
|
|
64
|
+
logger.logGlobalEvent(errorText);
|
|
65
|
+
return Promise.reject();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
return { commands, onFinish };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.Teardown = Teardown;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Readable } from 'stream';
|
|
3
3
|
import { CloseEvent, Command, CommandIdentifier, TimerEvent } from './command';
|
|
4
|
-
import { concurrently, ConcurrentlyCommandInput, ConcurrentlyOptions as BaseConcurrentlyOptions, ConcurrentlyResult } from './concurrently';
|
|
4
|
+
import { concurrently as createConcurrently, ConcurrentlyCommandInput, ConcurrentlyOptions as BaseConcurrentlyOptions, ConcurrentlyResult } from './concurrently';
|
|
5
5
|
import { FlowController } from './flow-control/flow-controller';
|
|
6
6
|
import { InputHandler } from './flow-control/input-handler';
|
|
7
7
|
import { KillOnSignal } from './flow-control/kill-on-signal';
|
|
@@ -10,9 +10,9 @@ import { LogError } from './flow-control/log-error';
|
|
|
10
10
|
import { LogExit } from './flow-control/log-exit';
|
|
11
11
|
import { LogOutput } from './flow-control/log-output';
|
|
12
12
|
import { LogTimings } from './flow-control/log-timings';
|
|
13
|
-
import { RestartProcess } from './flow-control/restart-process';
|
|
13
|
+
import { RestartDelay, RestartProcess } from './flow-control/restart-process';
|
|
14
14
|
import { Logger } from './logger';
|
|
15
|
-
export type ConcurrentlyOptions = BaseConcurrentlyOptions & {
|
|
15
|
+
export type ConcurrentlyOptions = Omit<BaseConcurrentlyOptions, 'abortSignal' | 'hide'> & {
|
|
16
16
|
/**
|
|
17
17
|
* Which command(s) should have their output hidden.
|
|
18
18
|
*/
|
|
@@ -26,13 +26,17 @@ export type ConcurrentlyOptions = BaseConcurrentlyOptions & {
|
|
|
26
26
|
* How many characters should a prefix have at most, used when the prefix format is `command`.
|
|
27
27
|
*/
|
|
28
28
|
prefixLength?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Pads short prefixes with spaces so that all prefixes have the same length.
|
|
31
|
+
*/
|
|
32
|
+
padPrefix?: boolean;
|
|
29
33
|
/**
|
|
30
34
|
* Whether output should be formatted to include prefixes and whether "event" logs will be logged.
|
|
31
35
|
*/
|
|
32
36
|
raw?: boolean;
|
|
33
37
|
/**
|
|
34
38
|
* Date format used when logging date/time.
|
|
35
|
-
* @see https://
|
|
39
|
+
* @see https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
|
|
36
40
|
*/
|
|
37
41
|
timestampFormat?: string;
|
|
38
42
|
defaultInputTarget?: CommandIdentifier;
|
|
@@ -44,7 +48,7 @@ export type ConcurrentlyOptions = BaseConcurrentlyOptions & {
|
|
|
44
48
|
*
|
|
45
49
|
* @see RestartProcess
|
|
46
50
|
*/
|
|
47
|
-
restartDelay?:
|
|
51
|
+
restartDelay?: RestartDelay;
|
|
48
52
|
/**
|
|
49
53
|
* How many times commands should be restarted when they exit with a failure.
|
|
50
54
|
*
|
|
@@ -63,12 +67,18 @@ export type ConcurrentlyOptions = BaseConcurrentlyOptions & {
|
|
|
63
67
|
* @see LogTimings
|
|
64
68
|
*/
|
|
65
69
|
timings?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Clean up command(s) to execute before exiting concurrently.
|
|
72
|
+
* These won't be prefixed and don't affect concurrently's exit code.
|
|
73
|
+
*/
|
|
74
|
+
teardown?: readonly string[];
|
|
66
75
|
/**
|
|
67
76
|
* List of additional arguments passed that will get replaced in each command.
|
|
68
77
|
* If not defined, no argument replacing will happen.
|
|
69
78
|
*/
|
|
70
79
|
additionalArguments?: string[];
|
|
71
80
|
};
|
|
72
|
-
declare
|
|
73
|
-
export
|
|
74
|
-
export { CloseEvent, Command, CommandIdentifier,
|
|
81
|
+
export declare function concurrently(commands: ConcurrentlyCommandInput[], options?: Partial<ConcurrentlyOptions>): ConcurrentlyResult;
|
|
82
|
+
export { ConcurrentlyCommandInput, ConcurrentlyResult, createConcurrently, Logger };
|
|
83
|
+
export { CloseEvent, Command, CommandIdentifier, TimerEvent };
|
|
84
|
+
export { FlowController, InputHandler, KillOnSignal, KillOthers, LogError, LogExit, LogOutput, LogTimings, RestartProcess, };
|
package/dist/src/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RestartProcess = exports.LogTimings = exports.LogOutput = exports.
|
|
6
|
+
exports.RestartProcess = exports.LogTimings = exports.LogOutput = exports.LogExit = exports.LogError = exports.KillOthers = exports.KillOnSignal = exports.InputHandler = exports.Command = exports.Logger = exports.createConcurrently = exports.concurrently = void 0;
|
|
7
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
4
8
|
const command_1 = require("./command");
|
|
5
9
|
Object.defineProperty(exports, "Command", { enumerable: true, get: function () { return command_1.Command; } });
|
|
6
10
|
const concurrently_1 = require("./concurrently");
|
|
7
|
-
Object.defineProperty(exports, "
|
|
11
|
+
Object.defineProperty(exports, "createConcurrently", { enumerable: true, get: function () { return concurrently_1.concurrently; } });
|
|
8
12
|
const input_handler_1 = require("./flow-control/input-handler");
|
|
9
13
|
Object.defineProperty(exports, "InputHandler", { enumerable: true, get: function () { return input_handler_1.InputHandler; } });
|
|
10
14
|
const kill_on_signal_1 = require("./flow-control/kill-on-signal");
|
|
@@ -19,27 +23,41 @@ const log_output_1 = require("./flow-control/log-output");
|
|
|
19
23
|
Object.defineProperty(exports, "LogOutput", { enumerable: true, get: function () { return log_output_1.LogOutput; } });
|
|
20
24
|
const log_timings_1 = require("./flow-control/log-timings");
|
|
21
25
|
Object.defineProperty(exports, "LogTimings", { enumerable: true, get: function () { return log_timings_1.LogTimings; } });
|
|
26
|
+
const logger_padding_1 = require("./flow-control/logger-padding");
|
|
22
27
|
const restart_process_1 = require("./flow-control/restart-process");
|
|
23
28
|
Object.defineProperty(exports, "RestartProcess", { enumerable: true, get: function () { return restart_process_1.RestartProcess; } });
|
|
29
|
+
const teardown_1 = require("./flow-control/teardown");
|
|
24
30
|
const logger_1 = require("./logger");
|
|
25
31
|
Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return logger_1.Logger; } });
|
|
26
|
-
|
|
32
|
+
function concurrently(commands, options = {}) {
|
|
33
|
+
// To avoid empty strings from hiding the output of commands that don't have a name,
|
|
34
|
+
// keep in the list of commands to hide only strings with some length.
|
|
35
|
+
// This might happen through the CLI when no `--hide` argument is specified, for example.
|
|
36
|
+
const hide = lodash_1.default.castArray(options.hide).filter((id) => id || id === 0);
|
|
27
37
|
const logger = new logger_1.Logger({
|
|
28
|
-
hide
|
|
38
|
+
hide,
|
|
29
39
|
prefixFormat: options.prefix,
|
|
30
|
-
|
|
40
|
+
commandLength: options.prefixLength,
|
|
31
41
|
raw: options.raw,
|
|
32
42
|
timestampFormat: options.timestampFormat,
|
|
33
43
|
});
|
|
44
|
+
if (options.prefixColors === false) {
|
|
45
|
+
logger.toggleColors(false);
|
|
46
|
+
}
|
|
47
|
+
const abortController = new AbortController();
|
|
34
48
|
return (0, concurrently_1.concurrently)(commands, {
|
|
35
49
|
maxProcesses: options.maxProcesses,
|
|
36
50
|
raw: options.raw,
|
|
37
51
|
successCondition: options.successCondition,
|
|
38
52
|
cwd: options.cwd,
|
|
53
|
+
hide,
|
|
39
54
|
logger,
|
|
40
55
|
outputStream: options.outputStream || process.stdout,
|
|
41
56
|
group: options.group,
|
|
57
|
+
abortSignal: abortController.signal,
|
|
42
58
|
controllers: [
|
|
59
|
+
// LoggerPadding needs to run before any other controllers that might output something
|
|
60
|
+
...(options.padPrefix ? [new logger_padding_1.LoggerPadding({ logger })] : []),
|
|
43
61
|
new log_error_1.LogError({ logger }),
|
|
44
62
|
new log_output_1.LogOutput({ logger }),
|
|
45
63
|
new log_exit_1.LogExit({ logger }),
|
|
@@ -49,7 +67,7 @@ exports.default = (commands, options = {}) => {
|
|
|
49
67
|
inputStream: options.inputStream || (options.handleInput ? process.stdin : undefined),
|
|
50
68
|
pauseInputStreamOnFinish: options.pauseInputStreamOnFinish,
|
|
51
69
|
}),
|
|
52
|
-
new kill_on_signal_1.KillOnSignal({ process }),
|
|
70
|
+
new kill_on_signal_1.KillOnSignal({ process, abortController }),
|
|
53
71
|
new restart_process_1.RestartProcess({
|
|
54
72
|
logger,
|
|
55
73
|
delay: options.restartDelay,
|
|
@@ -59,13 +77,16 @@ exports.default = (commands, options = {}) => {
|
|
|
59
77
|
logger,
|
|
60
78
|
conditions: options.killOthers || [],
|
|
61
79
|
killSignal: options.killSignal,
|
|
80
|
+
abortController,
|
|
62
81
|
}),
|
|
63
82
|
new log_timings_1.LogTimings({
|
|
64
83
|
logger: options.timings ? logger : undefined,
|
|
65
84
|
timestampFormat: options.timestampFormat,
|
|
66
85
|
}),
|
|
86
|
+
new teardown_1.Teardown({ logger, spawn: options.spawn, commands: options.teardown || [] }),
|
|
67
87
|
],
|
|
68
88
|
prefixColors: options.prefixColors || [],
|
|
69
89
|
additionalArguments: options.additionalArguments,
|
|
70
90
|
});
|
|
71
|
-
}
|
|
91
|
+
}
|
|
92
|
+
exports.concurrently = concurrently;
|
package/dist/src/logger.d.ts
CHANGED
|
@@ -4,13 +4,19 @@ export declare class Logger {
|
|
|
4
4
|
private readonly hide;
|
|
5
5
|
private readonly raw;
|
|
6
6
|
private readonly prefixFormat?;
|
|
7
|
-
private readonly
|
|
8
|
-
private readonly
|
|
7
|
+
private readonly commandLength;
|
|
8
|
+
private readonly dateFormatter;
|
|
9
|
+
private chalk;
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* How many characters should a prefix have.
|
|
12
|
+
* Prefixes shorter than this will be padded with spaces to the right.
|
|
13
|
+
*/
|
|
14
|
+
private prefixLength;
|
|
15
|
+
/**
|
|
16
|
+
* Last character emitted, and from which command.
|
|
11
17
|
* If `undefined`, then nothing has been logged yet.
|
|
12
18
|
*/
|
|
13
|
-
private
|
|
19
|
+
private lastWrite?;
|
|
14
20
|
/**
|
|
15
21
|
* Observable that emits when there's been output logged.
|
|
16
22
|
* If `command` is is `undefined`, then the log is for a global event.
|
|
@@ -19,11 +25,11 @@ export declare class Logger {
|
|
|
19
25
|
command: Command | undefined;
|
|
20
26
|
text: string;
|
|
21
27
|
}>;
|
|
22
|
-
constructor({ hide, prefixFormat,
|
|
28
|
+
constructor({ hide, prefixFormat, commandLength, raw, timestampFormat, }: {
|
|
23
29
|
/**
|
|
24
|
-
* Which
|
|
30
|
+
* Which commands should have their output hidden.
|
|
25
31
|
*/
|
|
26
|
-
hide?: CommandIdentifier
|
|
32
|
+
hide?: CommandIdentifier[];
|
|
27
33
|
/**
|
|
28
34
|
* Whether output should be formatted to include prefixes and whether "event" logs will be
|
|
29
35
|
* logged.
|
|
@@ -35,18 +41,27 @@ export declare class Logger {
|
|
|
35
41
|
*/
|
|
36
42
|
prefixFormat?: string;
|
|
37
43
|
/**
|
|
38
|
-
* How many characters should a prefix have at most
|
|
44
|
+
* How many characters should a prefix have at most when the format is `command`.
|
|
39
45
|
*/
|
|
40
|
-
|
|
46
|
+
commandLength?: number;
|
|
41
47
|
/**
|
|
42
48
|
* Date format used when logging date/time.
|
|
43
|
-
* @see https://
|
|
49
|
+
* @see https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
|
|
44
50
|
*/
|
|
45
51
|
timestampFormat?: string;
|
|
46
52
|
});
|
|
53
|
+
/**
|
|
54
|
+
* Toggles colors on/off globally.
|
|
55
|
+
*/
|
|
56
|
+
toggleColors(on: boolean): void;
|
|
47
57
|
private shortenText;
|
|
48
58
|
private getPrefixesFor;
|
|
59
|
+
getPrefixContent(command: Command): {
|
|
60
|
+
type: 'default' | 'template';
|
|
61
|
+
value: string;
|
|
62
|
+
} | undefined;
|
|
49
63
|
getPrefix(command: Command): string;
|
|
64
|
+
setPrefixLength(length: number): void;
|
|
50
65
|
colorText(command: Command, text: string): string;
|
|
51
66
|
/**
|
|
52
67
|
* Logs an event for a command (e.g. start, stop).
|
package/dist/src/logger.js
CHANGED
|
@@ -28,34 +28,53 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.Logger = void 0;
|
|
30
30
|
const chalk_1 = __importDefault(require("chalk"));
|
|
31
|
-
const format_1 = __importDefault(require("date-fns/format"));
|
|
32
31
|
const lodash_1 = __importDefault(require("lodash"));
|
|
33
32
|
const Rx = __importStar(require("rxjs"));
|
|
33
|
+
const date_format_1 = require("./date-format");
|
|
34
34
|
const defaults = __importStar(require("./defaults"));
|
|
35
|
+
const defaultChalk = chalk_1.default;
|
|
36
|
+
const noColorChalk = new chalk_1.default.Instance({ level: 0 });
|
|
35
37
|
class Logger {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
hide;
|
|
39
|
+
raw;
|
|
40
|
+
prefixFormat;
|
|
41
|
+
commandLength;
|
|
42
|
+
dateFormatter;
|
|
43
|
+
chalk = defaultChalk;
|
|
44
|
+
/**
|
|
45
|
+
* How many characters should a prefix have.
|
|
46
|
+
* Prefixes shorter than this will be padded with spaces to the right.
|
|
47
|
+
*/
|
|
48
|
+
prefixLength = 0;
|
|
49
|
+
/**
|
|
50
|
+
* Last character emitted, and from which command.
|
|
51
|
+
* If `undefined`, then nothing has been logged yet.
|
|
52
|
+
*/
|
|
53
|
+
lastWrite;
|
|
54
|
+
/**
|
|
55
|
+
* Observable that emits when there's been output logged.
|
|
56
|
+
* If `command` is is `undefined`, then the log is for a global event.
|
|
57
|
+
*/
|
|
58
|
+
output = new Rx.Subject();
|
|
59
|
+
constructor({ hide, prefixFormat, commandLength, raw = false, timestampFormat, }) {
|
|
60
|
+
this.hide = (hide || []).map(String);
|
|
48
61
|
this.raw = raw;
|
|
49
62
|
this.prefixFormat = prefixFormat;
|
|
50
|
-
this.
|
|
51
|
-
this.
|
|
63
|
+
this.commandLength = commandLength || defaults.prefixLength;
|
|
64
|
+
this.dateFormatter = new date_format_1.DateFormatter(timestampFormat || defaults.timestampFormat);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Toggles colors on/off globally.
|
|
68
|
+
*/
|
|
69
|
+
toggleColors(on) {
|
|
70
|
+
this.chalk = on ? defaultChalk : noColorChalk;
|
|
52
71
|
}
|
|
53
72
|
shortenText(text) {
|
|
54
|
-
if (!text || text.length <= this.
|
|
73
|
+
if (!text || text.length <= this.commandLength) {
|
|
55
74
|
return text;
|
|
56
75
|
}
|
|
57
76
|
const ellipsis = '..';
|
|
58
|
-
const prefixLength = this.
|
|
77
|
+
const prefixLength = this.commandLength - ellipsis.length;
|
|
59
78
|
const endLength = Math.floor(prefixLength / 2);
|
|
60
79
|
const beginningLength = prefixLength - endLength;
|
|
61
80
|
const beginnning = text.slice(0, beginningLength);
|
|
@@ -64,35 +83,50 @@ class Logger {
|
|
|
64
83
|
}
|
|
65
84
|
getPrefixesFor(command) {
|
|
66
85
|
return {
|
|
67
|
-
|
|
86
|
+
// When there's limited concurrency, the PID might not be immediately available,
|
|
87
|
+
// so avoid the string 'undefined' from becoming a prefix
|
|
88
|
+
pid: command.pid != null ? String(command.pid) : '',
|
|
68
89
|
index: String(command.index),
|
|
69
90
|
name: command.name,
|
|
70
91
|
command: this.shortenText(command.command),
|
|
71
|
-
time: (
|
|
92
|
+
time: this.dateFormatter.format(new Date()),
|
|
72
93
|
};
|
|
73
94
|
}
|
|
74
|
-
|
|
95
|
+
getPrefixContent(command) {
|
|
75
96
|
const prefix = this.prefixFormat || (command.name ? 'name' : 'index');
|
|
76
97
|
if (prefix === 'none') {
|
|
77
|
-
return
|
|
98
|
+
return;
|
|
78
99
|
}
|
|
79
100
|
const prefixes = this.getPrefixesFor(command);
|
|
80
101
|
if (Object.keys(prefixes).includes(prefix)) {
|
|
81
|
-
return
|
|
102
|
+
return { type: 'default', value: prefixes[prefix] };
|
|
82
103
|
}
|
|
83
|
-
|
|
104
|
+
const value = lodash_1.default.reduce(prefixes, (prev, val, key) => {
|
|
84
105
|
const keyRegex = new RegExp(lodash_1.default.escapeRegExp(`{${key}}`), 'g');
|
|
85
106
|
return prev.replace(keyRegex, String(val));
|
|
86
107
|
}, prefix);
|
|
108
|
+
return { type: 'template', value };
|
|
109
|
+
}
|
|
110
|
+
getPrefix(command) {
|
|
111
|
+
const content = this.getPrefixContent(command);
|
|
112
|
+
if (!content) {
|
|
113
|
+
return '';
|
|
114
|
+
}
|
|
115
|
+
return content.type === 'template'
|
|
116
|
+
? content.value.padEnd(this.prefixLength, ' ')
|
|
117
|
+
: `[${content.value.padEnd(this.prefixLength, ' ')}]`;
|
|
118
|
+
}
|
|
119
|
+
setPrefixLength(length) {
|
|
120
|
+
this.prefixLength = length;
|
|
87
121
|
}
|
|
88
122
|
colorText(command, text) {
|
|
89
123
|
let color;
|
|
90
124
|
if (command.prefixColor && command.prefixColor.startsWith('#')) {
|
|
91
|
-
color =
|
|
125
|
+
color = this.chalk.hex(command.prefixColor);
|
|
92
126
|
}
|
|
93
127
|
else {
|
|
94
|
-
const defaultColor = lodash_1.default.get(
|
|
95
|
-
color = lodash_1.default.get(
|
|
128
|
+
const defaultColor = lodash_1.default.get(this.chalk, defaults.prefixColors, this.chalk.reset);
|
|
129
|
+
color = lodash_1.default.get(this.chalk, command.prefixColor ?? '', defaultColor);
|
|
96
130
|
}
|
|
97
131
|
return color(text);
|
|
98
132
|
}
|
|
@@ -105,7 +139,14 @@ class Logger {
|
|
|
105
139
|
if (this.raw) {
|
|
106
140
|
return;
|
|
107
141
|
}
|
|
108
|
-
this
|
|
142
|
+
// Last write was from this command, but it didn't end with a line feed.
|
|
143
|
+
// Prepend one, otherwise the event's text will be concatenated to that write.
|
|
144
|
+
// A line feed is otherwise inserted anyway.
|
|
145
|
+
let prefix = '';
|
|
146
|
+
if (this.lastWrite?.command === command && this.lastWrite.char !== '\n') {
|
|
147
|
+
prefix = '\n';
|
|
148
|
+
}
|
|
149
|
+
this.logCommandText(prefix + this.chalk.reset(text) + '\n', command);
|
|
109
150
|
}
|
|
110
151
|
logCommandText(text, command) {
|
|
111
152
|
if (this.hide.includes(String(command.index)) || this.hide.includes(command.name)) {
|
|
@@ -123,7 +164,7 @@ class Logger {
|
|
|
123
164
|
if (this.raw) {
|
|
124
165
|
return;
|
|
125
166
|
}
|
|
126
|
-
this.log(
|
|
167
|
+
this.log(this.chalk.reset('-->') + ' ', this.chalk.reset(text) + '\n');
|
|
127
168
|
}
|
|
128
169
|
/**
|
|
129
170
|
* Logs a table from an input object array, like `console.table`.
|
|
@@ -181,21 +222,19 @@ class Logger {
|
|
|
181
222
|
}
|
|
182
223
|
// #70 - replace some ANSI code that would impact clearing lines
|
|
183
224
|
text = text.replace(/\u2026/g, '...');
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return prefix + line;
|
|
191
|
-
});
|
|
192
|
-
if (!this.lastChar || this.lastChar === '\n') {
|
|
225
|
+
// This write's interrupting another command, emit a line feed to start clean.
|
|
226
|
+
if (this.lastWrite && this.lastWrite.command !== command && this.lastWrite.char !== '\n') {
|
|
227
|
+
this.emit(this.lastWrite.command, '\n');
|
|
228
|
+
}
|
|
229
|
+
// Clean lines should emit a prefix
|
|
230
|
+
if (!this.lastWrite || this.lastWrite.char === '\n') {
|
|
193
231
|
this.emit(command, prefix);
|
|
194
232
|
}
|
|
195
|
-
|
|
196
|
-
this.emit(command,
|
|
233
|
+
const textToWrite = text.replaceAll('\n', (lf, i) => lf + (text[i + 1] ? prefix : ''));
|
|
234
|
+
this.emit(command, textToWrite);
|
|
197
235
|
}
|
|
198
236
|
emit(command, text) {
|
|
237
|
+
this.lastWrite = { command, char: text[text.length - 1] };
|
|
199
238
|
this.output.next({ command, text });
|
|
200
239
|
}
|
|
201
240
|
}
|
|
@@ -29,8 +29,11 @@ const Rx = __importStar(require("rxjs"));
|
|
|
29
29
|
* Class responsible for actually writing output onto a writable stream.
|
|
30
30
|
*/
|
|
31
31
|
class OutputWriter {
|
|
32
|
+
outputStream;
|
|
33
|
+
group;
|
|
34
|
+
buffers;
|
|
35
|
+
activeCommandIndex = 0;
|
|
32
36
|
constructor({ outputStream, group, commands, }) {
|
|
33
|
-
this.activeCommandIndex = 0;
|
|
34
37
|
this.outputStream = outputStream;
|
|
35
38
|
this.group = group;
|
|
36
39
|
this.buffers = commands.map(() => []);
|
|
@@ -42,7 +45,8 @@ class OutputWriter {
|
|
|
42
45
|
for (let i = command.index + 1; i < commands.length; i++) {
|
|
43
46
|
this.activeCommandIndex = i;
|
|
44
47
|
this.flushBuffer(i);
|
|
45
|
-
|
|
48
|
+
// TODO: Should errored commands also flush buffer?
|
|
49
|
+
if (commands[i].state !== 'exited') {
|
|
46
50
|
break;
|
|
47
51
|
}
|
|
48
52
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
export declare class PrefixColorSelector {
|
|
3
3
|
private colorGenerator;
|
|
4
|
-
constructor(customColors?: string[]);
|
|
4
|
+
constructor(customColors?: string | string[]);
|
|
5
5
|
/** A list of colors that are readable in a terminal. */
|
|
6
6
|
static get ACCEPTABLE_CONSOLE_COLORS(): ("stderr" | keyof chalk.Chalk | "supportsColor" | "Level" | "Color" | "ForegroundColor" | "BackgroundColor" | "Modifiers")[];
|
|
7
7
|
/**
|
|
@@ -52,8 +52,10 @@ function* createColorGenerator(customColors) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
class PrefixColorSelector {
|
|
55
|
+
colorGenerator;
|
|
55
56
|
constructor(customColors = []) {
|
|
56
|
-
|
|
57
|
+
const normalizedColors = typeof customColors === 'string' ? [customColors] : customColors;
|
|
58
|
+
this.colorGenerator = createColorGenerator(normalizedColors);
|
|
57
59
|
}
|
|
58
60
|
/** A list of colors that are readable in a terminal. */
|
|
59
61
|
static get ACCEPTABLE_CONSOLE_COLORS() {
|
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
/// <reference types="node" />
|
|
4
4
|
/// <reference types="node" />
|
|
5
5
|
/// <reference types="node" />
|
|
6
|
-
|
|
6
|
+
/// <reference types="node" />
|
|
7
|
+
import { ChildProcess, SpawnOptions } from 'child_process';
|
|
7
8
|
import supportsColor from 'supports-color';
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Spawns a command using `cmd.exe` on Windows, or `/bin/sh` elsewhere.
|
|
11
|
+
*/
|
|
12
|
+
export declare function spawn(command: string, options: SpawnOptions, spawn?: (command: string, args: string[], options: SpawnOptions) => ChildProcess, process?: Pick<NodeJS.Process, 'platform'>): ChildProcess;
|
|
13
|
+
export declare const getSpawnOpts: ({ colorSupport, cwd, process, ipc, stdio, env, }: {
|
|
9
14
|
/**
|
|
10
15
|
* What the color support of the spawned processes should be.
|
|
11
16
|
* If set to `false`, then no colors should be output.
|
|
@@ -23,10 +28,20 @@ export declare const getSpawnOpts: ({ colorSupport, cwd, process, raw, env, }: {
|
|
|
23
28
|
*/
|
|
24
29
|
cwd?: string | undefined;
|
|
25
30
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
31
|
+
* The file descriptor number at which the channel for inter-process communication
|
|
32
|
+
* should be set up.
|
|
33
|
+
*/
|
|
34
|
+
ipc?: number | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Which stdio mode to use. Raw implies inheriting the parent process' stdio.
|
|
37
|
+
*
|
|
38
|
+
* - `normal`: all of stdout, stderr and stdin are piped
|
|
39
|
+
* - `hidden`: stdin is piped, stdout/stderr outputs are ignored
|
|
40
|
+
* - `raw`: all of stdout, stderr and stdin are inherited from the main process
|
|
41
|
+
*
|
|
42
|
+
* Defaults to `normal`.
|
|
28
43
|
*/
|
|
29
|
-
|
|
44
|
+
stdio?: "raw" | "hidden" | "normal" | undefined;
|
|
30
45
|
/**
|
|
31
46
|
* Map of custom environment variables to include in the spawn options.
|
|
32
47
|
*/
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getSpawnOpts = exports.spawn = void 0;
|
|
7
|
+
const assert_1 = __importDefault(require("assert"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const supports_color_1 = __importDefault(require("supports-color"));
|
|
10
|
+
/**
|
|
11
|
+
* Spawns a command using `cmd.exe` on Windows, or `/bin/sh` elsewhere.
|
|
12
|
+
*/
|
|
13
|
+
// Implementation based off of https://github.com/mmalecki/spawn-command/blob/v0.0.2-1/lib/spawn-command.js
|
|
14
|
+
function spawn(command, options,
|
|
15
|
+
// For testing
|
|
16
|
+
spawn = child_process_1.spawn, process = global.process) {
|
|
17
|
+
let file = '/bin/sh';
|
|
18
|
+
let args = ['-c', command];
|
|
19
|
+
if (process.platform === 'win32') {
|
|
20
|
+
file = 'cmd.exe';
|
|
21
|
+
args = ['/s', '/c', `"${command}"`];
|
|
22
|
+
options.windowsVerbatimArguments = true;
|
|
23
|
+
}
|
|
24
|
+
return spawn(file, args, options);
|
|
25
|
+
}
|
|
26
|
+
exports.spawn = spawn;
|
|
27
|
+
const getSpawnOpts = ({ colorSupport = supports_color_1.default.stdout, cwd, process = global.process, ipc, stdio = 'normal', env = {}, }) => {
|
|
28
|
+
const stdioValues = stdio === 'normal'
|
|
29
|
+
? ['pipe', 'pipe', 'pipe']
|
|
30
|
+
: stdio === 'raw'
|
|
31
|
+
? ['inherit', 'inherit', 'inherit']
|
|
32
|
+
: ['pipe', 'ignore', 'ignore'];
|
|
33
|
+
if (ipc != null) {
|
|
34
|
+
// Avoid overriding the stdout/stderr/stdin
|
|
35
|
+
assert_1.default.ok(ipc > 2, '[concurrently] the IPC channel number should be > 2');
|
|
36
|
+
stdioValues[ipc] = 'ipc';
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
cwd: cwd || process.cwd(),
|
|
40
|
+
stdio: stdioValues,
|
|
41
|
+
...(/^win/.test(process.platform) && { detached: false }),
|
|
42
|
+
env: {
|
|
43
|
+
...(colorSupport ? { FORCE_COLOR: colorSupport.level.toString() } : {}),
|
|
44
|
+
...process.env,
|
|
45
|
+
...env,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
exports.getSpawnOpts = getSpawnOpts;
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Concurrently Documentation
|
|
2
|
+
|
|
3
|
+
## CLI
|
|
4
|
+
|
|
5
|
+
These articles cover using concurrently through CLI:
|
|
6
|
+
|
|
7
|
+
- [Prefixing](./cli/prefixing.md)
|
|
8
|
+
- [Output Control](./cli/output-control.md)
|
|
9
|
+
- [Shortcuts](./cli/shortcuts.md)
|
|
10
|
+
- [Restarting Commands](./cli/restarting.md)
|
|
11
|
+
- [Input Handling](./cli/input-handling.md)
|
|
12
|
+
- [Passthrough Arguments](./cli/passthrough-arguments.md)
|
|
13
|
+
- [Configuration](./cli/configuration.md)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
You might want to configure concurrently to always have certain flags on.
|
|
4
|
+
Any of concurrently's flags can be set via environment variables that are prefixed with `CONCURRENTLY_`.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
$ export CONCURRENTLY_KILL_OTHERS=true
|
|
8
|
+
$ export CONCURRENTLY_HANDLE_INPUT=true
|
|
9
|
+
# Equivalent to passing --kill-others and --handle-input
|
|
10
|
+
$ concurrently nodemon "echo 'hey nodemon, you won't last long'"
|
|
11
|
+
```
|