Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions .changeset/short-penguins-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@nomicfoundation/hardhat-errors": patch
"hardhat": patch
---

Add ability for task options to be hidden from the CLI ([#7426](https://github.com/NomicFoundation/hardhat/issues/7426))
7 changes: 7 additions & 0 deletions v-next/example-project/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ const greeting = task("hello", "Print a greeting")
description: "The greeting to print",
defaultValue: "Hello, World!",
})
.addOption({
name: "programmatic",
description: "An example to show a hidden option",
type: ArgumentType.BOOLEAN,
defaultValue: false,
hidden: true,
})
.setAction(async () => ({
default: async ({ greeting }, _) => {
console.log(greeting);
Expand Down
13 changes: 13 additions & 0 deletions v-next/hardhat-errors/src/descriptors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,12 @@ Please resolve the errors before rerunning the command.`,

Please install Hardhat locally using pnpm, npm or yarn, and try again.`,
},
GLOBAL_OPTION_HIDDEN_NOT_SUPPORTED: {
number: 23,
messageTemplate: `Global option "{globalOption}" from plugin "{plugin}" cannot be hidden`,
websiteTitle: "Global option cannot be hidden",
websiteDescription: `A global option was defined as hidden, but global options cannot be hidden.`,
},
},
INTERNAL: {
ASSERTION_ERROR: {
Expand Down Expand Up @@ -800,6 +806,13 @@ Please double check your arguments.`,

Please double check your arguments.`,
},
NO_HIDDEN_OPTION_CLI: {
number: 512,
messageTemplate:
'The option "{option}" is hidden and cannot be used from the CLI.',
websiteTitle: "Hidden options cannot be used from the CLI",
websiteDescription: `You are trying to use a hidden option from the CLI, which is not allowed.`,
},
},
BUILTIN_TASKS: {
RUN_FILE_NOT_FOUND: {
Expand Down
4 changes: 4 additions & 0 deletions v-next/hardhat/src/internal/cli/help/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export function parseOptions(task: Task): {
const positionalArguments = [];

for (const [optionName, option] of task.options) {
if (option.hidden === true) {
continue;
}

options.push({
name: toCommandLineOption(optionName),
shortName: toShortCommandLineOption(option.shortName),
Expand Down
9 changes: 9 additions & 0 deletions v-next/hardhat/src/internal/cli/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,15 @@ function parseOptions(
);
}

if (optionDefinition.hidden === true) {
throw new HardhatError(
HardhatError.ERRORS.CORE.ARGUMENTS.NO_HIDDEN_OPTION_CLI,
{
option: arg,
},
);
}

const optionName = optionDefinition.name;

// Check if the short name is valid again now that we know its type
Expand Down
8 changes: 4 additions & 4 deletions v-next/hardhat/src/internal/core/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {
ArgumentTypeToValueType,
OptionDefinition,
GlobalOptionDefinition,
} from "../../types/arguments.js";
import type { ConfigurationVariable } from "../../types/config.js";
import type {
Expand Down Expand Up @@ -75,7 +75,7 @@ export function globalOption<T extends ArgumentType>(options: {
description: string;
type?: T;
defaultValue: ArgumentTypeToValueType<T>;
}): OptionDefinition {
}): GlobalOptionDefinition {
return buildGlobalOptionDefinition(options);
}

Expand All @@ -86,7 +86,7 @@ export function globalFlag(options: {
name: string;
shortName?: string;
description: string;
}): OptionDefinition {
}): GlobalOptionDefinition {
return buildGlobalOptionDefinition({
...options,
type: ArgumentType.FLAG,
Expand All @@ -102,7 +102,7 @@ export function globalLevel(options: {
shortName?: string;
description: string;
defaultValue?: number;
}): OptionDefinition {
}): GlobalOptionDefinition {
return buildGlobalOptionDefinition({
...options,
type: ArgumentType.LEVEL,
Expand Down
15 changes: 13 additions & 2 deletions v-next/hardhat/src/internal/core/global-options.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {
ArgumentTypeToValueType,
ArgumentValue,
OptionDefinition,
GlobalOptionDefinition,
} from "../../types/arguments.js";
import type {
GlobalOptions,
Expand Down Expand Up @@ -69,6 +69,17 @@ export function buildGlobalOptionDefinitions(
}
}

// @ts-expect-error -- validation against js users
if (option.hidden !== undefined) {
throw new HardhatError(
HardhatError.ERRORS.CORE.GENERAL.GLOBAL_OPTION_HIDDEN_NOT_SUPPORTED,
{
plugin: plugin.id,
globalOption: option.name,
},
);
}

const validatedGlobalOption = buildGlobalOptionDefinition(option);

const mapEntry = {
Expand Down Expand Up @@ -108,7 +119,7 @@ export function buildGlobalOptionDefinition<
description: string;
type?: T;
defaultValue: ArgumentTypeToValueType<T>;
}): OptionDefinition {
}): GlobalOptionDefinition {
const argumentType = type ?? ArgumentType.STRING;

validateArgumentName(name);
Expand Down
5 changes: 5 additions & 0 deletions v-next/hardhat/src/internal/core/tasks/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,14 @@ export class NewTaskDefinitionBuilderImplementation<
description = "",
type,
defaultValue,
hidden,
}: {
name: NameT;
shortName?: string;
description?: string;
type?: TypeT;
defaultValue: ArgumentTypeToValueType<TypeT>;
hidden?: boolean;
}): NewTaskDefinitionBuilder<
ExtendTaskArguments<NameT, TypeT, TaskArgumentsT>
> {
Expand All @@ -112,6 +114,7 @@ export class NewTaskDefinitionBuilderImplementation<
description,
type: argumentType,
defaultValue,
hidden,
};

validateOption(optionDefinition, this.#usedNames, this.#id);
Expand All @@ -125,13 +128,15 @@ export class NewTaskDefinitionBuilderImplementation<
name: NameT;
shortName?: string;
description?: string;
hidden?: boolean;
}): NewTaskDefinitionBuilder<
ExtendTaskArguments<NameT, ArgumentType.FLAG, TaskArgumentsT>
> {
return this.addOption({
...flagConfig,
type: ArgumentType.FLAG,
defaultValue: false,
hidden: flagConfig.hidden,
});
}

Expand Down
8 changes: 8 additions & 0 deletions v-next/hardhat/src/types/arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,16 @@ export interface OptionDefinition<T extends ArgumentType = ArgumentType> {
description: string;
type: T;
defaultValue: ArgumentTypeToValueType<T>;
hidden?: boolean;
}

/**
* A global option is essentially identical to a regular OptionDefinition,
* except that it cannot be hidden.
*/
export type GlobalOptionDefinition<T extends ArgumentType = ArgumentType> =
Omit<OptionDefinition<T>, "hidden">;

/**
* A positional argument is used as `<value>` in the CLI, where its position
* matters. For example, `mv <from> <to>` has two positional arguments.
Expand Down
4 changes: 2 additions & 2 deletions v-next/hardhat/src/types/global-options.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { OptionDefinition } from "./arguments.js";
import type { GlobalOptionDefinition } from "./arguments.js";

/**
* The values of each global option for a certain instance of the Hardhat
Expand Down Expand Up @@ -30,7 +30,7 @@ export interface GlobalOptions {
*/
export interface GlobalOptionDefinitionsEntry {
pluginId: string;
option: OptionDefinition;
option: GlobalOptionDefinition;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions v-next/hardhat/src/types/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { OptionDefinition } from "./arguments.js";
import type { GlobalOptionDefinition } from "./arguments.js";
import type { HardhatHooks } from "./hooks.js";
import type { TaskDefinition } from "./tasks.js";

Expand Down Expand Up @@ -76,7 +76,7 @@ export interface HardhatPlugin {
/**
* An array of the global options that this plugin defines.
*/
globalOptions?: OptionDefinition[];
globalOptions?: GlobalOptionDefinition[];

/**
* An array of type definitions, which should be created using their builders.
Expand Down
2 changes: 2 additions & 0 deletions v-next/hardhat/src/types/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export interface NewTaskDefinitionBuilder<
description?: string;
type?: TypeT;
defaultValue: ArgumentTypeToValueType<TypeT>;
hidden?: boolean;
}): NewTaskDefinitionBuilder<
ExtendTaskArguments<NameT, TypeT, TaskArgumentsT>
>;
Expand Down Expand Up @@ -321,6 +322,7 @@ export interface TaskOverrideDefinitionBuilder<
description?: string;
type?: TypeT;
defaultValue: ArgumentTypeToValueType<TypeT>;
hidden?: boolean;
}): TaskOverrideDefinitionBuilder<
ExtendTaskArguments<NameT, TypeT, TaskArgumentsT>
>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { HardhatUserConfig } from "../../../../../src/config.js";

import { task } from "../../../../../src/config.js";

const customTask = task("test-task", "description")
.setAction(async () => ({
default: () => {},
}))
.addOption({
name: "opt",
description: "opt description",
defaultValue: "opt default value",
hidden: true,
})
.build();

const config: HardhatUserConfig = {
tasks: [customTask],
};

export default config;
16 changes: 16 additions & 0 deletions v-next/hardhat/test/internal/cli/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,22 @@ describe("main", function () {
});
});

describe("task with hidden option", function () {
useFixtureProject("cli/parsing/hidden-option");

it("should throw when passing a hidden option from the CLI", async function () {
const command = "npx hardhat test-task --opt <value>";

await assertRejectsWithHardhatError(
() => runMain(command),
HardhatError.ERRORS.CORE.ARGUMENTS.NO_HIDDEN_OPTION_CLI,
{
option: "--opt",
},
);
});
});

describe("global help", function () {
useFixtureProject("cli/parsing/base-project");

Expand Down
Loading
Loading