concurrently 9.2.0 → 10.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.
Files changed (149) hide show
  1. package/README.md +25 -15
  2. package/dist/bin/{concurrently.js → index.js} +23 -52
  3. package/dist/bin/index.spec.d.ts +1 -0
  4. package/dist/bin/index.spec.js +368 -0
  5. package/dist/bin/normalize-cli-command.d.ts +1 -0
  6. package/dist/bin/normalize-cli-command.js +15 -0
  7. package/dist/bin/normalize-cli-command.spec.d.ts +1 -0
  8. package/dist/bin/normalize-cli-command.spec.js +36 -0
  9. package/dist/bin/read-package-json.d.ts +4 -0
  10. package/dist/bin/read-package-json.js +17 -0
  11. package/dist/lib/__fixtures__/create-mock-instance.d.ts +2 -0
  12. package/dist/lib/__fixtures__/create-mock-instance.js +5 -0
  13. package/dist/lib/__fixtures__/fake-command.d.ts +6 -0
  14. package/dist/lib/__fixtures__/fake-command.js +37 -0
  15. package/dist/lib/assert.d.ts +10 -0
  16. package/dist/lib/assert.js +24 -0
  17. package/dist/lib/assert.spec.d.ts +1 -0
  18. package/dist/lib/assert.spec.js +41 -0
  19. package/dist/{src → lib}/command-parser/expand-arguments.d.ts +7 -7
  20. package/dist/lib/command-parser/expand-arguments.js +36 -0
  21. package/dist/lib/command-parser/expand-arguments.spec.d.ts +1 -0
  22. package/dist/lib/command-parser/expand-arguments.spec.js +57 -0
  23. package/dist/{src → lib}/command-parser/expand-shortcut.d.ts +2 -2
  24. package/dist/{src → lib}/command-parser/expand-shortcut.js +1 -5
  25. package/dist/lib/command-parser/expand-shortcut.spec.d.ts +1 -0
  26. package/dist/lib/command-parser/expand-shortcut.spec.js +36 -0
  27. package/dist/{src → lib}/command-parser/expand-wildcard.d.ts +2 -2
  28. package/dist/{src → lib}/command-parser/expand-wildcard.js +25 -31
  29. package/dist/lib/command-parser/expand-wildcard.spec.d.ts +1 -0
  30. package/dist/lib/command-parser/expand-wildcard.spec.js +288 -0
  31. package/dist/{src → lib}/command.d.ts +7 -10
  32. package/dist/{src → lib}/command.js +13 -32
  33. package/dist/lib/command.spec.d.ts +1 -0
  34. package/dist/lib/command.spec.js +369 -0
  35. package/dist/{src → lib}/completion-listener.d.ts +2 -3
  36. package/dist/{src → lib}/completion-listener.js +9 -36
  37. package/dist/lib/completion-listener.spec.d.ts +1 -0
  38. package/dist/lib/completion-listener.spec.js +229 -0
  39. package/dist/{src → lib}/concurrently.d.ts +10 -12
  40. package/dist/{src → lib}/concurrently.js +34 -47
  41. package/dist/lib/concurrently.spec.d.ts +1 -0
  42. package/dist/lib/concurrently.spec.js +320 -0
  43. package/dist/{src → lib}/date-format.d.ts +2 -2
  44. package/dist/{src → lib}/date-format.js +101 -77
  45. package/dist/lib/date-format.spec.d.ts +1 -0
  46. package/dist/lib/date-format.spec.js +480 -0
  47. package/dist/{src → lib}/defaults.d.ts +2 -6
  48. package/dist/{src → lib}/defaults.js +16 -23
  49. package/dist/{src → lib}/flow-control/input-handler.d.ts +4 -5
  50. package/dist/{src → lib}/flow-control/input-handler.js +8 -34
  51. package/dist/lib/flow-control/input-handler.spec.d.ts +1 -0
  52. package/dist/lib/flow-control/input-handler.spec.js +116 -0
  53. package/dist/{src → lib}/flow-control/kill-on-signal.d.ts +3 -5
  54. package/dist/{src → lib}/flow-control/kill-on-signal.js +3 -7
  55. package/dist/lib/flow-control/kill-on-signal.spec.d.ts +1 -0
  56. package/dist/lib/flow-control/kill-on-signal.spec.js +63 -0
  57. package/dist/{src → lib}/flow-control/kill-others.d.ts +3 -4
  58. package/dist/{src → lib}/flow-control/kill-others.js +8 -15
  59. package/dist/lib/flow-control/kill-others.spec.d.ts +1 -0
  60. package/dist/lib/flow-control/kill-others.spec.js +98 -0
  61. package/dist/{src → lib}/flow-control/log-error.d.ts +3 -3
  62. package/dist/{src → lib}/flow-control/log-error.js +1 -5
  63. package/dist/lib/flow-control/log-error.spec.d.ts +1 -0
  64. package/dist/lib/flow-control/log-error.spec.js +33 -0
  65. package/dist/{src → lib}/flow-control/log-exit.d.ts +3 -3
  66. package/dist/{src → lib}/flow-control/log-exit.js +1 -5
  67. package/dist/lib/flow-control/log-exit.spec.d.ts +1 -0
  68. package/dist/lib/flow-control/log-exit.spec.js +24 -0
  69. package/dist/{src → lib}/flow-control/log-output.d.ts +3 -3
  70. package/dist/{src → lib}/flow-control/log-output.js +1 -5
  71. package/dist/lib/flow-control/log-output.spec.d.ts +1 -0
  72. package/dist/lib/flow-control/log-output.spec.js +33 -0
  73. package/dist/{src → lib}/flow-control/log-timings.d.ts +3 -3
  74. package/dist/{src → lib}/flow-control/log-timings.js +11 -44
  75. package/dist/lib/flow-control/log-timings.spec.d.ts +1 -0
  76. package/dist/lib/flow-control/log-timings.spec.js +89 -0
  77. package/dist/{src → lib}/flow-control/logger-padding.d.ts +3 -3
  78. package/dist/{src → lib}/flow-control/logger-padding.js +7 -7
  79. package/dist/lib/flow-control/logger-padding.spec.d.ts +1 -0
  80. package/dist/lib/flow-control/logger-padding.spec.js +60 -0
  81. package/dist/{src → lib}/flow-control/output-error-handler.d.ts +4 -6
  82. package/dist/{src → lib}/flow-control/output-error-handler.js +3 -7
  83. package/dist/lib/flow-control/output-error-handler.spec.d.ts +1 -0
  84. package/dist/lib/flow-control/output-error-handler.spec.js +41 -0
  85. package/dist/{src → lib}/flow-control/restart-process.d.ts +4 -4
  86. package/dist/{src → lib}/flow-control/restart-process.js +10 -37
  87. package/dist/lib/flow-control/restart-process.spec.d.ts +1 -0
  88. package/dist/lib/flow-control/restart-process.spec.js +127 -0
  89. package/dist/{src → lib}/flow-control/teardown.d.ts +4 -5
  90. package/dist/{src → lib}/flow-control/teardown.js +6 -33
  91. package/dist/lib/flow-control/teardown.spec.d.ts +1 -0
  92. package/dist/lib/flow-control/teardown.spec.js +93 -0
  93. package/dist/{src → lib}/index.d.ts +21 -20
  94. package/dist/lib/index.js +98 -0
  95. package/dist/lib/jsonc.d.ts +8 -0
  96. package/dist/{src → lib}/jsonc.js +3 -8
  97. package/dist/lib/jsonc.spec.d.ts +1 -0
  98. package/dist/lib/jsonc.spec.js +73 -0
  99. package/dist/{src → lib}/logger.d.ts +3 -2
  100. package/dist/{src → lib}/logger.js +112 -53
  101. package/dist/lib/logger.spec.d.ts +1 -0
  102. package/dist/lib/logger.spec.js +507 -0
  103. package/dist/{src → lib}/observables.d.ts +1 -2
  104. package/dist/{src → lib}/observables.js +3 -7
  105. package/dist/lib/observables.spec.d.ts +1 -0
  106. package/dist/lib/observables.spec.js +29 -0
  107. package/dist/{src → lib}/output-writer.d.ts +3 -4
  108. package/dist/{src → lib}/output-writer.js +4 -31
  109. package/dist/lib/output-writer.spec.d.ts +1 -0
  110. package/dist/lib/output-writer.spec.js +96 -0
  111. package/dist/lib/prefix-color-selector.d.ts +21 -0
  112. package/dist/{src → lib}/prefix-color-selector.js +14 -31
  113. package/dist/lib/prefix-color-selector.spec.d.ts +1 -0
  114. package/dist/lib/prefix-color-selector.spec.js +159 -0
  115. package/dist/{src → lib}/spawn.d.ts +19 -16
  116. package/dist/lib/spawn.js +105 -0
  117. package/dist/lib/spawn.spec.d.ts +1 -0
  118. package/dist/lib/spawn.spec.js +100 -0
  119. package/dist/lib/utils.d.ts +25 -0
  120. package/dist/lib/utils.js +52 -0
  121. package/dist/lib/utils.spec.d.ts +1 -0
  122. package/dist/lib/utils.spec.js +58 -0
  123. package/dist/tsconfig.tsbuildinfo +1 -0
  124. package/docs/README.md +6 -0
  125. package/docs/cli/passthrough-arguments.md +8 -8
  126. package/docs/cli/prefixing.md +44 -4
  127. package/docs/cli/shortcuts.md +2 -2
  128. package/docs/shell-resolution.md +48 -0
  129. package/package.json +64 -85
  130. package/dist/bin/read-package.d.ts +0 -6
  131. package/dist/bin/read-package.js +0 -47
  132. package/dist/src/assert.d.ts +0 -5
  133. package/dist/src/assert.js +0 -16
  134. package/dist/src/command-parser/command-parser.d.ts +0 -19
  135. package/dist/src/command-parser/command-parser.js +0 -2
  136. package/dist/src/command-parser/expand-arguments.js +0 -39
  137. package/dist/src/command-parser/strip-quotes.d.ts +0 -16
  138. package/dist/src/command-parser/strip-quotes.js +0 -17
  139. package/dist/src/flow-control/flow-controller.d.ts +0 -13
  140. package/dist/src/flow-control/flow-controller.js +0 -2
  141. package/dist/src/index.js +0 -99
  142. package/dist/src/jsonc.d.ts +0 -8
  143. package/dist/src/prefix-color-selector.d.ts +0 -11
  144. package/dist/src/spawn.js +0 -49
  145. package/index.d.mts +0 -7
  146. package/index.d.ts +0 -11
  147. package/index.js +0 -14
  148. package/index.mjs +0 -10
  149. /package/dist/bin/{concurrently.d.ts → index.d.ts} +0 -0
@@ -0,0 +1,36 @@
1
+ import { expect, it } from 'vitest';
2
+ import { normalizeCliCommand } from './normalize-cli-command.js';
3
+ it('strips outer CLI wrapper double quotes', () => {
4
+ expect(normalizeCliCommand('"echo foo"')).toBe('echo foo');
5
+ });
6
+ it('strips outer CLI wrapper single quotes', () => {
7
+ expect(normalizeCliCommand("'echo foo'")).toBe('echo foo');
8
+ });
9
+ it('strips quotes around a single wrapped token', () => {
10
+ expect(normalizeCliCommand('"echo"')).toBe('echo');
11
+ expect(normalizeCliCommand("'echo'")).toBe('echo');
12
+ });
13
+ it('preserves quotes in well-formed shell commands', () => {
14
+ expect(normalizeCliCommand('"/usr/local/bin/mytool" --flag "some value"')).toBe('"/usr/local/bin/mytool" --flag "some value"');
15
+ });
16
+ it('preserves well-formed shell commands with multiple quote sets', () => {
17
+ expect(normalizeCliCommand('"/usr/local/bin/mytool" --flag "some value" --other "last arg"')).toBe('"/usr/local/bin/mytool" --flag "some value" --other "last arg"');
18
+ });
19
+ it('preserves single quotes in well-formed shell commands', () => {
20
+ expect(normalizeCliCommand("'printf' '%s %s' foo bar")).toBe("'printf' '%s %s' foo bar");
21
+ });
22
+ it('returns unquoted input unchanged', () => {
23
+ expect(normalizeCliCommand('echo foo')).toBe('echo foo');
24
+ });
25
+ it('returns an empty string unchanged', () => {
26
+ expect(normalizeCliCommand('')).toBe('');
27
+ });
28
+ it('leaves ambiguous input unchanged', () => {
29
+ expect(normalizeCliCommand('"echo foo')).toBe('"echo foo');
30
+ });
31
+ it('leaves input with an unclosed single quote unchanged', () => {
32
+ expect(normalizeCliCommand("echo foo'")).toBe("echo foo'");
33
+ });
34
+ it('leaves input with mismatched quote types unchanged', () => {
35
+ expect(normalizeCliCommand('"echo foo\'')).toBe('"echo foo\'');
36
+ });
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Read the package.json file of `concurrently`
3
+ */
4
+ export declare function readPackageJson(): Record<string, unknown>;
@@ -0,0 +1,17 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ /**
4
+ * Read the package.json file of `concurrently`
5
+ */
6
+ export function readPackageJson() {
7
+ let resolver;
8
+ try {
9
+ resolver = require.resolve;
10
+ }
11
+ catch {
12
+ resolver = createRequire(import.meta.url).resolve;
13
+ }
14
+ const path = resolver('concurrently/package.json');
15
+ const content = readFileSync(path, 'utf8');
16
+ return JSON.parse(content);
17
+ }
@@ -0,0 +1,2 @@
1
+ import { MockedObject } from 'vitest';
2
+ export declare function createMockInstance<T>(constructor: new (...args: any[]) => T): MockedObject<T>;
@@ -0,0 +1,5 @@
1
+ import { vi } from 'vitest';
2
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3
+ export function createMockInstance(constructor) {
4
+ return new (vi.mockObject(constructor))();
5
+ }
@@ -0,0 +1,6 @@
1
+ import { ChildProcess, CloseEvent, Command, CommandInfo } from '../command.js';
2
+ export declare class FakeCommand extends Command {
3
+ constructor(name?: string, command?: string, index?: number, info?: Partial<CommandInfo>);
4
+ }
5
+ export declare const createFakeProcess: (pid: number) => ChildProcess;
6
+ export declare const createFakeCloseEvent: (overrides?: Partial<CloseEvent>) => CloseEvent;
@@ -0,0 +1,37 @@
1
+ import EventEmitter from 'node:events';
2
+ import { PassThrough, Writable } from 'node:stream';
3
+ import { vi } from 'vitest';
4
+ import { Command } from '../command.js';
5
+ import { createMockInstance } from './create-mock-instance.js';
6
+ export class FakeCommand extends Command {
7
+ constructor(name = 'foo', command = 'echo foo', index = 0, info) {
8
+ super({
9
+ index,
10
+ name,
11
+ command,
12
+ ...info,
13
+ }, {}, vi.fn(), vi.fn());
14
+ this.stdin = createMockInstance(Writable);
15
+ this.start = vi.fn();
16
+ this.kill = vi.fn();
17
+ }
18
+ }
19
+ export const createFakeProcess = (pid) => Object.assign(new EventEmitter(), {
20
+ pid,
21
+ send: vi.fn(),
22
+ stdin: new PassThrough(),
23
+ stdout: new PassThrough(),
24
+ stderr: new PassThrough(),
25
+ });
26
+ export const createFakeCloseEvent = (overrides) => ({
27
+ command: new FakeCommand(),
28
+ index: 0,
29
+ killed: false,
30
+ exitCode: 0,
31
+ timings: {
32
+ startDate: new Date(),
33
+ endDate: new Date(),
34
+ durationSeconds: 0,
35
+ },
36
+ ...overrides,
37
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Asserts that some condition is true, and if not, prints a warning about it being deprecated.
3
+ * The message is printed only once.
4
+ */
5
+ export declare function assertDeprecated(check: boolean, name: string, message: string): void;
6
+ /**
7
+ * Asserts that some condition is true, and if not, prints a warning about the runtime not being well supported.
8
+ * The message is printed only once.
9
+ */
10
+ export declare function assertNotRuntime(check: boolean, name: string, message: string): void;
@@ -0,0 +1,24 @@
1
+ const deprecations = new Set();
2
+ /**
3
+ * Asserts that some condition is true, and if not, prints a warning about it being deprecated.
4
+ * The message is printed only once.
5
+ */
6
+ export function assertDeprecated(check, name, message) {
7
+ if (!check && !deprecations.has(name)) {
8
+ // eslint-disable-next-line no-console
9
+ console.warn(`[concurrently] ${name} is deprecated. ${message}`);
10
+ deprecations.add(name);
11
+ }
12
+ }
13
+ const runtimes = new Set();
14
+ /**
15
+ * Asserts that some condition is true, and if not, prints a warning about the runtime not being well supported.
16
+ * The message is printed only once.
17
+ */
18
+ export function assertNotRuntime(check, name, message) {
19
+ if (!check && !runtimes.has(name)) {
20
+ // eslint-disable-next-line no-console
21
+ console.warn(`[concurrently] Running via ${name} is not well supported. ${message}`);
22
+ runtimes.add(name);
23
+ }
24
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,41 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { assertDeprecated, assertNotRuntime } from './assert.js';
3
+ let consoleMock;
4
+ beforeEach(() => {
5
+ consoleMock = vi.spyOn(console, 'warn').mockImplementation(() => { });
6
+ });
7
+ afterEach(() => {
8
+ vi.clearAllMocks();
9
+ });
10
+ describe('assertDeprecated()', () => {
11
+ it('prints warning with name and message when condition is false', () => {
12
+ assertDeprecated(false, 'example-flag', 'This is an example message.');
13
+ expect(consoleMock).toHaveBeenLastCalledWith('[concurrently] example-flag is deprecated. This is an example message.');
14
+ });
15
+ it('prints same warning only once', () => {
16
+ assertDeprecated(false, 'example-flag', 'This is an example message.');
17
+ assertDeprecated(false, 'different-flag', 'This is another message.');
18
+ expect(consoleMock).toBeCalledTimes(1);
19
+ expect(consoleMock).toHaveBeenLastCalledWith('[concurrently] different-flag is deprecated. This is another message.');
20
+ });
21
+ it('prints nothing if condition is true', () => {
22
+ assertDeprecated(true, 'example-flag', 'This is an example message.');
23
+ expect(consoleMock).not.toHaveBeenCalled();
24
+ });
25
+ });
26
+ describe('assertNotRuntime()', () => {
27
+ it('prints warning with name and message when condition is false', () => {
28
+ assertNotRuntime(false, 'example-flag', 'This is an example message.');
29
+ expect(consoleMock).toHaveBeenLastCalledWith('[concurrently] Running via example-flag is not well supported. This is an example message.');
30
+ });
31
+ it('prints same warning only once', () => {
32
+ assertNotRuntime(false, 'example-flag', 'This is an example message.');
33
+ assertNotRuntime(false, 'different-flag', 'This is another message.');
34
+ expect(consoleMock).toBeCalledTimes(1);
35
+ expect(consoleMock).toHaveBeenLastCalledWith('[concurrently] Running via different-flag is not well supported. This is another message.');
36
+ });
37
+ it('prints nothing if condition is true', () => {
38
+ assertNotRuntime(true, 'example-flag', 'This is an example message.');
39
+ expect(consoleMock).not.toHaveBeenCalled();
40
+ });
41
+ });
@@ -1,5 +1,5 @@
1
- import { CommandInfo } from '../command';
2
- import { CommandParser } from './command-parser';
1
+ import { CommandInfo } from '../command.js';
2
+ import { CommandParser } from './command-parser.js';
3
3
  /**
4
4
  * Replace placeholders with additional arguments.
5
5
  */
@@ -9,10 +9,10 @@ export declare class ExpandArguments implements CommandParser {
9
9
  parse(commandInfo: CommandInfo): {
10
10
  command: string;
11
11
  name: string;
12
- env?: Record<string, unknown> | undefined;
13
- cwd?: string | undefined;
14
- prefixColor?: string | undefined;
15
- ipc?: number | undefined;
16
- raw?: boolean | undefined;
12
+ env?: Record<string, unknown>;
13
+ cwd?: string;
14
+ prefixColor?: string;
15
+ ipc?: number;
16
+ raw?: boolean;
17
17
  };
18
18
  }
@@ -0,0 +1,36 @@
1
+ import { quote } from 'shell-quote';
2
+ /**
3
+ * Replace placeholders with additional arguments.
4
+ */
5
+ export class ExpandArguments {
6
+ additionalArguments;
7
+ constructor(additionalArguments) {
8
+ this.additionalArguments = additionalArguments;
9
+ }
10
+ parse(commandInfo) {
11
+ const command = commandInfo.command.replace(/\\?\{([@*]|[1-9]\d*)\}/g, (match, placeholderTarget) => {
12
+ // Don't replace the placeholder if it is escaped by a backslash.
13
+ if (match.startsWith('\\')) {
14
+ return match.slice(1);
15
+ }
16
+ if (this.additionalArguments.length > 0) {
17
+ // Replace numeric placeholder if value exists in additional arguments.
18
+ if (+placeholderTarget <= this.additionalArguments.length) {
19
+ return quote([this.additionalArguments[+placeholderTarget - 1]]);
20
+ }
21
+ // Replace all arguments placeholder.
22
+ if (placeholderTarget === '@') {
23
+ return quote(this.additionalArguments);
24
+ }
25
+ // Replace combined arguments placeholder.
26
+ if (placeholderTarget === '*') {
27
+ return quote([this.additionalArguments.join(' ')]);
28
+ }
29
+ }
30
+ // Replace placeholder with empty string
31
+ // if value doesn't exist in additional arguments.
32
+ return '';
33
+ });
34
+ return { ...commandInfo, command };
35
+ }
36
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,57 @@
1
+ import { expect, it } from 'vitest';
2
+ import { ExpandArguments } from './expand-arguments.js';
3
+ const createCommandInfo = (command) => ({
4
+ command,
5
+ name: '',
6
+ });
7
+ it('returns command as is when no placeholders', () => {
8
+ const parser = new ExpandArguments(['foo', 'bar']);
9
+ const commandInfo = createCommandInfo('echo foo');
10
+ expect(parser.parse(commandInfo)).toEqual({ ...commandInfo, command: 'echo foo' });
11
+ });
12
+ it('single argument placeholder is replaced', () => {
13
+ const parser = new ExpandArguments(['foo', 'bar']);
14
+ const commandInfo = createCommandInfo('echo {1}');
15
+ expect(parser.parse(commandInfo)).toEqual({ ...commandInfo, command: 'echo foo' });
16
+ });
17
+ it('argument placeholder is replaced and quoted properly', () => {
18
+ const parser = new ExpandArguments(['foo bar']);
19
+ const commandInfo = createCommandInfo('echo {1}');
20
+ expect(parser.parse(commandInfo)).toEqual({ ...commandInfo, command: "echo 'foo bar'" });
21
+ });
22
+ it('multiple single argument placeholders are replaced', () => {
23
+ const parser = new ExpandArguments(['foo', 'bar']);
24
+ const commandInfo = createCommandInfo('echo {2} {1}');
25
+ expect(parser.parse(commandInfo)).toEqual({ ...commandInfo, command: 'echo bar foo' });
26
+ });
27
+ it('empty replacement with single placeholder and not enough passthrough arguments', () => {
28
+ const parser = new ExpandArguments(['foo', 'bar']);
29
+ const commandInfo = createCommandInfo('echo {3}');
30
+ expect(parser.parse(commandInfo)).toEqual({ ...commandInfo, command: 'echo ' });
31
+ });
32
+ it('empty replacement with all placeholder and no passthrough arguments', () => {
33
+ const parser = new ExpandArguments([]);
34
+ const commandInfo = createCommandInfo('echo {@}');
35
+ expect(parser.parse(commandInfo)).toEqual({ ...commandInfo, command: 'echo ' });
36
+ });
37
+ it('empty replacement with combined placeholder and no passthrough arguments', () => {
38
+ const parser = new ExpandArguments([]);
39
+ const commandInfo = createCommandInfo('echo {*}');
40
+ expect(parser.parse(commandInfo)).toEqual({ ...commandInfo, command: 'echo ' });
41
+ });
42
+ it('all arguments placeholder is replaced', () => {
43
+ const parser = new ExpandArguments(['foo', 'bar']);
44
+ const commandInfo = createCommandInfo('echo {@}');
45
+ expect(parser.parse(commandInfo)).toEqual({ ...commandInfo, command: 'echo foo bar' });
46
+ });
47
+ it('combined arguments placeholder is replaced', () => {
48
+ const parser = new ExpandArguments(['foo', 'bar']);
49
+ const commandInfo = createCommandInfo('echo {*}');
50
+ expect(parser.parse(commandInfo)).toEqual({ ...commandInfo, command: "echo 'foo bar'" });
51
+ });
52
+ it('escaped argument placeholders are not replaced', () => {
53
+ const parser = new ExpandArguments(['foo', 'bar']);
54
+ // Equals to single backslash on command line
55
+ const commandInfo = createCommandInfo('echo \\{1} \\{@} \\{*}');
56
+ expect(parser.parse(commandInfo)).toEqual({ ...commandInfo, command: 'echo {1} {@} {*}' });
57
+ });
@@ -1,5 +1,5 @@
1
- import { CommandInfo } from '../command';
2
- import { CommandParser } from './command-parser';
1
+ import { CommandInfo } from '../command.js';
2
+ import { CommandParser } from './command-parser.js';
3
3
  /**
4
4
  * Expands shortcuts according to the following table:
5
5
  *
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ExpandShortcut = void 0;
4
1
  /**
5
2
  * Expands shortcuts according to the following table:
6
3
  *
@@ -13,7 +10,7 @@ exports.ExpandShortcut = void 0;
13
10
  * | `node:<script>` | `node --run <script>` |
14
11
  * | `deno:<script>` | `deno task <script>` |
15
12
  */
16
- class ExpandShortcut {
13
+ export class ExpandShortcut {
17
14
  parse(commandInfo) {
18
15
  const [, prefix, script, args] = /^(npm|yarn|pnpm|bun|node|deno):(\S+)(.*)/.exec(commandInfo.command) || [];
19
16
  if (!script) {
@@ -36,4 +33,3 @@ class ExpandShortcut {
36
33
  };
37
34
  }
38
35
  }
39
- exports.ExpandShortcut = ExpandShortcut;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ExpandShortcut } from './expand-shortcut.js';
3
+ const parser = new ExpandShortcut();
4
+ const createCommandInfo = (command, name = '') => ({
5
+ name,
6
+ command,
7
+ });
8
+ it('returns same command if no prefix is present', () => {
9
+ const commandInfo = createCommandInfo('echo foo');
10
+ expect(parser.parse(commandInfo)).toBe(commandInfo);
11
+ });
12
+ describe.each([
13
+ ['npm', 'npm run'],
14
+ ['yarn', 'yarn run'],
15
+ ['pnpm', 'pnpm run'],
16
+ ['bun', 'bun run'],
17
+ ['node', 'node --run'],
18
+ ['deno', 'deno task'],
19
+ ])(`with '%s:' prefix`, (prefix, command) => {
20
+ it(`expands to "${command} <script> <args>"`, () => {
21
+ const commandInfo = createCommandInfo(`${prefix}:foo -- bar`, 'echo');
22
+ expect(parser.parse(commandInfo)).toEqual({
23
+ ...commandInfo,
24
+ name: 'echo',
25
+ command: `${command} foo -- bar`,
26
+ });
27
+ });
28
+ it('sets name to script name if none', () => {
29
+ const commandInfo = createCommandInfo(`${prefix}:foo -- bar`);
30
+ expect(parser.parse(commandInfo)).toEqual({
31
+ ...commandInfo,
32
+ name: 'foo',
33
+ command: `${command} foo -- bar`,
34
+ });
35
+ });
36
+ });
@@ -1,5 +1,5 @@
1
- import { CommandInfo } from '../command';
2
- import { CommandParser } from './command-parser';
1
+ import { CommandInfo } from '../command.js';
2
+ import { CommandParser } from './command-parser.js';
3
3
  /**
4
4
  * Finds wildcards in 'npm/yarn/pnpm/bun run', 'node --run' and 'deno task'
5
5
  * commands and replaces them with all matching scripts in the NodeJS and Deno
@@ -1,12 +1,6 @@
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.ExpandWildcard = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const lodash_1 = __importDefault(require("lodash"));
9
- const jsonc_1 = __importDefault(require("../jsonc"));
1
+ import fs from 'node:fs';
2
+ import JSONC from '../jsonc.js';
3
+ import { escapeRegExp } from '../utils.js';
10
4
  // Matches a negative filter surrounded by '(!' and ')'.
11
5
  const OMISSION = /\(!([^)]+)\)/;
12
6
  /**
@@ -14,30 +8,30 @@ const OMISSION = /\(!([^)]+)\)/;
14
8
  * commands and replaces them with all matching scripts in the NodeJS and Deno
15
9
  * configuration files of the current directory.
16
10
  */
17
- class ExpandWildcard {
11
+ export class ExpandWildcard {
18
12
  readDeno;
19
13
  readPackage;
20
14
  static readDeno() {
21
15
  try {
22
16
  let json = '{}';
23
- if (fs_1.default.existsSync('deno.json')) {
24
- json = fs_1.default.readFileSync('deno.json', { encoding: 'utf-8' });
17
+ if (fs.existsSync('deno.json')) {
18
+ json = fs.readFileSync('deno.json', { encoding: 'utf-8' });
25
19
  }
26
- else if (fs_1.default.existsSync('deno.jsonc')) {
27
- json = fs_1.default.readFileSync('deno.jsonc', { encoding: 'utf-8' });
20
+ else if (fs.existsSync('deno.jsonc')) {
21
+ json = fs.readFileSync('deno.jsonc', { encoding: 'utf-8' });
28
22
  }
29
- return jsonc_1.default.parse(json);
23
+ return JSONC.parse(json);
30
24
  }
31
- catch (e) {
25
+ catch {
32
26
  return {};
33
27
  }
34
28
  }
35
29
  static readPackage() {
36
30
  try {
37
- const json = fs_1.default.readFileSync('package.json', { encoding: 'utf-8' });
31
+ const json = fs.readFileSync('package.json', { encoding: 'utf-8' });
38
32
  return JSON.parse(json);
39
33
  }
40
- catch (e) {
34
+ catch {
41
35
  return {};
42
36
  }
43
37
  }
@@ -70,7 +64,7 @@ class ExpandWildcard {
70
64
  // - <npm|yarn|pnpm|bun> run <script> [args]
71
65
  // - node --run <script> [args]
72
66
  // - deno task <script> [args]
73
- const [, command, scriptGlob, args] = /((?:npm|yarn|pnpm|bun) (?:run)|node --run|deno task) (\S+)([^&]*)/.exec(commandInfo.command) || [];
67
+ const [, command, scriptGlob, args] = /((?:npm|yarn|pnpm|bun) run|node --run|deno task) (\S+)([^&]*)/.exec(commandInfo.command) || [];
74
68
  const wildcardPosition = (scriptGlob || '').indexOf('*');
75
69
  // If the regex didn't match an npm script, or it has no wildcard,
76
70
  // then we have nothing to do here
@@ -79,29 +73,29 @@ class ExpandWildcard {
79
73
  }
80
74
  const [, omission] = OMISSION.exec(scriptGlob) || [];
81
75
  const scriptGlobSansOmission = scriptGlob.replace(OMISSION, '');
82
- const preWildcard = lodash_1.default.escapeRegExp(scriptGlobSansOmission.slice(0, wildcardPosition));
83
- const postWildcard = lodash_1.default.escapeRegExp(scriptGlobSansOmission.slice(wildcardPosition + 1));
76
+ const preWildcard = escapeRegExp(scriptGlobSansOmission.slice(0, wildcardPosition));
77
+ const postWildcard = escapeRegExp(scriptGlobSansOmission.slice(wildcardPosition + 1));
84
78
  const wildcardRegex = new RegExp(`^${preWildcard}(.*?)${postWildcard}$`);
85
79
  // If 'commandInfo.name' doesn't match 'scriptGlob', this means a custom name
86
80
  // has been specified and thus becomes the prefix (as described in the README).
87
81
  const prefix = commandInfo.name !== scriptGlob ? commandInfo.name : '';
88
- return this.relevantScripts(command)
89
- .map((script) => {
90
- if (omission && RegExp(omission).test(script)) {
91
- return;
82
+ const commands = [];
83
+ for (const script of this.relevantScripts(command)) {
84
+ if (omission && new RegExp(omission).test(script)) {
85
+ continue;
92
86
  }
93
- const [, match] = wildcardRegex.exec(script) || [];
87
+ const result = wildcardRegex.exec(script);
88
+ const match = result?.[1];
94
89
  if (match !== undefined) {
95
- return {
90
+ commands.push({
96
91
  ...commandInfo,
97
92
  command: `${command} ${script}${args}`,
98
93
  // Will use an empty command name if no prefix has been specified and
99
94
  // the wildcard match is empty, e.g. if `npm:watch-*` matches `npm run watch-`.
100
95
  name: prefix + match,
101
- };
96
+ });
102
97
  }
103
- })
104
- .filter((commandInfo) => !!commandInfo);
98
+ }
99
+ return commands;
105
100
  }
106
101
  }
107
- exports.ExpandWildcard = ExpandWildcard;
@@ -0,0 +1 @@
1
+ export {};