hardhat 3.0.6 → 3.0.8

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.
Files changed (127) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/src/internal/builtin-plugins/coverage/hook-handlers/solidity.d.ts.map +1 -1
  3. package/dist/src/internal/builtin-plugins/coverage/hook-handlers/solidity.js +1 -2
  4. package/dist/src/internal/builtin-plugins/coverage/hook-handlers/solidity.js.map +1 -1
  5. package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.d.ts +1 -0
  6. package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.d.ts.map +1 -1
  7. package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.js +9 -2
  8. package/dist/src/internal/builtin-plugins/network-manager/edr/edr-provider.js.map +1 -1
  9. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/hre.d.ts.map +1 -1
  10. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/hre.js +13 -5
  11. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/hre.js.map +1 -1
  12. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/network.js +1 -1
  13. package/dist/src/internal/builtin-plugins/network-manager/hook-handlers/network.js.map +1 -1
  14. package/dist/src/internal/builtin-plugins/network-manager/network-manager.d.ts +4 -3
  15. package/dist/src/internal/builtin-plugins/network-manager/network-manager.d.ts.map +1 -1
  16. package/dist/src/internal/builtin-plugins/network-manager/network-manager.js +79 -37
  17. package/dist/src/internal/builtin-plugins/network-manager/network-manager.js.map +1 -1
  18. package/dist/src/internal/builtin-plugins/network-manager/type-validation.js +3 -3
  19. package/dist/src/internal/builtin-plugins/network-manager/type-validation.js.map +1 -1
  20. package/dist/src/internal/builtin-plugins/node/artifacts/build-info-watcher.d.ts +25 -0
  21. package/dist/src/internal/builtin-plugins/node/artifacts/build-info-watcher.d.ts.map +1 -0
  22. package/dist/src/internal/builtin-plugins/node/artifacts/build-info-watcher.js +56 -0
  23. package/dist/src/internal/builtin-plugins/node/artifacts/build-info-watcher.js.map +1 -0
  24. package/dist/src/internal/builtin-plugins/node/helpers.d.ts +12 -0
  25. package/dist/src/internal/builtin-plugins/node/helpers.d.ts.map +1 -1
  26. package/dist/src/internal/builtin-plugins/node/helpers.js +32 -0
  27. package/dist/src/internal/builtin-plugins/node/helpers.js.map +1 -1
  28. package/dist/src/internal/builtin-plugins/node/json-rpc/server.d.ts +3 -10
  29. package/dist/src/internal/builtin-plugins/node/json-rpc/server.d.ts.map +1 -1
  30. package/dist/src/internal/builtin-plugins/node/json-rpc/server.js +1 -1
  31. package/dist/src/internal/builtin-plugins/node/json-rpc/server.js.map +1 -1
  32. package/dist/src/internal/builtin-plugins/node/task-action.d.ts.map +1 -1
  33. package/dist/src/internal/builtin-plugins/node/task-action.js +14 -4
  34. package/dist/src/internal/builtin-plugins/node/task-action.js.map +1 -1
  35. package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.d.ts +13 -5
  36. package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.d.ts.map +1 -1
  37. package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.js +126 -58
  38. package/dist/src/internal/builtin-plugins/solidity/build-system/build-system.js.map +1 -1
  39. package/dist/src/internal/builtin-plugins/solidity/build-system/cache.d.ts +1 -1
  40. package/dist/src/internal/builtin-plugins/solidity/build-system/cache.d.ts.map +1 -1
  41. package/dist/src/internal/builtin-plugins/solidity/hook-handlers/hre.d.ts.map +1 -1
  42. package/dist/src/internal/builtin-plugins/solidity/hook-handlers/hre.js +14 -6
  43. package/dist/src/internal/builtin-plugins/solidity/hook-handlers/hre.js.map +1 -1
  44. package/dist/src/internal/builtin-plugins/solidity/index.d.ts.map +1 -1
  45. package/dist/src/internal/builtin-plugins/solidity/index.js +8 -0
  46. package/dist/src/internal/builtin-plugins/solidity/index.js.map +1 -1
  47. package/dist/src/internal/builtin-plugins/solidity/tasks/build.d.ts +4 -2
  48. package/dist/src/internal/builtin-plugins/solidity/tasks/build.d.ts.map +1 -1
  49. package/dist/src/internal/builtin-plugins/solidity/tasks/build.js +37 -10
  50. package/dist/src/internal/builtin-plugins/solidity/tasks/build.js.map +1 -1
  51. package/dist/src/internal/builtin-plugins/solidity-test/config.d.ts +4 -2
  52. package/dist/src/internal/builtin-plugins/solidity-test/config.d.ts.map +1 -1
  53. package/dist/src/internal/builtin-plugins/solidity-test/config.js +24 -4
  54. package/dist/src/internal/builtin-plugins/solidity-test/config.js.map +1 -1
  55. package/dist/src/internal/builtin-plugins/solidity-test/helpers.d.ts +1 -1
  56. package/dist/src/internal/builtin-plugins/solidity-test/helpers.d.ts.map +1 -1
  57. package/dist/src/internal/builtin-plugins/solidity-test/helpers.js +12 -3
  58. package/dist/src/internal/builtin-plugins/solidity-test/helpers.js.map +1 -1
  59. package/dist/src/internal/builtin-plugins/solidity-test/hook-handlers/config.d.ts.map +1 -1
  60. package/dist/src/internal/builtin-plugins/solidity-test/hook-handlers/config.js +1 -1
  61. package/dist/src/internal/builtin-plugins/solidity-test/hook-handlers/config.js.map +1 -1
  62. package/dist/src/internal/builtin-plugins/solidity-test/task-action.d.ts.map +1 -1
  63. package/dist/src/internal/builtin-plugins/solidity-test/task-action.js +16 -39
  64. package/dist/src/internal/builtin-plugins/solidity-test/task-action.js.map +1 -1
  65. package/dist/src/internal/builtin-plugins/solidity-test/type-extensions.d.ts +17 -7
  66. package/dist/src/internal/builtin-plugins/solidity-test/type-extensions.d.ts.map +1 -1
  67. package/dist/src/internal/core/hook-manager.d.ts.map +1 -1
  68. package/dist/src/internal/core/hook-manager.js +3 -1
  69. package/dist/src/internal/core/hook-manager.js.map +1 -1
  70. package/dist/src/internal/core/hre.d.ts +29 -1
  71. package/dist/src/internal/core/hre.d.ts.map +1 -1
  72. package/dist/src/internal/core/hre.js +57 -20
  73. package/dist/src/internal/core/hre.js.map +1 -1
  74. package/dist/src/internal/core/plugins/detect-plugin-npm-dependency-problems.d.ts +2 -1
  75. package/dist/src/internal/core/plugins/detect-plugin-npm-dependency-problems.d.ts.map +1 -1
  76. package/dist/src/internal/core/plugins/detect-plugin-npm-dependency-problems.js +5 -4
  77. package/dist/src/internal/core/plugins/detect-plugin-npm-dependency-problems.js.map +1 -1
  78. package/dist/src/internal/core/plugins/resolve-plugin-list.js +41 -4
  79. package/dist/src/internal/core/plugins/resolve-plugin-list.js.map +1 -1
  80. package/dist/src/internal/core/tasks/resolved-task.d.ts.map +1 -1
  81. package/dist/src/internal/core/tasks/resolved-task.js +1 -1
  82. package/dist/src/internal/core/tasks/resolved-task.js.map +1 -1
  83. package/dist/src/internal/utils/package.d.ts +1 -0
  84. package/dist/src/internal/utils/package.d.ts.map +1 -1
  85. package/dist/src/internal/utils/package.js +17 -1
  86. package/dist/src/internal/utils/package.js.map +1 -1
  87. package/dist/src/types/hre.d.ts +4 -0
  88. package/dist/src/types/hre.d.ts.map +1 -1
  89. package/dist/src/types/network.d.ts +40 -0
  90. package/dist/src/types/network.d.ts.map +1 -1
  91. package/dist/src/types/plugins.d.ts +8 -0
  92. package/dist/src/types/plugins.d.ts.map +1 -1
  93. package/dist/src/types/solidity/build-system.d.ts +23 -8
  94. package/dist/src/types/solidity/build-system.d.ts.map +1 -1
  95. package/package.json +5 -4
  96. package/src/internal/builtin-plugins/coverage/hook-handlers/solidity.ts +2 -3
  97. package/src/internal/builtin-plugins/network-manager/edr/edr-provider.ts +20 -1
  98. package/src/internal/builtin-plugins/network-manager/hook-handlers/hre.ts +36 -18
  99. package/src/internal/builtin-plugins/network-manager/hook-handlers/network.ts +1 -1
  100. package/src/internal/builtin-plugins/network-manager/network-manager.ts +137 -60
  101. package/src/internal/builtin-plugins/network-manager/type-validation.ts +3 -3
  102. package/src/internal/builtin-plugins/node/artifacts/build-info-watcher.ts +82 -0
  103. package/src/internal/builtin-plugins/node/helpers.ts +64 -0
  104. package/src/internal/builtin-plugins/node/json-rpc/server.ts +3 -10
  105. package/src/internal/builtin-plugins/node/task-action.ts +31 -5
  106. package/src/internal/builtin-plugins/solidity/build-system/build-system.ts +203 -98
  107. package/src/internal/builtin-plugins/solidity/build-system/cache.ts +1 -1
  108. package/src/internal/builtin-plugins/solidity/hook-handlers/hre.ts +22 -5
  109. package/src/internal/builtin-plugins/solidity/index.ts +8 -0
  110. package/src/internal/builtin-plugins/solidity/tasks/build.ts +59 -16
  111. package/src/internal/builtin-plugins/solidity-test/config.ts +46 -3
  112. package/src/internal/builtin-plugins/solidity-test/helpers.ts +17 -4
  113. package/src/internal/builtin-plugins/solidity-test/hook-handlers/config.ts +5 -1
  114. package/src/internal/builtin-plugins/solidity-test/task-action.ts +21 -48
  115. package/src/internal/builtin-plugins/solidity-test/type-extensions.ts +23 -9
  116. package/src/internal/core/hook-manager.ts +9 -1
  117. package/src/internal/core/hre.ts +102 -32
  118. package/src/internal/core/plugins/detect-plugin-npm-dependency-problems.ts +5 -0
  119. package/src/internal/core/plugins/resolve-plugin-list.ts +58 -4
  120. package/src/internal/core/tasks/resolved-task.ts +1 -0
  121. package/src/internal/utils/package.ts +31 -1
  122. package/src/types/hre.ts +4 -0
  123. package/src/types/network.ts +45 -0
  124. package/src/types/plugins.ts +5 -0
  125. package/src/types/solidity/build-system.ts +23 -7
  126. package/templates/hardhat-3/01-node-test-runner-viem/package.json +3 -3
  127. package/templates/hardhat-3/02-mocha-ethers/package.json +5 -5
@@ -1,19 +1,30 @@
1
1
  import type { EdrNetworkConfigOverride } from "../../../types/config.js";
2
2
  import type { NewTaskActionFunction } from "../../../types/tasks.js";
3
3
 
4
+ import path from "node:path";
5
+
4
6
  import {
5
7
  assertHardhatInvariant,
6
8
  HardhatError,
7
9
  } from "@nomicfoundation/hardhat-errors";
8
- import { exists } from "@nomicfoundation/hardhat-utils/fs";
10
+ import { ensureDir, exists } from "@nomicfoundation/hardhat-utils/fs";
9
11
  import chalk from "chalk";
12
+ import debug from "debug";
10
13
 
11
14
  import { DEFAULT_NETWORK_NAME } from "../../constants.js";
12
15
  import { isSupportedChainType } from "../../edr/chain-type.js";
16
+ import { BUILD_INFO_DIR_NAME } from "../artifacts/artifact-manager.js";
17
+ import { EdrProvider } from "../network-manager/edr/edr-provider.js";
13
18
 
14
- import { formatEdrNetworkConfigAccounts } from "./helpers.js";
19
+ import { watchBuildInfo } from "./artifacts/build-info-watcher.js";
20
+ import {
21
+ createBuildInfoUploadHandlerFrom,
22
+ formatEdrNetworkConfigAccounts,
23
+ } from "./helpers.js";
15
24
  import { JsonRpcServerImplementation } from "./json-rpc/server.js";
16
25
 
26
+ const log = debug("hardhat:core:tasks:node");
27
+
17
28
  interface NodeActionArguments {
18
29
  hostname?: string;
19
30
  port: number;
@@ -94,6 +105,11 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
94
105
  override: networkConfigOverride,
95
106
  });
96
107
 
108
+ assertHardhatInvariant(
109
+ provider instanceof EdrProvider,
110
+ "Provider must be EdrProvider, since only edr networks are supported",
111
+ );
112
+
97
113
  // NOTE: We enable logging for the node
98
114
  await provider.request({
99
115
  method: "hardhat_setLoggingEnabled",
@@ -112,7 +128,7 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
112
128
  }
113
129
  }
114
130
 
115
- const server: JsonRpcServerImplementation = new JsonRpcServerImplementation({
131
+ const server = new JsonRpcServerImplementation({
116
132
  hostname,
117
133
  port: args.port,
118
134
  provider,
@@ -128,7 +144,16 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
128
144
 
129
145
  console.log();
130
146
 
131
- // TODO(https://github.com/NomicFoundation/hardhat/issues/6040): Add build info watcher here
147
+ const buildInfoDirPath = path.join(
148
+ hre.config.paths.artifacts,
149
+ BUILD_INFO_DIR_NAME,
150
+ );
151
+ await ensureDir(buildInfoDirPath);
152
+
153
+ const buildInfoWatcher = await watchBuildInfo(
154
+ buildInfoDirPath,
155
+ createBuildInfoUploadHandlerFrom(buildInfoDirPath, provider, log),
156
+ );
132
157
 
133
158
  // NOTE: Before creating the node, we check if the input network config is of type edr.
134
159
  // We only proceed if it is. Hence, we can assume that the output network config is of type edr as well.
@@ -139,7 +164,8 @@ const nodeAction: NewTaskActionFunction<NodeActionArguments> = async (
139
164
 
140
165
  console.log(await formatEdrNetworkConfigAccounts(networkConfig.accounts));
141
166
 
142
- await server.waitUntilClosed();
167
+ await server.afterClosed();
168
+ await buildInfoWatcher.close();
143
169
  };
144
170
 
145
171
  export default nodeAction;
@@ -15,6 +15,7 @@ import type {
15
15
  GetCompilationJobsResult,
16
16
  EmitArtifactsResult,
17
17
  RunCompilationJobResult,
18
+ BuildScope,
18
19
  } from "../../../../types/solidity/build-system.js";
19
20
  import type { CompilationJob } from "../../../../types/solidity/compilation-job.js";
20
21
  import type {
@@ -32,8 +33,10 @@ import {
32
33
  } from "@nomicfoundation/hardhat-errors";
33
34
  import {
34
35
  exists,
36
+ ensureDir,
35
37
  getAllDirectoriesMatching,
36
38
  getAllFilesMatching,
39
+ move,
37
40
  readJsonFile,
38
41
  remove,
39
42
  writeJsonFile,
@@ -41,6 +44,7 @@ import {
41
44
  writeUtf8File,
42
45
  } from "@nomicfoundation/hardhat-utils/fs";
43
46
  import { shortenPath } from "@nomicfoundation/hardhat-utils/path";
47
+ import { createSpinner } from "@nomicfoundation/hardhat-utils/spinner";
44
48
  import { pluralize } from "@nomicfoundation/hardhat-utils/string";
45
49
  import chalk from "chalk";
46
50
  import debug from "debug";
@@ -91,7 +95,6 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
91
95
  readonly #hooks: HookManager;
92
96
  readonly #options: SolidityBuildSystemOptions;
93
97
  #compileCache: CompileCache = {};
94
- readonly #defaultConcurrency = Math.max(os.cpus().length - 1, 1);
95
98
  #downloadedCompilers = false;
96
99
 
97
100
  constructor(hooks: HookManager, options: SolidityBuildSystemOptions) {
@@ -99,37 +102,90 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
99
102
  this.#options = options;
100
103
  }
101
104
 
102
- public async getRootFilePaths(): Promise<string[]> {
103
- const localFilesToCompile = (
104
- await Promise.all(
105
- this.#options.soliditySourcesPaths.map((dir) =>
106
- getAllFilesMatching(
107
- dir,
108
- (f) => f.endsWith(".sol") && !f.endsWith(".t.sol"),
109
- ),
110
- ),
111
- )
112
- ).flat(1);
105
+ public async getScope(fsPath: string): Promise<BuildScope> {
106
+ if (
107
+ fsPath.startsWith(this.#options.solidityTestsPath) &&
108
+ fsPath.endsWith(".sol")
109
+ ) {
110
+ return "tests";
111
+ }
113
112
 
114
- const npmFilesToBuild = this.#options.solidityConfig.npmFilesToBuild.map(
115
- npmModuleToNpmRootPath,
116
- );
113
+ for (const sourcesPath of this.#options.soliditySourcesPaths) {
114
+ if (fsPath.startsWith(sourcesPath) && fsPath.endsWith(".t.sol")) {
115
+ return "tests";
116
+ }
117
+ }
118
+
119
+ return "contracts";
120
+ }
117
121
 
118
- return [...localFilesToCompile, ...npmFilesToBuild];
122
+ public async getRootFilePaths(
123
+ options: { scope?: BuildScope } = {},
124
+ ): Promise<string[]> {
125
+ const scope = options.scope ?? "contracts";
126
+
127
+ switch (scope) {
128
+ case "contracts":
129
+ const localFilesToCompile = (
130
+ await Promise.all(
131
+ this.#options.soliditySourcesPaths.map((dir) =>
132
+ getAllFilesMatching(
133
+ dir,
134
+ (f) => f.endsWith(".sol") && !f.endsWith(".t.sol"),
135
+ ),
136
+ ),
137
+ )
138
+ ).flat(1);
139
+
140
+ const npmFilesToBuild =
141
+ this.#options.solidityConfig.npmFilesToBuild.map(
142
+ npmModuleToNpmRootPath,
143
+ );
144
+
145
+ return [...localFilesToCompile, ...npmFilesToBuild];
146
+ case "tests":
147
+ let rootFilePaths = (
148
+ await Promise.all([
149
+ getAllFilesMatching(this.#options.solidityTestsPath, (f) =>
150
+ f.endsWith(".sol"),
151
+ ),
152
+ ...this.#options.soliditySourcesPaths.map(async (dir) => {
153
+ return getAllFilesMatching(dir, (f) => f.endsWith(".t.sol"));
154
+ }),
155
+ ])
156
+ ).flat(1);
157
+
158
+ // NOTE: We remove duplicates in case there is an intersection between
159
+ // the tests.solidity paths and the sources paths
160
+ rootFilePaths = Array.from(new Set(rootFilePaths));
161
+ return rootFilePaths;
162
+ }
119
163
  }
120
164
 
121
165
  public async build(
122
166
  rootFilePaths: string[],
123
- options?: BuildOptions,
167
+ _options?: BuildOptions,
124
168
  ): Promise<CompilationJobCreationError | Map<string, FileBuildResult>> {
125
- if (options?.quiet !== true) {
126
- console.log("Compiling your Solidity contracts...");
127
- }
169
+ const options: Required<BuildOptions> = {
170
+ buildProfile: DEFAULT_BUILD_PROFILE,
171
+ concurrency: Math.max(os.cpus().length - 1, 1),
172
+ force: false,
173
+ isolated: false,
174
+ quiet: false,
175
+ scope: "contracts",
176
+ ..._options,
177
+ };
128
178
 
129
- await this.#downloadConfiguredCompilers(options?.quiet);
179
+ const spinner = createSpinner({
180
+ text: `Compiling your Solidity ${options.scope}...`,
181
+ enabled: !options.quiet,
182
+ });
183
+
184
+ await this.#downloadConfiguredCompilers(options.quiet);
185
+
186
+ spinner.start();
130
187
 
131
- const buildProfileName = options?.buildProfile ?? DEFAULT_BUILD_PROFILE;
132
- const { buildProfile } = this.#getBuildProfile(buildProfileName);
188
+ const { buildProfile } = this.#getBuildProfile(options.buildProfile);
133
189
 
134
190
  const compilationJobsResult = await this.getCompilationJobs(
135
191
  rootFilePaths,
@@ -137,6 +193,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
137
193
  );
138
194
 
139
195
  if ("reason" in compilationJobsResult) {
196
+ spinner.stop();
140
197
  return compilationJobsResult;
141
198
  }
142
199
 
@@ -171,7 +228,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
171
228
  };
172
229
  },
173
230
  {
174
- concurrency: options?.concurrency ?? this.#defaultConcurrency,
231
+ concurrency: options.concurrency,
175
232
  // An error when running the compiler is not a compilation failure, but
176
233
  // a fatal failure trying to run it, so we just throw on the first error
177
234
  stopOnError: true,
@@ -198,6 +255,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
198
255
  const emitArtifactsResult = await this.emitArtifacts(
199
256
  compilationResult.compilationJob,
200
257
  compilationResult.compilerOutput,
258
+ options,
201
259
  );
202
260
 
203
261
  const { artifactsPerFile } = emitArtifactsResult;
@@ -213,6 +271,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
213
271
  compilationResult,
214
272
  emitArtifactsResult,
215
273
  buildProfile.isolated,
274
+ options.scope,
216
275
  );
217
276
  }),
218
277
  );
@@ -222,6 +281,8 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
222
281
 
223
282
  const resultsMap: Map<string, FileBuildResult> = new Map();
224
283
 
284
+ spinner.stop();
285
+
225
286
  for (const result of results) {
226
287
  const contractArtifactsGenerated = isSuccessfulBuild
227
288
  ? contractArtifactsGeneratedByCompilationJob.get(result.compilationJob)
@@ -239,7 +300,6 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
239
300
  );
240
301
 
241
302
  this.#printSolcErrorsAndWarnings(errors);
242
-
243
303
  const successfulResult = !this.#hasCompilationErrors(
244
304
  result.compilerOutput,
245
305
  );
@@ -279,7 +339,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
279
339
  }
280
340
  }
281
341
 
282
- if (options?.quiet !== true) {
342
+ if (!options.quiet) {
283
343
  if (isSuccessfulBuild) {
284
344
  await this.#printCompilationResult(runnableCompilationJobs);
285
345
  }
@@ -426,6 +486,11 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
426
486
  buildInfoOutputPath,
427
487
  typeFilePath,
428
488
  ]) {
489
+ // Type declaration file can be undefined (e.g. for solidity tests)
490
+ if (outputFilePath === undefined) {
491
+ continue;
492
+ }
493
+
429
494
  if (!(await exists(outputFilePath))) {
430
495
  rootFilesToCompile.add(rootFile);
431
496
  break;
@@ -603,18 +668,23 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
603
668
  public async emitArtifacts(
604
669
  runnableCompilationJob: CompilationJob,
605
670
  compilerOutput: CompilerOutput,
671
+ options: { scope?: BuildScope } = {},
606
672
  ): Promise<EmitArtifactsResult> {
673
+ const scope = options.scope ?? "contracts";
674
+
607
675
  const artifactsPerFile = new Map<string, string[]>();
608
676
  const typeFilePaths = new Map<string, string>();
609
677
  const buildId = await runnableCompilationJob.getBuildId();
610
678
 
679
+ const artifactsDirectory = await this.getArtifactsDirectory(scope);
680
+
611
681
  // We emit the artifacts for each root file, first emitting one artifact
612
682
  // for each contract, and then one declaration file for the entire file,
613
683
  // which defines their types and augments the ArtifactMap type.
614
684
  for (const [userSourceName, root] of runnableCompilationJob.dependencyGraph
615
685
  .getRoots()
616
686
  .entries()) {
617
- const fileFolder = path.join(this.#options.artifactsPath, userSourceName);
687
+ const fileFolder = path.join(artifactsDirectory, userSourceName);
618
688
 
619
689
  // If the folder exists, we remove it first, as we don't want to leave
620
690
  // any old artifacts there.
@@ -652,45 +722,60 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
652
722
 
653
723
  artifactsPerFile.set(userSourceName, paths);
654
724
 
655
- const artifactsDeclarationFilePath = path.join(
656
- fileFolder,
657
- "artifacts.d.ts",
658
- );
659
- typeFilePaths.set(userSourceName, artifactsDeclarationFilePath);
725
+ // Write the type declaration file, only for contracts
726
+ if (scope === "contracts") {
727
+ const artifactsDeclarationFilePath = path.join(
728
+ fileFolder,
729
+ "artifacts.d.ts",
730
+ );
731
+ typeFilePaths.set(userSourceName, artifactsDeclarationFilePath);
660
732
 
661
- const artifactsDeclarationFile = getArtifactsDeclarationFile(artifacts);
733
+ const artifactsDeclarationFile = getArtifactsDeclarationFile(artifacts);
662
734
 
663
- await writeUtf8File(
664
- artifactsDeclarationFilePath,
665
- artifactsDeclarationFile,
666
- );
735
+ await writeUtf8File(
736
+ artifactsDeclarationFilePath,
737
+ artifactsDeclarationFile,
738
+ );
739
+ }
667
740
  }
668
741
 
669
742
  // Once we have emitted all the contract artifacts and its declaration
670
743
  // file, we emit the build info file and its output file.
671
744
  const buildInfoId = buildId;
672
745
 
673
- const buildInfoPath = path.join(
674
- this.#options.artifactsPath,
746
+ const buildInfoCacheDirPath = path.join(
747
+ this.#options.cachePath,
675
748
  `build-info`,
749
+ );
750
+
751
+ await ensureDir(buildInfoCacheDirPath);
752
+
753
+ const buildInfoCachePath = path.join(
754
+ buildInfoCacheDirPath,
676
755
  `${buildInfoId}.json`,
677
756
  );
678
757
 
679
- const buildInfoOutputPath = path.join(
680
- this.#options.artifactsPath,
681
- `build-info`,
758
+ const buildInfoOutputCachePath = path.join(
759
+ buildInfoCacheDirPath,
682
760
  `${buildInfoId}.output.json`,
683
761
  );
684
762
 
685
763
  // BuildInfo and BuildInfoOutput files are large, so we write them
686
764
  // concurrently, and keep their lifetimes separated and small.
765
+ // NOTE: First, we write the build info file and its output to the cache
766
+ // directory. Once both are successfully written, we move them to the
767
+ // artifacts directory sequentially, ensuring the build info file is moved
768
+ // last. This approach minimizes the risk of having corrupted build info
769
+ // files in the artifacts directory and ensures other processes, like
770
+ // `hardhat node`, can safely monitor the build info file as an indicator
771
+ // for build completion.
687
772
  await Promise.all([
688
773
  (async () => {
689
774
  const buildInfo = await getBuildInfo(runnableCompilationJob);
690
775
 
691
776
  // TODO: Maybe formatting the build info is slow, but it's mostly
692
777
  // strings, so it probably shouldn't be a problem.
693
- await writeJsonFile(buildInfoPath, buildInfo);
778
+ await writeJsonFile(buildInfoCachePath, buildInfo);
694
779
  })(),
695
780
  (async () => {
696
781
  const buildInfoOutput = await getBuildInfoOutput(
@@ -703,10 +788,24 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
703
788
  // TODO: Earlier in the build process, very similar files are created on disk by the
704
789
  // Compiler. Instead of creating them again, we should consider copying/moving them.
705
790
  // This would require changing the format of the build info output file.
706
- await writeJsonFileAsStream(buildInfoOutputPath, buildInfoOutput);
791
+ await writeJsonFileAsStream(buildInfoOutputCachePath, buildInfoOutput);
707
792
  })(),
708
793
  ]);
709
794
 
795
+ const buildInfoDirPath = path.join(artifactsDirectory, `build-info`);
796
+
797
+ await ensureDir(buildInfoDirPath);
798
+
799
+ const buildInfoPath = path.join(buildInfoDirPath, `${buildInfoId}.json`);
800
+
801
+ const buildInfoOutputPath = path.join(
802
+ buildInfoDirPath,
803
+ `${buildInfoId}.output.json`,
804
+ );
805
+
806
+ await move(buildInfoOutputCachePath, buildInfoOutputPath);
807
+ await move(buildInfoCachePath, buildInfoPath);
808
+
710
809
  return {
711
810
  artifactsPerFile,
712
811
  buildInfoPath,
@@ -715,55 +814,54 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
715
814
  };
716
815
  }
717
816
 
718
- public async cleanupArtifacts(rootFilePaths: string[]): Promise<void> {
817
+ public async getArtifactsDirectory(scope: BuildScope): Promise<string> {
818
+ return scope === "contracts"
819
+ ? this.#options.artifactsPath
820
+ : path.join(this.#options.cachePath, "test-artifacts");
821
+ }
822
+
823
+ public async cleanupArtifacts(
824
+ rootFilePaths: string[],
825
+ options: { scope?: BuildScope } = {},
826
+ ): Promise<void> {
719
827
  log(`Cleaning up artifacts`);
720
828
 
829
+ const scope = options.scope ?? "contracts";
830
+ const artifactsDirectory = await this.getArtifactsDirectory(scope);
831
+
721
832
  const userSourceNames = rootFilePaths.map((rootFilePath) => {
722
833
  const parsed = parseRootPath(rootFilePath);
723
834
  return isNpmParsedRootPath(parsed)
724
835
  ? parsed.npmPath
725
- : path.relative(this.#options.projectRoot, parsed.fsPath);
836
+ : toForwardSlash(
837
+ path.relative(this.#options.projectRoot, parsed.fsPath),
838
+ );
726
839
  });
727
840
 
728
841
  const userSourceNamesSet = new Set(userSourceNames);
729
842
 
730
843
  for (const file of await getAllDirectoriesMatching(
731
- this.#options.artifactsPath,
844
+ artifactsDirectory,
732
845
  (d) => d.endsWith(".sol"),
733
846
  )) {
734
- const relativePath = path.relative(this.#options.artifactsPath, file);
735
-
736
- const testDirectorySubpath = path.relative(
737
- this.#options.projectRoot,
738
- this.#options.solidityTestsPath,
739
- );
740
- const hasTestFileExtension = file.endsWith(".t.sol");
741
- const isInsideTestFolder = relativePath.startsWith(
742
- testDirectorySubpath + path.sep,
847
+ const relativePath = toForwardSlash(
848
+ path.relative(artifactsDirectory, file),
743
849
  );
744
850
 
745
- // Skip test artifacts, since our full compilation doesn't include them, they would incorrectly be marked for deletion
746
- if (hasTestFileExtension || isInsideTestFolder) {
747
- continue;
748
- }
749
-
750
851
  if (!userSourceNamesSet.has(relativePath)) {
751
852
  await remove(file);
752
853
  }
753
854
  }
754
855
 
755
- const buildInfosDir = path.join(this.#options.artifactsPath, `build-info`);
856
+ const buildInfosDir = path.join(artifactsDirectory, `build-info`);
756
857
 
757
858
  // TODO: This logic is duplicated with respect to the artifacts manager
758
859
  const artifactPaths = await getAllFilesMatching(
759
- this.#options.artifactsPath,
860
+ artifactsDirectory,
760
861
  (p) =>
761
862
  p.endsWith(".json") && // Only consider json files
762
863
  // Ignore top level json files
763
- p.indexOf(
764
- path.sep,
765
- this.#options.artifactsPath.length + path.sep.length,
766
- ) !== -1,
864
+ p.indexOf(path.sep, artifactsDirectory.length + path.sep.length) !== -1,
767
865
  (dir) => dir !== buildInfosDir,
768
866
  );
769
867
 
@@ -793,40 +891,41 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
793
891
  }
794
892
  }
795
893
 
796
- // Get duplicated contract names
797
- const artifactNameCounts = new Map<string, number>();
798
- for (const artifactPath of artifactPaths) {
799
- const basename = path.basename(artifactPath);
800
- const name = basename.substring(0, basename.indexOf("."));
894
+ // These steps only apply when compiling contracts
895
+ if (scope === "contracts") {
896
+ // Get duplicated contract names and write a top-level artifacts.d.ts file
897
+ const artifactNameCounts = new Map<string, number>();
898
+ for (const artifactPath of artifactPaths) {
899
+ const basename = path.basename(artifactPath);
900
+ const name = basename.substring(0, basename.indexOf("."));
801
901
 
802
- let count = artifactNameCounts.get(name);
803
- if (count === undefined) {
804
- count = 0;
805
- }
902
+ const count = artifactNameCounts.get(name) ?? 0;
806
903
 
807
- artifactNameCounts.set(name, count + 1);
808
- }
904
+ artifactNameCounts.set(name, count + 1);
905
+ }
809
906
 
810
- const duplicatedNames = [...artifactNameCounts.entries()]
811
- .filter(([_, count]) => count > 1)
812
- .map(([name, _]) => name);
907
+ const duplicatedNames = [...artifactNameCounts.entries()]
908
+ .filter(([_, count]) => count > 1)
909
+ .map(([name, _]) => name);
813
910
 
814
- const duplicatedContractNamesDeclarationFilePath = path.join(
815
- this.#options.artifactsPath,
816
- "artifacts.d.ts",
817
- );
911
+ const duplicatedContractNamesDeclarationFilePath = path.join(
912
+ artifactsDirectory,
913
+ "artifacts.d.ts",
914
+ );
818
915
 
819
- await writeUtf8File(
820
- duplicatedContractNamesDeclarationFilePath,
821
- getDuplicatedContractNamesDeclarationFile(duplicatedNames),
822
- );
916
+ await writeUtf8File(
917
+ duplicatedContractNamesDeclarationFilePath,
918
+ getDuplicatedContractNamesDeclarationFile(duplicatedNames),
919
+ );
823
920
 
824
- await this.#hooks.runHandlerChain(
825
- "solidity",
826
- "onCleanUpArtifacts",
827
- [artifactPaths],
828
- async () => {},
829
- );
921
+ // Run the onCleanUpArtifacts hook
922
+ await this.#hooks.runHandlerChain(
923
+ "solidity",
924
+ "onCleanUpArtifacts",
925
+ [artifactPaths],
926
+ async () => {},
927
+ );
928
+ }
830
929
  }
831
930
 
832
931
  public async compileBuildInfo(
@@ -900,6 +999,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
900
999
  result: CompilationResult,
901
1000
  emitArtifactsResult: EmitArtifactsResult,
902
1001
  isolated: boolean,
1002
+ scope: BuildScope,
903
1003
  ): Promise<void> {
904
1004
  const rootFilePaths = result.compilationJob.dependencyGraph
905
1005
  .getRoots()
@@ -923,9 +1023,10 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
923
1023
 
924
1024
  const typeFilePath = emitArtifactsResult.typeFilePaths.get(rootFilePath);
925
1025
 
1026
+ // Type declaration file is not generated for solidity tests
926
1027
  assertHardhatInvariant(
927
- typeFilePath !== undefined,
928
- `No type file found on map for ${rootFilePath}`,
1028
+ scope === "tests" || typeFilePath !== undefined,
1029
+ `No type file found on map for contract ${rootFilePath}`,
929
1030
  );
930
1031
 
931
1032
  const jobHash = await individualJob.getBuildId();
@@ -985,7 +1086,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
985
1086
  >();
986
1087
 
987
1088
  if (runnableCompilationJobs.length === 0) {
988
- console.log("\nNothing to compile");
1089
+ console.log("Nothing to compile");
989
1090
  }
990
1091
 
991
1092
  for (const job of runnableCompilationJobs) {
@@ -1035,3 +1136,7 @@ export class SolidityBuildSystemImplementation implements SolidityBuildSystem {
1035
1136
  }
1036
1137
  }
1037
1138
  }
1139
+
1140
+ function toForwardSlash(str: string): string {
1141
+ return str.split(/[\\\/]/).join(path.posix.sep);
1142
+ }
@@ -20,7 +20,7 @@ export interface CompileCacheEntry {
20
20
  buildInfoPath: string;
21
21
  buildInfoOutputPath: string;
22
22
  artifactPaths: string[];
23
- typeFilePath: string;
23
+ typeFilePath?: string;
24
24
  wasm: boolean;
25
25
  }
26
26
 
@@ -13,6 +13,7 @@ import type {
13
13
  RunCompilationJobOptions,
14
14
  RunCompilationJobResult,
15
15
  SolidityBuildSystem,
16
+ BuildScope,
16
17
  } from "../../../../types/solidity/build-system.js";
17
18
  import type { CompilationJob } from "../../../../types/solidity/compilation-job.js";
18
19
  import type {
@@ -33,9 +34,16 @@ class LazySolidityBuildSystem implements SolidityBuildSystem {
33
34
  this.#options = options;
34
35
  }
35
36
 
36
- public async getRootFilePaths(): Promise<string[]> {
37
+ public async getRootFilePaths(
38
+ options: { scope?: BuildScope } = {},
39
+ ): Promise<string[]> {
37
40
  const buildSystem = await this.#getBuildSystem();
38
- return buildSystem.getRootFilePaths();
41
+ return buildSystem.getRootFilePaths(options);
42
+ }
43
+
44
+ public async getScope(fsPath: string): Promise<BuildScope> {
45
+ const buildSystem = await this.#getBuildSystem();
46
+ return buildSystem.getScope(fsPath);
39
47
  }
40
48
 
41
49
  public async build(
@@ -78,14 +86,18 @@ class LazySolidityBuildSystem implements SolidityBuildSystem {
78
86
  public async emitArtifacts(
79
87
  compilationJob: CompilationJob,
80
88
  compilerOutput: CompilerOutput,
89
+ options: { scope?: BuildScope } = {},
81
90
  ): Promise<EmitArtifactsResult> {
82
91
  const buildSystem = await this.#getBuildSystem();
83
- return buildSystem.emitArtifacts(compilationJob, compilerOutput);
92
+ return buildSystem.emitArtifacts(compilationJob, compilerOutput, options);
84
93
  }
85
94
 
86
- public async cleanupArtifacts(rootFilePaths: string[]): Promise<void> {
95
+ public async cleanupArtifacts(
96
+ rootFilePaths: string[],
97
+ options: { scope?: BuildScope } = {},
98
+ ): Promise<void> {
87
99
  const buildSystem = await this.#getBuildSystem();
88
- return buildSystem.cleanupArtifacts(rootFilePaths);
100
+ return buildSystem.cleanupArtifacts(rootFilePaths, options);
89
101
  }
90
102
 
91
103
  public async compileBuildInfo(
@@ -96,6 +108,11 @@ class LazySolidityBuildSystem implements SolidityBuildSystem {
96
108
  return buildSystem.compileBuildInfo(buildInfo, options);
97
109
  }
98
110
 
111
+ public async getArtifactsDirectory(scope: BuildScope): Promise<string> {
112
+ const buildSystem = await this.#getBuildSystem();
113
+ return buildSystem.getArtifactsDirectory(scope);
114
+ }
115
+
99
116
  async #getBuildSystem(): Promise<SolidityBuildSystem> {
100
117
  const { SolidityBuildSystemImplementation } = await import(
101
118
  "../build-system/build-system.js"