hardhat 3.5.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/dist/src/internal/builtin-plugins/flatten/task-action.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/flatten/task-action.js +23 -7
- package/dist/src/internal/builtin-plugins/flatten/task-action.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.d.ts +2 -5
- package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.js +79 -12
- package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity/build-system/warning-suppression.d.ts +6 -10
- package/dist/src/internal/builtin-plugins/solidity/build-system/warning-suppression.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity/build-system/warning-suppression.js +54 -43
- package/dist/src/internal/builtin-plugins/solidity/build-system/warning-suppression.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity/hook-handlers/hre.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity/hook-handlers/hre.js +2 -1
- package/dist/src/internal/builtin-plugins/solidity/hook-handlers/hre.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity/tasks/build.d.ts.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity/tasks/build.js +1 -5
- package/dist/src/internal/builtin-plugins/solidity/tasks/build.js.map +1 -1
- package/dist/src/internal/builtin-plugins/solidity/type-extensions.d.ts +53 -1
- package/dist/src/internal/builtin-plugins/solidity/type-extensions.d.ts.map +1 -1
- package/dist/src/internal/cli/error-handler.d.ts +11 -2
- package/dist/src/internal/cli/error-handler.d.ts.map +1 -1
- package/dist/src/internal/cli/error-handler.js +72 -46
- package/dist/src/internal/cli/error-handler.js.map +1 -1
- package/dist/src/internal/cli/help/utils.d.ts.map +1 -1
- package/dist/src/internal/cli/help/utils.js +3 -2
- package/dist/src/internal/cli/help/utils.js.map +1 -1
- package/dist/src/internal/cli/init/init.d.ts.map +1 -1
- package/dist/src/internal/cli/init/init.js +115 -4
- package/dist/src/internal/cli/init/init.js.map +1 -1
- package/dist/src/internal/cli/init/package-manager.d.ts +7 -1
- package/dist/src/internal/cli/init/package-manager.d.ts.map +1 -1
- package/dist/src/internal/cli/init/package-manager.js +14 -2
- package/dist/src/internal/cli/init/package-manager.js.map +1 -1
- package/dist/src/internal/cli/main.d.ts.map +1 -1
- package/dist/src/internal/cli/main.js +4 -2
- package/dist/src/internal/cli/main.js.map +1 -1
- package/dist/src/internal/cli/telemetry/error-classification/classifier.d.ts +2 -1
- package/dist/src/internal/cli/telemetry/error-classification/classifier.d.ts.map +1 -1
- package/dist/src/internal/cli/telemetry/error-classification/classifier.js +2 -1
- package/dist/src/internal/cli/telemetry/error-classification/classifier.js.map +1 -1
- package/dist/src/internal/core/tasks/resolved-task.d.ts.map +1 -1
- package/dist/src/internal/core/tasks/resolved-task.js +3 -2
- package/dist/src/internal/core/tasks/resolved-task.js.map +1 -1
- package/dist/src/internal/core/tasks/utils.d.ts +3 -0
- package/dist/src/internal/core/tasks/utils.d.ts.map +1 -1
- package/dist/src/internal/core/tasks/utils.js +6 -0
- package/dist/src/internal/core/tasks/utils.js.map +1 -1
- package/dist/src/internal/core/tasks/validations.js +3 -3
- package/dist/src/internal/core/tasks/validations.js.map +1 -1
- package/dist/src/internal/using-hardhat2-plugin-errors.d.ts.map +1 -1
- package/dist/src/internal/using-hardhat2-plugin-errors.js +8 -11
- package/dist/src/internal/using-hardhat2-plugin-errors.js.map +1 -1
- package/dist/src/types/solidity/build-system.d.ts +22 -2
- package/dist/src/types/solidity/build-system.d.ts.map +1 -1
- package/dist/src/types/solidity/build-system.js.map +1 -1
- package/package.json +4 -3
- package/skills/hardhat/SKILL.md +152 -0
- package/skills/hardhat-toolbox-mocha-ethers/SKILL.md +119 -0
- package/skills/hardhat-toolbox-viem/SKILL.md +126 -0
- package/src/internal/builtin-plugins/flatten/task-action.ts +37 -7
- package/src/internal/builtin-plugins/solidity/build-system/build-system.ts +126 -21
- package/src/internal/builtin-plugins/solidity/build-system/warning-suppression.ts +81 -59
- package/src/internal/builtin-plugins/solidity/hook-handlers/hre.ts +3 -2
- package/src/internal/builtin-plugins/solidity/tasks/build.ts +1 -6
- package/src/internal/builtin-plugins/solidity/type-extensions.ts +69 -0
- package/src/internal/cli/error-handler.ts +103 -72
- package/src/internal/cli/help/utils.ts +9 -2
- package/src/internal/cli/init/init.ts +141 -0
- package/src/internal/cli/init/package-manager.ts +18 -1
- package/src/internal/cli/main.ts +5 -3
- package/src/internal/cli/telemetry/error-classification/classifier.ts +2 -1
- package/src/internal/core/tasks/resolved-task.ts +5 -2
- package/src/internal/core/tasks/utils.ts +13 -0
- package/src/internal/core/tasks/validations.ts +3 -3
- package/src/internal/using-hardhat2-plugin-errors.ts +8 -11
- package/src/types/solidity/build-system.ts +24 -5
- package/templates/hardhat-3/01-node-test-runner-viem/AGENTS.md +20 -0
- package/templates/hardhat-3/01-node-test-runner-viem/package.json +3 -3
- package/templates/hardhat-3/02-mocha-ethers/AGENTS.md +20 -0
- package/templates/hardhat-3/02-mocha-ethers/package.json +3 -3
- package/templates/hardhat-3/03-minimal/package.json +1 -1
|
@@ -5,10 +5,13 @@ import type {
|
|
|
5
5
|
FileBuildResult,
|
|
6
6
|
SolidityBuildSystem,
|
|
7
7
|
} from "../../../types/solidity/build-system.js";
|
|
8
|
+
import type { CompilationJob } from "../../../types/solidity/compilation-job.js";
|
|
9
|
+
import type { CompilerOutputError } from "../../../types/solidity/compiler-io.js";
|
|
8
10
|
import type {
|
|
9
11
|
Compiler,
|
|
10
12
|
CompilerInput,
|
|
11
13
|
CompilerOutput,
|
|
14
|
+
ResolvedBuildOptions,
|
|
12
15
|
} from "../../../types/solidity.js";
|
|
13
16
|
|
|
14
17
|
declare module "../../../types/config.js" {
|
|
@@ -359,6 +362,7 @@ declare module "../../../types/hooks.js" {
|
|
|
359
362
|
* @param compilerConfig The compiler configuration to get a compiler for.
|
|
360
363
|
* @param next A function to call the next handler for this hook.
|
|
361
364
|
* @returns A Compiler instance.
|
|
365
|
+
* @deprecated This hook will soon be removed.
|
|
362
366
|
*/
|
|
363
367
|
getCompiler: (
|
|
364
368
|
context: HookContext,
|
|
@@ -377,6 +381,8 @@ declare module "../../../types/hooks.js" {
|
|
|
377
381
|
* @param artifactPaths The file paths of artifacts that remain after cleanup.
|
|
378
382
|
* @param next A function to call the next handler for this hook, or the
|
|
379
383
|
* default implementation if no more handlers exist.
|
|
384
|
+
* @deprecated This hook will soon be removed. Use
|
|
385
|
+
* `processArtifactsAfterSuccessfulBuild` instead.
|
|
380
386
|
*/
|
|
381
387
|
onCleanUpArtifacts: (
|
|
382
388
|
context: HookContext,
|
|
@@ -402,6 +408,7 @@ declare module "../../../types/hooks.js" {
|
|
|
402
408
|
* default implementation if no more handlers exist.
|
|
403
409
|
*
|
|
404
410
|
* @returns The modified file content.
|
|
411
|
+
* @deprecated This hook will soon be removed.
|
|
405
412
|
*/
|
|
406
413
|
preprocessProjectFileBeforeBuilding(
|
|
407
414
|
context: HookContext,
|
|
@@ -427,6 +434,7 @@ declare module "../../../types/hooks.js" {
|
|
|
427
434
|
* default implementation if no more handlers exist.
|
|
428
435
|
*
|
|
429
436
|
* @returns The modified solc input.
|
|
437
|
+
* @deprecated This hook will soon be removed.
|
|
430
438
|
*/
|
|
431
439
|
preprocessSolcInputBeforeBuilding(
|
|
432
440
|
context: HookContext,
|
|
@@ -437,6 +445,9 @@ declare module "../../../types/hooks.js" {
|
|
|
437
445
|
) => Promise<CompilerInput>,
|
|
438
446
|
): Promise<CompilerInput>;
|
|
439
447
|
|
|
448
|
+
/**
|
|
449
|
+
* @deprecated This hook will soon be removed.
|
|
450
|
+
*/
|
|
440
451
|
readSourceFile: (
|
|
441
452
|
context: HookContext,
|
|
442
453
|
absolutePath: string,
|
|
@@ -482,6 +493,7 @@ declare module "../../../types/hooks.js" {
|
|
|
482
493
|
* @param solcConfig The compiler configuration (version, type, etc.).
|
|
483
494
|
* @param next A function to call the next handler for this hook, or the
|
|
484
495
|
* default implementation if no more handlers exist.
|
|
496
|
+
* @deprecated This hook will soon be removed. Use `getCompilationJobErrors` instead.
|
|
485
497
|
*/
|
|
486
498
|
invokeSolc(
|
|
487
499
|
context: HookContext,
|
|
@@ -510,6 +522,7 @@ declare module "../../../types/hooks.js" {
|
|
|
510
522
|
* @param next A function to get remappings from other sources (including default behavior).
|
|
511
523
|
* @returns An array of remapping sources, each containing an array of remapping strings
|
|
512
524
|
* and the source path they came from.
|
|
525
|
+
* @deprecated This hook will soon be removed.
|
|
513
526
|
*/
|
|
514
527
|
readNpmPackageRemappings: (
|
|
515
528
|
context: HookContext,
|
|
@@ -523,5 +536,61 @@ declare module "../../../types/hooks.js" {
|
|
|
523
536
|
nextPackagePath: string,
|
|
524
537
|
) => Promise<Array<{ remappings: string[]; source: string }>>,
|
|
525
538
|
) => Promise<Array<{ remappings: string[]; source: string }>>;
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Sequential hook run when a solidity build finished successfully,
|
|
542
|
+
* and the artifacts are ready to be processed by a plugin.
|
|
543
|
+
*
|
|
544
|
+
* This hook is only run for the "contracts" scope, and never includes
|
|
545
|
+
* test artifacts in its parameters.
|
|
546
|
+
*
|
|
547
|
+
* @param context The hook context.
|
|
548
|
+
* @param artifactPaths All the contract artifact paths, including
|
|
549
|
+
* pre-existing ones.
|
|
550
|
+
* @param buildRootFilePaths The root file paths provided to the build,
|
|
551
|
+
* as absolute paths or `npm:<package-name>/<file-path>` identifiers. In
|
|
552
|
+
* unified mode this may include test files.
|
|
553
|
+
* @param buildOptions The resolved options used during the build, with
|
|
554
|
+
* the build system's defaults filled in for any field the caller didn't
|
|
555
|
+
* provide.
|
|
556
|
+
*/
|
|
557
|
+
processArtifactsAfterSuccessfulBuild(
|
|
558
|
+
context: HookContext,
|
|
559
|
+
artifactPaths: readonly string[],
|
|
560
|
+
buildRootFilePaths: readonly string[],
|
|
561
|
+
buildOptions: Readonly<ResolvedBuildOptions>,
|
|
562
|
+
): Promise<void>;
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* A hook run to get and potentially process the errors of the compiler
|
|
566
|
+
* output after it was run by the build system.
|
|
567
|
+
*
|
|
568
|
+
* This hook allows plugin authors to process the compiler output errors
|
|
569
|
+
* list before it's used by the build system to report them to the users,
|
|
570
|
+
* but it doesn't let plugins alter the logic that determines if a
|
|
571
|
+
* compilation job succeeded or failed.
|
|
572
|
+
*
|
|
573
|
+
* This hook must not mutate the parameters passed to `next`. Doing so can
|
|
574
|
+
* have unexpected behavior, and will eventually crash Hardhat.
|
|
575
|
+
*
|
|
576
|
+
* The recommended way to use this hook is to call `next` first, and then
|
|
577
|
+
* work with the returned list.
|
|
578
|
+
*
|
|
579
|
+
* @param context The hook context.
|
|
580
|
+
* @param compilationJob The compilation job run by the build system.
|
|
581
|
+
* @param compilerOutput The output returned by the compiler.
|
|
582
|
+
* @param next A function to call the next handler for this hook.
|
|
583
|
+
* @returns The processed compiler output error list.
|
|
584
|
+
*/
|
|
585
|
+
getCompilationJobErrors(
|
|
586
|
+
context: HookContext,
|
|
587
|
+
compilationJob: Readonly<CompilationJob>,
|
|
588
|
+
compilerOutput: Readonly<CompilerOutput>,
|
|
589
|
+
next: (
|
|
590
|
+
nextContext: HookContext,
|
|
591
|
+
nextCompilationJob: Readonly<CompilationJob>,
|
|
592
|
+
nextCompilerOutput: Readonly<CompilerOutput>,
|
|
593
|
+
) => Promise<CompilerOutputError[]>,
|
|
594
|
+
): Promise<CompilerOutputError[]>;
|
|
526
595
|
}
|
|
527
596
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type * as ClassifierT from "./telemetry/error-classification/classifier.js";
|
|
2
|
+
|
|
1
3
|
import { styleText } from "node:util";
|
|
2
4
|
|
|
3
5
|
import {
|
|
@@ -6,19 +8,24 @@ import {
|
|
|
6
8
|
} from "@nomicfoundation/hardhat-errors";
|
|
7
9
|
|
|
8
10
|
import { HARDHAT_NAME, HARDHAT_WEBSITE_URL } from "../constants.js";
|
|
9
|
-
|
|
11
|
+
|
|
12
|
+
// The classifier may import many unrelated things top-level to do its job, so
|
|
13
|
+
// we load it lazily.
|
|
14
|
+
let classifierModule: typeof ClassifierT | undefined;
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
* The different categories of errors that can be handled by hardhat cli.
|
|
13
18
|
* Each category has a different way of being formatted and displayed.
|
|
14
19
|
* To add new categories, add a new entry to this enum and update the
|
|
15
|
-
* {@link getErrorWithCategory} and
|
|
16
|
-
* accordingly.
|
|
20
|
+
* {@link getErrorWithCategory} function and the switch inside
|
|
21
|
+
* {@link printErrorMessages} accordingly.
|
|
17
22
|
*/
|
|
18
23
|
enum ErrorCategory {
|
|
19
24
|
HARDHAT = "HARDHAT",
|
|
20
25
|
PLUGIN = "PLUGIN",
|
|
21
26
|
COMMUNITY_PLUGIN = "COMMUNITY_PLUGIN",
|
|
27
|
+
HH2_TO_HH3_MIGRATION = "HH2_TO_HH3_MIGRATION",
|
|
28
|
+
CJS_TO_ESM_MIGRATION = "CJS_TO_ESM_MIGRATION",
|
|
22
29
|
OTHER = "OTHER",
|
|
23
30
|
}
|
|
24
31
|
|
|
@@ -35,55 +42,91 @@ type ErrorWithCategory =
|
|
|
35
42
|
category: ErrorCategory.COMMUNITY_PLUGIN;
|
|
36
43
|
categorizedError: HardhatPluginError;
|
|
37
44
|
}
|
|
45
|
+
| {
|
|
46
|
+
category: ErrorCategory.HH2_TO_HH3_MIGRATION;
|
|
47
|
+
categorizedError: Error;
|
|
48
|
+
}
|
|
49
|
+
| {
|
|
50
|
+
category: ErrorCategory.CJS_TO_ESM_MIGRATION;
|
|
51
|
+
categorizedError: Error;
|
|
52
|
+
}
|
|
38
53
|
| {
|
|
39
54
|
category: ErrorCategory.OTHER;
|
|
40
55
|
categorizedError: unknown;
|
|
41
56
|
};
|
|
42
57
|
|
|
43
|
-
/**
|
|
44
|
-
* The different messages that can be displayed for each category of errors.
|
|
45
|
-
* - `formattedErrorMessage`: the main error message that is always displayed.
|
|
46
|
-
* - `showMoreInfoMessage`: an optional message that can be displayed to
|
|
47
|
-
* provide more information about the error. It is only displayed when stack
|
|
48
|
-
* traces are hidden.
|
|
49
|
-
* - `postErrorStackTraceMessage` an optional message that can be displayed
|
|
50
|
-
* after the stack trace. It is only displayed when stack traces are shown.
|
|
51
|
-
*/
|
|
52
|
-
interface ErrorMessages {
|
|
53
|
-
formattedErrorMessage: string;
|
|
54
|
-
showMoreInfoMessage?: string;
|
|
55
|
-
postErrorStackTraceMessage?: string;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
58
|
/**
|
|
59
59
|
* Formats and logs error messages based on the category the error belongs to.
|
|
60
60
|
*
|
|
61
|
+
* For each category, we produce up to three pieces:
|
|
62
|
+
* - `formattedErrorMessage`: the main error message that is always displayed.
|
|
63
|
+
* - `showMoreInfoMessage`: an optional message that points the user at more
|
|
64
|
+
* info. It is only displayed when the stack trace is hidden.
|
|
65
|
+
* - `postErrorStackTraceMessage`: an optional message that is displayed after
|
|
66
|
+
* the stack trace.
|
|
67
|
+
*
|
|
61
68
|
* @param error the error to handle. Supported categories are defined in
|
|
62
69
|
* {@link ErrorCategory}.
|
|
63
70
|
* @param shouldShowStackTraces whether to show stack traces or not. If true,
|
|
64
71
|
* the stack trace is always shown. If false, the stack trace is only shown for
|
|
65
|
-
* errors of category {@link ErrorCategory.OTHER}
|
|
72
|
+
* errors of category {@link ErrorCategory.OTHER},
|
|
73
|
+
* {@link ErrorCategory.HH2_TO_HH3_MIGRATION}, and
|
|
74
|
+
* {@link ErrorCategory.CJS_TO_ESM_MIGRATION}.
|
|
66
75
|
* @param print the function used to print the error message, defaults to
|
|
67
76
|
* `console.error`. Useful for testing to capture error messages.
|
|
68
77
|
*/
|
|
69
|
-
export function printErrorMessages(
|
|
78
|
+
export async function printErrorMessages(
|
|
70
79
|
error: Error,
|
|
71
80
|
shouldShowStackTraces: boolean = false,
|
|
72
81
|
print: (message: string | Error) => void = console.error,
|
|
73
|
-
): void {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
): Promise<void> {
|
|
83
|
+
const { category, categorizedError } = await getErrorWithCategory(error);
|
|
84
|
+
|
|
85
|
+
let formattedErrorMessage: string;
|
|
86
|
+
let showMoreInfoMessage: string | undefined;
|
|
87
|
+
let postErrorStackTraceMessage: string | undefined;
|
|
88
|
+
|
|
89
|
+
switch (category) {
|
|
90
|
+
case ErrorCategory.HARDHAT:
|
|
91
|
+
formattedErrorMessage = `${styleText(["red", "bold"], `Error ${categorizedError.errorCode}:`)} ${categorizedError.formattedMessage}`;
|
|
92
|
+
showMoreInfoMessage = `For more info go to ${HARDHAT_WEBSITE_URL}${categorizedError.errorCode} or run ${HARDHAT_NAME} with --show-stack-traces`;
|
|
93
|
+
break;
|
|
94
|
+
case ErrorCategory.PLUGIN:
|
|
95
|
+
formattedErrorMessage = `${styleText(["red", "bold"], `Error ${categorizedError.errorCode} in plugin ${categorizedError.pluginId}:`)} ${categorizedError.formattedMessage}`;
|
|
96
|
+
showMoreInfoMessage = `For more info go to ${HARDHAT_WEBSITE_URL}${categorizedError.errorCode} or run ${HARDHAT_NAME} with --show-stack-traces`;
|
|
97
|
+
break;
|
|
98
|
+
case ErrorCategory.COMMUNITY_PLUGIN:
|
|
99
|
+
formattedErrorMessage = `${styleText(["red", "bold"], `Error in community plugin ${categorizedError.pluginId}:`)} ${categorizedError.message}`;
|
|
100
|
+
showMoreInfoMessage = `For more info run ${HARDHAT_NAME} with --show-stack-traces`;
|
|
101
|
+
break;
|
|
102
|
+
case ErrorCategory.HH2_TO_HH3_MIGRATION:
|
|
103
|
+
formattedErrorMessage = styleText(
|
|
104
|
+
["red", "bold"],
|
|
105
|
+
`Hardhat 3 migration error:`,
|
|
106
|
+
);
|
|
107
|
+
postErrorStackTraceMessage = `It looks like you are migrating from Hardhat 2 to Hardhat 3. The following error often shows up during this kind of migration.\nPlease read https://hardhat.org/migrate-from-hardhat2 to learn how to migrate your project to Hardhat 3.`;
|
|
108
|
+
break;
|
|
109
|
+
case ErrorCategory.CJS_TO_ESM_MIGRATION:
|
|
110
|
+
formattedErrorMessage = styleText(
|
|
111
|
+
["red", "bold"],
|
|
112
|
+
`Hardhat 3 migration error:`,
|
|
113
|
+
);
|
|
114
|
+
postErrorStackTraceMessage = `It looks like you are migrating from CommonJS to ESM. The following error often shows up during this kind of migration.\nPlease read https://hardhat.org/migrate-to-esm to learn how to migrate your project to ESM.`;
|
|
115
|
+
break;
|
|
116
|
+
case ErrorCategory.OTHER:
|
|
117
|
+
formattedErrorMessage = styleText(
|
|
118
|
+
["red", "bold"],
|
|
119
|
+
`An unexpected error occurred:`,
|
|
120
|
+
);
|
|
121
|
+
postErrorStackTraceMessage = `If you think this is a bug in Hardhat, please report it here: ${HARDHAT_WEBSITE_URL}report-bug`;
|
|
122
|
+
break;
|
|
77
123
|
}
|
|
78
124
|
|
|
79
125
|
const showStackTraces =
|
|
80
126
|
shouldShowStackTraces ||
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
showMoreInfoMessage,
|
|
85
|
-
postErrorStackTraceMessage,
|
|
86
|
-
} = getErrorMessages(error);
|
|
127
|
+
category === ErrorCategory.OTHER ||
|
|
128
|
+
category === ErrorCategory.HH2_TO_HH3_MIGRATION ||
|
|
129
|
+
category === ErrorCategory.CJS_TO_ESM_MIGRATION;
|
|
87
130
|
|
|
88
131
|
print(formattedErrorMessage);
|
|
89
132
|
|
|
@@ -100,7 +143,7 @@ export function printErrorMessages(
|
|
|
100
143
|
}
|
|
101
144
|
}
|
|
102
145
|
|
|
103
|
-
function getErrorWithCategory(error: Error): ErrorWithCategory {
|
|
146
|
+
async function getErrorWithCategory(error: Error): Promise<ErrorWithCategory> {
|
|
104
147
|
if (HardhatError.isHardhatError(error)) {
|
|
105
148
|
if (error.pluginId === undefined) {
|
|
106
149
|
return {
|
|
@@ -122,49 +165,37 @@ function getErrorWithCategory(error: Error): ErrorWithCategory {
|
|
|
122
165
|
};
|
|
123
166
|
}
|
|
124
167
|
|
|
168
|
+
if (classifierModule === undefined) {
|
|
169
|
+
classifierModule = await import(
|
|
170
|
+
"./telemetry/error-classification/classifier.js"
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Pass `ignoreDevelopmentTimeFilter=true` so the migration footer also shows
|
|
175
|
+
// when hardhat is run from inside its own monorepo — the dev-time filter is
|
|
176
|
+
// a telemetry concern (don't report to Sentry), unrelated to display routing.
|
|
177
|
+
const classifierCategory = classifierModule.classifyError(error, true);
|
|
178
|
+
if (
|
|
179
|
+
classifierCategory ===
|
|
180
|
+
classifierModule.ErrorCategory.HH2_TO_HH3_MIGRATION_ERROR
|
|
181
|
+
) {
|
|
182
|
+
return {
|
|
183
|
+
category: ErrorCategory.HH2_TO_HH3_MIGRATION,
|
|
184
|
+
categorizedError: error,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (
|
|
188
|
+
classifierCategory ===
|
|
189
|
+
classifierModule.ErrorCategory.CJS_TO_ESM_MIGRATION_ERROR
|
|
190
|
+
) {
|
|
191
|
+
return {
|
|
192
|
+
category: ErrorCategory.CJS_TO_ESM_MIGRATION,
|
|
193
|
+
categorizedError: error,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
125
197
|
return {
|
|
126
198
|
category: ErrorCategory.OTHER,
|
|
127
199
|
categorizedError: error,
|
|
128
200
|
};
|
|
129
201
|
}
|
|
130
|
-
|
|
131
|
-
function getErrorMessages(error: Error): ErrorMessages {
|
|
132
|
-
const { category, categorizedError } = getErrorWithCategory(error);
|
|
133
|
-
switch (category) {
|
|
134
|
-
case ErrorCategory.HARDHAT:
|
|
135
|
-
return {
|
|
136
|
-
formattedErrorMessage: `${styleText(["red", "bold"], `Error ${categorizedError.errorCode}:`)} ${categorizedError.formattedMessage}`,
|
|
137
|
-
showMoreInfoMessage: `For more info go to ${HARDHAT_WEBSITE_URL}${categorizedError.errorCode} or run ${HARDHAT_NAME} with --show-stack-traces`,
|
|
138
|
-
};
|
|
139
|
-
case ErrorCategory.PLUGIN:
|
|
140
|
-
return {
|
|
141
|
-
formattedErrorMessage: `${styleText(["red", "bold"], `Error ${categorizedError.errorCode} in plugin ${categorizedError.pluginId}:`)} ${categorizedError.formattedMessage}`,
|
|
142
|
-
showMoreInfoMessage: `For more info go to ${HARDHAT_WEBSITE_URL}${categorizedError.errorCode} or run ${HARDHAT_NAME} with --show-stack-traces`,
|
|
143
|
-
};
|
|
144
|
-
case ErrorCategory.COMMUNITY_PLUGIN:
|
|
145
|
-
return {
|
|
146
|
-
formattedErrorMessage: `${styleText(["red", "bold"], `Error in community plugin ${categorizedError.pluginId}:`)} ${categorizedError.message}`,
|
|
147
|
-
showMoreInfoMessage: `For more info run ${HARDHAT_NAME} with --show-stack-traces`,
|
|
148
|
-
};
|
|
149
|
-
case ErrorCategory.OTHER:
|
|
150
|
-
return {
|
|
151
|
-
formattedErrorMessage: styleText(
|
|
152
|
-
["red", "bold"],
|
|
153
|
-
`An unexpected error occurred:`,
|
|
154
|
-
),
|
|
155
|
-
postErrorStackTraceMessage: `If you think this is a bug in Hardhat, please report it here: ${HARDHAT_WEBSITE_URL}report-bug`,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
function printUsingHardhat2Error(
|
|
160
|
-
error: UsingHardhat2PluginError,
|
|
161
|
-
print: (message: string | Error) => void = console.error,
|
|
162
|
-
): void {
|
|
163
|
-
print(styleText(["red", "bold"], `Hardhat 3 installation error:`));
|
|
164
|
-
print("");
|
|
165
|
-
if (error.callerRelativePath !== undefined) {
|
|
166
|
-
print(error.message);
|
|
167
|
-
} else {
|
|
168
|
-
print(error.stack);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
@@ -7,6 +7,8 @@ import type { Task } from "../../../types/tasks.js";
|
|
|
7
7
|
|
|
8
8
|
import { camelToKebabCase } from "@nomicfoundation/hardhat-utils/string";
|
|
9
9
|
|
|
10
|
+
import { isArgumentRequired } from "../../core/tasks/utils.js";
|
|
11
|
+
|
|
10
12
|
export const GLOBAL_NAME_PADDING = 6;
|
|
11
13
|
|
|
12
14
|
interface ArgumentDescriptor {
|
|
@@ -84,11 +86,16 @@ export function parseOptions(task: Task): {
|
|
|
84
86
|
});
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
for (const {
|
|
89
|
+
for (const {
|
|
90
|
+
name,
|
|
91
|
+
description,
|
|
92
|
+
defaultValue,
|
|
93
|
+
type,
|
|
94
|
+
} of task.positionalArguments) {
|
|
88
95
|
positionalArguments.push({
|
|
89
96
|
name,
|
|
90
97
|
description: trimFullStop(description),
|
|
91
|
-
isRequired: defaultValue
|
|
98
|
+
isRequired: isArgumentRequired(type, defaultValue),
|
|
92
99
|
...(defaultValue !== undefined && {
|
|
93
100
|
defaultValue: Array.isArray(defaultValue)
|
|
94
101
|
? defaultValue.join(", ")
|
|
@@ -14,11 +14,16 @@ import {
|
|
|
14
14
|
copy,
|
|
15
15
|
ensureDir,
|
|
16
16
|
exists,
|
|
17
|
+
getAllFilesMatching,
|
|
17
18
|
isDirectory,
|
|
18
19
|
mkdir,
|
|
19
20
|
readJsonFile,
|
|
21
|
+
remove,
|
|
22
|
+
symlink,
|
|
20
23
|
writeJsonFile,
|
|
24
|
+
writeUtf8File,
|
|
21
25
|
} from "@nomicfoundation/hardhat-utils/fs";
|
|
26
|
+
import { findClosestPackageRoot } from "@nomicfoundation/hardhat-utils/package";
|
|
22
27
|
import { resolveFromRoot } from "@nomicfoundation/hardhat-utils/path";
|
|
23
28
|
import * as semver from "semver";
|
|
24
29
|
|
|
@@ -35,6 +40,7 @@ import { sendErrorTelemetry } from "../telemetry/error-reporter/reporter.js";
|
|
|
35
40
|
import {
|
|
36
41
|
getDevDependenciesInstallationCommand,
|
|
37
42
|
getPackageManager,
|
|
43
|
+
getVersion,
|
|
38
44
|
installsPeerDependenciesByDefault,
|
|
39
45
|
} from "./package-manager.js";
|
|
40
46
|
import {
|
|
@@ -590,6 +596,10 @@ export async function copyProjectFiles(
|
|
|
590
596
|
await copy(absoluteTemplatePath, absoluteWorkspacePath);
|
|
591
597
|
}
|
|
592
598
|
|
|
599
|
+
await installSkills(workspace, template, force);
|
|
600
|
+
await createClaudeMd(workspace, force);
|
|
601
|
+
await createDotClaude(workspace);
|
|
602
|
+
|
|
593
603
|
console.log(`✨ ${styleText("cyan", `Template files copied`)} ✨`);
|
|
594
604
|
}
|
|
595
605
|
|
|
@@ -659,6 +669,133 @@ export async function copyProjectFilesNonInteractive(
|
|
|
659
669
|
absoluteWorkspacePath,
|
|
660
670
|
);
|
|
661
671
|
}
|
|
672
|
+
|
|
673
|
+
await installSkills(workspace, template);
|
|
674
|
+
await createClaudeMd(workspace);
|
|
675
|
+
await createDotClaude(workspace);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Skills published from `packages/hardhat/skills/` and the npm package whose presence
|
|
680
|
+
* in a template's dependencies opts that template into installing the skill.
|
|
681
|
+
* Each skill is tied to exactly one package so they can be versioned and
|
|
682
|
+
* upgraded independently.
|
|
683
|
+
*/
|
|
684
|
+
const SKILL_PACKAGES: ReadonlyArray<{
|
|
685
|
+
packageName: string;
|
|
686
|
+
skillName: string;
|
|
687
|
+
}> = [
|
|
688
|
+
{ packageName: "hardhat", skillName: "hardhat" },
|
|
689
|
+
{
|
|
690
|
+
packageName: "@nomicfoundation/hardhat-toolbox-viem",
|
|
691
|
+
skillName: "hardhat-toolbox-viem",
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
packageName: "@nomicfoundation/hardhat-toolbox-mocha-ethers",
|
|
695
|
+
skillName: "hardhat-toolbox-mocha-ethers",
|
|
696
|
+
},
|
|
697
|
+
];
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* For agent-aware templates (those that ship an `AGENTS.md`), copies any
|
|
701
|
+
* skills from `packages/hardhat/skills/` whose corresponding package is a template
|
|
702
|
+
* dependency into `<workspace>/.agents/skills/<skill-name>/`.
|
|
703
|
+
*/
|
|
704
|
+
async function installSkills(
|
|
705
|
+
workspace: string,
|
|
706
|
+
template: Template,
|
|
707
|
+
force?: boolean,
|
|
708
|
+
): Promise<void> {
|
|
709
|
+
if (!template.files.includes("AGENTS.md")) {
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const deps = {
|
|
714
|
+
...template.packageJson.dependencies,
|
|
715
|
+
...template.packageJson.devDependencies,
|
|
716
|
+
};
|
|
717
|
+
const relevantSkills = SKILL_PACKAGES.filter(
|
|
718
|
+
({ packageName }) => deps[packageName] !== undefined,
|
|
719
|
+
);
|
|
720
|
+
if (relevantSkills.length === 0) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const hardhatPackageRoot = await findClosestPackageRoot(import.meta.url);
|
|
725
|
+
const skillsRoot = path.join(hardhatPackageRoot, "skills");
|
|
726
|
+
|
|
727
|
+
for (const { skillName } of relevantSkills) {
|
|
728
|
+
const skillSrcDir = path.join(skillsRoot, skillName);
|
|
729
|
+
const skillDestDir = path.join(workspace, ".agents", "skills", skillName);
|
|
730
|
+
const skillFiles = await getAllFilesMatching(skillSrcDir);
|
|
731
|
+
for (const file of skillFiles) {
|
|
732
|
+
const dest = path.join(skillDestDir, path.relative(skillSrcDir, file));
|
|
733
|
+
if (force !== true && (await exists(dest))) {
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
await ensureDir(path.dirname(dest));
|
|
737
|
+
await copy(file, dest);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Creates a `CLAUDE.md` file if an `AGENTS.md` file exists. Uses a symlink
|
|
744
|
+
* except on Windows, where a file is created that references `AGENTS.md`.
|
|
745
|
+
* Overwrites an existing `CLAUDE.md` only if `force` is true.
|
|
746
|
+
*/
|
|
747
|
+
async function createClaudeMd(
|
|
748
|
+
workspace: string,
|
|
749
|
+
force?: boolean,
|
|
750
|
+
): Promise<void> {
|
|
751
|
+
const agentsMdPath = path.join(workspace, "AGENTS.md");
|
|
752
|
+
if (!(await exists(agentsMdPath))) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
const claudeMdPath = path.join(workspace, "CLAUDE.md");
|
|
757
|
+
if (await exists(claudeMdPath, { followSymlinks: false })) {
|
|
758
|
+
if (force !== true) {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
await remove(claudeMdPath);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (process.platform === "win32") {
|
|
765
|
+
await writeUtf8File(claudeMdPath, "@AGENTS.md\n");
|
|
766
|
+
} else {
|
|
767
|
+
await symlink("AGENTS.md", claudeMdPath);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Creates `.claude` if `.agents` exists. Uses a symlink except on Windows,
|
|
773
|
+
* where the whole directory is copied. Does nothing if `.claude` already
|
|
774
|
+
* exists; the `force` flag is intentionally not used here because
|
|
775
|
+
* overwriting depends on whether the existing `.claude` is a file, directory,
|
|
776
|
+
* or symlink, and on the OS, which is more complexity than is worthwhile.
|
|
777
|
+
*/
|
|
778
|
+
async function createDotClaude(workspace: string): Promise<void> {
|
|
779
|
+
const agentsDirPath = path.join(workspace, ".agents");
|
|
780
|
+
if (!(await exists(agentsDirPath))) {
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const claudeDirPath = path.join(workspace, ".claude");
|
|
785
|
+
if (await exists(claudeDirPath, { followSymlinks: false })) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (process.platform === "win32") {
|
|
790
|
+
const agentsFiles = await getAllFilesMatching(agentsDirPath);
|
|
791
|
+
for (const file of agentsFiles) {
|
|
792
|
+
const dest = path.join(claudeDirPath, path.relative(agentsDirPath, file));
|
|
793
|
+
await ensureDir(path.dirname(dest));
|
|
794
|
+
await copy(file, dest);
|
|
795
|
+
}
|
|
796
|
+
} else {
|
|
797
|
+
await symlink(".agents", claudeDirPath);
|
|
798
|
+
}
|
|
662
799
|
}
|
|
663
800
|
|
|
664
801
|
/**
|
|
@@ -694,6 +831,8 @@ export async function installProjectDependencies({
|
|
|
694
831
|
);
|
|
695
832
|
|
|
696
833
|
const packageManager = getPackageManager();
|
|
834
|
+
const packageManagerVersion = await getVersion(workspace, packageManager);
|
|
835
|
+
const packageManagerMajorVersion = packageManagerVersion?.[0];
|
|
697
836
|
|
|
698
837
|
// Find the template dev dependencies that are not already installed
|
|
699
838
|
const templateDependencies = template.packageJson.devDependencies ?? {};
|
|
@@ -730,6 +869,7 @@ export async function installProjectDependencies({
|
|
|
730
869
|
const command = getDevDependenciesInstallationCommand(
|
|
731
870
|
packageManager,
|
|
732
871
|
dependenciesToInstall,
|
|
872
|
+
packageManagerMajorVersion,
|
|
733
873
|
);
|
|
734
874
|
const commandString = command.join(" ");
|
|
735
875
|
|
|
@@ -788,6 +928,7 @@ export async function installProjectDependencies({
|
|
|
788
928
|
const command = getDevDependenciesInstallationCommand(
|
|
789
929
|
packageManager,
|
|
790
930
|
dependenciesToUpdate,
|
|
931
|
+
packageManagerMajorVersion,
|
|
791
932
|
);
|
|
792
933
|
const commandString = command.join(" ");
|
|
793
934
|
|
|
@@ -104,11 +104,16 @@ export function getPackageManager(): PackageManager {
|
|
|
104
104
|
*
|
|
105
105
|
* @param packageManager The package manager to use.
|
|
106
106
|
* @param dependencies The dependencies to install.
|
|
107
|
+
* @param packageManagerMajorVersion The major version of the package manager,
|
|
108
|
+
* if known. Used to opt into version-specific flags (e.g. pnpm 11's
|
|
109
|
+
* `--allow-build=esbuild`, required because `tsx` ships `esbuild` whose
|
|
110
|
+
* postinstall script is otherwise blocked).
|
|
107
111
|
* @returns The installation command.
|
|
108
112
|
*/
|
|
109
113
|
export function getDevDependenciesInstallationCommand(
|
|
110
114
|
packageManager: PackageManager,
|
|
111
115
|
dependencies: string[],
|
|
116
|
+
packageManagerMajorVersion?: number,
|
|
112
117
|
): string[] {
|
|
113
118
|
const packageManagerToCommand: Record<PackageManager, string[]> = {
|
|
114
119
|
npm: ["npm", "install", "--save-dev"],
|
|
@@ -118,6 +123,18 @@ export function getDevDependenciesInstallationCommand(
|
|
|
118
123
|
bun: ["bun", "add", "--dev"],
|
|
119
124
|
};
|
|
120
125
|
const command = packageManagerToCommand[packageManager];
|
|
126
|
+
|
|
127
|
+
// NOTE: adding an unnecessary flag doesn't result in an error
|
|
128
|
+
// nor warning, so we don't check if hardhat or tsx are being
|
|
129
|
+
// installed.
|
|
130
|
+
if (
|
|
131
|
+
packageManager === "pnpm" &&
|
|
132
|
+
packageManagerMajorVersion !== undefined &&
|
|
133
|
+
packageManagerMajorVersion >= 11
|
|
134
|
+
) {
|
|
135
|
+
command.push("--allow-build=esbuild");
|
|
136
|
+
}
|
|
137
|
+
|
|
121
138
|
// We quote all the dependency identifiers so that they can be run on a shell
|
|
122
139
|
// without semver symbols interfering with the command
|
|
123
140
|
command.push(
|
|
@@ -205,7 +222,7 @@ export async function installsPeerDependenciesByDefault(
|
|
|
205
222
|
}
|
|
206
223
|
}
|
|
207
224
|
|
|
208
|
-
async function getVersion(
|
|
225
|
+
export async function getVersion(
|
|
209
226
|
workspace: string,
|
|
210
227
|
packageManager: PackageManager,
|
|
211
228
|
version?: string,
|
package/src/internal/cli/main.ts
CHANGED
|
@@ -40,6 +40,7 @@ import { parseArgumentValue } from "../core/arguments.js";
|
|
|
40
40
|
import { buildGlobalOptionDefinitions } from "../core/global-options.js";
|
|
41
41
|
import { resolveProjectRoot } from "../core/hre.js";
|
|
42
42
|
import { resolvePluginList } from "../core/plugins/resolve-plugin-list.js";
|
|
43
|
+
import { isArgumentRequired } from "../core/tasks/utils.js";
|
|
43
44
|
import { setGlobalHardhatRuntimeEnvironment } from "../global-hre-instance.js";
|
|
44
45
|
import { createHardhatRuntimeEnvironment } from "../hre-initialization.js";
|
|
45
46
|
|
|
@@ -318,7 +319,7 @@ export async function main(
|
|
|
318
319
|
}
|
|
319
320
|
} catch (error) {
|
|
320
321
|
ensureError(error);
|
|
321
|
-
printErrorMessages(error, builtinGlobalOptions?.showStackTraces);
|
|
322
|
+
await printErrorMessages(error, builtinGlobalOptions?.showStackTraces);
|
|
322
323
|
|
|
323
324
|
try {
|
|
324
325
|
await sendErrorTelemetry(error);
|
|
@@ -767,8 +768,9 @@ function validateRequiredArguments(
|
|
|
767
768
|
taskArguments: TaskArguments,
|
|
768
769
|
) {
|
|
769
770
|
const missingRequiredArgument = argumentDefinitions.find(
|
|
770
|
-
({ defaultValue, name }) =>
|
|
771
|
-
defaultValue
|
|
771
|
+
({ defaultValue, name, type }) =>
|
|
772
|
+
isArgumentRequired(type, defaultValue) &&
|
|
773
|
+
taskArguments[name] === undefined,
|
|
772
774
|
);
|
|
773
775
|
|
|
774
776
|
if (missingRequiredArgument === undefined) {
|
|
@@ -70,7 +70,8 @@ import {
|
|
|
70
70
|
* @param error The error to classify.
|
|
71
71
|
* @param ignoreDevelopmentTimeFilter If true, the classifier will ignore the
|
|
72
72
|
* development-time filter, which is used to exclude errors that happen during
|
|
73
|
-
* development of Hardhat itself.
|
|
73
|
+
* development of Hardhat itself. Set this from display-side callers (where the
|
|
74
|
+
* dev-time skip is irrelevant) and from tests.
|
|
74
75
|
* @returns The error category.
|
|
75
76
|
*/
|
|
76
77
|
export function classifyError(
|