Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task fixes #5513

Merged
merged 3 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 38 additions & 159 deletions v-next/core/src/internal/tasks/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ import { HardhatError } from "@ignored/hardhat-vnext-errors";

import { ArgumentType } from "../../types/arguments.js";
import { TaskDefinitionType } from "../../types/tasks.js";
import {
RESERVED_ARGUMENT_NAMES,
isArgumentValueValid,
isArgumentNameValid,
} from "../arguments.js";

import { formatTaskId, isValidActionUrl } from "./utils.js";
import { formatTaskId } from "./utils.js";
import {
validateAction,
validateId,
validateOption,
validatePositionalArgument,
} from "./validations.js";

export class EmptyTaskDefinitionBuilderImplementation
implements EmptyTaskDefinitionBuilder
Expand All @@ -34,11 +35,7 @@ export class EmptyTaskDefinitionBuilderImplementation
#description: string;

constructor(id: string | string[], description: string = "") {
if (id.length === 0) {
throw new HardhatError(
HardhatError.ERRORS.TASK_DEFINITIONS.EMPTY_TASK_ID,
);
}
validateId(id);

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

constructor(id: string | string[], description: string = "") {
if (id.length === 0) {
throw new HardhatError(
HardhatError.ERRORS.TASK_DEFINITIONS.EMPTY_TASK_ID,
);
}
validateId(id);

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

public setAction(action: NewTaskActionFunction | string): this {
if (typeof action === "string" && !isValidActionUrl(action)) {
throw new HardhatError(
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_FILE_ACTION,
{
action,
},
);
}
validateAction(action);

this.#action = action;

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

if (!isArgumentNameValid(name)) {
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.INVALID_NAME, {
name,
});
}

if (this.#usedNames.has(name)) {
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.DUPLICATED_NAME, {
name,
});
}

if (RESERVED_ARGUMENT_NAMES.has(name)) {
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.RESERVED_NAME, {
name,
});
}

if (!isArgumentValueValid(argumentType, defaultValue)) {
throw new HardhatError(
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_VALUE_FOR_TYPE,
{
value: defaultValue,
name: "defaultValue",
type: argumentType,
task: formatTaskId(this.#id),
},
);
}

this.#usedNames.add(name);

this.#options[name] = {
const optionDefinition = {
name,
description,
type: argumentType,
defaultValue,
};

validateOption(optionDefinition, this.#usedNames, this.#id);

this.#options[name] = optionDefinition;

return this;
}

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

if (!isArgumentNameValid(name)) {
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.INVALID_NAME, {
name,
});
}

if (this.#usedNames.has(name)) {
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.DUPLICATED_NAME, {
name,
});
}

if (RESERVED_ARGUMENT_NAMES.has(name)) {
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.RESERVED_NAME, {
name,
});
}

if (defaultValue !== undefined) {
if (!isArgumentValueValid(argumentType, defaultValue, isVariadic)) {
throw new HardhatError(
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_VALUE_FOR_TYPE,
{
value: defaultValue,
name: "defaultValue",
type: argumentType,
task: formatTaskId(this.#id),
},
);
}
}

if (this.#positionalArgs.length > 0) {
const lastArg = this.#positionalArgs[this.#positionalArgs.length - 1];

if (lastArg.isVariadic) {
throw new HardhatError(
HardhatError.ERRORS.TASK_DEFINITIONS.POSITIONAL_ARG_AFTER_VARIADIC,
{
name,
},
);
}

if (lastArg.defaultValue !== undefined && defaultValue === undefined) {
throw new HardhatError(
HardhatError.ERRORS.TASK_DEFINITIONS.REQUIRED_ARG_AFTER_OPTIONAL,
{
name,
},
);
}
}

this.#usedNames.add(name);

this.#positionalArgs.push({
const positionalArgDef = {
name,
description,
type: argumentType,
defaultValue,
isVariadic,
});
};

const lastArg = this.#positionalArgs.at(-1);
validatePositionalArgument(
positionalArgDef,
this.#usedNames,
this.#id,
lastArg,
);

this.#positionalArgs.push(positionalArgDef);

return this;
}
Expand All @@ -303,11 +215,7 @@ export class TaskOverrideDefinitionBuilderImplementation
#action?: TaskOverrideActionFunction | string;

constructor(id: string | string[]) {
if (id.length === 0) {
throw new HardhatError(
HardhatError.ERRORS.TASK_DEFINITIONS.EMPTY_TASK_ID,
);
}
validateId(id);

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

public setAction(action: TaskOverrideActionFunction | string): this {
if (typeof action === "string" && !isValidActionUrl(action)) {
throw new HardhatError(
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_FILE_ACTION,
{
action,
},
);
}
validateAction(action);

this.#action = action;

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

if (!isArgumentNameValid(name)) {
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.INVALID_NAME, {
name,
});
}

if (name in this.#options) {
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.DUPLICATED_NAME, {
name,
});
}

if (RESERVED_ARGUMENT_NAMES.has(name)) {
throw new HardhatError(HardhatError.ERRORS.ARGUMENTS.RESERVED_NAME, {
name,
});
}

if (!isArgumentValueValid(argumentType, defaultValue)) {
throw new HardhatError(
HardhatError.ERRORS.TASK_DEFINITIONS.INVALID_VALUE_FOR_TYPE,
{
value: defaultValue,
name: "defaultValue",
type: argumentType,
task: formatTaskId(this.#id),
},
);
}

this.#options[name] = {
const optionDefinition = {
name,
description,
type: argumentType,
defaultValue,
};

validateOption(
optionDefinition,
new Set(Object.keys(this.#options)),
this.#id,
);

this.#options[name] = optionDefinition;

return this;
}

Expand Down
45 changes: 20 additions & 25 deletions v-next/core/src/internal/tasks/resolved-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,34 +100,29 @@ export class ResolvedTask implements Task {
}

const providedArgumentNames = new Set(Object.keys(taskArguments));

// Validate and resolve the task options
for (const option of this.options.values()) {
const value = taskArguments[option.name];

this.#validateArgumentType(option, value);

// resolve defaults for optional arguments
if (value === undefined) {
taskArguments[option.name] = option.defaultValue;
const argumentDefinitions = [
...this.options.values(),
...this.positionalArguments,
];
const validatedTaskArguments: TaskArguments = {};
for (const argumentDefinition of argumentDefinitions) {
const value = taskArguments[argumentDefinition.name];
const isPositional = "isVariadic" in argumentDefinition;

if (isPositional) {
this.#validateRequiredArgument(argumentDefinition, value);
}

providedArgumentNames.delete(option.name);
}

// Validate and resolve the task positional arguments
for (const argument of this.positionalArguments) {
const value = taskArguments[argument.name];

this.#validateRequiredArgument(argument, value);
this.#validateArgumentType(argument, value, argument.isVariadic);
this.#validateArgumentType(
argumentDefinition,
value,
isPositional && argumentDefinition.isVariadic,
);

// resolve defaults for optional arguments
if (value === undefined && argument.defaultValue !== undefined) {
taskArguments[argument.name] = argument.defaultValue;
}
validatedTaskArguments[argumentDefinition.name] =
value ?? argumentDefinition.defaultValue;

providedArgumentNames.delete(argument.name);
providedArgumentNames.delete(argumentDefinition.name);
}

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

return next(taskArguments);
return next(validatedTaskArguments);
}

/**
Expand Down
Loading
Loading