Skip to content

Commit 05a1a52

Browse files
authored
Merge pull request #5513 from NomicFoundation/task-fixes
Task fixes
2 parents 04240ee + 76b414e commit 05a1a52

File tree

8 files changed

+890
-236
lines changed

8 files changed

+890
-236
lines changed

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

+38-159
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ import { HardhatError } from "@ignored/hardhat-vnext-errors";
1818

1919
import { ArgumentType } from "../../types/arguments.js";
2020
import { TaskDefinitionType } from "../../types/tasks.js";
21-
import {
22-
RESERVED_ARGUMENT_NAMES,
23-
isArgumentValueValid,
24-
isArgumentNameValid,
25-
} from "../arguments.js";
2621

27-
import { formatTaskId, isValidActionUrl } from "./utils.js";
22+
import { formatTaskId } from "./utils.js";
23+
import {
24+
validateAction,
25+
validateId,
26+
validateOption,
27+
validatePositionalArgument,
28+
} from "./validations.js";
2829

2930
export class EmptyTaskDefinitionBuilderImplementation
3031
implements EmptyTaskDefinitionBuilder
@@ -34,11 +35,7 @@ export class EmptyTaskDefinitionBuilderImplementation
3435
#description: string;
3536

3637
constructor(id: string | string[], description: string = "") {
37-
if (id.length === 0) {
38-
throw new HardhatError(
39-
HardhatError.ERRORS.TASK_DEFINITIONS.EMPTY_TASK_ID,
40-
);
41-
}
38+
validateId(id);
4239

4340
this.#id = Array.isArray(id) ? id : [id];
4441
this.#description = description;
@@ -72,11 +69,7 @@ export class NewTaskDefinitionBuilderImplementation
7269
#action?: NewTaskActionFunction | string;
7370

7471
constructor(id: string | string[], description: string = "") {
75-
if (id.length === 0) {
76-
throw new HardhatError(
77-
HardhatError.ERRORS.TASK_DEFINITIONS.EMPTY_TASK_ID,
78-
);
79-
}
72+
validateId(id);
8073

8174
this.#id = Array.isArray(id) ? id : [id];
8275
this.#description = description;
@@ -88,14 +81,7 @@ export class NewTaskDefinitionBuilderImplementation
8881
}
8982

9083
public setAction(action: NewTaskActionFunction | string): this {
91-
if (typeof action === "string" && !isValidActionUrl(action)) {
92-
throw new HardhatError(
93-
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_FILE_ACTION,
94-
{
95-
action,
96-
},
97-
);
98-
}
84+
validateAction(action);
9985

10086
this.#action = action;
10187

@@ -115,45 +101,17 @@ export class NewTaskDefinitionBuilderImplementation
115101
}): this {
116102
const argumentType = type ?? ArgumentType.STRING;
117103

118-
if (!isArgumentNameValid(name)) {
119-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.INVALID_NAME, {
120-
name,
121-
});
122-
}
123-
124-
if (this.#usedNames.has(name)) {
125-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.DUPLICATED_NAME, {
126-
name,
127-
});
128-
}
129-
130-
if (RESERVED_ARGUMENT_NAMES.has(name)) {
131-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.RESERVED_NAME, {
132-
name,
133-
});
134-
}
135-
136-
if (!isArgumentValueValid(argumentType, defaultValue)) {
137-
throw new HardhatError(
138-
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_VALUE_FOR_TYPE,
139-
{
140-
value: defaultValue,
141-
name: "defaultValue",
142-
type: argumentType,
143-
task: formatTaskId(this.#id),
144-
},
145-
);
146-
}
147-
148-
this.#usedNames.add(name);
149-
150-
this.#options[name] = {
104+
const optionDefinition = {
151105
name,
152106
description,
153107
type: argumentType,
154108
defaultValue,
155109
};
156110

111+
validateOption(optionDefinition, this.#usedNames, this.#id);
112+
113+
this.#options[name] = optionDefinition;
114+
157115
return this;
158116
}
159117

@@ -223,69 +181,23 @@ export class NewTaskDefinitionBuilderImplementation
223181
}): this {
224182
const argumentType = type ?? ArgumentType.STRING;
225183

226-
if (!isArgumentNameValid(name)) {
227-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.INVALID_NAME, {
228-
name,
229-
});
230-
}
231-
232-
if (this.#usedNames.has(name)) {
233-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.DUPLICATED_NAME, {
234-
name,
235-
});
236-
}
237-
238-
if (RESERVED_ARGUMENT_NAMES.has(name)) {
239-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.RESERVED_NAME, {
240-
name,
241-
});
242-
}
243-
244-
if (defaultValue !== undefined) {
245-
if (!isArgumentValueValid(argumentType, defaultValue, isVariadic)) {
246-
throw new HardhatError(
247-
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_VALUE_FOR_TYPE,
248-
{
249-
value: defaultValue,
250-
name: "defaultValue",
251-
type: argumentType,
252-
task: formatTaskId(this.#id),
253-
},
254-
);
255-
}
256-
}
257-
258-
if (this.#positionalArgs.length > 0) {
259-
const lastArg = this.#positionalArgs[this.#positionalArgs.length - 1];
260-
261-
if (lastArg.isVariadic) {
262-
throw new HardhatError(
263-
HardhatError.ERRORS.TASK_DEFINITIONS.POSITIONAL_ARG_AFTER_VARIADIC,
264-
{
265-
name,
266-
},
267-
);
268-
}
269-
270-
if (lastArg.defaultValue !== undefined && defaultValue === undefined) {
271-
throw new HardhatError(
272-
HardhatError.ERRORS.TASK_DEFINITIONS.REQUIRED_ARG_AFTER_OPTIONAL,
273-
{
274-
name,
275-
},
276-
);
277-
}
278-
}
279-
280-
this.#usedNames.add(name);
281-
282-
this.#positionalArgs.push({
184+
const positionalArgDef = {
283185
name,
284186
description,
285187
type: argumentType,
286188
defaultValue,
287189
isVariadic,
288-
});
190+
};
191+
192+
const lastArg = this.#positionalArgs.at(-1);
193+
validatePositionalArgument(
194+
positionalArgDef,
195+
this.#usedNames,
196+
this.#id,
197+
lastArg,
198+
);
199+
200+
this.#positionalArgs.push(positionalArgDef);
289201

290202
return this;
291203
}
@@ -303,11 +215,7 @@ export class TaskOverrideDefinitionBuilderImplementation
303215
#action?: TaskOverrideActionFunction | string;
304216

305217
constructor(id: string | string[]) {
306-
if (id.length === 0) {
307-
throw new HardhatError(
308-
HardhatError.ERRORS.TASK_DEFINITIONS.EMPTY_TASK_ID,
309-
);
310-
}
218+
validateId(id);
311219

312220
this.#id = Array.isArray(id) ? id : [id];
313221
}
@@ -318,14 +226,7 @@ export class TaskOverrideDefinitionBuilderImplementation
318226
}
319227

320228
public setAction(action: TaskOverrideActionFunction | string): this {
321-
if (typeof action === "string" && !isValidActionUrl(action)) {
322-
throw new HardhatError(
323-
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_FILE_ACTION,
324-
{
325-
action,
326-
},
327-
);
328-
}
229+
validateAction(action);
329230

330231
this.#action = action;
331232

@@ -345,43 +246,21 @@ export class TaskOverrideDefinitionBuilderImplementation
345246
}): this {
346247
const argumentType = type ?? ArgumentType.STRING;
347248

348-
if (!isArgumentNameValid(name)) {
349-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.INVALID_NAME, {
350-
name,
351-
});
352-
}
353-
354-
if (name in this.#options) {
355-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.DUPLICATED_NAME, {
356-
name,
357-
});
358-
}
359-
360-
if (RESERVED_ARGUMENT_NAMES.has(name)) {
361-
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.RESERVED_NAME, {
362-
name,
363-
});
364-
}
365-
366-
if (!isArgumentValueValid(argumentType, defaultValue)) {
367-
throw new HardhatError(
368-
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_VALUE_FOR_TYPE,
369-
{
370-
value: defaultValue,
371-
name: "defaultValue",
372-
type: argumentType,
373-
task: formatTaskId(this.#id),
374-
},
375-
);
376-
}
377-
378-
this.#options[name] = {
249+
const optionDefinition = {
379250
name,
380251
description,
381252
type: argumentType,
382253
defaultValue,
383254
};
384255

256+
validateOption(
257+
optionDefinition,
258+
new Set(Object.keys(this.#options)),
259+
this.#id,
260+
);
261+
262+
this.#options[name] = optionDefinition;
263+
385264
return this;
386265
}
387266

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

+20-25
Original file line numberDiff line numberDiff line change
@@ -100,34 +100,29 @@ export class ResolvedTask implements Task {
100100
}
101101

102102
const providedArgumentNames = new Set(Object.keys(taskArguments));
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;
103+
const argumentDefinitions = [
104+
...this.options.values(),
105+
...this.positionalArguments,
106+
];
107+
const validatedTaskArguments: TaskArguments = {};
108+
for (const argumentDefinition of argumentDefinitions) {
109+
const value = taskArguments[argumentDefinition.name];
110+
const isPositional = "isVariadic" in argumentDefinition;
111+
112+
if (isPositional) {
113+
this.#validateRequiredArgument(argumentDefinition, value);
113114
}
114-
115-
providedArgumentNames.delete(option.name);
116-
}
117-
118-
// Validate and resolve the task positional arguments
119-
for (const argument of this.positionalArguments) {
120-
const value = taskArguments[argument.name];
121-
122-
this.#validateRequiredArgument(argument, value);
123-
this.#validateArgumentType(argument, value, argument.isVariadic);
115+
this.#validateArgumentType(
116+
argumentDefinition,
117+
value,
118+
isPositional && argumentDefinition.isVariadic,
119+
);
124120

125121
// resolve defaults for optional arguments
126-
if (value === undefined && argument.defaultValue !== undefined) {
127-
taskArguments[argument.name] = argument.defaultValue;
128-
}
122+
validatedTaskArguments[argumentDefinition.name] =
123+
value ?? argumentDefinition.defaultValue;
129124

130-
providedArgumentNames.delete(argument.name);
125+
providedArgumentNames.delete(argumentDefinition.name);
131126
}
132127

133128
// At this point, the set should be empty as all the task arguments have
@@ -166,7 +161,7 @@ export class ResolvedTask implements Task {
166161
);
167162
};
168163

169-
return next(taskArguments);
164+
return next(validatedTaskArguments);
170165
}
171166

172167
/**

0 commit comments

Comments
 (0)