Skip to content

Commit 9bd70ac

Browse files
committed
Improve typing
1 parent 76193f2 commit 9bd70ac

File tree

9 files changed

+108
-109
lines changed

9 files changed

+108
-109
lines changed

v-next/core/src/config.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import type { ArgumentTypeToValueType } from "./types/arguments.js";
1+
import type {
2+
ArgumentTypeToValueType,
3+
OptionDefinition,
4+
} from "./types/arguments.js";
25
import type { ConfigurationVariable } from "./types/config.js";
3-
import type { GlobalOptionDefinition } from "./types/global-options.js";
46
import type {
57
EmptyTaskDefinitionBuilder,
68
NewTaskDefinitionBuilder,
@@ -63,7 +65,7 @@ export function globalOption<T extends ArgumentType>(options: {
6365
description: string;
6466
type?: T;
6567
defaultValue: ArgumentTypeToValueType<T>;
66-
}): GlobalOptionDefinition {
68+
}): OptionDefinition {
6769
return buildGlobalOptionDefinition(options);
6870
}
6971

@@ -73,7 +75,7 @@ export function globalOption<T extends ArgumentType>(options: {
7375
export function globalFlag(options: {
7476
name: string;
7577
description: string;
76-
}): GlobalOptionDefinition {
78+
}): OptionDefinition {
7779
return buildGlobalOptionDefinition({
7880
...options,
7981
type: ArgumentType.BOOLEAN,

v-next/core/src/internal/global-options.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type {
22
ArgumentTypeToValueType,
33
ArgumentValue,
4+
OptionDefinition,
45
} from "../types/arguments.js";
56
import type {
67
GlobalOptions,
7-
GlobalOptionDefinition,
88
GlobalOptionDefinitions,
99
} from "../types/global-options.js";
1010
import type { HardhatPlugin } from "../types/plugins.js";
@@ -80,7 +80,7 @@ export function buildGlobalOptionDefinition<T extends ArgumentType>({
8080
description: string;
8181
type?: T;
8282
defaultValue: ArgumentTypeToValueType<T>;
83-
}): GlobalOptionDefinition {
83+
}): OptionDefinition {
8484
const argumentType = type ?? ArgumentType.STRING;
8585

8686
if (!isArgumentNameValid(name)) {

v-next/core/src/internal/tasks/builders.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import type { ArgumentTypeToValueType } from "../../types/arguments.js";
21
import type {
3-
TaskOptionDefinition,
2+
ArgumentTypeToValueType,
3+
OptionDefinition,
4+
PositionalArgumentDefinition,
5+
} from "../../types/arguments.js";
6+
import type {
47
NewTaskActionFunction,
58
NewTaskDefinitionBuilder,
69
NewTaskDefinition,
7-
TaskPositionalArgumentDefinition,
810
TaskOverrideActionFunction,
911
TaskOverrideDefinitionBuilder,
1012
TaskOverrideDefinition,
@@ -62,8 +64,8 @@ export class NewTaskDefinitionBuilderImplementation
6264
readonly #id: string[];
6365
readonly #usedNames: Set<string> = new Set();
6466

65-
readonly #options: Record<string, TaskOptionDefinition> = {};
66-
readonly #positionalArgs: TaskPositionalArgumentDefinition[] = [];
67+
readonly #options: Record<string, OptionDefinition> = {};
68+
readonly #positionalArgs: PositionalArgumentDefinition[] = [];
6769

6870
#description: string;
6971

@@ -294,7 +296,7 @@ export class TaskOverrideDefinitionBuilderImplementation
294296
{
295297
readonly #id: string[];
296298

297-
readonly #options: Record<string, TaskOptionDefinition> = {};
299+
readonly #options: Record<string, OptionDefinition> = {};
298300

299301
#description?: string;
300302

v-next/core/src/internal/tasks/resolved-task.ts

+30-25
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
import type { ArgumentValue } from "../../types/arguments.js";
1+
import type {
2+
ArgumentValue,
3+
OptionDefinition,
4+
PositionalArgumentDefinition,
5+
} from "../../types/arguments.js";
26
import type { HardhatRuntimeEnvironment } from "../../types/hre.js";
37
import type {
4-
TaskOptionDefinition,
58
NewTaskActionFunction,
6-
TaskPositionalArgumentDefinition,
79
Task,
810
TaskActions,
911
TaskArguments,
1012
TaskOverrideActionFunction,
11-
TaskArgumentDefinition,
1213
} from "../../types/tasks.js";
1314

1415
import {
@@ -48,8 +49,8 @@ export class ResolvedTask implements Task {
4849
id: string[],
4950
description: string,
5051
action: NewTaskActionFunction | string,
51-
options: Record<string, TaskOptionDefinition>,
52-
positionalArguments: TaskPositionalArgumentDefinition[],
52+
options: Record<string, OptionDefinition>,
53+
positionalArguments: PositionalArgumentDefinition[],
5354
pluginId?: string,
5455
): ResolvedTask {
5556
return new ResolvedTask(
@@ -68,8 +69,8 @@ export class ResolvedTask implements Task {
6869
public readonly id: string[],
6970
public readonly description: string,
7071
public readonly actions: TaskActions,
71-
public readonly options: Map<string, TaskOptionDefinition>,
72-
public readonly positionalArguments: TaskPositionalArgumentDefinition[],
72+
public readonly options: Map<string, OptionDefinition>,
73+
public readonly positionalArguments: PositionalArgumentDefinition[],
7374
public readonly pluginId: string | undefined,
7475
public readonly subtasks: Map<string, Task>,
7576
hre: HardhatRuntimeEnvironment,
@@ -98,25 +99,34 @@ export class ResolvedTask implements Task {
9899
});
99100
}
100101

101-
// Normalize arguments into a single iterable
102-
const allArguments: TaskArgumentDefinition[] = [
103-
...this.options.values(),
104-
...this.positionalArguments,
105-
];
106-
107102
const providedArgumentNames = new Set(Object.keys(taskArguments));
108-
for (const argument of allArguments) {
103+
104+
// Validate and resolve the task options
105+
for (const option of this.options.values()) {
106+
const value = taskArguments[option.name];
107+
108+
this.#validateArgumentType(option, value);
109+
110+
// resolve defaults for optional arguments
111+
if (value === undefined) {
112+
taskArguments[option.name] = option.defaultValue;
113+
}
114+
115+
providedArgumentNames.delete(option.name);
116+
}
117+
118+
// Validate and resolve the task positional arguments
119+
for (const argument of this.positionalArguments) {
109120
const value = taskArguments[argument.name];
110121

111122
this.#validateRequiredArgument(argument, value);
112-
this.#validateArgumentType(argument, value);
123+
this.#validateArgumentType(argument, value, argument.isVariadic);
113124

114125
// resolve defaults for optional arguments
115126
if (value === undefined && argument.defaultValue !== undefined) {
116127
taskArguments[argument.name] = argument.defaultValue;
117128
}
118129

119-
// Remove processed argument from the set
120130
providedArgumentNames.delete(argument.name);
121131
}
122132

@@ -166,7 +176,7 @@ export class ResolvedTask implements Task {
166176
* @throws HardhatError if the argument is required and doesn't have a value.
167177
*/
168178
#validateRequiredArgument(
169-
argument: TaskArgumentDefinition,
179+
argument: PositionalArgumentDefinition,
170180
value: ArgumentValue | ArgumentValue[],
171181
) {
172182
if (argument.defaultValue === undefined && value === undefined) {
@@ -188,20 +198,15 @@ export class ResolvedTask implements Task {
188198
* @throws HardhatError if the argument has an invalid type.
189199
*/
190200
#validateArgumentType(
191-
argument: TaskArgumentDefinition,
201+
argument: OptionDefinition | PositionalArgumentDefinition,
192202
value: ArgumentValue | ArgumentValue[],
203+
isVariadic: boolean = false,
193204
) {
194205
// skip type validation for optional arguments with undefined value
195206
if (value === undefined && argument.defaultValue !== undefined) {
196207
return;
197208
}
198209

199-
// check if the argument is variadic
200-
const isPositionalArgument = (
201-
arg: TaskArgumentDefinition,
202-
): arg is TaskPositionalArgumentDefinition => "isVariadic" in arg;
203-
const isVariadic = isPositionalArgument(argument) && argument.isVariadic;
204-
205210
// check if the value is valid for the argument type
206211
if (!isArgumentValueValid(argument.type, value, isVariadic)) {
207212
throw new HardhatError(

v-next/core/src/types/arguments.ts

+34
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,37 @@ export type ArgumentValue = ArgumentValueTypes[keyof ArgumentValueTypes];
4343
*/
4444
export type ArgumentTypeToValueType<T extends ArgumentType> =
4545
ArgumentValueTypes[T];
46+
47+
/**
48+
* Options in CLI are specified as `--<name> value`, where `--<name>` is the
49+
* option's name and `value` is the argument it takes. For example,
50+
* `--network mainnet` sets the network option to `mainnet`.
51+
*
52+
* Options can also be flags, which are boolean options that don't take an
53+
* argument. For example, `--help` is a flag that shows the help message.
54+
*
55+
* Options are always optional and can be provided in any order.
56+
*/
57+
export interface OptionDefinition<T extends ArgumentType = ArgumentType> {
58+
name: string;
59+
description: string;
60+
type: ArgumentType;
61+
defaultValue: ArgumentTypeToValueType<T>;
62+
}
63+
64+
/**
65+
* A positional argument is used as `<value>` in the CLI, where its position
66+
* matters. For example, `mv <from> <to>` has two positional arguments.
67+
*
68+
* If the argument is variadic, it accepts multiple values. A variadic argument
69+
* must be the last positional argument and consumes all remaining values.
70+
*/
71+
export interface PositionalArgumentDefinition<
72+
T extends ArgumentType = ArgumentType,
73+
> {
74+
name: string;
75+
description: string;
76+
type: T;
77+
defaultValue?: ArgumentTypeToValueType<T> | Array<ArgumentTypeToValueType<T>>;
78+
isVariadic: boolean;
79+
}

v-next/core/src/types/global-options.ts

+12-22
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,18 @@
1-
import type { ArgumentType, ArgumentTypeToValueType } from "./arguments.js";
1+
import type { OptionDefinition } from "./arguments.js";
22

33
/**
4-
* A global option with an associated value and a default if not provided by
5-
* the user. They are available in the Hardhat Runtime Environment.
4+
* The values of each global option for a certain instance of the Hardhat
5+
* Runtime Environment are defined here. This interface can be extended through
6+
* module augmentation to include additional global options as needed.
67
*
7-
* They can be provided in these different ways:
8-
* 1. Through environment variables, with the format
9-
* `HARDHAT_<OPTION_NAME_WITH_THIS_CASING>`.
10-
* 2. Through the CLI with the format `--<option-name-with-this-casing> <value>`.
11-
* 2.1. Through the CLI with the format `--<option-name-with-this-casing>` if
12-
* the option is boolean and its default value is `false`.
8+
* Global options can be provided in several ways and are accessible through
9+
* the Hardhat Runtime Environment:
10+
* 1. Environment variables: `HARDHAT_<OPTION_NAME_WITH_THIS_CASING>`
11+
* 2. CLI arguments:
12+
* - `--<option-name-with-this-casing> <value>` for options with values
13+
* - `--<option-name-with-this-casing>` for boolean options with a false default
1314
*
14-
* If both are present, the second one takes precedence.
15-
*/
16-
export interface GlobalOptionDefinition<T extends ArgumentType = ArgumentType> {
17-
name: string;
18-
description: string;
19-
type: ArgumentType;
20-
defaultValue: ArgumentTypeToValueType<T>;
21-
}
22-
23-
/**
24-
* The values of each global option for a certain instance of the Hardhat
25-
* Runtime Environment.
15+
* CLI arguments take precedence over environment variables.
2616
*/
2717
// eslint-disable-next-line @typescript-eslint/no-empty-interface -- To be used through module augmentation
2818
export interface GlobalOptions {}
@@ -33,7 +23,7 @@ export interface GlobalOptions {}
3323
*/
3424
export interface GlobalOptionDefinitionsEntry {
3525
pluginId: string;
36-
option: GlobalOptionDefinition;
26+
option: OptionDefinition;
3727
}
3828

3929
/**

v-next/core/src/types/plugins.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { GlobalOptionDefinition } from "./global-options.js";
1+
import type { OptionDefinition } from "./arguments.js";
22
import type { HardhatHooks } from "./hooks.js";
33
import type { TaskDefinition } from "./tasks.js";
44

@@ -67,7 +67,7 @@ export interface HardhatPlugin {
6767
/**
6868
* An array of the global options that this plugin defines.
6969
*/
70-
globalOptions?: GlobalOptionDefinition[];
70+
globalOptions?: OptionDefinition[];
7171

7272
/**
7373
* An array of type definitions, which should be created using their builders.

v-next/core/src/types/tasks.ts

+7-39
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import type {
2+
OptionDefinition,
23
ArgumentType,
34
ArgumentTypeToValueType,
45
ArgumentValue,
6+
PositionalArgumentDefinition,
57
} from "./arguments.js";
68
import type { HardhatRuntimeEnvironment } from "./hre.js";
79

@@ -27,40 +29,6 @@ declare module "./config.js" {
2729
}
2830
}
2931

30-
export interface TaskArgumentDefinition<T extends ArgumentType = ArgumentType> {
31-
name: string;
32-
description: string;
33-
type: T;
34-
defaultValue?: ArgumentTypeToValueType<T> | Array<ArgumentTypeToValueType<T>>;
35-
}
36-
37-
/**
38-
* A task option is one that is used as `--<name> value` in the CLI.
39-
*
40-
* They have a name, description, type, and an optional default value.
41-
*
42-
* If the type is ArgumentType.BOOLEAN, the default value is `false`, the
43-
* argument is considered a flag, and can be used as `--<name>` to set it to
44-
* `true`.
45-
*/
46-
export interface TaskOptionDefinition<T extends ArgumentType = ArgumentType>
47-
extends TaskArgumentDefinition<T> {
48-
defaultValue: ArgumentTypeToValueType<T>;
49-
}
50-
51-
/**
52-
* A positional task argument is one that is used as `<value>` in the CLI, and whose
53-
* position matters. For example, `mv <from> <to>` has two positional arguments.
54-
*
55-
* If the argument is variadic, it can have multiple values. A variadic argument
56-
* can only be the last positional argument, and it consumes all the remaining values.
57-
*/
58-
export interface TaskPositionalArgumentDefinition<
59-
T extends ArgumentType = ArgumentType,
60-
> extends TaskArgumentDefinition<T> {
61-
isVariadic: boolean;
62-
}
63-
6432
/**
6533
* A type representing the concrete arguments of a task. That is,
6634
* the actual values passed to it.
@@ -125,9 +93,9 @@ export interface NewTaskDefinition {
12593

12694
action: NewTaskActionFunction | string;
12795

128-
options: Record<string, TaskOptionDefinition>;
96+
options: Record<string, OptionDefinition>;
12997

130-
positionalArguments: TaskPositionalArgumentDefinition[];
98+
positionalArguments: PositionalArgumentDefinition[];
13199
}
132100

133101
/**
@@ -142,7 +110,7 @@ export interface TaskOverrideDefinition {
142110

143111
action: TaskOverrideActionFunction | string;
144112

145-
options: Record<string, TaskOptionDefinition>;
113+
options: Record<string, OptionDefinition>;
146114
}
147115

148116
/**
@@ -343,12 +311,12 @@ export interface Task {
343311
/**
344312
* The task options.
345313
*/
346-
options: Map<string, TaskOptionDefinition>;
314+
options: Map<string, OptionDefinition>;
347315

348316
/**
349317
* The task positional arguments.
350318
*/
351-
positionalArguments: TaskPositionalArgumentDefinition[];
319+
positionalArguments: PositionalArgumentDefinition[];
352320

353321
/**
354322
* Whether the task is an empty task.

0 commit comments

Comments
 (0)