Skip to content
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
9 changes: 9 additions & 0 deletions .changeset/twenty-pillows-switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@nomicfoundation/example-project": patch
"@nomicfoundation/hardhat-ignition": patch
"@nomicfoundation/hardhat-mocha": patch
"@nomicfoundation/hardhat-node-test-runner": patch
"hardhat": patch
---

Allow undefined for default values in global options and other argument types ([#6596](https://github.com/NomicFoundation/hardhat/issues/6596)
4 changes: 3 additions & 1 deletion v-next/example-project/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import hardhatEthersPlugin from "@nomicfoundation/hardhat-ethers";
import hardhatChaiMatchersPlugin from "@nomicfoundation/hardhat-ethers-chai-matchers";
import hardhatTypechain from "@nomicfoundation/hardhat-typechain";
import hardhatIgnitionViem from "@nomicfoundation/hardhat-ignition-viem";
import { ArgumentType } from "hardhat/types/arguments";

util.inspect.defaultOptions.depth = null;

Expand Down Expand Up @@ -50,7 +51,8 @@ const exampleTaskOverride = task("example2")
.addOption({
name: "grep",
description: "Only run tests matching the given string or regexp",
defaultValue: "",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
})
.build();

Expand Down
12 changes: 6 additions & 6 deletions v-next/hardhat-ignition/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,22 @@ const hardhatIgnitionPlugin: HardhatPlugin = {
})
.addOption({
name: "parameters",
type: ArgumentType.FILE,
type: ArgumentType.FILE_WITHOUT_DEFAULT,
description:
"A relative path to a JSON file to use for the module parameters",
defaultValue: "", // TODO: HH3 check this comes through correctly
defaultValue: undefined,
})
.addOption({
name: "deploymentId",
type: ArgumentType.STRING,
type: ArgumentType.STRING_WITHOUT_DEFAULT,
description: "Set the id of the deployment",
defaultValue: "", // TODO: HH3 check this comes through correctly
defaultValue: undefined,
})
.addOption({
name: "defaultSender",
type: ArgumentType.STRING,
type: ArgumentType.STRING_WITHOUT_DEFAULT,
description: "Set the default sender for the deployment",
defaultValue: "", // TODO: HH3 check this comes through correctly
defaultValue: undefined,
})
.addOption({
name: "strategy",
Expand Down
3 changes: 1 addition & 2 deletions v-next/hardhat-ignition/src/internal/tasks/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,7 @@ const taskDeploy: NewTaskActionFunction<TaskDeployArguments> = async (
}

let parameters: DeploymentParameters | undefined;
// TODO: HH3 Remove the use of "" as a default value
if (parametersInput === undefined || parametersInput === "") {
if (parametersInput === undefined) {
parameters = await resolveParametersFromModuleName(
userModule.id,
hre.config.paths.ignition,
Expand Down
4 changes: 3 additions & 1 deletion v-next/hardhat-mocha/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { HardhatPlugin } from "hardhat/types/plugins";

import { task } from "hardhat/config";
import { ArgumentType } from "hardhat/types/arguments";

import "./type-extensions.js";

Expand All @@ -20,7 +21,8 @@ const hardhatPlugin: HardhatPlugin = {
.addOption({
name: "grep",
description: "Only run tests matching the given string or regexp",
defaultValue: "",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
})
.addFlag({
name: "noCompile",
Expand Down
4 changes: 3 additions & 1 deletion v-next/hardhat-node-test-runner/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { HardhatPlugin } from "hardhat/types/plugins";

import { task } from "hardhat/config";
import { ArgumentType } from "hardhat/types/arguments";

import "./type-extensions.js";

Expand All @@ -20,7 +21,8 @@ const hardhatPlugin: HardhatPlugin = {
.addOption({
name: "grep",
description: "Only run tests matching the given string or regexp",
defaultValue: "",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
})
.addFlag({
name: "noCompile",
Expand Down
4 changes: 2 additions & 2 deletions v-next/hardhat/src/internal/builtin-global-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export const BUILTIN_GLOBAL_OPTIONS_DEFINITIONS: GlobalOptionDefinitions =
option: globalOption({
name: "config",
description: "A Hardhat config file.",
type: ArgumentType.STRING,
defaultValue: "",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
}),
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default async (): Promise<Partial<HardhatRuntimeEnvironmentHooks>> => ({

if (networkManager === undefined) {
networkManager = new NetworkManagerImplementation(
hre.globalOptions.network !== ""
hre.globalOptions.network !== undefined
? hre.globalOptions.network
: hre.config.defaultNetwork,
hre.config.defaultChainType,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { HardhatPlugin } from "../../../types/plugins.js";

import { ArgumentType } from "../../../types/arguments.js";
import { globalOption } from "../../core/config.js";

import "./type-extensions/config.js";
Expand All @@ -18,7 +19,8 @@ const hardhatPlugin: HardhatPlugin = {
globalOption({
name: "network",
description: "The network to connect to",
defaultValue: "",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
}),
],
npmPackage: "hardhat",
Expand Down
9 changes: 6 additions & 3 deletions v-next/hardhat/src/internal/builtin-plugins/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const hardhatPlugin: HardhatPlugin = {
name: "hostname",
description:
"The host to which to bind to for new connections (Defaults to 127.0.0.1 running locally, and 0.0.0.0 in Docker)",
defaultValue: "",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
})
.addOption({
name: "port",
Expand All @@ -23,7 +24,8 @@ const hardhatPlugin: HardhatPlugin = {
name: "chainType",
description:
"The chain type to connect to. If not specified, the default chain type will be used.",
defaultValue: "",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
})
.addOption({
name: "chainId",
Expand All @@ -35,7 +37,8 @@ const hardhatPlugin: HardhatPlugin = {
.addOption({
name: "fork",
description: "The URL of the JSON-RPC server to fork from",
defaultValue: "",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
})
.addOption({
name: "forkBlockNumber",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
hre,
) => {
const network =
hre.globalOptions.network !== ""
hre.globalOptions.network !== undefined
? hre.globalOptions.network
: hre.config.defaultNetwork;

Expand All @@ -49,7 +49,7 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
// as much as needed.
const networkConfigOverride: EdrNetworkConfigOverride = {};

if (args.chainType !== "") {
if (args.chainType !== undefined) {
if (!isEdrSupportedChainType(args.chainType)) {
// NOTE: We could make the error more specific here.
throw new HardhatError(
Expand All @@ -69,7 +69,7 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
}

// NOTE: --fork-block-number is only valid if --fork is specified
if (args.fork !== "") {
if (args.fork !== undefined) {
networkConfigOverride.forking = {
enabled: true,
url: args.fork,
Expand Down Expand Up @@ -104,7 +104,7 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
// the default hostname is "127.0.0.1" unless we are inside a docker
// container, in that case we use "0.0.0.0"
let hostname = args.hostname;
if (hostname === "") {
if (hostname === undefined) {
const insideDocker = await exists("/.dockerenv");
if (insideDocker) {
hostname = "0.0.0.0";
Expand Down
21 changes: 19 additions & 2 deletions v-next/hardhat/src/internal/cli/help/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,30 @@ export function getSection(
return `\n${title}:\n\n${items
.sort((a, b) => a.name.localeCompare(b.name))
.map(({ name, description, defaultValue }) => {
const defaultValueStr =
defaultValue !== undefined ? ` (default: ${defaultValue})` : "";
const defaultValueStr = getDefaultValueString(defaultValue);
return ` ${name.padEnd(namePadding)}${description}${defaultValueStr}`;
})
.join("\n")}\n`;
}

function getDefaultValueString(
defaultValue: ArgumentTypeToValueType<ArgumentType>,
): string {
if (Array.isArray(defaultValue)) {
if (defaultValue.length === 0) {
return "";
} else {
return ` (default: ${JSON.stringify(defaultValue)})`;
}
}

if (defaultValue === undefined) {
return "";
}

return ` (default: ${defaultValue})`;
}

export function getUsageString(
task: Task,
options: ReturnType<typeof parseOptions>["options"],
Expand Down
6 changes: 6 additions & 0 deletions v-next/hardhat/src/internal/core/arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ const argumentTypeValidators: Record<
[ArgumentType.BIGINT]: (value): value is bigint => typeof value === "bigint",
[ArgumentType.FLOAT]: (value): value is number => typeof value === "number",
[ArgumentType.FILE]: (value): value is string => typeof value === "string",
[ArgumentType.STRING_WITHOUT_DEFAULT]: (value): value is string | undefined =>
typeof value === "string" || value === undefined,
[ArgumentType.FILE_WITHOUT_DEFAULT]: (value): value is string | undefined =>
typeof value === "string" || value === undefined,
};

/**
Expand All @@ -121,6 +125,8 @@ export function parseArgumentValue(
name: string,
): ArgumentValue {
switch (type) {
case ArgumentType.STRING_WITHOUT_DEFAULT:
case ArgumentType.FILE_WITHOUT_DEFAULT:
case ArgumentType.STRING:
case ArgumentType.FILE:
return value;
Expand Down
21 changes: 20 additions & 1 deletion v-next/hardhat/src/internal/core/config-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,11 @@ export function validateOptions(
});
}

if (option.defaultValue === undefined) {
if (
option.type !== ArgumentType.STRING_WITHOUT_DEFAULT &&
option.type !== ArgumentType.FILE_WITHOUT_DEFAULT &&
option.defaultValue === undefined
) {
validationErrors.push({
path: [...path, name, "defaultValue"],
message: "option defaultValue must be defined",
Expand All @@ -392,6 +396,19 @@ export function validateOptions(
}
break;
}
case ArgumentType.FILE_WITHOUT_DEFAULT:
case ArgumentType.STRING_WITHOUT_DEFAULT: {
if (
typeof option.defaultValue !== "string" &&
option.defaultValue !== undefined
) {
validationErrors.push({
path: [...path, name, "defaultValue"],
message: "option defaultValue must be a string or undefined",
});
}
break;
}
case ArgumentType.BOOLEAN: {
if (typeof option.defaultValue !== "boolean") {
validationErrors.push({
Expand Down Expand Up @@ -457,6 +474,8 @@ export function validatePositionalArguments(

if (arg.defaultValue !== undefined) {
switch (arg.type) {
case ArgumentType.STRING_WITHOUT_DEFAULT:
case ArgumentType.FILE_WITHOUT_DEFAULT:
case ArgumentType.STRING:
case ArgumentType.FILE: {
if (
Expand Down
4 changes: 3 additions & 1 deletion v-next/hardhat/src/internal/core/global-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ export function buildGlobalOptionDefinitions(
* Builds a global option definition, validating the name, type, and default
* value.
*/
export function buildGlobalOptionDefinition<T extends ArgumentType>({
export function buildGlobalOptionDefinition<
T extends ArgumentType = ArgumentType.STRING,
>({
name,
description,
type,
Expand Down
4 changes: 4 additions & 0 deletions v-next/hardhat/src/types/arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export enum ArgumentType {
BIGINT = "BIGINT",
FLOAT = "FLOAT",
FILE = "FILE",
STRING_WITHOUT_DEFAULT = "STRING_WITHOUT_DEFAULT",
FILE_WITHOUT_DEFAULT = "FILE_WITHOUT_DEFAULT",
}

/**
Expand All @@ -20,6 +22,8 @@ export interface ArgumentValueTypes {
[ArgumentType.BIGINT]: bigint;
[ArgumentType.FLOAT]: number;
[ArgumentType.FILE]: string;
[ArgumentType.STRING_WITHOUT_DEFAULT]: string | undefined;
[ArgumentType.FILE_WITHOUT_DEFAULT]: string | undefined;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe("JSON-RPC handler", function () {

it("should respond to a request with undefined params", async function () {
const hostname = (await exists("/.dockerenv")) ? "0.0.0.0" : "127.0.0.1";
const port = 8545;
const port = 8546;

const connection = await hre.network.connect();
const server = new JsonRpcServerImplementation({
Expand Down
12 changes: 6 additions & 6 deletions v-next/hardhat/test/internal/core/tasks/task-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ describe("TaskManagerImplementation", () => {
globalOption({
name: "globalOption1",
description: "",
type: ArgumentType.STRING,
defaultValue: "",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
}),
],
},
Expand Down Expand Up @@ -422,8 +422,8 @@ describe("TaskManagerImplementation", () => {
globalOption({
name: "arg1",
description: "",
type: ArgumentType.STRING,
defaultValue: "",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
}),
],
},
Expand Down Expand Up @@ -462,8 +462,8 @@ describe("TaskManagerImplementation", () => {
globalOption({
name: "arg1",
description: "",
type: ArgumentType.STRING,
defaultValue: "",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
}),
],
},
Expand Down
2 changes: 1 addition & 1 deletion v-next/hardhat/test/internal/hre-initialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ describe("HRE initialization", () => {
verbose: false,
version: false,
myGlobalOption: "default",
network: "",
network: undefined,
});
});
});
Expand Down
Loading