Skip to content

Commit a4bc7fd

Browse files
committed
Task manager cleanup and error handling
1 parent 070fbfb commit a4bc7fd

File tree

3 files changed

+120
-70
lines changed

3 files changed

+120
-70
lines changed

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

+71-70
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import {
2+
HardhatError,
3+
assertHardhatInvariant,
4+
} from "@nomicfoundation/hardhat-errors";
5+
16
import { GlobalParameterMap } from "../../types/global-parameters.js";
27
import { HardhatRuntimeEnvironment } from "../../types/hre.js";
38
import {
@@ -10,7 +15,7 @@ import {
1015
} from "../../types/tasks.js";
1116

1217
import { ResolvedTask } from "./resolved-task.js";
13-
import { formatTaskId } from "./utils.js";
18+
import { formatTaskId, getActorFragment } from "./utils.js";
1419

1520
export class TaskManagerImplementation implements TaskManager {
1621
readonly #hre: HardhatRuntimeEnvironment;
@@ -22,6 +27,7 @@ export class TaskManagerImplementation implements TaskManager {
2227
) {
2328
this.#hre = hre;
2429

30+
// reduce plugin tasks
2531
for (const plugin of this.#hre.config.plugins) {
2632
if (plugin.tasks === undefined) {
2733
continue;
@@ -36,82 +42,90 @@ export class TaskManagerImplementation implements TaskManager {
3642
}
3743
}
3844

45+
// reduce global user defined tasks
3946
for (const taskDefinition of this.#hre.config.tasks) {
4047
this.#reduceTaskDefinition(globalParameterIndex, taskDefinition);
4148
}
4249
}
4350

4451
public getTask(taskId: string | string[]): Task {
4552
taskId = Array.isArray(taskId) ? taskId : [taskId];
46-
4753
if (taskId.length === 0) {
48-
throw new Error(`Invalid task id "${formatTaskId(taskId)}"`);
54+
throw new HardhatError(
55+
HardhatError.ERRORS.TASK_DEFINITIONS.TASK_NOT_FOUND,
56+
{
57+
id: formatTaskId(taskId),
58+
},
59+
);
4960
}
5061

5162
let tasks = this.#rootTasks;
52-
let task: Task;
53-
63+
let task: Task | undefined;
5464
for (let i = 0; i < taskId.length; i++) {
5565
const idFragment = taskId[i];
56-
5766
const currentTask = tasks.get(idFragment);
58-
5967
if (currentTask === undefined) {
60-
throw new Error(
61-
`Task "${formatTaskId(taskId.slice(0, i + 1))}" not found.`,
68+
throw new HardhatError(
69+
HardhatError.ERRORS.TASK_DEFINITIONS.TASK_NOT_FOUND,
70+
{
71+
id: formatTaskId(taskId.slice(0, i + 1)),
72+
},
6273
);
6374
}
6475

6576
task = currentTask;
6677
tasks = task.subtasks;
6778
}
6879

69-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Can't be undefined as we set it in the non-empty loop.
70-
return task!;
80+
// task can't be undefined as we set it in the non-empty loop
81+
assertHardhatInvariant(task !== undefined, "Task not found");
82+
83+
return task;
7184
}
7285

7386
#insertTask(taskId: string[], task: Task, pluginId?: string) {
7487
if (taskId.length === 0) {
75-
throw new Error(`Invalid task id "${formatTaskId(taskId)}"`);
88+
throw new HardhatError(
89+
HardhatError.ERRORS.TASK_DEFINITIONS.EMPTY_TASK_ID,
90+
);
7691
}
7792

93+
// Traverse all the parent tasks to check that they exist
7894
let tasks = this.#rootTasks;
79-
8095
for (let i = 0; i < taskId.length - 1; i++) {
8196
const idFragment = taskId[i];
82-
8397
const currentTask = tasks.get(idFragment);
84-
8598
if (currentTask === undefined) {
86-
throw new Error(
87-
`Task "${formatTaskId(taskId.slice(0, i + 1))}" not found and trying to define subtask "${formatTaskId(taskId)}". Define an empty task if you just want to define subtasks`,
99+
throw new HardhatError(
100+
HardhatError.ERRORS.TASK_DEFINITIONS.SUBTASK_WITHOUT_PARENT,
101+
{
102+
task: formatTaskId(taskId.slice(0, i + 1)),
103+
subtask: formatTaskId(taskId),
104+
},
88105
);
89106
}
90107

91108
tasks = currentTask.subtasks;
92109
}
93110

94-
const existingTask = tasks.get(taskId[taskId.length - 1]);
95-
96-
if (existingTask === undefined) {
97-
tasks.set(taskId[taskId.length - 1], task);
98-
return;
99-
}
100-
101-
const definedByMessage =
102-
existingTask.pluginId !== undefined
103-
? ` by plugin ${existingTask.pluginId}`
104-
: "";
105-
106-
if (pluginId !== undefined) {
107-
throw new Error(
108-
`Plugin ${pluginId} is trying to define the task "${formatTaskId(taskId)}" but it is already defined${definedByMessage}`,
111+
// Check that the task doesn't already exist
112+
const lastIdFragment = taskId[taskId.length - 1];
113+
const existingTask = tasks.get(lastIdFragment);
114+
if (existingTask !== undefined) {
115+
const exPluginId = existingTask.pluginId;
116+
throw new HardhatError(
117+
HardhatError.ERRORS.TASK_DEFINITIONS.TASK_ALREADY_DEFINED,
118+
{
119+
actorFragment: getActorFragment(pluginId),
120+
task: formatTaskId(taskId),
121+
definedByFragment:
122+
exPluginId !== undefined ? ` by plugin ${exPluginId}` : "",
123+
},
109124
);
110125
}
111126

112-
throw new Error(
113-
`You are trying to defined the task "${formatTaskId(taskId)}" is already defined${definedByMessage}`,
114-
);
127+
// Insert the task
128+
tasks.set(lastIdFragment, task);
115129
}
116130

117131
#reduceTaskDefinition(
@@ -159,6 +173,7 @@ export class TaskManagerImplementation implements TaskManager {
159173
);
160174

161175
this.#processTaskOverride(taskDefinition);
176+
break;
162177
}
163178
}
164179
}
@@ -170,17 +185,16 @@ export class TaskManagerImplementation implements TaskManager {
170185
) {
171186
for (const namedParamName of Object.keys(taskDefinition.namedParameters)) {
172187
const globalParamEntry = globalParameterIndex.get(namedParamName);
173-
174188
if (globalParamEntry !== undefined) {
175-
if (pluginId === undefined) {
176-
throw new Error(
177-
`Trying to define task "${formatTaskId(taskDefinition.id)}" with the named parameter ${namedParamName} but it is already defined as a global parameter by plugin ${globalParamEntry.pluginId}`,
178-
);
179-
} else {
180-
throw new Error(
181-
`Plugin ${pluginId} trying to define task "${formatTaskId(taskDefinition.id)}" with the named parameter ${namedParamName} but it is already defined as a global parameter by plugin ${globalParamEntry.pluginId}`,
182-
);
183-
}
189+
throw new HardhatError(
190+
HardhatError.ERRORS.TASK_DEFINITIONS.TASK_PARAMETER_ALREADY_DEFINED,
191+
{
192+
actorFragment: getActorFragment(pluginId),
193+
task: formatTaskId(taskDefinition.id),
194+
namedParamName,
195+
globalParamPluginId: globalParamEntry.pluginId,
196+
},
197+
);
184198
}
185199
}
186200
}
@@ -190,34 +204,21 @@ export class TaskManagerImplementation implements TaskManager {
190204
pluginId?: string,
191205
) {
192206
const task = this.getTask(taskDefinition.id);
193-
if (task === undefined) {
194-
if (pluginId !== undefined) {
195-
throw new Error(
196-
`Plugin ${pluginId} is trying to override the task "${formatTaskId(taskDefinition.id)}" but it hasn't been defined`,
197-
);
198-
} else {
199-
throw new Error(
200-
`Trying to override the task "${formatTaskId(taskDefinition.id)}" but it hasn't been defined`,
207+
for (const [namedParamName, namedParamValue] of Object.entries(
208+
taskDefinition.namedParameters,
209+
)) {
210+
if (task.namedParameters.has(namedParamName)) {
211+
throw new HardhatError(
212+
HardhatError.ERRORS.TASK_DEFINITIONS.TASK_OVERRIDE_PARAMETER_ALREADY_DEFINED,
213+
{
214+
actorFragment: getActorFragment(pluginId),
215+
namedParamName,
216+
task: formatTaskId(taskDefinition.id),
217+
},
201218
);
202219
}
203-
}
204-
205-
for (const paramName of Object.keys(taskDefinition.namedParameters)) {
206-
if (task.namedParameters.has(paramName)) {
207-
if (pluginId !== undefined) {
208-
throw new Error(
209-
`Plugin ${pluginId} is trying to override the named parameter ${paramName} of the task "${formatTaskId(taskDefinition.id)}" but it is already defined`,
210-
);
211-
} else {
212-
throw new Error(
213-
`Trying to override the named parameter ${paramName} of the task "${formatTaskId(taskDefinition.id)}" but it is already defined`,
214-
);
215-
}
216-
}
217-
}
218220

219-
for (const namedParam of Object.values(taskDefinition.namedParameters)) {
220-
task.namedParameters.set(namedParam.name, namedParam);
221+
task.namedParameters.set(namedParamName, namedParamValue);
221222
}
222223

223224
if (taskDefinition.description !== undefined) {

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

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ export function formatTaskId(taskId: string | string[]): string {
66
return taskId.join(" ");
77
}
88

9+
export function getActorFragment(pluginId: string | undefined): string {
10+
return pluginId !== undefined ? `Plugin ${pluginId} is` : "You are";
11+
}
12+
913
const FILE_PROTOCOL_PATTERN = /^file:\/\/.+/;
1014

1115
export function isValidActionUrl(action: string): boolean {

v-next/hardhat-errors/src/descriptors.ts

+45
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,51 @@ Please ensure that an action is defined for each task.`,
197197
websiteDescription:
198198
"Required positional parameters must be defined before optional ones in a task definition.",
199199
},
200+
TASK_NOT_FOUND: {
201+
number: 205,
202+
messageTemplate: "Task %task% not found",
203+
websiteTitle: "Task not found",
204+
websiteDescription: "The provided task name does not match any task.",
205+
},
206+
SUBTASK_WITHOUT_PARENT: {
207+
number: 206,
208+
messageTemplate:
209+
"Task %task% not found when attempting to define subtask %subtask%. If you intend to only define subtasks, please first define %task% as an empty task",
210+
websiteTitle: "Subtask without parent",
211+
websiteDescription:
212+
"The parent task of the subtask being defined was not found. If you intend to only define subtasks, please first define the parent task as an empty task.",
213+
},
214+
TASK_ALREADY_DEFINED: {
215+
number: 207,
216+
messageTemplate:
217+
"%actorFragment% trying to define the task %task% but it is already defined%definedByFragment%",
218+
websiteTitle: "Task already defined",
219+
websiteDescription:
220+
"The task is already defined. Please ensure that tasks are uniquely named to avoid conflicts.",
221+
},
222+
EMPTY_TASK_ID: {
223+
number: 208,
224+
messageTemplate: "Task id cannot be an empty string or an empty array",
225+
websiteTitle: "Empty task id",
226+
websiteDescription:
227+
"The task id cannot be an empty string or an empty array. Please ensure that the array of task names is not empty.",
228+
},
229+
TASK_PARAMETER_ALREADY_DEFINED: {
230+
number: 209,
231+
messageTemplate:
232+
"%actorFragment% trying to define task %task% with the named parameter %namedParamName% but it is already defined as a global parameter by plugin %globalParamPluginId%",
233+
websiteTitle: "Task parameter already defined",
234+
websiteDescription:
235+
"The task named parameter is already defined as a global parameter by another plugin. Please ensure that task parameters are uniquely named to avoid conflicts.",
236+
},
237+
TASK_OVERRIDE_PARAMETER_ALREADY_DEFINED: {
238+
number: 210,
239+
messageTemplate:
240+
"%actorFragment% trying to override the named parameter %namedParamName% of the task %task% but it is already defined",
241+
websiteTitle: "Task override parameter already defined",
242+
websiteDescription:
243+
"An attempt is being made to override a named parameter that has already been defined. Please ensure that the parameter is not defined before trying to override it.",
244+
},
200245
},
201246
ARGUMENTS: {
202247
INVALID_VALUE_FOR_TYPE: {

0 commit comments

Comments
 (0)