concurrently
Advanced tools
Comparing version 7.5.0 to 7.6.0
@@ -57,4 +57,5 @@ #!/usr/bin/env node | ||
describe: 'How many processes should run at once.\n' + | ||
'New processes only spawn after all restart tries of a process.', | ||
type: 'number', | ||
'New processes only spawn after all restart tries of a process.\n' + | ||
'Exact number or a percent of CPUs available (for example "50%")', | ||
type: 'string', | ||
}, | ||
@@ -61,0 +62,0 @@ names: { |
@@ -12,6 +12,6 @@ import { CommandInfo } from '../command'; | ||
name: string; | ||
env?: Record<string, unknown>; | ||
cwd?: string; | ||
prefixColor?: string; | ||
env?: Record<string, unknown> | undefined; | ||
cwd?: string | undefined; | ||
prefixColor?: string | undefined; | ||
}; | ||
} |
@@ -15,5 +15,2 @@ "use strict"; | ||
class ExpandNpmWildcard { | ||
constructor(readPackage = ExpandNpmWildcard.readPackage) { | ||
this.readPackage = readPackage; | ||
} | ||
static readPackage() { | ||
@@ -28,2 +25,5 @@ try { | ||
} | ||
constructor(readPackage = ExpandNpmWildcard.readPackage) { | ||
this.readPackage = readPackage; | ||
} | ||
parse(commandInfo) { | ||
@@ -45,3 +45,5 @@ const [, npmCmd, cmdName, args] = commandInfo.command.match(/(npm|yarn|pnpm) run (\S+)([^&]*)/) || []; | ||
const wildcardRegex = new RegExp(`^${preWildcard}(.*?)${postWildcard}$`); | ||
const currentName = commandInfo.name || ''; | ||
// If 'commandInfo.name' doesn't match 'cmdName', this means a custom name | ||
// has been specified and thus becomes the prefix (as described in the README). | ||
const prefix = commandInfo.name !== cmdName ? commandInfo.name : ''; | ||
return this.scripts | ||
@@ -60,5 +62,5 @@ .map((script) => { | ||
command: `${npmCmd} run ${script}${args}`, | ||
// Will use an empty command name if command has no name and the wildcard match is empty, | ||
// e.g. if `npm:watch-*` matches `npm run watch-`. | ||
name: currentName + match[1], | ||
// Will use an empty command name if no prefix has been specified and | ||
// the wildcard match is empty, e.g. if `npm:watch-*` matches `npm run watch-`. | ||
name: prefix + match[1], | ||
}; | ||
@@ -65,0 +67,0 @@ } |
@@ -10,6 +10,6 @@ import { CommandInfo } from '../command'; | ||
name: string; | ||
env?: Record<string, unknown>; | ||
cwd?: string; | ||
prefixColor?: string; | ||
env?: Record<string, unknown> | undefined; | ||
cwd?: string | undefined; | ||
prefixColor?: string | undefined; | ||
}; | ||
} |
@@ -11,3 +11,3 @@ /// <reference types="node" /> | ||
*/ | ||
export declare type CommandIdentifier = string | number; | ||
export type CommandIdentifier = string | number; | ||
export interface CommandInfo { | ||
@@ -62,11 +62,11 @@ /** | ||
*/ | ||
export declare type ChildProcess = EventEmitter & Pick<BaseChildProcess, 'pid' | 'stdin' | 'stdout' | 'stderr'>; | ||
export type ChildProcess = EventEmitter & Pick<BaseChildProcess, 'pid' | 'stdin' | 'stdout' | 'stderr'>; | ||
/** | ||
* Interface for a function that must kill the process with `pid`, optionally sending `signal` to it. | ||
*/ | ||
export declare type KillProcess = (pid: number, signal?: string) => void; | ||
export type KillProcess = (pid: number, signal?: string) => void; | ||
/** | ||
* Interface for a function that spawns a command and returns its child process instance. | ||
*/ | ||
export declare type SpawnCommand = (command: string, options: SpawnOptions) => ChildProcess; | ||
export type SpawnCommand = (command: string, options: SpawnOptions) => ChildProcess; | ||
export declare class Command implements CommandInfo { | ||
@@ -82,3 +82,3 @@ private readonly killProcess; | ||
/** @inheritdoc */ | ||
readonly prefixColor: string; | ||
readonly prefixColor?: string; | ||
/** @inheritdoc */ | ||
@@ -98,2 +98,3 @@ readonly env: Record<string, unknown>; | ||
exited: boolean; | ||
/** @deprecated */ | ||
get killable(): boolean; | ||
@@ -111,2 +112,11 @@ constructor({ index, name, command, prefixColor, env, cwd }: CommandInfo & { | ||
kill(code?: string): void; | ||
/** | ||
* Detects whether a command can be killed. | ||
* | ||
* Also works as a type guard on the input `command`. | ||
*/ | ||
static canKill(command: Command): command is Command & { | ||
pid: number; | ||
process: ChildProcess; | ||
}; | ||
} |
@@ -29,2 +29,6 @@ "use strict"; | ||
class Command { | ||
/** @deprecated */ | ||
get killable() { | ||
return Command.canKill(this); | ||
} | ||
constructor({ index, name, command, prefixColor, env, cwd }, spawnOpts, spawn, killProcess) { | ||
@@ -42,3 +46,3 @@ this.close = new Rx.Subject(); | ||
this.prefixColor = prefixColor; | ||
this.env = env; | ||
this.env = env || {}; | ||
this.cwd = cwd; | ||
@@ -49,5 +53,2 @@ this.killProcess = killProcess; | ||
} | ||
get killable() { | ||
return !!this.process; | ||
} | ||
/** | ||
@@ -78,3 +79,3 @@ * Starts this command, piping output, error and close events onto the corresponding observables. | ||
index: this.index, | ||
exitCode: exitCode === null ? signal : exitCode, | ||
exitCode: exitCode !== null && exitCode !== void 0 ? exitCode : String(signal), | ||
killed: this.killed, | ||
@@ -90,3 +91,3 @@ timings: { | ||
child.stderr && pipeTo(Rx.fromEvent(child.stderr, 'data'), this.stderr); | ||
this.stdin = child.stdin; | ||
this.stdin = child.stdin || undefined; | ||
} | ||
@@ -97,3 +98,3 @@ /** | ||
kill(code) { | ||
if (this.killable) { | ||
if (Command.canKill(this)) { | ||
this.killed = true; | ||
@@ -103,2 +104,10 @@ this.killProcess(this.pid, code); | ||
} | ||
/** | ||
* Detects whether a command can be killed. | ||
* | ||
* Also works as a type guard on the input `command`. | ||
*/ | ||
static canKill(command) { | ||
return !!command.pid && !!command.process; | ||
} | ||
} | ||
@@ -105,0 +114,0 @@ exports.Command = Command; |
@@ -12,3 +12,3 @@ import * as Rx from 'rxjs'; | ||
*/ | ||
export declare type SuccessCondition = 'first' | 'last' | 'all' | `command-${string | number}` | `!command-${string | number}`; | ||
export type SuccessCondition = 'first' | 'last' | 'all' | `command-${string | number}` | `!command-${string | number}`; | ||
/** | ||
@@ -40,2 +40,3 @@ * Provides logic to determine whether lists of commands ran successfully. | ||
listen(commands: Command[]): Promise<CloseEvent[]>; | ||
private emitWithScheduler; | ||
} |
@@ -70,6 +70,9 @@ "use strict"; | ||
return Rx.lastValueFrom(Rx.merge(...closeStreams).pipe((0, operators_1.bufferCount)(closeStreams.length), (0, operators_1.switchMap)((exitInfos) => this.isSuccess(exitInfos) | ||
? Rx.of(exitInfos, this.scheduler) | ||
: Rx.throwError(exitInfos, this.scheduler)), (0, operators_1.take)(1))); | ||
? this.emitWithScheduler(Rx.of(exitInfos)) | ||
: this.emitWithScheduler(Rx.throwError(exitInfos))), (0, operators_1.take)(1))); | ||
} | ||
emitWithScheduler(input) { | ||
return this.scheduler ? input.pipe(Rx.observeOn(this.scheduler)) : input; | ||
} | ||
} | ||
exports.CompletionListener = CompletionListener; |
@@ -12,4 +12,6 @@ /// <reference types="node" /> | ||
*/ | ||
export declare type ConcurrentlyCommandInput = string | Partial<CommandInfo>; | ||
export declare type ConcurrentlyResult = { | ||
export type ConcurrentlyCommandInput = string | ({ | ||
command: string; | ||
} & Partial<CommandInfo>); | ||
export type ConcurrentlyResult = { | ||
/** | ||
@@ -27,3 +29,3 @@ * All commands created and ran by concurrently. | ||
}; | ||
export declare type ConcurrentlyOptions = { | ||
export type ConcurrentlyOptions = { | ||
logger?: Logger; | ||
@@ -44,2 +46,3 @@ /** | ||
* Maximum number of commands to run at once. | ||
* Exact number or a percent of CPUs available (for example "50%"). | ||
* | ||
@@ -49,3 +52,3 @@ * If undefined, then all processes will start in parallel. | ||
*/ | ||
maxProcesses?: number; | ||
maxProcesses?: number | string; | ||
/** | ||
@@ -52,0 +55,0 @@ * Whether commands should be spawned in raw mode. |
@@ -9,2 +9,3 @@ "use strict"; | ||
const lodash_1 = __importDefault(require("lodash")); | ||
const os_1 = require("os"); | ||
const spawn_command_1 = __importDefault(require("spawn-command")); | ||
@@ -73,3 +74,3 @@ const tree_kill_1 = __importDefault(require("tree-kill")); | ||
outputStream: options.outputStream, | ||
group: options.group, | ||
group: !!options.group, | ||
commands, | ||
@@ -80,3 +81,5 @@ }); | ||
const commandsLeft = commands.slice(); | ||
const maxProcesses = Math.max(1, Number(options.maxProcesses) || commandsLeft.length); | ||
const maxProcesses = Math.max(1, (typeof options.maxProcesses === 'string' && options.maxProcesses.endsWith('%') | ||
? Math.round(((0, os_1.cpus)().length * Number(options.maxProcesses.slice(0, -1))) / 100) | ||
: Number(options.maxProcesses)) || commandsLeft.length); | ||
for (let i = 0; i < maxProcesses; i++) { | ||
@@ -98,9 +101,5 @@ maybeRunMore(commandsLeft); | ||
if (typeof command === 'string') { | ||
return { | ||
command, | ||
name: '', | ||
env: {}, | ||
cwd: '', | ||
}; | ||
return mapToCommandInfo({ command }); | ||
} | ||
assert_1.default.ok(command.command, '[concurrently] command cannot be empty'); | ||
return { | ||
@@ -107,0 +106,0 @@ command: command.command, |
@@ -18,6 +18,6 @@ /// <reference types="node" /> | ||
private readonly defaultInputTarget; | ||
private readonly inputStream; | ||
private readonly inputStream?; | ||
private readonly pauseInputStreamOnFinish; | ||
constructor({ defaultInputTarget, inputStream, pauseInputStreamOnFinish, logger, }: { | ||
inputStream: Readable; | ||
inputStream?: Readable; | ||
logger: Logger; | ||
@@ -24,0 +24,0 @@ defaultInputTarget?: CommandIdentifier; |
@@ -47,7 +47,8 @@ "use strict"; | ||
handle(commands) { | ||
if (!this.inputStream) { | ||
const { inputStream } = this; | ||
if (!inputStream) { | ||
return { commands }; | ||
} | ||
Rx.fromEvent(this.inputStream, 'data') | ||
.pipe((0, operators_1.map)((data) => data.toString())) | ||
Rx.fromEvent(inputStream, 'data') | ||
.pipe((0, operators_1.map)((data) => String(data))) | ||
.subscribe((data) => { | ||
@@ -71,3 +72,3 @@ const dataParts = data.split(/:(.+)/); | ||
// https://github.com/kimmobrunfeldt/concurrently/issues/252 | ||
this.inputStream.pause(); | ||
inputStream.pause(); | ||
} | ||
@@ -74,0 +75,0 @@ }, |
import { Command } from '../command'; | ||
import { Logger } from '../logger'; | ||
import { FlowController } from './flow-controller'; | ||
export declare type ProcessCloseCondition = 'failure' | 'success'; | ||
export type ProcessCloseCondition = 'failure' | 'success'; | ||
/** | ||
* Sends a SIGTERM signal to all commands when one of the exits with a matching condition. | ||
* Sends a SIGTERM signal to all commands when one of the commands exits with a matching condition. | ||
*/ | ||
@@ -8,0 +8,0 @@ export declare class KillOthers implements FlowController { |
@@ -9,4 +9,5 @@ "use strict"; | ||
const operators_1 = require("rxjs/operators"); | ||
const command_1 = require("../command"); | ||
/** | ||
* Sends a SIGTERM signal to all commands when one of the exits with a matching condition. | ||
* Sends a SIGTERM signal to all commands when one of the commands exits with a matching condition. | ||
*/ | ||
@@ -25,3 +26,3 @@ class KillOthers { | ||
closeStates.forEach((closeState) => closeState.subscribe(() => { | ||
const killableCommands = commands.filter((command) => command.killable); | ||
const killableCommands = commands.filter((command) => command_1.Command.canKill(command)); | ||
if (killableCommands.length) { | ||
@@ -28,0 +29,0 @@ this.logger.logGlobalEvent('Sending SIGTERM to other processes..'); |
@@ -30,2 +30,3 @@ "use strict"; | ||
exports.LogTimings = void 0; | ||
const assert = __importStar(require("assert")); | ||
const format_1 = __importDefault(require("date-fns/format")); | ||
@@ -40,6 +41,2 @@ const lodash_1 = __importDefault(require("lodash")); | ||
class LogTimings { | ||
constructor({ logger, timestampFormat = defaults.timestampFormat, }) { | ||
this.logger = logger; | ||
this.timestampFormat = timestampFormat; | ||
} | ||
static mapCloseEventToTimingInfo({ command, timings, killed, exitCode, }) { | ||
@@ -55,3 +52,8 @@ const readableDurationMs = (timings.endDate.getTime() - timings.startDate.getTime()).toLocaleString(); | ||
} | ||
constructor({ logger, timestampFormat = defaults.timestampFormat, }) { | ||
this.logger = logger; | ||
this.timestampFormat = timestampFormat; | ||
} | ||
printExitInfoTimingTable(exitInfos) { | ||
assert.ok(this.logger); | ||
const exitInfoTable = (0, lodash_1.default)(exitInfos) | ||
@@ -67,3 +69,4 @@ .sortBy(({ timings }) => timings.durationSeconds) | ||
handle(commands) { | ||
if (!this.logger) { | ||
const { logger } = this; | ||
if (!logger) { | ||
return { commands }; | ||
@@ -76,3 +79,3 @@ } | ||
const formattedStartDate = (0, format_1.default)(startDate, this.timestampFormat); | ||
this.logger.logCommandEvent(`${command.command} started at ${formattedStartDate}`, command); | ||
logger.logCommandEvent(`${command.command} started at ${formattedStartDate}`, command); | ||
} | ||
@@ -82,3 +85,3 @@ else { | ||
const formattedEndDate = (0, format_1.default)(endDate, this.timestampFormat); | ||
this.logger.logCommandEvent(`${command.command} stopped at ${formattedEndDate} after ${durationMs.toLocaleString()}ms`, command); | ||
logger.logCommandEvent(`${command.command} stopped at ${formattedEndDate} after ${durationMs.toLocaleString()}ms`, command); | ||
} | ||
@@ -85,0 +88,0 @@ }); |
@@ -14,7 +14,7 @@ /// <reference types="node" /> | ||
*/ | ||
colorSupport?: Pick<supportsColor.supportsColor.Level, 'level'> | false; | ||
colorSupport?: false | Pick<supportsColor.supportsColor.Level, "level"> | undefined; | ||
/** | ||
* The NodeJS process. | ||
*/ | ||
process?: Pick<NodeJS.Process, 'cwd' | 'platform' | 'env'>; | ||
process?: Pick<NodeJS.Process, "cwd" | "env" | "platform"> | undefined; | ||
/** | ||
@@ -24,3 +24,3 @@ * A custom working directory to spawn processes in. | ||
*/ | ||
cwd?: string; | ||
cwd?: string | undefined; | ||
/** | ||
@@ -30,7 +30,7 @@ * Whether to customize the options for spawning processes in raw mode. | ||
*/ | ||
raw?: boolean; | ||
raw?: boolean | undefined; | ||
/** | ||
* Map of custom environment variables to include in the spawn options. | ||
*/ | ||
env?: Record<string, unknown>; | ||
env?: Record<string, unknown> | undefined; | ||
}) => SpawnOptions; |
@@ -15,3 +15,3 @@ /// <reference types="node" /> | ||
import { Logger } from './logger'; | ||
export declare type ConcurrentlyOptions = BaseConcurrentlyOptions & { | ||
export type ConcurrentlyOptions = BaseConcurrentlyOptions & { | ||
/** | ||
@@ -18,0 +18,0 @@ * Which command(s) should have their output hidden. |
@@ -49,3 +49,3 @@ "use strict"; | ||
defaultInputTarget: options.defaultInputTarget, | ||
inputStream: options.inputStream || (options.handleInput && process.stdin), | ||
inputStream: options.inputStream || (options.handleInput ? process.stdin : undefined), | ||
pauseInputStreamOnFinish: options.pauseInputStreamOnFinish, | ||
@@ -61,6 +61,6 @@ }), | ||
logger, | ||
conditions: options.killOthers, | ||
conditions: options.killOthers || [], | ||
}), | ||
new log_timings_1.LogTimings({ | ||
logger: options.timings ? logger : null, | ||
logger: options.timings ? logger : undefined, | ||
timestampFormat: options.timestampFormat, | ||
@@ -67,0 +67,0 @@ }), |
@@ -89,2 +89,3 @@ "use strict"; | ||
colorText(command, text) { | ||
var _a; | ||
let color; | ||
@@ -96,3 +97,3 @@ if (command.prefixColor && command.prefixColor.startsWith('#')) { | ||
const defaultColor = lodash_1.default.get(chalk_1.default, defaults.prefixColors, chalk_1.default.reset); | ||
color = lodash_1.default.get(chalk_1.default, command.prefixColor, defaultColor); | ||
color = lodash_1.default.get(chalk_1.default, (_a = command.prefixColor) !== null && _a !== void 0 ? _a : '', defaultColor); | ||
} | ||
@@ -99,0 +100,0 @@ return color(text); |
@@ -28,3 +28,3 @@ "use strict"; | ||
} | ||
currentColor = nextAutoColors.shift(); | ||
currentColor = String(nextAutoColors.shift()); | ||
} | ||
@@ -31,0 +31,0 @@ yield currentColor; // Auto color |
{ | ||
"name": "concurrently", | ||
"version": "7.5.0", | ||
"version": "7.6.0", | ||
"description": "Run commands concurrently", | ||
@@ -28,5 +28,5 @@ "main": "index.js", | ||
"clean": "tsc --build --clean", | ||
"format": "prettier --ignore-path .gitignore --check '**/{!(package-lock).json,*.y?(a)ml,*.md}'", | ||
"format": "prettier --check '**/*.{json,y?(a)ml,md}'", | ||
"format:fix": "npm run format -- --write", | ||
"lint": "eslint . --ext mjs,js,ts --ignore-path .gitignore", | ||
"lint": "eslint --ignore-path .gitignore --ext mjs,js,ts .", | ||
"lint:fix": "npm run lint -- --fix", | ||
@@ -90,3 +90,3 @@ "prepublishOnly": "npm run build", | ||
"string-argv": "^0.3.1", | ||
"typescript": "~4.8.3" | ||
"typescript": "~4.9.3" | ||
}, | ||
@@ -106,4 +106,4 @@ "files": [ | ||
"*.m?{js,ts}": "eslint --fix", | ||
"{!(package-lock).json,*.{y?(a)ml,md}}": "prettier --write" | ||
"*.{json,y?(a)ml,md}": "prettier --write" | ||
} | ||
} |
@@ -6,3 +6,3 @@ # concurrently | ||
[![Weekly Downloads on NPM](https://img.shields.io/npm/dw/concurrently?label=Downloads&logo=npm)](https://www.npmjs.com/package/concurrently) | ||
[![CI Status](https://img.shields.io/github/workflow/status/open-cli-tools/concurrently/CI?label=CI&logo=github)](https://github.com/open-cli-tools/concurrently/actions/workflows/ci.yml) | ||
[![CI Status](https://img.shields.io/github/workflow/status/open-cli-tools/concurrently/Test?label=CI&logo=github)](https://github.com/open-cli-tools/concurrently/actions/workflows/test.yml) | ||
[![Coverage Status](https://img.shields.io/coveralls/github/open-cli-tools/concurrently/main?label=Coverage&logo=coveralls)](https://coveralls.io/github/open-cli-tools/concurrently?branch=main) | ||
@@ -19,3 +19,3 @@ | ||
- [Why](#why) | ||
- [Install](#install) | ||
- [Installation](#installation) | ||
- [Usage](#usage) | ||
@@ -46,16 +46,13 @@ - [API](#api) | ||
## Install | ||
## Installation | ||
The tool is written in Node.js, but you can use it to run **any** commands. | ||
**concurrently** can be installed in the global scope (if you'd like to have it available and use it on the whole system) or locally for a specific package (for example if you'd like to use it in the `scripts` section of your package): | ||
```bash | ||
npm install -g concurrently | ||
``` | ||
| | npm | Yarn | pnpm | | ||
| ----------- | ----------------------- | ------------------------------ | -------------------------- | | ||
| **Global** | `npm i -g concurrently` | `yarn global add concurrently` | `pnpm add -g concurrently` | | ||
| **Local**\* | `npm i concurrently -D` | `yarn add concurrently -D` | `pnpm add -D concurrently` | | ||
or if you are using it from npm scripts: | ||
<sub>\* It's recommended to add **concurrently** as `devDependencies` as it's usually used for developing purposes. Please change this flag if this doesn't apply in your case.</sub> | ||
```bash | ||
npm install concurrently --save | ||
``` | ||
## Usage | ||
@@ -66,2 +63,4 @@ | ||
The tool is written in Node.js, but you can use it to run **any** commands. | ||
Remember to surround separate commands with quotes: | ||
@@ -153,4 +152,5 @@ | ||
-m, --max-processes How many processes should run at once. | ||
Exact number or a percent of CPUs available (for example "50%"). | ||
New processes only spawn after all restart tries | ||
of a process. [number] | ||
of a process. [string] | ||
-n, --names List of custom names to be used in prefix | ||
@@ -157,0 +157,0 @@ template. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
118659
2448