concurrently 10.0.4 → 10.0.6

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 CHANGED
@@ -103,7 +103,7 @@ Check out documentation and other usage examples in the [`docs` directory](./doc
103
103
  - `handleInput`: when `true`, reads input from `process.stdin`.
104
104
  - `inputStream`: a [`Readable` stream](https://nodejs.org/docs/latest/api/stream.html#readable-streams)
105
105
  to read the input from. Should only be used in the rare instance you would like to stream anything other than `process.stdin`. Overrides `handleInput`.
106
- - `pauseInputStreamOnFinish`: by default, pauses the input stream (`process.stdin` when `handleInput` is enabled, or `inputStream` if provided) when all of the processes have finished. If you need to read from the input stream after `concurrently` has finished, set this to `false`. ([#252](https://github.com/kimmobrunfeldt/concurrently/issues/252)).
106
+ - `pauseInputStreamOnFinish`: by default, pauses the input stream (`process.stdin` when `handleInput` is enabled, or `inputStream` if provided) when all of the processes have finished. If you need to read from the input stream after `concurrently` has finished, set this to `false`. ([#252](https://github.com/open-cli-tools/concurrently/issues/252)).
107
107
  - `killOthersOn`: once the first command exits with one of these statuses, kill other commands.
108
108
  Can be an array containing the strings `success` (status code zero) and/or `failure` (non-zero exit status).
109
109
  - `maxProcesses`: how many processes should run at once.
@@ -139,7 +139,9 @@ Check out documentation and other usage examples in the [`docs` directory](./doc
139
139
  Example:
140
140
 
141
141
  ```js
142
- const concurrently = require('concurrently');
142
+ import path from 'node:path';
143
+ import { concurrently } from 'concurrently';
144
+
143
145
  const { result } = concurrently(
144
146
  [
145
147
  'npm:watch-*',
@@ -148,14 +150,14 @@ const { result } = concurrently(
148
150
  {
149
151
  command: 'watch',
150
152
  name: 'watch',
151
- cwd: path.resolve(__dirname, 'scripts/watchers'),
153
+ cwd: path.resolve(import.meta.dirname, 'scripts/watchers'),
152
154
  },
153
155
  ],
154
156
  {
155
157
  prefix: 'name',
156
158
  killOthersOn: ['failure', 'success'],
157
159
  restartTries: 3,
158
- cwd: path.resolve(__dirname, 'scripts'),
160
+ cwd: path.resolve(import.meta.dirname, 'scripts'),
159
161
  },
160
162
  );
161
163
  result.then(success, failure);
@@ -0,0 +1,13 @@
1
+ import { spawnSync as baseSpawnSync } from 'node:child_process';
2
+ export type SpawnSync = typeof baseSpawnSync;
3
+ /**
4
+ * On Windows, concurrently pipes child-process output through a freshly-allocated console whose
5
+ * codepage may not be UTF-8, garbling non-ASCII output from commands that rely on it (see
6
+ * open-cli-tools/concurrently#302). This sets the console's codepage to UTF-8 once, which spawned
7
+ * children then inherit, and returns a function that restores the original codepage.
8
+ *
9
+ * No-ops (and never throws) if not on Windows, if the codepage is already UTF-8, or if reading/setting
10
+ * it fails for any reason -- this is a best-effort convenience, never something that should block
11
+ * concurrently from running.
12
+ */
13
+ export declare function ensureUtf8Codepage(spawnSync?: SpawnSync, process?: Pick<NodeJS.Process, 'platform'>): () => void;
@@ -0,0 +1,61 @@
1
+ import { spawnSync as baseSpawnSync } from 'node:child_process';
2
+ import nodeProcess from 'node:process';
3
+ const UTF8_CODEPAGE = 65001;
4
+ /**
5
+ * On Windows, concurrently pipes child-process output through a freshly-allocated console whose
6
+ * codepage may not be UTF-8, garbling non-ASCII output from commands that rely on it (see
7
+ * open-cli-tools/concurrently#302). This sets the console's codepage to UTF-8 once, which spawned
8
+ * children then inherit, and returns a function that restores the original codepage.
9
+ *
10
+ * No-ops (and never throws) if not on Windows, if the codepage is already UTF-8, or if reading/setting
11
+ * it fails for any reason -- this is a best-effort convenience, never something that should block
12
+ * concurrently from running.
13
+ */
14
+ export function ensureUtf8Codepage(spawnSync = baseSpawnSync, process = nodeProcess) {
15
+ const noop = () => { };
16
+ if (process.platform !== 'win32') {
17
+ return noop;
18
+ }
19
+ try {
20
+ const original = readCodepage(spawnSync);
21
+ if (original == null || original === UTF8_CODEPAGE) {
22
+ return noop;
23
+ }
24
+ if (!setCodepage(spawnSync, UTF8_CODEPAGE)) {
25
+ return noop;
26
+ }
27
+ let restored = false;
28
+ return () => {
29
+ if (restored) {
30
+ return;
31
+ }
32
+ restored = true;
33
+ try {
34
+ setCodepage(spawnSync, original);
35
+ }
36
+ catch {
37
+ // Best-effort restore; nothing more we can do.
38
+ }
39
+ };
40
+ }
41
+ catch {
42
+ return noop;
43
+ }
44
+ }
45
+ function readCodepage(spawnSync) {
46
+ const result = spawnSync('cmd.exe', ['/s', '/c', 'chcp'], { encoding: 'utf8' });
47
+ if (result.error || result.status !== 0 || !result.stdout) {
48
+ return null;
49
+ }
50
+ // Locale-dependent text (e.g. "Active code page: 65001." or "Aktive Codepage: 65001."),
51
+ // so match on the number rather than the surrounding words.
52
+ const match = /(\d+)/.exec(result.stdout);
53
+ return match ? Number(match[1]) : null;
54
+ }
55
+ function setCodepage(spawnSync, codepage) {
56
+ // `chcp` changes the codepage of the console this process is attached to regardless of where its
57
+ // own stdio points, so output can be safely discarded here rather than inherited -- otherwise its
58
+ // confirmation text (e.g. "Active code page: 65001.") would leak into concurrently's own output.
59
+ const result = spawnSync('cmd.exe', ['/s', '/c', `chcp ${codepage}`], { stdio: 'ignore' });
60
+ return !result.error && result.status === 0;
61
+ }
package/dist/bin/index.js CHANGED
@@ -5,6 +5,7 @@ import { hideBin } from 'yargs/helpers';
5
5
  import * as defaults from '../lib/defaults.js';
6
6
  import { concurrently } from '../lib/index.js';
7
7
  import { castArray, splitOutsideParens } from '../lib/utils.js';
8
+ import { ensureUtf8Codepage } from './console-codepage.js';
8
9
  import { normalizeCliCommand } from './normalize-cli-command.js';
9
10
  import { readPackageJson } from './read-package-json.js';
10
11
  const version = String(readPackageJson().version);
@@ -197,33 +198,47 @@ if (!commands.length) {
197
198
  program.showHelp();
198
199
  process.exit();
199
200
  }
200
- concurrently(commands.map((command, index) => ({
201
- command: normalizeCliCommand(String(command)),
202
- name: names[index],
203
- })), {
204
- handleInput: args.handleInput,
205
- defaultInputTarget: args.defaultInputTarget,
206
- killOthersOn: args.killOthers
207
- ? ['success', 'failure']
208
- : args.killOthersOnFail
209
- ? ['failure']
210
- : [],
211
- killSignal: args.killSignal,
212
- killTimeout: args.killTimeout,
213
- maxProcesses: args.maxProcesses,
214
- raw: args.raw,
215
- hide: args.hide.split(','),
216
- group: args.group,
217
- prefix: args.prefix,
218
- prefixColors: splitOutsideParens(args.prefixColors, ','),
219
- prefixLength: args.prefixLength,
220
- padPrefix: args.padPrefix,
221
- restartDelay: args.restartAfter === 'exponential' ? 'exponential' : Number(args.restartAfter),
222
- restartTries: args.restartTries,
223
- successCondition: args.success,
224
- timestampFormat: args.timestampFormat,
225
- timings: args.timings,
226
- shell: args.shell,
227
- teardown: args.teardown,
228
- additionalArguments: args.passthroughArguments ? additionalArguments : undefined,
229
- }).result.then(() => process.exit(0), () => process.exit(1));
201
+ // Piped output on Windows uses the console's active codepage, which may not be UTF-8 -- see
202
+ // open-cli-tools/concurrently#302. Raw mode inherits the real console directly instead of piping
203
+ // through concurrently, so it isn't affected and doesn't need this.
204
+ const restoreCodepage = args.raw ? () => { } : ensureUtf8Codepage();
205
+ const exitProcess = (code) => {
206
+ restoreCodepage();
207
+ process.exit(code);
208
+ };
209
+ try {
210
+ concurrently(commands.map((command, index) => ({
211
+ command: normalizeCliCommand(String(command)),
212
+ name: names[index],
213
+ })), {
214
+ handleInput: args.handleInput,
215
+ defaultInputTarget: args.defaultInputTarget,
216
+ killOthersOn: args.killOthers
217
+ ? ['success', 'failure']
218
+ : args.killOthersOnFail
219
+ ? ['failure']
220
+ : [],
221
+ killSignal: args.killSignal,
222
+ killTimeout: args.killTimeout,
223
+ maxProcesses: args.maxProcesses,
224
+ raw: args.raw,
225
+ hide: args.hide.split(','),
226
+ group: args.group,
227
+ prefix: args.prefix,
228
+ prefixColors: splitOutsideParens(args.prefixColors, ','),
229
+ prefixLength: args.prefixLength,
230
+ padPrefix: args.padPrefix,
231
+ restartDelay: args.restartAfter === 'exponential' ? 'exponential' : Number(args.restartAfter),
232
+ restartTries: args.restartTries,
233
+ successCondition: args.success,
234
+ timestampFormat: args.timestampFormat,
235
+ timings: args.timings,
236
+ shell: args.shell,
237
+ teardown: args.teardown,
238
+ additionalArguments: args.passthroughArguments ? additionalArguments : undefined,
239
+ }).result.then(() => exitProcess(0), () => exitProcess(1));
240
+ }
241
+ catch (error) {
242
+ restoreCodepage();
243
+ throw error;
244
+ }
@@ -28,8 +28,14 @@ export class ExpandWildcard {
28
28
  }
29
29
  static readPackage() {
30
30
  try {
31
- const json = fs.readFileSync('package.json', { encoding: 'utf-8' });
32
- return JSON.parse(json);
31
+ let json = '{}';
32
+ if (fs.existsSync('package.json')) {
33
+ json = fs.readFileSync('package.json', { encoding: 'utf-8' });
34
+ }
35
+ else if (fs.existsSync('package.json5')) {
36
+ json = fs.readFileSync('package.json5', { encoding: 'utf-8' });
37
+ }
38
+ return JSONC.parse(json);
33
39
  }
34
40
  catch {
35
41
  return {};
@@ -41,7 +41,7 @@ export class KillOthers {
41
41
  }
42
42
  setTimeout(() => {
43
43
  const killableCommands = commands.filter((command) => Command.canKill(command));
44
- if (killableCommands) {
44
+ if (killableCommands.length) {
45
45
  this.logger.logGlobalEvent(`Sending SIGKILL to ${killableCommands.length} processes..`);
46
46
  killableCommands.forEach((command) => command.kill('SIGKILL'));
47
47
  }
@@ -1 +1 @@
1
- {"root":["../bin/index.spec.ts","../bin/index.ts","../bin/normalize-cli-command.spec.ts","../bin/normalize-cli-command.ts","../bin/read-package-json.ts","../lib/assert.spec.ts","../lib/assert.ts","../lib/command.spec.ts","../lib/command.ts","../lib/completion-listener.spec.ts","../lib/completion-listener.ts","../lib/concurrently.spec.ts","../lib/concurrently.ts","../lib/date-format.spec.ts","../lib/date-format.ts","../lib/defaults.ts","../lib/index.ts","../lib/jsonc.spec.ts","../lib/jsonc.ts","../lib/logger.spec.ts","../lib/logger.ts","../lib/observables.spec.ts","../lib/observables.ts","../lib/output-writer.spec.ts","../lib/output-writer.ts","../lib/prefix-color-selector.spec.ts","../lib/prefix-color-selector.ts","../lib/spawn.spec.ts","../lib/spawn.ts","../lib/utils.spec.ts","../lib/utils.ts","../lib/__fixtures__/create-mock-instance.ts","../lib/__fixtures__/fake-command.ts","../lib/command-parser/command-parser.d.ts","../lib/command-parser/expand-arguments.spec.ts","../lib/command-parser/expand-arguments.ts","../lib/command-parser/expand-shortcut.spec.ts","../lib/command-parser/expand-shortcut.ts","../lib/command-parser/expand-wildcard.spec.ts","../lib/command-parser/expand-wildcard.ts","../lib/declarations/intl.d.ts","../lib/flow-control/flow-controller.ts","../lib/flow-control/input-handler.spec.ts","../lib/flow-control/input-handler.ts","../lib/flow-control/kill-on-signal.spec.ts","../lib/flow-control/kill-on-signal.ts","../lib/flow-control/kill-others.spec.ts","../lib/flow-control/kill-others.ts","../lib/flow-control/log-error.spec.ts","../lib/flow-control/log-error.ts","../lib/flow-control/log-exit.spec.ts","../lib/flow-control/log-exit.ts","../lib/flow-control/log-output.spec.ts","../lib/flow-control/log-output.ts","../lib/flow-control/log-timings.spec.ts","../lib/flow-control/log-timings.ts","../lib/flow-control/logger-padding.spec.ts","../lib/flow-control/logger-padding.ts","../lib/flow-control/output-error-handler.spec.ts","../lib/flow-control/output-error-handler.ts","../lib/flow-control/restart-process.spec.ts","../lib/flow-control/restart-process.ts","../lib/flow-control/teardown.spec.ts","../lib/flow-control/teardown.ts"],"version":"5.9.3"}
1
+ {"root":["../bin/console-codepage.spec.ts","../bin/console-codepage.ts","../bin/index.spec.ts","../bin/index.ts","../bin/normalize-cli-command.spec.ts","../bin/normalize-cli-command.ts","../bin/read-package-json.ts","../lib/assert.spec.ts","../lib/assert.ts","../lib/command.spec.ts","../lib/command.ts","../lib/completion-listener.spec.ts","../lib/completion-listener.ts","../lib/concurrently.spec.ts","../lib/concurrently.ts","../lib/date-format.spec.ts","../lib/date-format.ts","../lib/defaults.ts","../lib/index.ts","../lib/jsonc.spec.ts","../lib/jsonc.ts","../lib/logger.spec.ts","../lib/logger.ts","../lib/observables.spec.ts","../lib/observables.ts","../lib/output-writer.spec.ts","../lib/output-writer.ts","../lib/prefix-color-selector.spec.ts","../lib/prefix-color-selector.ts","../lib/spawn.spec.ts","../lib/spawn.ts","../lib/utils.spec.ts","../lib/utils.ts","../lib/__fixtures__/create-mock-instance.ts","../lib/__fixtures__/fake-command.ts","../lib/command-parser/command-parser.d.ts","../lib/command-parser/expand-arguments.spec.ts","../lib/command-parser/expand-arguments.ts","../lib/command-parser/expand-shortcut.spec.ts","../lib/command-parser/expand-shortcut.ts","../lib/command-parser/expand-wildcard.spec.ts","../lib/command-parser/expand-wildcard.ts","../lib/declarations/intl.d.ts","../lib/flow-control/flow-controller.ts","../lib/flow-control/input-handler.spec.ts","../lib/flow-control/input-handler.ts","../lib/flow-control/kill-on-signal.spec.ts","../lib/flow-control/kill-on-signal.ts","../lib/flow-control/kill-others.spec.ts","../lib/flow-control/kill-others.ts","../lib/flow-control/log-error.spec.ts","../lib/flow-control/log-error.ts","../lib/flow-control/log-exit.spec.ts","../lib/flow-control/log-exit.ts","../lib/flow-control/log-output.spec.ts","../lib/flow-control/log-output.ts","../lib/flow-control/log-timings.spec.ts","../lib/flow-control/log-timings.ts","../lib/flow-control/logger-padding.spec.ts","../lib/flow-control/logger-padding.ts","../lib/flow-control/output-error-handler.spec.ts","../lib/flow-control/output-error-handler.ts","../lib/flow-control/restart-process.spec.ts","../lib/flow-control/restart-process.ts","../lib/flow-control/teardown.spec.ts","../lib/flow-control/teardown.ts"],"version":"5.9.3"}
@@ -77,6 +77,7 @@ $ concurrently -c red,blue 'echo Hello there' 'echo General Kenobi!'
77
77
  - `red`
78
78
  - `white`
79
79
  - `yellow`
80
+
80
81
  </details>
81
82
 
82
83
  Colors can take modifiers too. Several can be applied at once by appending `.<modifier 1>.<modifier 2>` and so on.
@@ -96,6 +97,7 @@ $ concurrently -c '#23de43.inverse,bold.blue.dim' 'echo Hello there' 'echo Gener
96
97
  - `italic`
97
98
  - `strikethrough`
98
99
  - `underline`
100
+
99
101
  </details>
100
102
 
101
103
  A background color can be set in a similarly fashion.
@@ -116,6 +118,7 @@ $ concurrently -c bgGray,red.bgBlack 'echo Hello there' 'echo General Kenobi!'
116
118
  - `bgRed`
117
119
  - `bgWhite`
118
120
  - `bgYellow`
121
+
119
122
  </details>
120
123
 
121
124
  ### Advanced Color Functions
@@ -1,6 +1,6 @@
1
1
  # Command Shortcuts
2
2
 
3
- Package managers that execute scripts from a `package.json` or `deno.(json|jsonc)` file can be shortened when in concurrently.<br/>
3
+ Package managers that execute scripts from a `package.json` / `package.json5` or `deno.(json|jsonc)` file can be shortened when in concurrently.<br/>
4
4
  The following are supported:
5
5
 
6
6
  | Syntax | Expands to |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "concurrently",
3
3
  "type": "module",
4
- "version": "10.0.4",
4
+ "version": "10.0.6",
5
5
  "description": "Run commands concurrently",
6
6
  "author": "Kimmo Brunfeldt",
7
7
  "license": "MIT",
@@ -41,7 +41,7 @@
41
41
  "dependencies": {
42
42
  "chalk": "5.6.2",
43
43
  "rxjs": "7.8.2",
44
- "shell-quote": "1.9.0",
44
+ "shell-quote": "1.12.0",
45
45
  "supports-color": "10.2.2",
46
46
  "tree-kill": "1.2.2",
47
47
  "yargs": "18.0.0"
@@ -49,26 +49,26 @@
49
49
  "devDependencies": {
50
50
  "@eslint/js": "^10.0.1",
51
51
  "@hirez_io/observer-spy": "^2.2.0",
52
- "@types/node": "^22.20.0",
52
+ "@types/node": "^22.20.1",
53
53
  "@types/yargs": "^17.0.35",
54
- "@vitest/coverage-v8": "^4.1.6",
55
- "@vitest/eslint-plugin": "^1.6.17",
54
+ "@vitest/coverage-v8": "^4.1.10",
55
+ "@vitest/eslint-plugin": "^1.6.27",
56
56
  "ctrlc-wrapper": "^0.0.5",
57
- "eslint": "^10.4.0",
57
+ "eslint": "^10.8.1",
58
58
  "eslint-config-flat-gitignore": "^2.3.0",
59
59
  "eslint-config-prettier": "^10.1.8",
60
60
  "eslint-plugin-import-lite": "^0.6.0",
61
- "eslint-plugin-prettier": "^5.5.5",
61
+ "eslint-plugin-prettier": "^5.5.6",
62
62
  "eslint-plugin-simple-import-sort": "^13.0.0",
63
- "globals": "^17.6.0",
63
+ "globals": "^17.11.0",
64
64
  "husky": "^9.1.7",
65
- "lint-staged": "^17.0.5",
66
- "prettier": "^3.8.3",
65
+ "lint-staged": "^17.3.0",
66
+ "prettier": "^3.9.6",
67
67
  "safe-publish-latest": "^2.0.0",
68
68
  "string-argv": "^0.3.2",
69
69
  "typescript": "~5.9.3",
70
- "typescript-eslint": "^8.59.3",
71
- "vitest": "^4.1.6"
70
+ "typescript-eslint": "^8.67.0",
71
+ "vitest": "^4.1.10"
72
72
  },
73
73
  "lint-staged": {
74
74
  "*.{js,ts}": "eslint --fix",