Skip to content

Commit 02730b0

Browse files
authored
Merge pull request #5532 from NomicFoundation/refactor-validations
Refactor argument validations
2 parents ee578f4 + d89cb45 commit 02730b0

File tree

4 files changed

+119
-130
lines changed

4 files changed

+119
-130
lines changed

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

+53
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,66 @@ export const RESERVED_ARGUMENT_NAMES: Set<string> = new Set([]);
1212

1313
const VALID_ARGUMENT_NAME_PATTERN = /^[a-z][a-zA-Z0-9]*$/;
1414

15+
/**
16+
* Validates an argument name, throwing an error if it is invalid.
17+
*
18+
* @param name The name of the argument.
19+
* @throws {HardhatError} with descriptor:
20+
* - {@link HardhatError.ERRORS.ARGUMENTS.INVALID_NAME} if the name is invalid.
21+
* A valid name must start with a lowercase letter and contain only
22+
* alphanumeric characters.
23+
* - {@link HardhatError.ERRORS.ARGUMENTS.RESERVED_NAME} if the name is
24+
* reserved. See {@link RESERVED_ARGUMENT_NAMES}.
25+
*/
26+
export function validateArgumentName(name: string): void {
27+
if (!isArgumentNameValid(name)) {
28+
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.INVALID_NAME, {
29+
name,
30+
});
31+
}
32+
33+
if (RESERVED_ARGUMENT_NAMES.has(name)) {
34+
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.RESERVED_NAME, {
35+
name,
36+
});
37+
}
38+
}
39+
1540
/**
1641
* Returns true if the given name is a valid argument name.
1742
*/
1843
export function isArgumentNameValid(name: string): boolean {
1944
return VALID_ARGUMENT_NAME_PATTERN.test(name);
2045
}
2146

47+
/**
48+
* Validates an argument value, throwing an error if it is invalid.
49+
*
50+
* @param name The name of the argument.
51+
* @param expectedType The expected type of the argument. One of {@link ArgumentType}.
52+
* @param value The value of the argument.
53+
* @param isVariadic Whether the argument is variadic.
54+
* @throws {HardhatError} with descriptor {@link HardhatError.ERRORS.ARGUMENTS.INVALID_VALUE_FOR_TYPE}
55+
* if the value is invalid for the expected type.
56+
*/
57+
export function validateArgumentValue(
58+
name: string,
59+
expectedType: ArgumentType,
60+
value: ArgumentValue | ArgumentValue[],
61+
isVariadic: boolean = false,
62+
): void {
63+
if (!isArgumentValueValid(expectedType, value, isVariadic)) {
64+
throw new HardhatError(
65+
HardhatError.ERRORS.ARGUMENTS.INVALID_VALUE_FOR_TYPE,
66+
{
67+
name,
68+
type: expectedType,
69+
value,
70+
},
71+
);
72+
}
73+
}
74+
2275
/**
2376
* Checks if an argument value is valid for a given argument type.
2477
*

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

+4-24
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ import { camelToSnakeCase } from "@ignored/hardhat-vnext-utils/string";
1515
import { ArgumentType } from "../types/arguments.js";
1616

1717
import {
18-
RESERVED_ARGUMENT_NAMES,
19-
isArgumentValueValid,
20-
isArgumentNameValid,
2118
parseArgumentValue,
19+
validateArgumentValue,
20+
validateArgumentName,
2221
} from "./arguments.js";
2322

2423
/**
@@ -83,28 +82,9 @@ export function buildGlobalOptionDefinition<T extends ArgumentType>({
8382
}): OptionDefinition {
8483
const argumentType = type ?? ArgumentType.STRING;
8584

86-
if (!isArgumentNameValid(name)) {
87-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.INVALID_NAME, {
88-
name,
89-
});
90-
}
91-
92-
if (RESERVED_ARGUMENT_NAMES.has(name)) {
93-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.RESERVED_NAME, {
94-
name,
95-
});
96-
}
85+
validateArgumentName(name);
9786

98-
if (!isArgumentValueValid(argumentType, defaultValue)) {
99-
throw new HardhatError(
100-
HardhatError.ERRORS.ARGUMENTS.INVALID_VALUE_FOR_TYPE,
101-
{
102-
value: defaultValue,
103-
name: "defaultValue",
104-
type,
105-
},
106-
);
107-
}
87+
validateArgumentValue("defaultValue", argumentType, defaultValue);
10888

10989
return {
11090
name,

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

+11-37
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ import {
1818
} from "@ignored/hardhat-vnext-errors";
1919
import { ensureError } from "@ignored/hardhat-vnext-utils/error";
2020

21-
import { isArgumentValueValid } from "../arguments.js";
2221
import { detectPluginNpmDependencyProblems } from "../plugins/detect-plugin-npm-dependency-problems.js";
2322

2423
import { formatTaskId } from "./utils.js";
24+
import { validateTaskArgumentValue } from "./validations.js";
2525

2626
export class ResolvedTask implements Task {
2727
readonly #hre: HardhatRuntimeEnvironment;
@@ -112,11 +112,16 @@ export class ResolvedTask implements Task {
112112
if (isPositional) {
113113
this.#validateRequiredArgument(argumentDefinition, value);
114114
}
115-
this.#validateArgumentType(
116-
argumentDefinition,
117-
value,
118-
isPositional && argumentDefinition.isVariadic,
119-
);
115+
116+
if (value !== undefined) {
117+
validateTaskArgumentValue(
118+
argumentDefinition.name,
119+
argumentDefinition.type,
120+
value,
121+
isPositional && argumentDefinition.isVariadic,
122+
this.id,
123+
);
124+
}
120125

121126
// resolve defaults for optional arguments
122127
validatedTaskArguments[argumentDefinition.name] =
@@ -185,37 +190,6 @@ export class ResolvedTask implements Task {
185190
}
186191
}
187192

188-
/**
189-
* Validates that a argument has the correct type. If the argument is optional
190-
* and doesn't have a value, the type is not validated as it will be resolved
191-
* to the default value.
192-
*
193-
* @throws HardhatError if the argument has an invalid type.
194-
*/
195-
#validateArgumentType(
196-
argument: OptionDefinition | PositionalArgumentDefinition,
197-
value: ArgumentValue | ArgumentValue[],
198-
isVariadic: boolean = false,
199-
) {
200-
// skip type validation for optional arguments with undefined value
201-
if (value === undefined && argument.defaultValue !== undefined) {
202-
return;
203-
}
204-
205-
// check if the value is valid for the argument type
206-
if (!isArgumentValueValid(argument.type, value, isVariadic)) {
207-
throw new HardhatError(
208-
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_VALUE_FOR_TYPE,
209-
{
210-
value,
211-
name: argument.name,
212-
type: argument.type,
213-
task: formatTaskId(this.id),
214-
},
215-
);
216-
}
217-
}
218-
219193
/**
220194
* Validates that no extra arguments were provided in the task arguments.
221195
*

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

+51-69
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,7 @@ import type {
77

88
import { HardhatError } from "@ignored/hardhat-vnext-errors";
99

10-
import {
11-
isArgumentNameValid,
12-
isArgumentValueValid,
13-
RESERVED_ARGUMENT_NAMES,
14-
} from "../arguments.js";
10+
import { validateArgumentName, validateArgumentValue } from "../arguments.js";
1511

1612
import { formatTaskId } from "./utils.js";
1713

@@ -33,75 +29,70 @@ export function validateAction(action: unknown): void {
3329
}
3430

3531
export function validateOption(
36-
optionDefinition: OptionDefinition,
32+
{ name, type, defaultValue }: OptionDefinition,
3733
usedNames: Set<string>,
3834
taskId: string | string[],
3935
): void {
40-
validateArgumentName(optionDefinition.name);
36+
validateArgumentName(name);
4137

42-
if (usedNames.has(optionDefinition.name)) {
38+
if (usedNames.has(name)) {
4339
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.DUPLICATED_NAME, {
44-
name: optionDefinition.name,
40+
name,
4541
});
4642
}
4743

48-
validateArgumentValue({
49-
name: "defaultValue",
50-
value: optionDefinition.defaultValue,
51-
expectedType: optionDefinition.type,
52-
taskId: formatTaskId(taskId),
53-
});
44+
validateTaskArgumentValue("defaultValue", type, defaultValue, false, taskId);
5445

55-
usedNames.add(optionDefinition.name);
46+
usedNames.add(name);
5647
}
5748

5849
export function validatePositionalArgument(
59-
positionalArgDef: PositionalArgumentDefinition,
50+
{ name, type, defaultValue, isVariadic }: PositionalArgumentDefinition,
6051
usedNames: Set<string>,
6152
taskId: string | string[],
6253
lastArg?: PositionalArgumentDefinition,
6354
): void {
64-
validateArgumentName(positionalArgDef.name);
55+
validateArgumentName(name);
6556

66-
if (usedNames.has(positionalArgDef.name)) {
57+
if (usedNames.has(name)) {
6758
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.DUPLICATED_NAME, {
68-
name: positionalArgDef.name,
59+
name,
6960
});
7061
}
7162

72-
if (positionalArgDef.defaultValue !== undefined) {
73-
validateArgumentValue({
74-
name: "defaultValue",
75-
value: positionalArgDef.defaultValue,
76-
isVariadic: positionalArgDef.isVariadic,
77-
expectedType: positionalArgDef.type,
78-
taskId: formatTaskId(taskId),
79-
});
63+
if (defaultValue !== undefined) {
64+
validateTaskArgumentValue(
65+
"defaultValue",
66+
type,
67+
defaultValue,
68+
isVariadic,
69+
taskId,
70+
);
8071
}
8172

8273
if (lastArg !== undefined && lastArg.isVariadic) {
8374
throw new HardhatError(
8475
HardhatError.ERRORS.TASK_DEFINITIONS.POSITIONAL_ARG_AFTER_VARIADIC,
8576
{
86-
name: positionalArgDef.name,
77+
name,
8778
},
8879
);
8980
}
9081

9182
if (
9283
lastArg !== undefined &&
9384
lastArg.defaultValue !== undefined &&
94-
positionalArgDef.defaultValue === undefined
85+
defaultValue === undefined
9586
) {
9687
throw new HardhatError(
9788
HardhatError.ERRORS.TASK_DEFINITIONS.REQUIRED_ARG_AFTER_OPTIONAL,
9889
{
99-
name: positionalArgDef.name,
90+
name,
10091
},
10192
);
10293
}
10394

104-
usedNames.add(positionalArgDef.name);
95+
usedNames.add(name);
10596
}
10697

10798
const FILE_PROTOCOL_PATTERN = /^file:\/\/.+/;
@@ -110,42 +101,33 @@ function isValidActionUrl(action: string): boolean {
110101
return FILE_PROTOCOL_PATTERN.test(action);
111102
}
112103

113-
function validateArgumentName(name: string): void {
114-
if (!isArgumentNameValid(name)) {
115-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.INVALID_NAME, {
116-
name,
117-
});
118-
}
119-
120-
if (RESERVED_ARGUMENT_NAMES.has(name)) {
121-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.RESERVED_NAME, {
122-
name,
123-
});
124-
}
125-
}
126-
127-
function validateArgumentValue({
128-
name,
129-
expectedType,
130-
isVariadic = false,
131-
value,
132-
taskId,
133-
}: {
134-
name: string;
135-
expectedType: ArgumentType;
136-
isVariadic?: boolean;
137-
value: ArgumentValue | ArgumentValue[];
138-
taskId: string | string[];
139-
}): void {
140-
if (!isArgumentValueValid(expectedType, value, isVariadic)) {
141-
throw new HardhatError(
142-
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_VALUE_FOR_TYPE,
143-
{
144-
value,
145-
name,
146-
type: expectedType,
147-
task: formatTaskId(taskId),
148-
},
149-
);
104+
export function validateTaskArgumentValue(
105+
name: string,
106+
expectedType: ArgumentType,
107+
value: ArgumentValue | ArgumentValue[],
108+
isVariadic: boolean,
109+
taskId: string | string[],
110+
): void {
111+
try {
112+
validateArgumentValue(name, expectedType, value, isVariadic);
113+
} catch (error) {
114+
if (
115+
HardhatError.isHardhatError(
116+
error,
117+
HardhatError.ERRORS.ARGUMENTS.INVALID_VALUE_FOR_TYPE,
118+
)
119+
) {
120+
throw new HardhatError(
121+
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_VALUE_FOR_TYPE,
122+
{
123+
name,
124+
type: expectedType,
125+
value,
126+
task: formatTaskId(taskId),
127+
},
128+
);
129+
}
130+
131+
throw error;
150132
}
151133
}

0 commit comments

Comments
 (0)