UNPKG

hardhat

Version:

Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.

150 lines 6.87 kB
import { HardhatError, assertHardhatInvariant, } from "@nomicfoundation/hardhat-errors"; import { ensureError } from "@nomicfoundation/hardhat-utils/error"; import { detectPluginNpmDependencyProblems } from "../plugins/detect-plugin-npm-dependency-problems.js"; import { formatTaskId } from "./utils.js"; import { validateTaskArgumentValue } from "./validations.js"; export class ResolvedTask { id; description; actions; options; positionalArguments; pluginId; subtasks; #hre; static createEmptyTask(hre, id, description, pluginId) { return new ResolvedTask(id, description, [{ pluginId, action: undefined }], new Map(), [], pluginId, new Map(), hre); } static createNewTask(hre, id, description, action, options, positionalArguments, pluginId) { return new ResolvedTask(id, description, [{ pluginId, action }], new Map(Object.entries(options)), positionalArguments, pluginId, new Map(), hre); } constructor(id, description, actions, options, positionalArguments, pluginId, subtasks, hre) { this.id = id; this.description = description; this.actions = actions; this.options = options; this.positionalArguments = positionalArguments; this.pluginId = pluginId; this.subtasks = subtasks; this.#hre = hre; } get isEmpty() { return this.actions.length === 1 && this.actions[0].action === undefined; } /** * This method runs the task with the given arguments. * It validates the arguments, resolves the file actions, and runs the task * actions by calling them in order. * * @param taskArguments The arguments to run the task with. * @returns The result of running the task. * @throws HardhatError if the task is empty, a required argument is missing, * a argument has an invalid type, or the file actions can't be resolved. */ async run(taskArguments = {}) { if (this.isEmpty) { throw new HardhatError(HardhatError.ERRORS.CORE.TASK_DEFINITIONS.EMPTY_TASK, { task: formatTaskId(this.id), }); } const providedArgumentNames = new Set(Object.keys(taskArguments)); const argumentDefinitions = [ ...this.options.values(), ...this.positionalArguments, ]; const validatedTaskArguments = {}; for (const argumentDefinition of argumentDefinitions) { const value = taskArguments[argumentDefinition.name]; const isPositional = "isVariadic" in argumentDefinition; if (isPositional) { this.#validateRequiredArgument(argumentDefinition, value); } if (value !== undefined) { validateTaskArgumentValue(argumentDefinition.name, argumentDefinition.type, value, isPositional && argumentDefinition.isVariadic, this.id); } // resolve defaults for optional arguments validatedTaskArguments[argumentDefinition.name] = value ?? argumentDefinition.defaultValue; providedArgumentNames.delete(argumentDefinition.name); } // At this point, the set should be empty as all the task arguments have // been processed. If there are any extra arguments, an error is thrown this.#validateExtraArguments(providedArgumentNames); const next = async (nextTaskArguments, currentIndex = this.actions.length - 1) => { // The first action may be empty if the task was originally an empty task const currentAction = this.actions[currentIndex].action ?? (async () => ({ default: () => { }, })); const actionFn = await this.#resolveImportAction(currentAction, this.actions[currentIndex].pluginId); if (currentIndex === 0) { /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We know that the first action in the array is a NewTaskActionFunction */ return actionFn(nextTaskArguments, this.#hre); } return actionFn(nextTaskArguments, this.#hre, async (newTaskArguments) => { return next(newTaskArguments, currentIndex - 1); }); }; return next(validatedTaskArguments); } /** * Validates that a required argument has a value. A argument is required if * it doesn't have a default value. * * @throws HardhatError if the argument is required and doesn't have a value. */ #validateRequiredArgument(argument, value) { if (argument.defaultValue === undefined && value === undefined) { throw new HardhatError(HardhatError.ERRORS.CORE.TASK_DEFINITIONS.MISSING_VALUE_FOR_TASK_ARGUMENT, { argument: argument.name, task: formatTaskId(this.id), }); } } /** * Validates that no extra arguments were provided in the task arguments. * * @throws HardhatError if extra arguments were provided. The error message * includes the name of the first extra argument. */ #validateExtraArguments(providedArgumentNames) { if (providedArgumentNames.size > 0) { throw new HardhatError(HardhatError.ERRORS.CORE.TASK_DEFINITIONS.UNRECOGNIZED_TASK_OPTION, { option: [...providedArgumentNames][0], task: formatTaskId(this.id), }); } } /** * Resolves the action file for a task. The action file is imported and the * default export function is returned. * * @throws HardhatError if the module can't be imported or doesn't have a * default export function. */ async #resolveImportAction(action, actionPluginId) { let resolvedActionFn; try { resolvedActionFn = await action(); } catch (error) { ensureError(error); if (actionPluginId !== undefined) { const plugin = this.#hre.config.plugins.find((p) => p.id === actionPluginId); assertHardhatInvariant(plugin !== undefined, `Plugin with id ${actionPluginId} not found.`); await detectPluginNpmDependencyProblems(this.#hre.config.paths.root, plugin, error); } throw new HardhatError(HardhatError.ERRORS.CORE.TASK_DEFINITIONS.INVALID_ACTION_IMPORT, { task: formatTaskId(this.id), }, error); } if (typeof resolvedActionFn.default !== "function") { throw new HardhatError(HardhatError.ERRORS.CORE.TASK_DEFINITIONS.INVALID_ACTION, { task: formatTaskId(this.id), }); } return resolvedActionFn.default; } } //# sourceMappingURL=resolved-task.js.map