Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: open-cli-tools/concurrently
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v8.1.0
Choose a base ref
...
head repository: open-cli-tools/concurrently
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v8.2.0
Choose a head ref
  • 3 commits
  • 4 files changed
  • 2 contributors

Commits on Jun 9, 2023

  1. Support REPL languages that use colons (#393)

    Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
    wyattades and paescuj authored Jun 9, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    LastDragon-ru Aleksei Lebedev
    Copy the full SHA
    ffd3760 View commit details
  2. Update dependencies (#427)

    paescuj authored Jun 9, 2023

    Verified

    This commit was signed with the committer’s verified signature. The key has expired.
    LastDragon-ru Aleksei Lebedev
    Copy the full SHA
    d1fd1a8 View commit details
  3. 8.2.0

    paescuj committed Jun 9, 2023
    Copy the full SHA
    c36f059 View commit details
Showing with 1,080 additions and 1,016 deletions.
  1. +19 −19 package.json
  2. +1,006 −978 pnpm-lock.yaml
  3. +35 −10 src/flow-control/input-handler.spec.ts
  4. +20 −9 src/flow-control/input-handler.ts
38 changes: 19 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "concurrently",
"version": "8.1.0",
"version": "8.2.0",
"description": "Run commands concurrently",
"main": "index.js",
"types": "dist/src/index.d.ts",
@@ -51,31 +51,31 @@
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2",
"date-fns": "^2.29.3",
"date-fns": "^2.30.0",
"lodash": "^4.17.21",
"rxjs": "^7.8.0",
"shell-quote": "^1.8.0",
"spawn-command": "0.0.2-1",
"rxjs": "^7.8.1",
"shell-quote": "^1.8.1",
"spawn-command": "0.0.2",
"supports-color": "^8.1.1",
"tree-kill": "^1.2.2",
"yargs": "^17.7.1"
"yargs": "^17.7.2"
},
"devDependencies": {
"@hirez_io/observer-spy": "^2.2.0",
"@swc/core": "^1.3.42",
"@swc/jest": "^0.2.24",
"@types/jest": "^29.5.0",
"@types/lodash": "^4.14.192",
"@types/node": "^14.18.42",
"@swc/core": "^1.3.62",
"@swc/jest": "^0.2.26",
"@types/jest": "^29.5.2",
"@types/lodash": "^4.14.195",
"@types/node": "^14.18.48",
"@types/shell-quote": "^1.7.1",
"@types/supports-color": "^8.1.1",
"@types/yargs": "^17.0.24",
"@typescript-eslint/eslint-plugin": "^5.57.0",
"@typescript-eslint/parser": "^5.57.0",
"@typescript-eslint/eslint-plugin": "^5.59.9",
"@typescript-eslint/parser": "^5.59.9",
"coveralls-next": "^4.2.0",
"ctrlc-wrapper": "^0.0.4",
"esbuild": "~0.17.14",
"eslint": "^8.37.0",
"esbuild": "~0.17.19",
"eslint": "^8.42.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.2.1",
@@ -84,11 +84,11 @@
"husky": "^8.0.3",
"jest": "^29.5.0",
"jest-create-mock-instance": "^2.0.0",
"lint-staged": "^13.2.0",
"prettier": "^2.8.7",
"lint-staged": "^13.2.2",
"prettier": "^2.8.8",
"safe-publish-latest": "^2.0.0",
"string-argv": "^0.3.1",
"typescript": "~5.0.2"
"string-argv": "^0.3.2",
"typescript": "~5.1.3"
},
"files": [
"dist",
1,984 changes: 1,006 additions & 978 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

45 changes: 35 additions & 10 deletions src/flow-control/input-handler.spec.ts
Original file line number Diff line number Diff line change
@@ -54,22 +54,48 @@ it('forwards input stream to target index specified in input', () => {
controller.handle(commands);

inputStream.write('1:something');
inputStream.write('1:multi\nline\n');

expect(commands[0].stdin?.write).not.toHaveBeenCalled();
expect(commands[1].stdin?.write).toHaveBeenCalledTimes(1);
expect(commands[1].stdin?.write).toHaveBeenCalledTimes(2);
expect(commands[1].stdin?.write).toHaveBeenCalledWith('something');
expect(commands[1].stdin?.write).toHaveBeenCalledWith('multi\nline\n');
});

it('forwards input stream to target index specified in input when input contains colon', () => {
controller.handle(commands);

inputStream.emit('data', Buffer.from('1::something'));
inputStream.emit('data', Buffer.from('1:some:thing'));
inputStream.emit('data', Buffer.from('1: :something'));
inputStream.emit('data', Buffer.from('1::something'));

expect(commands[0].stdin?.write).not.toHaveBeenCalled();
expect(commands[1].stdin?.write).toHaveBeenCalledTimes(2);
expect(commands[1].stdin?.write).toHaveBeenCalledWith(':something');
expect(commands[1].stdin?.write).toHaveBeenCalledTimes(3);
expect(commands[1].stdin?.write).toHaveBeenCalledWith('some:thing');
expect(commands[1].stdin?.write).toHaveBeenCalledWith(' :something');
expect(commands[1].stdin?.write).toHaveBeenCalledWith(':something');
});

it('does not forward input stream when input contains colon in a different format', () => {
controller.handle(commands);

inputStream.emit('data', Buffer.from('Ruby0::Const::Syntax'));
inputStream.emit('data', Buffer.from('1:Ruby1::Const::Syntax'));
inputStream.emit('data', Buffer.from('ruby_symbol_arg :my_symbol'));
inputStream.emit('data', Buffer.from('ruby_symbol_arg(:my_symbol)'));
inputStream.emit('data', Buffer.from('{ruby_key: :my_val}'));
inputStream.emit('data', Buffer.from('{:ruby_key=>:my_val}'));
inputStream.emit('data', Buffer.from('js_obj = {key: "my_val"}'));

expect(commands[1].stdin?.write).toHaveBeenCalledTimes(1);
expect(commands[1].stdin?.write).toHaveBeenCalledWith('Ruby1::Const::Syntax');
expect(commands[0].stdin?.write).toHaveBeenCalledTimes(6);
expect(commands[0].stdin?.write).toHaveBeenCalledWith('Ruby0::Const::Syntax');
expect(commands[0].stdin?.write).toHaveBeenCalledWith('ruby_symbol_arg :my_symbol');
expect(commands[0].stdin?.write).toHaveBeenCalledWith('ruby_symbol_arg(:my_symbol)');
expect(commands[0].stdin?.write).toHaveBeenCalledWith('{ruby_key: :my_val}');
expect(commands[0].stdin?.write).toHaveBeenCalledWith('{:ruby_key=>:my_val}');
expect(commands[0].stdin?.write).toHaveBeenCalledWith('js_obj = {key: "my_val"}');
});

it('forwards input stream to target name specified in input', () => {
@@ -90,20 +116,19 @@ it('logs error if command has no stdin open', () => {

expect(commands[1].stdin?.write).not.toHaveBeenCalled();
expect(logger.logGlobalEvent).toHaveBeenCalledWith(
'Unable to find command 0, or it has no stdin open\n'
'Unable to find command "0", or it has no stdin open\n'
);
});

it('logs error if command is not found', () => {
it('fallback to default input stream if command is not found', () => {
controller.handle(commands);

inputStream.write('foobar:something');

expect(commands[0].stdin?.write).not.toHaveBeenCalled();
expect(commands[0].stdin?.write).toHaveBeenCalledTimes(1);
expect(commands[0].stdin?.write).toHaveBeenCalledWith('foobar:something');
expect(commands[1].stdin?.write).not.toHaveBeenCalled();
expect(logger.logGlobalEvent).toHaveBeenCalledWith(
'Unable to find command foobar, or it has no stdin open\n'
);
expect(logger.logGlobalEvent).not.toHaveBeenCalled();
});

it('pauses input stream when finished', () => {
29 changes: 20 additions & 9 deletions src/flow-control/input-handler.ts
Original file line number Diff line number Diff line change
@@ -48,24 +48,35 @@ export class InputHandler implements FlowController {
return { commands };
}

const commandsMap = new Map<string, Command>();
for (const command of commands) {
commandsMap.set(command.index.toString(), command);
commandsMap.set(command.name, command);
}

Rx.fromEvent(inputStream, 'data')
.pipe(map((data) => String(data)))
.subscribe((data) => {
const dataParts = data.split(/:(.+)/);
const targetId = dataParts.length > 1 ? dataParts[0] : this.defaultInputTarget;
const input = dataParts[1] || data;
let command: Command | undefined, input: string;

const dataParts = data.split(/:(.+)/s);
let target = dataParts[0];

const command = commands.find(
(command) =>
command.name === targetId ||
command.index.toString() === targetId.toString()
);
if (dataParts.length > 1 && (command = commandsMap.get(target))) {
input = dataParts[1];
} else {
// If `target` does not match a registered command,
// fallback to `defaultInputTarget` and forward the whole input data
target = this.defaultInputTarget.toString();
command = commandsMap.get(target);
input = data;
}

if (command && command.stdin) {
command.stdin.write(input);
} else {
this.logger.logGlobalEvent(
`Unable to find command ${targetId}, or it has no stdin open\n`
`Unable to find command "${target}", or it has no stdin open\n`
);
}
});