Skip to content

Commit

Permalink
Refactor types of Execa methods (#1114)
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky authored Jun 4, 2024
1 parent 077749d commit faab26e
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 115 deletions.
60 changes: 39 additions & 21 deletions types/methods/command.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,6 @@ import type {SyncResult} from '../return/result.js';
import type {ResultPromise} from '../subprocess/subprocess.js';
import type {SimpleTemplateString} from './template.js';

type ExecaCommand<OptionsType extends Options> = {
<NewOptionsType extends Options = {}>(options: NewOptionsType): ExecaCommand<OptionsType & NewOptionsType>;

(...templateString: SimpleTemplateString): ResultPromise<OptionsType>;

<NewOptionsType extends Options = {}>(
command: string,
options?: NewOptionsType,
): ResultPromise<OptionsType & NewOptionsType>;
};

/**
Executes a command. `command` is a string that includes both the `file` and its `arguments`.
Expand All @@ -36,18 +25,27 @@ for await (const commandAndArguments of getReplLine()) {
}
```
*/
export declare const execaCommand: ExecaCommand<{}>;
export declare const execaCommand: ExecaCommandMethod<{}>;

type ExecaCommandMethod<OptionsType extends Options> =
& ExecaCommandBind<OptionsType>
& ExecaCommandTemplate<OptionsType>
& ExecaCommandArray<OptionsType>;

type ExecaCommandSync<OptionsType extends SyncOptions> = {
<NewOptionsType extends SyncOptions = {}>(options: NewOptionsType): ExecaCommandSync<OptionsType & NewOptionsType>;
// `execaCommand(options)` binding
type ExecaCommandBind<OptionsType extends Options> =
<NewOptionsType extends Options = {}>(options: NewOptionsType)
=> ExecaCommandMethod<OptionsType & NewOptionsType>;

(...templateString: SimpleTemplateString): SyncResult<OptionsType>;
// `execaCommand`command`` template syntax
type ExecaCommandTemplate<OptionsType extends Options> =
(...templateString: SimpleTemplateString)
=> ResultPromise<OptionsType>;

<NewOptionsType extends SyncOptions = {}>(
command: string,
options?: NewOptionsType,
): SyncResult<OptionsType & NewOptionsType>;
};
// `execaCommand('command', {})` array syntax
type ExecaCommandArray<OptionsType extends Options> =
<NewOptionsType extends Options = {}>(command: string, options?: NewOptionsType)
=> ResultPromise<OptionsType & NewOptionsType>;

/**
Same as `execaCommand()` but synchronous.
Expand All @@ -67,7 +65,27 @@ for (const commandAndArguments of getReplLine()) {
}
```
*/
export declare const execaCommandSync: ExecaCommandSync<{}>;
export declare const execaCommandSync: ExecaCommandSyncMethod<{}>;

type ExecaCommandSyncMethod<OptionsType extends SyncOptions> =
& ExecaCommandSyncBind<OptionsType>
& ExecaCommandSyncTemplate<OptionsType>
& ExecaCommandSyncArray<OptionsType>;

// `execaCommandSync(options)` binding
type ExecaCommandSyncBind<OptionsType extends SyncOptions> =
<NewOptionsType extends SyncOptions = {}>(options: NewOptionsType)
=> ExecaCommandSyncMethod<OptionsType & NewOptionsType>;

// `execaCommandSync`command`` template syntax
type ExecaCommandSyncTemplate<OptionsType extends SyncOptions> =
(...templateString: SimpleTemplateString)
=> SyncResult<OptionsType>;

// `execaCommandSync('command', {})` array syntax
type ExecaCommandSyncArray<OptionsType extends SyncOptions> =
<NewOptionsType extends SyncOptions = {}>(command: string, options?: NewOptionsType)
=> SyncResult<OptionsType & NewOptionsType>;

/**
Split a `command` string into an array. For example, `'npm run build'` returns `['npm', 'run', 'build']` and `'argument otherArgument'` returns `['argument', 'otherArgument']`.
Expand Down
48 changes: 30 additions & 18 deletions types/methods/main-async.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,6 @@ import type {Options} from '../arguments/options.js';
import type {ResultPromise} from '../subprocess/subprocess.js';
import type {TemplateString} from './template.js';

type Execa<OptionsType extends Options> = {
<NewOptionsType extends Options = {}>(options: NewOptionsType): Execa<OptionsType & NewOptionsType>;

(...templateString: TemplateString): ResultPromise<OptionsType>;

<NewOptionsType extends Options = {}>(
file: string | URL,
arguments?: readonly string[],
options?: NewOptionsType,
): ResultPromise<OptionsType & NewOptionsType>;

<NewOptionsType extends Options = {}>(
file: string | URL,
options?: NewOptionsType,
): ResultPromise<OptionsType & NewOptionsType>;
};

/**
Executes a command using `file ...arguments`.
Expand Down Expand Up @@ -336,4 +319,33 @@ $ NODE_DEBUG=execa node build.js
[00:57:44.747] [1] ✘ (done in 89ms)
```
*/
export declare const execa: Execa<{}>;
export declare const execa: ExecaMethod<{}>;

/**
`execa()` method either exported by Execa, or bound using `execa(options)`.
*/
type ExecaMethod<OptionsType extends Options> =
& ExecaBind<OptionsType>
& ExecaTemplate<OptionsType>
& ExecaArrayLong<OptionsType>
& ExecaArrayShort<OptionsType>;

// `execa(options)` binding
type ExecaBind<OptionsType extends Options> =
<NewOptionsType extends Options = {}>(options: NewOptionsType)
=> ExecaMethod<OptionsType & NewOptionsType>;

// `execa`command`` template syntax
type ExecaTemplate<OptionsType extends Options> =
(...templateString: TemplateString)
=> ResultPromise<OptionsType>;

// `execa('file', ['argument'], {})` array syntax
type ExecaArrayLong<OptionsType extends Options> =
<NewOptionsType extends Options = {}>(file: string | URL, arguments?: readonly string[], options?: NewOptionsType)
=> ResultPromise<OptionsType & NewOptionsType>;

// `execa('file', {})` array syntax
type ExecaArrayShort<OptionsType extends Options> =
<NewOptionsType extends Options = {}>(file: string | URL, options?: NewOptionsType)
=> ResultPromise<OptionsType & NewOptionsType>;
47 changes: 29 additions & 18 deletions types/methods/main-sync.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,6 @@ import type {SyncOptions} from '../arguments/options.js';
import type {SyncResult} from '../return/result.js';
import type {TemplateString} from './template.js';

type ExecaSync<OptionsType extends SyncOptions> = {
<NewOptionsType extends SyncOptions = {}>(options: NewOptionsType): ExecaSync<OptionsType & NewOptionsType>;

(...templateString: TemplateString): SyncResult<OptionsType>;

<NewOptionsType extends SyncOptions = {}>(
file: string | URL,
arguments?: readonly string[],
options?: NewOptionsType,
): SyncResult<OptionsType & NewOptionsType>;

<NewOptionsType extends SyncOptions = {}>(
file: string | URL,
options?: NewOptionsType,
): SyncResult<OptionsType & NewOptionsType>;
};

/**
Same as `execa()` but synchronous.
Expand All @@ -39,4 +22,32 @@ const {stdout} = execaSync`npm run build`;
console.log(stdout);
```
*/
export declare const execaSync: ExecaSync<{}>;
export declare const execaSync: ExecaSyncMethod<{}>;

// For the moment, we purposely do not export `ExecaSyncMethod` and `ExecaScriptSyncMethod`.
// This is because synchronous invocation is discouraged.
type ExecaSyncMethod<OptionsType extends SyncOptions> =
& ExecaSyncBind<OptionsType>
& ExecaSyncTemplate<OptionsType>
& ExecaSyncArrayLong<OptionsType>
& ExecaSyncArrayShort<OptionsType>;

// `execaSync(options)` binding
type ExecaSyncBind<OptionsType extends SyncOptions> =
<NewOptionsType extends SyncOptions = {}>(options: NewOptionsType)
=> ExecaSyncMethod<OptionsType & NewOptionsType>;

// `execaSync`command`` template syntax
type ExecaSyncTemplate<OptionsType extends SyncOptions> =
(...templateString: TemplateString)
=> SyncResult<OptionsType>;

// `execaSync('file', ['argument'], {})` array syntax
type ExecaSyncArrayLong<OptionsType extends SyncOptions> =
<NewOptionsType extends SyncOptions = {}>(file: string | URL, arguments?: readonly string[], options?: NewOptionsType)
=> SyncResult<OptionsType & NewOptionsType>;

// `execaSync('file', {})` array syntax
type ExecaSyncArrayShort<OptionsType extends SyncOptions> =
<NewOptionsType extends SyncOptions = {}>(file: string | URL, options?: NewOptionsType)
=> SyncResult<OptionsType & NewOptionsType>;
48 changes: 30 additions & 18 deletions types/methods/node.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,6 @@ import type {Options} from '../arguments/options.js';
import type {ResultPromise} from '../subprocess/subprocess.js';
import type {TemplateString} from './template.js';

type ExecaNode<OptionsType extends Options> = {
<NewOptionsType extends Options = {}>(options: NewOptionsType): ExecaNode<OptionsType & NewOptionsType>;

(...templateString: TemplateString): ResultPromise<OptionsType>;

<NewOptionsType extends Options = {}>(
scriptPath: string | URL,
arguments?: readonly string[],
options?: NewOptionsType,
): ResultPromise<OptionsType & NewOptionsType>;

<NewOptionsType extends Options = {}>(
scriptPath: string | URL,
options?: NewOptionsType,
): ResultPromise<OptionsType & NewOptionsType>;
};

/**
Same as `execa()` but using the `node: true` option.
Executes a Node.js file using `node scriptPath ...arguments`.
Expand All @@ -45,4 +28,33 @@ await execa({node: true})`file.js argument`;
await execa`node file.js argument`;
```
*/
export declare const execaNode: ExecaNode<{}>;
export declare const execaNode: ExecaNodeMethod<{}>;

/**
`execaNode()` method either exported by Execa, or bound using `execaNode(options)`.
*/
type ExecaNodeMethod<OptionsType extends Options> =
& ExecaNodeBind<OptionsType>
& ExecaNodeTemplate<OptionsType>
& ExecaNodeArrayLong<OptionsType>
& ExecaNodeArrayShort<OptionsType>;

// `execaNode(options)` binding
type ExecaNodeBind<OptionsType extends Options> =
<NewOptionsType extends Options = {}>(options: NewOptionsType)
=> ExecaNodeMethod<OptionsType & NewOptionsType>;

// `execaNode`command`` template syntax
type ExecaNodeTemplate<OptionsType extends Options> =
(...templateString: TemplateString)
=> ResultPromise<OptionsType>;

// `execaNode('script', ['argument'], {})` array syntax
type ExecaNodeArrayLong<OptionsType extends Options> =
<NewOptionsType extends Options = {}>(scriptPath: string | URL, arguments?: readonly string[], options?: NewOptionsType)
=> ResultPromise<OptionsType & NewOptionsType>;

// `execaNode('script', {})` array syntax
type ExecaNodeArrayShort<OptionsType extends Options> =
<NewOptionsType extends Options = {}>(scriptPath: string | URL, options?: NewOptionsType)
=> ResultPromise<OptionsType & NewOptionsType>;
103 changes: 63 additions & 40 deletions types/methods/script.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,6 @@ import type {SyncResult} from '../return/result.js';
import type {ResultPromise} from '../subprocess/subprocess.js';
import type {TemplateString} from './template.js';

type ExecaScriptCommon<OptionsType extends CommonOptions> = {
<NewOptionsType extends CommonOptions = {}>(options: NewOptionsType): ExecaScript<OptionsType & NewOptionsType>;

(...templateString: TemplateString): ResultPromise<StricterOptions<OptionsType, Options>>;

<NewOptionsType extends Options = {}>(
file: string | URL,
arguments?: readonly string[],
options?: NewOptionsType,
): ResultPromise<StricterOptions<OptionsType & NewOptionsType, Options>>;

<NewOptionsType extends Options = {}>(
file: string | URL,
options?: NewOptionsType,
): ResultPromise<StricterOptions<OptionsType & NewOptionsType, Options>>;
};

type ExecaScriptSync<OptionsType extends CommonOptions> = {
<NewOptionsType extends SyncOptions = {}>(options: NewOptionsType): ExecaScriptSync<OptionsType & NewOptionsType>;

(...templateString: TemplateString): SyncResult<StricterOptions<OptionsType, SyncOptions>>;

<NewOptionsType extends SyncOptions = {}>(
file: string | URL,
arguments?: readonly string[],
options?: NewOptionsType,
): SyncResult<StricterOptions<OptionsType & NewOptionsType, SyncOptions>>;

<NewOptionsType extends SyncOptions = {}>(
file: string | URL,
options?: NewOptionsType,
): SyncResult<StricterOptions<OptionsType & NewOptionsType, SyncOptions>>;
};

type ExecaScript<OptionsType extends CommonOptions> = {
sync: ExecaScriptSync<OptionsType>;
s: ExecaScriptSync<OptionsType>;
} & ExecaScriptCommon<OptionsType>;

/**
Same as `execa()` but using script-friendly default options.
Expand Down Expand Up @@ -87,4 +48,66 @@ $ NODE_DEBUG=execa node build.js
[00:57:44.747] [1] ✘ (done in 89ms)
```
*/
export const $: ExecaScript<{}>;
export const $: ExecaScriptMethod<{}>;

/**
`$()` method either exported by Execa, or bound using `$(options)`.
*/
type ExecaScriptMethod<OptionsType extends CommonOptions> =
& ExecaScriptBind<OptionsType>
& ExecaScriptTemplate<OptionsType>
& ExecaScriptArrayLong<OptionsType>
& ExecaScriptArrayShort<OptionsType>
& {sync: ExecaScriptSyncMethod<OptionsType>}
& {s: ExecaScriptSyncMethod<OptionsType>};

// `$(options)` binding
type ExecaScriptBind<OptionsType extends CommonOptions> =
<NewOptionsType extends CommonOptions = {}>(options: NewOptionsType)
=> ExecaScriptMethod<OptionsType & NewOptionsType>;

// `$`command`` template syntax
type ExecaScriptTemplate<OptionsType extends CommonOptions> =
(...templateString: TemplateString)
=> ResultPromise<StricterOptions<OptionsType, Options>>;

// `$('file', ['arg'], {})` array syntax
type ExecaScriptArrayLong<OptionsType extends CommonOptions> =
<NewOptionsType extends Options = {}>(file: string | URL, arguments?: readonly string[], options?: NewOptionsType)
=> ResultPromise<StricterOptions<OptionsType & NewOptionsType, Options>>;

// `$('file', {})` array syntax
type ExecaScriptArrayShort<OptionsType extends CommonOptions> =
<NewOptionsType extends Options = {}>(file: string | URL, options?: NewOptionsType)
=> ResultPromise<StricterOptions<OptionsType & NewOptionsType, Options>>;

// We must intersect the overloaded methods with & instead of using a simple object as a workaround for a TypeScript bug
// See https://github.com/microsoft/TypeScript/issues/58765
/**
`$.sync()` method either exported by Execa, or bound using `$.sync(options)`.
*/
type ExecaScriptSyncMethod<OptionsType extends CommonOptions> =
& ExecaScriptSyncBind<OptionsType>
& ExecaScriptSyncTemplate<OptionsType>
& ExecaScriptSyncArrayLong<OptionsType>
& ExecaScriptSyncArrayShort<OptionsType>;

// `$.sync(options)` binding
type ExecaScriptSyncBind<OptionsType extends CommonOptions> =
<NewOptionsType extends SyncOptions = {}>(options: NewOptionsType)
=> ExecaScriptSyncMethod<OptionsType & NewOptionsType>;

// $.sync`command` template syntax
type ExecaScriptSyncTemplate<OptionsType extends CommonOptions> =
(...templateString: TemplateString)
=> SyncResult<StricterOptions<OptionsType, SyncOptions>>;

// `$.sync('file', ['arg'], {})` array syntax
type ExecaScriptSyncArrayLong<OptionsType extends CommonOptions> =
<NewOptionsType extends SyncOptions = {}>(file: string | URL, arguments?: readonly string[], options?: NewOptionsType)
=> SyncResult<StricterOptions<OptionsType & NewOptionsType, SyncOptions>>;

// `$.sync('file', {})` array syntax
type ExecaScriptSyncArrayShort<OptionsType extends CommonOptions> =
<NewOptionsType extends SyncOptions = {}>(file: string | URL, options?: NewOptionsType)
=> SyncResult<StricterOptions<OptionsType & NewOptionsType, SyncOptions>>;

0 comments on commit faab26e

Please sign in to comment.