concurrently 10.0.4 → 10.0.5

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.
@@ -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 {};
@@ -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.5",
5
5
  "description": "Run commands concurrently",
6
6
  "author": "Kimmo Brunfeldt",
7
7
  "license": "MIT",
@@ -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",