Skip to content

Commit cbbdf86

Browse files
Merge branch 'v-next' of github.com:NomicFoundation/hardhat into remove-defaultNetwork
2 parents 6cf9076 + f5a835d commit cbbdf86

File tree

27 files changed

+1569
-222
lines changed

27 files changed

+1569
-222
lines changed

.changeset/green-hornets-joke.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@nomicfoundation/hardhat-errors": patch
3+
"hardhat": patch
4+
---
5+
6+
Added support for short option names

.changeset/long-bats-nail.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"hardhat": patch
3+
"@nomicfoundation/hardhat-typechain": patch
4+
---
5+
6+
Added FLAG and LEVEL arguments types

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

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,32 @@ Please double check your arguments.`,
738738
'The global option "--config" cannot be used with the "init" command',
739739
websiteDescription: `The global option "--config" cannot be used with the "init" command.
740740
741+
Please double check your arguments.`,
742+
},
743+
CANNOT_GROUP_OPTIONS: {
744+
number: 509,
745+
messageTemplate:
746+
'Invalid option "{option}". Options cannot be grouped together. Try providing the options separately.',
747+
websiteTitle: "Options grouping is not supported",
748+
websiteDescription: `Options cannot be grouped together.
749+
750+
Please double check your arguments, and try providing the options separately.`,
751+
},
752+
CANNOT_REPEAT_OPTIONS: {
753+
number: 510,
754+
messageTemplate:
755+
'Invalid option "{option}". Options of type "{type}" cannot be repeated.',
756+
websiteTitle: "Options repetition is not supported",
757+
websiteDescription: `Some options cannot be repeated.
758+
759+
Please double check your arguments.`,
760+
},
761+
INVALID_SHORT_NAME: {
762+
number: 511,
763+
messageTemplate: `Argument short name "{name}" is invalid. It must consist of exactly one letter.`,
764+
websiteTitle: "Invalid short argument name",
765+
websiteDescription: `One of your Hardhat or task short argument names is invalid.
766+
741767
Please double check your arguments.`,
742768
},
743769
},
@@ -979,7 +1005,7 @@ Remaining test suites: {suites}`,
9791005
{error}`,
9801006
websiteTitle: "Project file resolution error",
9811007
websiteDescription: `There was an error while resolving the project file.
982-
1008+
9831009
Please double-check your configuration. If it keeps happening, please report it.`,
9841010
},
9851011
NPM_ROOT_RESOLUTION_ERROR: {
@@ -989,7 +1015,7 @@ Please double-check your configuration. If it keeps happening, please report it.
9891015
{error}`,
9901016
websiteTitle: "Npm file resolution error",
9911017
websiteDescription: `There was an error while resolving an npm module that you are trying to compile and generate artifacts for.
992-
1018+
9931019
Please double-check your configuration. If it keeps happening, please report it.`,
9941020
},
9951021
IMPORT_RESOLUTION_ERROR: {

v-next/hardhat-typechain/src/index.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { HardhatPlugin } from "hardhat/types/plugins";
22

33
import "./type-extensions.js";
4-
import { globalOption } from "hardhat/config";
5-
import { ArgumentType } from "hardhat/types/arguments";
4+
import { globalFlag } from "hardhat/config";
65

76
const hardhatTypechain: HardhatPlugin = {
87
id: "hardhat-typechain",
@@ -15,11 +14,9 @@ const hardhatTypechain: HardhatPlugin = {
1514
async () => (await import("@nomicfoundation/hardhat-ethers")).default,
1615
],
1716
globalOptions: [
18-
globalOption({
17+
globalFlag({
1918
name: "noTypechain",
2019
description: "Disables the typechain type generation",
21-
defaultValue: false,
22-
type: ArgumentType.BOOLEAN,
2320
}),
2421
],
2522
};

v-next/hardhat/src/internal/builtin-global-options.ts

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { GlobalOptionDefinitions } from "../types/global-options.js";
22

3-
import { globalOption } from "../config.js";
3+
import { globalFlag, globalOption } from "../config.js";
44
import { ArgumentType } from "../types/arguments.js";
55

66
export const BUILTIN_GLOBAL_OPTIONS_DEFINITIONS: GlobalOptionDefinitions =
@@ -21,60 +21,50 @@ export const BUILTIN_GLOBAL_OPTIONS_DEFINITIONS: GlobalOptionDefinitions =
2121
"help",
2222
{
2323
pluginId: "builtin",
24-
option: globalOption({
24+
option: globalFlag({
2525
name: "help",
2626
description:
2727
"Shows this message, or a task's help if its name is provided.",
28-
type: ArgumentType.BOOLEAN,
29-
defaultValue: false,
3028
}),
3129
},
3230
],
3331
[
3432
"init",
3533
{
3634
pluginId: "builtin",
37-
option: globalOption({
35+
option: globalFlag({
3836
name: "init",
3937
description: "Initializes a Hardhat project.",
40-
type: ArgumentType.BOOLEAN,
41-
defaultValue: false,
4238
}),
4339
},
4440
],
4541
[
4642
"showStackTraces",
4743
{
4844
pluginId: "builtin",
49-
option: globalOption({
45+
option: globalFlag({
5046
name: "showStackTraces",
5147
description: "Show stack traces (always enabled on CI servers).",
52-
type: ArgumentType.BOOLEAN,
53-
defaultValue: false,
5448
}),
5549
},
5650
],
5751
[
5852
"verbose",
5953
{
6054
pluginId: "builtin",
61-
option: globalOption({
55+
option: globalFlag({
6256
name: "verbose",
6357
description: "Enables Hardhat verbose logging.",
64-
type: ArgumentType.BOOLEAN,
65-
defaultValue: false,
6658
}),
6759
},
6860
],
6961
[
7062
"version",
7163
{
7264
pluginId: "builtin",
73-
option: globalOption({
65+
option: globalFlag({
7466
name: "version",
7567
description: "Shows hardhat's version.",
76-
type: ArgumentType.BOOLEAN,
77-
defaultValue: false,
7868
}),
7969
},
8070
],

v-next/hardhat/src/internal/builtin-plugins/coverage/index.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
import type { HardhatPlugin } from "../../../types/plugins.js";
22

3-
import { ArgumentType } from "../../../types/arguments.js";
4-
import { globalOption } from "../../core/config.js";
3+
import { globalFlag } from "../../core/config.js";
54

65
import "./type-extensions.js";
76

87
const hardhatPlugin: HardhatPlugin = {
98
id: "builtin:coverage",
109
tasks: [],
1110
globalOptions: [
12-
globalOption({
11+
globalFlag({
1312
name: "coverage",
1413
description: "Enables code coverage",
15-
type: ArgumentType.BOOLEAN,
16-
defaultValue: false,
1714
}),
1815
],
1916
hookHandlers: {

v-next/hardhat/src/internal/cli/help/utils.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const GLOBAL_NAME_PADDING = 6;
1111

1212
interface ArgumentDescriptor {
1313
name: string;
14+
shortName?: string;
1415
description: string;
1516
type?: ArgumentType;
1617
defaultValue?: ArgumentTypeToValueType<ArgumentType>;
@@ -22,6 +23,7 @@ export function parseGlobalOptions(
2223
): ArgumentDescriptor[] {
2324
return [...globalOptionDefinitions].map(([, { option }]) => ({
2425
name: toCommandLineOption(option.name),
26+
shortName: toShortCommandLineOption(option.shortName),
2527
description: option.description,
2628
}));
2729
}
@@ -69,6 +71,7 @@ export function parseOptions(task: Task): {
6971
for (const [optionName, option] of task.options) {
7072
options.push({
7173
name: toCommandLineOption(optionName),
74+
shortName: toShortCommandLineOption(option.shortName),
7275
description: option.description,
7376
type: option.type,
7477
...(option.defaultValue !== undefined && {
@@ -97,8 +100,20 @@ export function toCommandLineOption(optionName: string): string {
97100
return `--${camelToKebabCase(optionName)}`;
98101
}
99102

100-
export function getLongestNameLength(tasks: Array<{ name: string }>): number {
101-
return tasks.reduce((acc, { name }) => Math.max(acc, name.length), 0);
103+
export function toShortCommandLineOption(
104+
optionShortName?: string,
105+
): string | undefined {
106+
return optionShortName !== undefined ? `-${optionShortName}` : undefined;
107+
}
108+
109+
export function getLongestNameLength(
110+
tasks: Array<{ name: string; shortName?: string }>,
111+
): number {
112+
return tasks.reduce(
113+
(acc, { name, shortName }) =>
114+
Math.max(acc, getNameString(name, shortName).length),
115+
0,
116+
);
102117
}
103118

104119
export function getSection(
@@ -108,13 +123,18 @@ export function getSection(
108123
): string {
109124
return `\n${title}:\n\n${items
110125
.sort((a, b) => a.name.localeCompare(b.name))
111-
.map(({ name, description, defaultValue }) => {
126+
.map(({ name, shortName, description, defaultValue }) => {
127+
const nameStr = getNameString(name, shortName);
112128
const defaultValueStr = getDefaultValueString(defaultValue);
113-
return ` ${name.padEnd(namePadding)}${description}${defaultValueStr}`;
129+
return ` ${nameStr.padEnd(namePadding)}${description}${defaultValueStr}`.trimEnd();
114130
})
115131
.join("\n")}\n`;
116132
}
117133

134+
function getNameString(name: string, shortName?: string): string {
135+
return shortName !== undefined ? [name, shortName].join(", ") : name;
136+
}
137+
118138
function getDefaultValueString(
119139
defaultValue: ArgumentTypeToValueType<ArgumentType>,
120140
): string {
@@ -143,7 +163,15 @@ export function getUsageString(
143163
if (options.length > 0) {
144164
output += ` ${options
145165
.sort((a, b) => a.name.localeCompare(b.name))
146-
.map((o) => `[${o.name}${o.type === "BOOLEAN" ? "" : ` <${o.type}>`}]`)
166+
.map((o) => {
167+
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- We want to explicitly handle all the other types via the default case
168+
switch (o.type) {
169+
case "FLAG":
170+
return `[${o.name}]`;
171+
default:
172+
return `[${o.name} <${o.type}>]`;
173+
}
174+
})
147175
.join(" ")}`;
148176
}
149177

v-next/hardhat/src/internal/cli/main.ts

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -372,12 +372,12 @@ function getTaskFromCliArguments(
372372

373373
const arg = cliArguments[i];
374374

375-
if (arg.startsWith("--")) {
375+
if (arg.startsWith("-")) {
376376
/* A standalone '--' is ok because it is used to separate CLI tool arguments
377377
* from task arguments, ensuring the tool passes subsequent options directly
378378
* to the task. Everything after "--" should be considered as a positional
379379
* argument. */
380-
if (arg.length === 2 || task !== undefined) {
380+
if (arg === "--" || task !== undefined) {
381381
break;
382382
}
383383

@@ -474,6 +474,16 @@ function parseOptions(
474474
providedArguments: TaskArguments,
475475
ignoreUnknownOption = false,
476476
) {
477+
const optionDefinitionsByShortName = new Map<string, OptionDefinition>();
478+
for (const optionDefinition of optionDefinitions.values()) {
479+
if (optionDefinition.shortName !== undefined) {
480+
optionDefinitionsByShortName.set(
481+
optionDefinition.shortName,
482+
optionDefinition,
483+
);
484+
}
485+
}
486+
477487
for (let i = 0; i < cliArguments.length; i++) {
478488
if (usedCliArguments[i]) {
479489
continue;
@@ -489,13 +499,32 @@ function parseOptions(
489499

490500
const arg = cliArguments[i];
491501

492-
if (arg.startsWith("--") === false) {
502+
let optionDefinition: OptionDefinition | undefined;
503+
504+
const providedByName = arg.startsWith("--");
505+
const providedByShortName = !providedByName && arg.startsWith("-");
506+
507+
if (providedByName) {
508+
const name = kebabToCamelCase(arg.substring(2));
509+
optionDefinition = optionDefinitions.get(name);
510+
} else if (providedByShortName) {
511+
const shortName = arg[1];
512+
513+
// Check if the short name is valid
514+
if (Array.from(arg.substring(1)).some((c) => c !== shortName)) {
515+
throw new HardhatError(
516+
HardhatError.ERRORS.CORE.ARGUMENTS.CANNOT_GROUP_OPTIONS,
517+
{
518+
option: arg,
519+
},
520+
);
521+
}
522+
523+
optionDefinition = optionDefinitionsByShortName.get(shortName);
524+
} else {
493525
continue;
494526
}
495527

496-
const optionName = kebabToCamelCase(arg.substring(2));
497-
const optionDefinition = optionDefinitions.get(optionName);
498-
499528
if (optionDefinition === undefined) {
500529
if (ignoreUnknownOption === true) {
501530
continue;
@@ -511,32 +540,38 @@ function parseOptions(
511540
);
512541
}
513542

514-
usedCliArguments[i] = true;
543+
const optionName = optionDefinition.name;
515544

516-
if (optionDefinition.type === ArgumentType.BOOLEAN) {
517-
if (
518-
usedCliArguments[i + 1] !== undefined &&
519-
usedCliArguments[i + 1] === false &&
520-
(cliArguments[i + 1] === "true" || cliArguments[i + 1] === "false")
521-
) {
522-
// The argument could be followed by a boolean value if it does not
523-
// behaves like a flag
524-
providedArguments[optionName] = parseArgumentValue(
525-
cliArguments[i + 1],
526-
ArgumentType.BOOLEAN,
527-
optionName,
528-
);
545+
// Check if the short name is valid again now that we know its type
546+
// E.g. --flag --flag
547+
const optionAlreadyProvided = providedArguments[optionName] !== undefined;
548+
// E.g. -ff
549+
const shortOptionGroupedAndRepeated = providedByShortName && arg.length > 2;
550+
const isLevelOption = optionDefinition.type === ArgumentType.LEVEL;
551+
if (
552+
optionAlreadyProvided ||
553+
(shortOptionGroupedAndRepeated && !isLevelOption)
554+
) {
555+
throw new HardhatError(
556+
HardhatError.ERRORS.CORE.ARGUMENTS.CANNOT_REPEAT_OPTIONS,
557+
{
558+
option: arg,
559+
type: optionDefinition.type,
560+
},
561+
);
562+
}
529563

530-
usedCliArguments[i + 1] = true;
531-
continue;
532-
}
564+
usedCliArguments[i] = true;
533565

534-
if (optionDefinition.defaultValue === false) {
535-
// If the default value for the argument is false, the argument behaves
536-
// like a flag, so there is no need to specify the value
537-
providedArguments[optionName] = true;
538-
continue;
539-
}
566+
if (optionDefinition.type === ArgumentType.FLAG) {
567+
providedArguments[optionName] = true;
568+
continue;
569+
} else if (
570+
optionDefinition.type === ArgumentType.LEVEL &&
571+
providedByShortName
572+
) {
573+
providedArguments[optionName] = arg.length - 1;
574+
continue;
540575
} else if (
541576
usedCliArguments[i + 1] !== undefined &&
542577
usedCliArguments[i + 1] === false

0 commit comments

Comments
 (0)