refactor: move brother_node development artifact to dev/test-nodes subdirectory

Development Artifact Cleanup:
 BROTHER_NODE REORGANIZATION: Moved development test node to appropriate location
- dev/test-nodes/brother_node/: Moved from root directory for better organization
- Contains development configuration, test logs, and test chain data
- No impact on production systems - purely development/testing artifact

 DEVELOPMENT ARTIFACTS IDENTIFIED:
- Chain ID: aitbc-brother-chain (test/development chain)
- Ports: 8010 (P2P) and 8011 (RPC) - different from production
- Environment: .env file with test configuration
- Logs: rpc.log and node.log from development testing session (March 15, 2026)

 ROOT DIRECTORY CLEANUP: Removed development clutter from production directory
- brother_node/ moved to dev/test-nodes/brother_node/
- Root directory now contains only production-ready components
- Development artifacts properly organized in dev/ subdirectory

DIRECTORY STRUCTURE IMPROVEMENT:
📁 dev/test-nodes/: Development and testing node configurations
🏗️ Root Directory: Clean production structure with only essential components
🧪 Development Isolation: Test environments separated from production

BENEFITS:
 Clean Production Directory: No development artifacts in root
 Better Organization: Development nodes grouped in dev/ subdirectory
 Clear Separation: Production vs development environments clearly distinguished
 Maintainability: Easier to identify and manage development components

RESULT: Successfully moved brother_node development artifact to dev/test-nodes/ subdirectory, cleaning up the root directory while preserving development testing environment for future use.
This commit is contained in:
2026-03-30 17:09:06 +02:00
parent bf730dcb4a
commit 816e258d4c
11734 changed files with 2001707 additions and 0 deletions

3
dev/env/node_modules/hardhat/dist/src/cli.d.ts generated vendored Executable file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
export {};
//# sourceMappingURL=cli.d.ts.map

1
dev/env/node_modules/hardhat/dist/src/cli.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":""}

22
dev/env/node_modules/hardhat/dist/src/cli.js generated vendored Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env node
import { printNodeJsVersionWarningIfNecessary } from "./internal/cli/node-version.js";
// We enable the sourcemaps before loading main, so that everything except this
// small file is loaded with sourcemaps enabled.
process.setSourceMapsEnabled(true);
// We also print this warning before loading main, so that if there is some
// unsupported js syntax or Node API elsewhere, we get to print it before
// crashing.
printNodeJsVersionWarningIfNecessary();
// eslint-disable-next-line no-restricted-syntax -- Allow top-level await here
const { main } = await import("./internal/cli/main.js");
function isTsxRequired() {
const tsNativeRuntimes = ["Deno"];
// environments that support typescript natively don't need tsx
if (tsNativeRuntimes.some((env) => env in globalThis)) {
return false;
}
return true;
}
// eslint-disable-next-line no-restricted-syntax -- We do want TLA here
await main(process.argv.slice(2), { registerTsx: isTsxRequired() });
//# sourceMappingURL=cli.js.map

1
dev/env/node_modules/hardhat/dist/src/cli.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oCAAoC,EAAE,MAAM,gCAAgC,CAAC;AAEtF,+EAA+E;AAC/E,gDAAgD;AAChD,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;AAEnC,2EAA2E;AAC3E,yEAAyE;AACzE,YAAY;AACZ,oCAAoC,EAAE,CAAC;AAEvC,8EAA8E;AAC9E,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;AAExD,SAAS,aAAa;IACpB,MAAM,gBAAgB,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,+DAA+D;IAC/D,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC;QACtD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,uEAAuE;AACvE,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,WAAW,EAAE,aAAa,EAAE,EAAE,CAAC,CAAC"}

47
dev/env/node_modules/hardhat/dist/src/config.d.ts generated vendored Executable file
View File

@@ -0,0 +1,47 @@
export type * from "./internal/core/config.js";
export * from "./internal/core/config.js";
export type { HardhatUserConfig } from "./types/config.js";
import "./internal/builtin-plugins/index.js";
import type { HardhatUserConfig } from "./types/config.js";
/**
* Defines a Hardhat user config.
*
* This function is normally expected to be used in your `hardhat.config.ts` file
* like this:
*
* ```js
* import { defineConfig } from "hardhat/config";
*
* export default defineConfig({
* // Your config ...
* });
* ```
* @note If using `--isolatedDeclarations`, you should import the type
* `HardhatUserConfig` from `hardhat/config` instead of relying on the return
* type of this function.
*
* @param config Your config. See {@link https://hardhat.org/config}.
* @returns The config.
*/
export declare function defineConfig(config: HardhatUserConfig): HardhatUserConfig;
/**
* @deprecated This function is part of the Hardhat 2 plugin API.
*/
export declare function extendConfig(..._args: any): any;
/**
* @deprecated This function is part of the Hardhat 2 plugin API.
*/
export declare function extendEnvironment(..._args: any): any;
/**
* @deprecated This function is part of the Hardhat 2 plugin API.
*/
export declare function extendProvider(..._args: any): any;
/**
* @deprecated This function is part of the Hardhat 2 plugin API.
*/
export declare function scope(..._args: any): any;
/**
* @deprecated This function is part of the Hardhat 2 plugin API.
*/
export declare function subtask(..._args: any): any;
//# sourceMappingURL=config.d.ts.map

1
dev/env/node_modules/hardhat/dist/src/config.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,mBAAmB,2BAA2B,CAAC;AAC/C,cAAc,2BAA2B,CAAC;AAE1C,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAI3D,OAAO,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAI3D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CASzE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,KAAK,EAAE,GAAG,GAAG,GAAG,CAE/C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,KAAK,EAAE,GAAG,GAAG,GAAG,CAEpD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,KAAK,EAAE,GAAG,GAAG,GAAG,CAEjD;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,GAAG,KAAK,EAAE,GAAG,GAAG,GAAG,CAExC;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,GAAG,KAAK,EAAE,GAAG,GAAG,GAAG,CAE1C"}

66
dev/env/node_modules/hardhat/dist/src/config.js generated vendored Executable file
View File

@@ -0,0 +1,66 @@
export * from "./internal/core/config.js";
// NOTE: We import the builtin plugins in this module, so that their
// type-extensions are loaded when the user imports `hardhat/config`.
import "./internal/builtin-plugins/index.js";
import { throwUsingHardhat2PluginError } from "./internal/using-hardhat2-plugin-errors.js";
/**
* Defines a Hardhat user config.
*
* This function is normally expected to be used in your `hardhat.config.ts` file
* like this:
*
* ```js
* import { defineConfig } from "hardhat/config";
*
* export default defineConfig({
* // Your config ...
* });
* ```
* @note If using `--isolatedDeclarations`, you should import the type
* `HardhatUserConfig` from `hardhat/config` instead of relying on the return
* type of this function.
*
* @param config Your config. See {@link https://hardhat.org/config}.
* @returns The config.
*/
export function defineConfig(config) {
// In reality, this function doesn't do anything, it just returns your config.
// Why does it exist?
// - It gives autocomplete of the config both to js and ts users.
// - It allows you to define and export the config in a single statement,
// having type-safety without much more verbosity.
// - While it doesn't do anything, it feels mandatory, so most users will
// use it and have a better user experience.
return config;
}
/**
* @deprecated This function is part of the Hardhat 2 plugin API.
*/
export function extendConfig(..._args) {
throwUsingHardhat2PluginError();
}
/**
* @deprecated This function is part of the Hardhat 2 plugin API.
*/
export function extendEnvironment(..._args) {
throwUsingHardhat2PluginError();
}
/**
* @deprecated This function is part of the Hardhat 2 plugin API.
*/
export function extendProvider(..._args) {
throwUsingHardhat2PluginError();
}
/**
* @deprecated This function is part of the Hardhat 2 plugin API.
*/
export function scope(..._args) {
throwUsingHardhat2PluginError();
}
/**
* @deprecated This function is part of the Hardhat 2 plugin API.
*/
export function subtask(..._args) {
throwUsingHardhat2PluginError();
}
//# sourceMappingURL=config.js.map

1
dev/env/node_modules/hardhat/dist/src/config.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AACA,cAAc,2BAA2B,CAAC;AAI1C,oEAAoE;AACpE,qEAAqE;AACrE,OAAO,qCAAqC,CAAC;AAG7C,OAAO,EAAE,6BAA6B,EAAE,MAAM,4CAA4C,CAAC;AAE3F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,YAAY,CAAC,MAAyB;IACpD,8EAA8E;IAC9E,qBAAqB;IACrB,kEAAkE;IAClE,0EAA0E;IAC1E,qDAAqD;IACrD,0EAA0E;IAC1E,+CAA+C;IAC/C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,GAAG,KAAU;IACxC,6BAA6B,EAAE,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAG,KAAU;IAC7C,6BAA6B,EAAE,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,GAAG,KAAU;IAC1C,6BAA6B,EAAE,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,GAAG,KAAU;IACjC,6BAA6B,EAAE,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,GAAG,KAAU;IACnC,6BAA6B,EAAE,CAAC;AAClC,CAAC"}

5
dev/env/node_modules/hardhat/dist/src/hre.d.ts generated vendored Executable file
View File

@@ -0,0 +1,5 @@
import "./internal/builtin-plugins/index.js";
export { importUserConfig } from "./internal/config-loading.js";
export { resolveHardhatConfigPath } from "./internal/config-loading.js";
export { createHardhatRuntimeEnvironment } from "./internal/hre-initialization.js";
//# sourceMappingURL=hre.d.ts.map

1
dev/env/node_modules/hardhat/dist/src/hre.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"hre.d.ts","sourceRoot":"","sources":["../../src/hre.ts"],"names":[],"mappings":"AAEA,OAAO,qCAAqC,CAAC;AAE7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,MAAM,kCAAkC,CAAC"}

7
dev/env/node_modules/hardhat/dist/src/hre.js generated vendored Executable file
View File

@@ -0,0 +1,7 @@
// NOTE: We import the builtin plugins in this module, so that their
// type-extensions are loaded when the user imports `hardhat/hre`.
import "./internal/builtin-plugins/index.js";
export { importUserConfig } from "./internal/config-loading.js";
export { resolveHardhatConfigPath } from "./internal/config-loading.js";
export { createHardhatRuntimeEnvironment } from "./internal/hre-initialization.js";
//# sourceMappingURL=hre.js.map

1
dev/env/node_modules/hardhat/dist/src/hre.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"hre.js","sourceRoot":"","sources":["../../src/hre.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,kEAAkE;AAClE,OAAO,qCAAqC,CAAC;AAE7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,MAAM,kCAAkC,CAAC"}

21
dev/env/node_modules/hardhat/dist/src/index.d.ts generated vendored Executable file
View File

@@ -0,0 +1,21 @@
import type { ArtifactManager } from "./types/artifacts.js";
import type { HardhatConfig } from "./types/config.js";
import type { GlobalOptions } from "./types/global-options.js";
import type { HookManager } from "./types/hooks.js";
import type { HardhatRuntimeEnvironment } from "./types/hre.js";
import type { NetworkManager } from "./types/network.js";
import type { SolidityBuildSystem } from "./types/solidity/build-system.js";
import type { TaskManager } from "./types/tasks.js";
import type { UserInterruptionManager } from "./types/user-interruptions.js";
import "./internal/builtin-plugins/index.js";
declare const hre: HardhatRuntimeEnvironment;
export declare const config: HardhatConfig;
export declare const tasks: TaskManager;
export declare const globalOptions: GlobalOptions;
export declare const hooks: HookManager;
export declare const interruptions: UserInterruptionManager;
export declare const network: NetworkManager;
export declare const artifacts: ArtifactManager;
export declare const solidity: SolidityBuildSystem;
export default hre;
//# sourceMappingURL=index.d.ts.map

1
dev/env/node_modules/hardhat/dist/src/index.d.ts.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAM7E,OAAO,qCAAqC,CAAC;AAE7C,QAAA,MAAM,GAAG,EAAE,yBAEyC,CAAC;AAErD,eAAO,MAAM,MAAM,EAAE,aAA0B,CAAC;AAChD,eAAO,MAAM,KAAK,EAAE,WAAuB,CAAC;AAC5C,eAAO,MAAM,aAAa,EAAE,aAAiC,CAAC;AAC9D,eAAO,MAAM,KAAK,EAAE,WAAuB,CAAC;AAC5C,eAAO,MAAM,aAAa,EAAE,uBAA2C,CAAC;AAIxE,eAAO,MAAM,OAAO,EAAE,cAA4B,CAAC;AACnD,eAAO,MAAM,SAAS,EAAE,eAA+B,CAAC;AACxD,eAAO,MAAM,QAAQ,EAAE,mBAAkC,CAAC;AAE1D,eAAe,GAAG,CAAC"}

19
dev/env/node_modules/hardhat/dist/src/index.js generated vendored Executable file
View File

@@ -0,0 +1,19 @@
import { getOrCreateGlobalHardhatRuntimeEnvironment } from "./internal/hre-initialization.js";
// NOTE: We import the builtin plugins in this module, so that their
// type-extensions are loaded when the user imports `hardhat`.
import "./internal/builtin-plugins/index.js";
const hre =
// eslint-disable-next-line no-restricted-syntax -- Allow top-level await here
await getOrCreateGlobalHardhatRuntimeEnvironment();
export const config = hre.config;
export const tasks = hre.tasks;
export const globalOptions = hre.globalOptions;
export const hooks = hre.hooks;
export const interruptions = hre.interruptions;
// NOTE: This is a small architectural violation, as the network manager comes
// from a builtin plugin, and plugins can't add their own exports here.
export const network = hre.network;
export const artifacts = hre.artifacts;
export const solidity = hre.solidity;
export default hre;
//# sourceMappingURL=index.js.map

1
dev/env/node_modules/hardhat/dist/src/index.js.map generated vendored Executable file
View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,0CAA0C,EAAE,MAAM,kCAAkC,CAAC;AAE9F,oEAAoE;AACpE,8DAA8D;AAC9D,OAAO,qCAAqC,CAAC;AAE7C,MAAM,GAAG;AACP,8EAA8E;AAC9E,MAAM,0CAA0C,EAAE,CAAC;AAErD,MAAM,CAAC,MAAM,MAAM,GAAkB,GAAG,CAAC,MAAM,CAAC;AAChD,MAAM,CAAC,MAAM,KAAK,GAAgB,GAAG,CAAC,KAAK,CAAC;AAC5C,MAAM,CAAC,MAAM,aAAa,GAAkB,GAAG,CAAC,aAAa,CAAC;AAC9D,MAAM,CAAC,MAAM,KAAK,GAAgB,GAAG,CAAC,KAAK,CAAC;AAC5C,MAAM,CAAC,MAAM,aAAa,GAA4B,GAAG,CAAC,aAAa,CAAC;AAExE,8EAA8E;AAC9E,uEAAuE;AACvE,MAAM,CAAC,MAAM,OAAO,GAAmB,GAAG,CAAC,OAAO,CAAC;AACnD,MAAM,CAAC,MAAM,SAAS,GAAoB,GAAG,CAAC,SAAS,CAAC;AACxD,MAAM,CAAC,MAAM,QAAQ,GAAwB,GAAG,CAAC,QAAQ,CAAC;AAE1D,eAAe,GAAG,CAAC"}

View File

@@ -0,0 +1,3 @@
import type { GlobalOptionDefinitions } from "../types/global-options.js";
export declare const BUILTIN_GLOBAL_OPTIONS_DEFINITIONS: GlobalOptionDefinitions;
//# sourceMappingURL=builtin-global-options.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"builtin-global-options.d.ts","sourceRoot":"","sources":["../../../src/internal/builtin-global-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAK1E,eAAO,MAAM,kCAAkC,EAAE,uBAwD7C,CAAC"}

View File

@@ -0,0 +1,58 @@
import { globalFlag, globalOption } from "../config.js";
import { ArgumentType } from "../types/arguments.js";
export const BUILTIN_GLOBAL_OPTIONS_DEFINITIONS = new Map([
[
"config",
{
pluginId: "builtin",
option: globalOption({
name: "config",
description: "A Hardhat config file.",
type: ArgumentType.STRING_WITHOUT_DEFAULT,
defaultValue: undefined,
}),
},
],
[
"help",
{
pluginId: "builtin",
option: globalFlag({
name: "help",
shortName: "h",
description: "Show this message, or a task's help if its name is provided.",
}),
},
],
[
"init",
{
pluginId: "builtin",
option: globalFlag({
name: "init",
description: "Initializes a Hardhat project.",
}),
},
],
[
"showStackTraces",
{
pluginId: "builtin",
option: globalFlag({
name: "showStackTraces",
description: "Show stack traces (always enabled on CI servers).",
}),
},
],
[
"version",
{
pluginId: "builtin",
option: globalFlag({
name: "version",
description: "Show the version of hardhat.",
}),
},
],
]);
//# sourceMappingURL=builtin-global-options.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"builtin-global-options.js","sourceRoot":"","sources":["../../../src/internal/builtin-global-options.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,CAAC,MAAM,kCAAkC,GAC7C,IAAI,GAAG,CAAC;IACN;QACE,QAAQ;QACR;YACE,QAAQ,EAAE,SAAS;YACnB,MAAM,EAAE,YAAY,CAAC;gBACnB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,wBAAwB;gBACrC,IAAI,EAAE,YAAY,CAAC,sBAAsB;gBACzC,YAAY,EAAE,SAAS;aACxB,CAAC;SACH;KACF;IACD;QACE,MAAM;QACN;YACE,QAAQ,EAAE,SAAS;YACnB,MAAM,EAAE,UAAU,CAAC;gBACjB,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,GAAG;gBACd,WAAW,EACT,8DAA8D;aACjE,CAAC;SACH;KACF;IACD;QACE,MAAM;QACN;YACE,QAAQ,EAAE,SAAS;YACnB,MAAM,EAAE,UAAU,CAAC;gBACjB,IAAI,EAAE,MAAM;gBACZ,WAAW,EAAE,gCAAgC;aAC9C,CAAC;SACH;KACF;IACD;QACE,iBAAiB;QACjB;YACE,QAAQ,EAAE,SAAS;YACnB,MAAM,EAAE,UAAU,CAAC;gBACjB,IAAI,EAAE,iBAAiB;gBACvB,WAAW,EAAE,mDAAmD;aACjE,CAAC;SACH;KACF;IACD;QACE,SAAS;QACT;YACE,QAAQ,EAAE,SAAS;YACnB,MAAM,EAAE,UAAU,CAAC;gBACjB,IAAI,EAAE,SAAS;gBACf,WAAW,EAAE,8BAA8B;aAC5C,CAAC;SACH;KACF;CACF,CAAC,CAAC"}

View File

@@ -0,0 +1,42 @@
import type { ArtifactManager, GetArtifactByName } from "../../../types/artifacts.js";
export declare const BUILD_INFO_DIR_NAME = "build-info";
export declare const EDIT_DISTANCE_THRESHOLD = 3;
/**
* We cache the info that we read from the file system, otherwise we would
* have to traverse the filesystem every time we need to get the an artifact.
*
* To keep our view of the filesystem consistent, we cache everything at the
* same time, using this interface to organize the data.
*/
interface FsData {
allArtifactPaths: ReadonlySet<string>;
allFullyQualifiedNames: ReadonlySet<string>;
bareNameToFullyQualifiedNameMap: Map<string, ReadonlySet<string>>;
fullyQualifiedNameToArtifactPath: Map<string, string>;
}
export declare class ArtifactManagerImplementation implements ArtifactManager {
#private;
constructor(artifactsPath: string, readFsData?: () => Promise<FsData>);
readArtifact<ContractNameT extends string>(contractNameOrFullyQualifiedName: ContractNameT): Promise<GetArtifactByName<ContractNameT>>;
getArtifactPath(contractNameOrFullyQualifiedName: string): Promise<string>;
artifactExists(contractNameOrFullyQualifiedName: string): Promise<boolean>;
getBuildInfoId(contractNameOrFullyQualifiedName: string): Promise<string | undefined>;
getAllFullyQualifiedNames(): Promise<ReadonlySet<string>>;
getAllArtifactPaths(): Promise<ReadonlySet<string>>;
getAllBuildInfoIds(): Promise<ReadonlySet<string>>;
getBuildInfoPath(buildInfoId: string): Promise<string | undefined>;
getBuildInfoOutputPath(buildInfoId: string): Promise<string | undefined>;
clearCache(): Promise<void>;
}
/**
* Returns the edit-distance between two given strings using Levenshtein distance.
*
* @param a First string being compared
* @param b Second string being compared
* @returns distance between the two strings (lower number == more similar)
* @see https://github.com/gustf/js-levenshtein
* @license MIT - https://github.com/gustf/js-levenshtein/blob/master/LICENSE
*/
export declare function editDistance(a: string, b: string): number;
export {};
//# sourceMappingURL=artifact-manager.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"artifact-manager.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/artifacts/artifact-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AAerC,eAAO,MAAM,mBAAmB,eAAe,CAAC;AAChD,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC;;;;;;GAMG;AACH,UAAU,MAAM;IACd,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC5C,+BAA+B,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAClE,gCAAgC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvD;AAED,qBAAa,6BAA8B,YAAW,eAAe;;gBASvD,aAAa,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC;IAKxD,YAAY,CAAC,aAAa,SAAS,MAAM,EACpD,gCAAgC,EAAE,aAAa,GAC9C,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;IAQ/B,eAAe,CAC1B,gCAAgC,EAAE,MAAM,GACvC,OAAO,CAAC,MAAM,CAAC;IAgBL,cAAc,CACzB,gCAAgC,EAAE,MAAM,GACvC,OAAO,CAAC,OAAO,CAAC;IAmBN,cAAc,CACzB,gCAAgC,EAAE,MAAM,GACvC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAMjB,yBAAyB,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAKzD,mBAAmB,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAKnD,kBAAkB,IAAI,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IASlD,gBAAgB,CAC3B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAUjB,sBAAsB,CACjC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAYjB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAmLzC;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAqGzD"}

View File

@@ -0,0 +1,274 @@
import { EOL } from "node:os";
import path from "node:path";
import { assertHardhatInvariant, HardhatError, } from "@nomicfoundation/hardhat-errors";
import { exists, getAllFilesMatching, readJsonFile, } from "@nomicfoundation/hardhat-utils/fs";
export const BUILD_INFO_DIR_NAME = "build-info";
export const EDIT_DISTANCE_THRESHOLD = 3;
export class ArtifactManagerImplementation {
#artifactsPath;
// This function can be overridden in the constructor for testing purposes.
// This class will call it whenever the fsData is not already cached, and will
// cache the result.
#readFsData;
#fsData;
constructor(artifactsPath, readFsData) {
this.#artifactsPath = artifactsPath;
this.#readFsData = readFsData ?? (() => this.#readFsDataFromFileSystem());
}
async readArtifact(contractNameOrFullyQualifiedName) {
const artifactPath = await this.getArtifactPath(contractNameOrFullyQualifiedName);
return readJsonFile(artifactPath);
}
async getArtifactPath(contractNameOrFullyQualifiedName) {
const fqn = await this.#getFullyQualifiedName(contractNameOrFullyQualifiedName);
const { fullyQualifiedNameToArtifactPath } = await this.#getFsData();
const artifactPath = fullyQualifiedNameToArtifactPath.get(fqn);
assertHardhatInvariant(artifactPath !== undefined, "Artifact path should be defined");
return artifactPath;
}
async artifactExists(contractNameOrFullyQualifiedName) {
try {
// This throw if the artifact doesn't exist
await this.getArtifactPath(contractNameOrFullyQualifiedName);
return true;
}
catch (error) {
if (HardhatError.isHardhatError(error)) {
if (error.number === HardhatError.ERRORS.CORE.ARTIFACTS.NOT_FOUND.number) {
return false;
}
}
throw error;
}
}
async getBuildInfoId(contractNameOrFullyQualifiedName) {
const artifact = await this.readArtifact(contractNameOrFullyQualifiedName);
return artifact.buildInfoId;
}
async getAllFullyQualifiedNames() {
const { allFullyQualifiedNames } = await this.#getFsData();
return allFullyQualifiedNames;
}
async getAllArtifactPaths() {
const { allArtifactPaths } = await this.#getFsData();
return allArtifactPaths;
}
async getAllBuildInfoIds() {
const paths = await getAllFilesMatching(path.join(this.#artifactsPath, BUILD_INFO_DIR_NAME), (p) => p.endsWith(".json") && !p.endsWith(".output.json"));
return new Set(paths.map((p) => path.basename(p, ".json")));
}
async getBuildInfoPath(buildInfoId) {
const buildInfoPath = path.join(this.#artifactsPath, BUILD_INFO_DIR_NAME, buildInfoId + ".json");
return (await exists(buildInfoPath)) ? buildInfoPath : undefined;
}
async getBuildInfoOutputPath(buildInfoId) {
const buildInfoOutputPath = path.join(this.#artifactsPath, BUILD_INFO_DIR_NAME, buildInfoId + ".output.json");
return (await exists(buildInfoOutputPath))
? buildInfoOutputPath
: undefined;
}
async clearCache() {
this.#fsData = undefined;
}
async #getFullyQualifiedName(contractNameOrFullyQualifiedName) {
const { bareNameToFullyQualifiedNameMap, allFullyQualifiedNames } = await this.#getFsData();
if (this.#isFullyQualifiedName(contractNameOrFullyQualifiedName)) {
if (allFullyQualifiedNames.has(contractNameOrFullyQualifiedName)) {
return contractNameOrFullyQualifiedName;
}
this.#throwNotFoundError(contractNameOrFullyQualifiedName, bareNameToFullyQualifiedNameMap.keys(), allFullyQualifiedNames);
}
const fqns = bareNameToFullyQualifiedNameMap.get(contractNameOrFullyQualifiedName);
if (fqns === undefined || fqns.size === 0) {
this.#throwNotFoundError(contractNameOrFullyQualifiedName, bareNameToFullyQualifiedNameMap.keys(), allFullyQualifiedNames);
}
if (fqns.size !== 1) {
throw new HardhatError(HardhatError.ERRORS.CORE.ARTIFACTS.MULTIPLE_FOUND, {
contractName: contractNameOrFullyQualifiedName,
candidates: Array.from(fqns).join(EOL),
});
}
const [fqn] = fqns;
return fqn;
}
#throwNotFoundError(contractNameOrFullyQualifiedName, allBareNames, allFullyQualifiedNames) {
const names = this.#isFullyQualifiedName(contractNameOrFullyQualifiedName)
? allFullyQualifiedNames
: allBareNames;
const similarNames = this.#getSimilarStrings(contractNameOrFullyQualifiedName, names);
const suggestion = this.#formatSimilarNameSuggestions(contractNameOrFullyQualifiedName, similarNames);
throw new HardhatError(HardhatError.ERRORS.CORE.ARTIFACTS.NOT_FOUND, {
contractName: contractNameOrFullyQualifiedName,
suggestion,
});
}
#isFullyQualifiedName(name) {
return name.includes(":");
}
#getFullyQualifiedNameFromArtifactAbsolutePath(artifactPath) {
const relativePath = path.relative(this.#artifactsPath, artifactPath);
const sourceName = path.dirname(relativePath).split(path.sep).join("/");
const contractName = path.basename(relativePath, ".json");
return `${sourceName}:${contractName}`;
}
/**
* Filters an array of strings to only include the strings that are similar to
* the given string.
*
* @param stringToCompare The string to the other strings with.
* @param otherStrings The strings to filter.
* @param maxEditDistance The maximum edit distance to consider as a match.
* @returns The array of matches, sorted by increasing edit distance.
*/
#getSimilarStrings(stringToCompare, otherStrings, maxEditDistance = EDIT_DISTANCE_THRESHOLD) {
return [...otherStrings]
.map((s) => [s, editDistance(s, stringToCompare)])
.sort(([_, d1], [__, d2]) => d1 - d2)
.filter(([_, d]) => d <= maxEditDistance)
.map(([s]) => s);
}
#formatSimilarNameSuggestions(contractNameOrFullyQualifiedName, similarNames) {
const contractNameType = this.#isFullyQualifiedName(contractNameOrFullyQualifiedName)
? "fully qualified contract name"
: "contract name";
switch (similarNames.length) {
case 0:
return "";
case 1:
return `Did you mean "${similarNames[0]}"?`;
default:
return `We found some that were similar:
${similarNames.map((n) => ` * ${n}`).join(EOL)}
Please replace "${contractNameOrFullyQualifiedName}" with the correct ${contractNameType} wherever you are trying to read its artifact.
`;
}
}
async #getFsData() {
if (this.#fsData === undefined) {
this.#fsData = await this.#readFsData();
}
return this.#fsData;
}
async #readFsDataFromFileSystem() {
const buildInfosDir = path.join(this.#artifactsPath, BUILD_INFO_DIR_NAME);
const allArtifactPaths = await getAllFilesMatching(this.#artifactsPath, (p) => p.endsWith(".json") && // Only consider json files
// Ignore top level json files
p.indexOf(path.sep, this.#artifactsPath.length + path.sep.length) !==
-1, (dir) => dir !== buildInfosDir);
const allFullyQualifiedNames = new Set();
const bareNameToFullyQualifiedNameMap = new Map();
const fullyQualifiedNameToArtifactPath = new Map();
for (const p of allArtifactPaths) {
const bareName = path.basename(p, ".json");
const fqn = this.#getFullyQualifiedNameFromArtifactAbsolutePath(p);
allFullyQualifiedNames.add(fqn);
fullyQualifiedNameToArtifactPath.set(fqn, p);
const fqns = bareNameToFullyQualifiedNameMap.get(bareName);
if (fqns === undefined) {
bareNameToFullyQualifiedNameMap.set(bareName, new Set([fqn]));
}
else {
fqns.add(fqn);
}
}
return {
allArtifactPaths: new Set(allArtifactPaths),
allFullyQualifiedNames,
bareNameToFullyQualifiedNameMap,
fullyQualifiedNameToArtifactPath,
};
}
}
/**
* Returns the edit-distance between two given strings using Levenshtein distance.
*
* @param a First string being compared
* @param b Second string being compared
* @returns distance between the two strings (lower number == more similar)
* @see https://github.com/gustf/js-levenshtein
* @license MIT - https://github.com/gustf/js-levenshtein/blob/master/LICENSE
*/
export function editDistance(a, b) {
function _min(_d0, _d1, _d2, _bx, _ay) {
return _d0 < _d1 || _d2 < _d1
? _d0 > _d2
? _d2 + 1
: _d0 + 1
: _bx === _ay
? _d1
: _d1 + 1;
}
if (a === b) {
return 0;
}
if (a.length > b.length) {
[a, b] = [b, a];
}
let la = a.length;
let lb = b.length;
while (la > 0 && a.charCodeAt(la - 1) === b.charCodeAt(lb - 1)) {
la--;
lb--;
}
let offset = 0;
while (offset < la && a.charCodeAt(offset) === b.charCodeAt(offset)) {
offset++;
}
la -= offset;
lb -= offset;
if (la === 0 || lb < 3) {
return lb;
}
let x = 0;
let y;
let d0;
let d1;
let d2;
let d3;
let dd = 0; // typescript gets angry if we don't assign here
let dy;
let ay;
let bx0;
let bx1;
let bx2;
let bx3;
const vector = [];
for (y = 0; y < la; y++) {
vector.push(y + 1);
vector.push(a.charCodeAt(offset + y));
}
const len = vector.length - 1;
for (; x < lb - 3;) {
bx0 = b.charCodeAt(offset + (d0 = x));
bx1 = b.charCodeAt(offset + (d1 = x + 1));
bx2 = b.charCodeAt(offset + (d2 = x + 2));
bx3 = b.charCodeAt(offset + (d3 = x + 3));
dd = x += 4;
for (y = 0; y < len; y += 2) {
dy = vector[y];
ay = vector[y + 1];
d0 = _min(dy, d0, d1, bx0, ay);
d1 = _min(d0, d1, d2, bx1, ay);
d2 = _min(d1, d2, d3, bx2, ay);
dd = _min(d2, d3, dd, bx3, ay);
vector[y] = dd;
d3 = d2;
d2 = d1;
d1 = d0;
d0 = dy;
}
}
for (; x < lb;) {
bx0 = b.charCodeAt(offset + (d0 = x));
dd = ++x;
for (y = 0; y < len; y += 2) {
dy = vector[y];
vector[y] = dd = _min(dy, d0, dd, bx0, vector[y + 1]);
d0 = dy;
}
}
return dd;
}
//# sourceMappingURL=artifact-manager.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
import type { HardhatRuntimeEnvironmentHooks } from "../../../../types/hooks.js";
declare const _default: () => Promise<Partial<HardhatRuntimeEnvironmentHooks>>;
export default _default;
//# sourceMappingURL=hre.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"hre.d.ts","sourceRoot":"","sources":["../../../../../../src/internal/builtin-plugins/artifacts/hook-handlers/hre.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,4BAA4B,CAAC;8BAuFxD,OAAO,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;AAAzE,wBAQE"}

View File

@@ -0,0 +1,64 @@
class LazyArtifactManager {
#artifactsPath;
#artifactManager;
constructor(artifactsPath) {
this.#artifactManager = undefined;
this.#artifactsPath = artifactsPath;
}
async readArtifact(contractNameOrFullyQualifiedName) {
const artifactManager = await this.#getArtifactManager();
return artifactManager.readArtifact(contractNameOrFullyQualifiedName);
}
async getArtifactPath(contractNameOrFullyQualifiedName) {
const artifactManager = await this.#getArtifactManager();
return artifactManager.getArtifactPath(contractNameOrFullyQualifiedName);
}
async artifactExists(contractNameOrFullyQualifiedName) {
const artifactManager = await this.#getArtifactManager();
return artifactManager.artifactExists(contractNameOrFullyQualifiedName);
}
async getAllFullyQualifiedNames() {
const artifactManager = await this.#getArtifactManager();
return artifactManager.getAllFullyQualifiedNames();
}
async getAllArtifactPaths() {
const artifactManager = await this.#getArtifactManager();
return artifactManager.getAllArtifactPaths();
}
async getBuildInfoId(contractNameOrFullyQualifiedName) {
const artifactManager = await this.#getArtifactManager();
return artifactManager.getBuildInfoId(contractNameOrFullyQualifiedName);
}
async getAllBuildInfoIds() {
const artifactManager = await this.#getArtifactManager();
return artifactManager.getAllBuildInfoIds();
}
async getBuildInfoPath(buildInfoId) {
const artifactManager = await this.#getArtifactManager();
return artifactManager.getBuildInfoPath(buildInfoId);
}
async getBuildInfoOutputPath(buildInfoId) {
const artifactManager = await this.#getArtifactManager();
return artifactManager.getBuildInfoOutputPath(buildInfoId);
}
async clearCache() {
const artifactManager = await this.#getArtifactManager();
return artifactManager.clearCache();
}
async #getArtifactManager() {
if (this.#artifactManager === undefined) {
const { ArtifactManagerImplementation } = await import("../artifact-manager.js");
this.#artifactManager = new ArtifactManagerImplementation(this.#artifactsPath);
}
return this.#artifactManager;
}
}
export default async () => {
const handlers = {
created: async (_context, hre) => {
hre.artifacts = new LazyArtifactManager(hre.config.paths.artifacts);
},
};
return handlers;
};
//# sourceMappingURL=hre.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"hre.js","sourceRoot":"","sources":["../../../../../../src/internal/builtin-plugins/artifacts/hook-handlers/hre.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,5 @@
import type { HardhatPlugin } from "../../../types/plugins.js";
import "./type-extensions.js";
declare const hardhatPlugin: HardhatPlugin;
export default hardhatPlugin;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/artifacts/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,sBAAsB,CAAC;AAE9B,QAAA,MAAM,aAAa,EAAE,aAMpB,CAAC;AAEF,eAAe,aAAa,CAAC"}

View File

@@ -0,0 +1,10 @@
import "./type-extensions.js";
const hardhatPlugin = {
id: "builtin:artifacts",
hookHandlers: {
hre: () => import("./hook-handlers/hre.js"),
},
npmPackage: "hardhat",
};
export default hardhatPlugin;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/artifacts/index.ts"],"names":[],"mappings":"AACA,OAAO,sBAAsB,CAAC;AAE9B,MAAM,aAAa,GAAkB;IACnC,EAAE,EAAE,mBAAmB;IACvB,YAAY,EAAE;QACZ,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC;KAC5C;IACD,UAAU,EAAE,SAAS;CACtB,CAAC;AAEF,eAAe,aAAa,CAAC"}

View File

@@ -0,0 +1,7 @@
import type { ArtifactManager } from "../../../types/artifacts.js";
declare module "../../../types/hre.js" {
interface HardhatRuntimeEnvironment {
artifacts: ArtifactManager;
}
}
//# sourceMappingURL=type-extensions.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"type-extensions.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/artifacts/type-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,QAAQ,uBAAuB,CAAC;IACrC,UAAU,yBAAyB;QACjC,SAAS,EAAE,eAAe,CAAC;KAC5B;CACF"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=type-extensions.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"type-extensions.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/artifacts/type-extensions.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,5 @@
import type { HardhatPlugin } from "../../../types/plugins.js";
import "./type-extensions.js";
declare const hardhatPlugin: HardhatPlugin;
export default hardhatPlugin;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/clean/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAI/D,OAAO,sBAAsB,CAAC;AAE9B,QAAA,MAAM,aAAa,EAAE,aAYpB,CAAC;AAEF,eAAe,aAAa,CAAC"}

View File

@@ -0,0 +1,17 @@
import { task } from "../../core/config.js";
import "./type-extensions.js";
const hardhatPlugin = {
id: "builtin:clean",
tasks: [
task("clean", "Clear the cache and delete all artifacts")
.addFlag({
name: "global",
description: "Clear the global cache",
})
.setAction(async () => import("./task-action.js"))
.build(),
],
npmPackage: "hardhat",
};
export default hardhatPlugin;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/clean/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE5C,OAAO,sBAAsB,CAAC;AAE9B,MAAM,aAAa,GAAkB;IACnC,EAAE,EAAE,eAAe;IACnB,KAAK,EAAE;QACL,IAAI,CAAC,OAAO,EAAE,0CAA0C,CAAC;aACtD,OAAO,CAAC;YACP,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,wBAAwB;SACtC,CAAC;aACD,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;aACjD,KAAK,EAAE;KACX;IACD,UAAU,EAAE,SAAS;CACtB,CAAC;AAEF,eAAe,aAAa,CAAC"}

View File

@@ -0,0 +1,7 @@
import type { NewTaskActionFunction } from "../../../types/tasks.js";
interface CleanActionArguments {
global: boolean;
}
declare const cleanAction: NewTaskActionFunction<CleanActionArguments>;
export default cleanAction;
//# sourceMappingURL=task-action.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"task-action.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/clean/task-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAKrE,UAAU,oBAAoB;IAC5B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,QAAA,MAAM,WAAW,EAAE,qBAAqB,CAAC,oBAAoB,CAa5D,CAAC;AAEF,eAAe,WAAW,CAAC"}

View File

@@ -0,0 +1,13 @@
import { emptyDir, remove } from "@nomicfoundation/hardhat-utils/fs";
import { getCacheDir } from "@nomicfoundation/hardhat-utils/global-dir";
const cleanAction = async ({ global }, { config, hooks }) => {
if (global) {
const globalCacheDir = await getCacheDir();
await emptyDir(globalCacheDir);
}
await emptyDir(config.paths.cache);
await remove(config.paths.artifacts);
await hooks.runParallelHandlers("clean", "onClean", []);
};
export default cleanAction;
//# sourceMappingURL=task-action.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"task-action.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/clean/task-action.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AAMxE,MAAM,WAAW,GAAgD,KAAK,EACpE,EAAE,MAAM,EAAE,EACV,EAAE,MAAM,EAAE,KAAK,EAAE,EACjB,EAAE;IACF,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,cAAc,GAAG,MAAM,WAAW,EAAE,CAAC;QAC3C,MAAM,QAAQ,CAAC,cAAc,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAErC,MAAM,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;AAC1D,CAAC,CAAC;AAEF,eAAe,WAAW,CAAC"}

View File

@@ -0,0 +1,10 @@
import "../../../types/hooks.js";
declare module "../../../types/hooks.js" {
interface HardhatHooks {
clean: CleanHooks;
}
interface CleanHooks {
onClean: (context: HookContext) => Promise<void>;
}
}
//# sourceMappingURL=type-extensions.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"type-extensions.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/clean/type-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,yBAAyB,CAAC;AACjC,OAAO,QAAQ,yBAAyB,CAAC;IACvC,UAAiB,YAAY;QAC3B,KAAK,EAAE,UAAU,CAAC;KACnB;IAED,UAAiB,UAAU;QACzB,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KAClD;CACF"}

View File

@@ -0,0 +1,2 @@
import "../../../types/hooks.js";
//# sourceMappingURL=type-extensions.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"type-extensions.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/clean/type-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,yBAAyB,CAAC"}

View File

@@ -0,0 +1,4 @@
import type { HardhatPlugin } from "../../../types/plugins.js";
declare const hardhatPlugin: HardhatPlugin;
export default hardhatPlugin;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/console/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAI/D,QAAA,MAAM,aAAa,EAAE,aAuBpB,CAAC;AAEF,eAAe,aAAa,CAAC"}

View File

@@ -0,0 +1,27 @@
import { task } from "../../core/config.js";
const hardhatPlugin = {
id: "builtin:console",
tasks: [
task("console", "Open a hardhat console")
.addOption({
name: "history",
description: "Path to a history file",
defaultValue: "console-history.txt",
})
.addFlag({
name: "noCompile",
description: "Do not compile the project before starting the console",
})
.addVariadicArgument({
name: "commands",
description: "Commands to run when the console starts",
defaultValue: [],
})
.setAction(async () => import("./task-action.js"))
.build(),
],
dependencies: () => [import("../solidity/index.js")],
npmPackage: "hardhat",
};
export default hardhatPlugin;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/console/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE5C,MAAM,aAAa,GAAkB;IACnC,EAAE,EAAE,iBAAiB;IACrB,KAAK,EAAE;QACL,IAAI,CAAC,SAAS,EAAE,wBAAwB,CAAC;aACtC,SAAS,CAAC;YACT,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,wBAAwB;YACrC,YAAY,EAAE,qBAAqB;SACpC,CAAC;aACD,OAAO,CAAC;YACP,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,wDAAwD;SACtE,CAAC;aACD,mBAAmB,CAAC;YACnB,IAAI,EAAE,UAAU;YAChB,WAAW,EAAE,yCAAyC;YACtD,YAAY,EAAE,EAAE;SACjB,CAAC;aACD,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;aACjD,KAAK,EAAE;KACX;IACD,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;IACpD,UAAU,EAAE,SAAS;CACtB,CAAC;AAEF,eAAe,aAAa,CAAC"}

View File

@@ -0,0 +1,11 @@
import type { NewTaskActionFunction } from "../../../types/tasks.js";
import repl from "node:repl";
interface ConsoleActionArguments {
commands: string[];
history: string;
noCompile: boolean;
options?: repl.ReplOptions;
}
declare const consoleAction: NewTaskActionFunction<ConsoleActionArguments>;
export default consoleAction;
//# sourceMappingURL=task-action.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"task-action.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/console/task-action.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGrE,OAAO,IAAI,MAAM,WAAW,CAAC;AAQ7B,UAAU,sBAAsB;IAC9B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IAEnB,OAAO,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC;CAC5B;AAED,QAAA,MAAM,aAAa,EAAE,qBAAqB,CAAC,sBAAsB,CAsEhE,CAAC;AAEF,eAAe,aAAa,CAAC"}

View File

@@ -0,0 +1,65 @@
import repl from "node:repl";
import { resolveFromRoot } from "@nomicfoundation/hardhat-utils/path";
import chalk from "chalk";
import debug from "debug";
const log = debug("hardhat:core:tasks:console");
const consoleAction = async ({ commands, history, noCompile, options }, hre) => {
const handlers = {
requestSecretInput: async () => {
console.error(chalk.red("Secrets are not yet supported in the console task"));
process.exit(1);
},
};
hre.hooks.registerHandlers("userInterruptions", handlers);
try {
// Resolve the history path if it is not empty
let historyPath;
if (history !== "") {
const cacheDir = hre.config.paths.cache;
historyPath = resolveFromRoot(cacheDir, history);
}
if (!noCompile) {
await hre.tasks.getTask("build").run({ noTests: true, quiet: true });
}
return await new Promise(async (resolve) => {
// Start a new REPL server with the default options
const replServer = repl.start(options);
// Resolve the task action promise only when the REPL server exits
replServer.on("exit", () => {
resolve(replServer);
});
// Add the Hardhat Runtime Environment to the REPL context
replServer.context.hre = hre;
replServer.context.config = hre.config;
replServer.context.tasks = hre.tasks;
replServer.context.globalOptions = hre.globalOptions;
replServer.context.hooks = hre.hooks;
replServer.context.interruptions = hre.interruptions;
// NOTE: This is a small architectural violation, as the network manager
// comes from a builtin plugin, and plugins can't add their own exports
// here. We may consider adding a hook for this in the future.
replServer.context.network = hre.network;
// Set up the REPL history file if the historyPath has been set
if (historyPath !== undefined) {
await new Promise((resolveSetupHistory) => {
replServer.setupHistory(historyPath, (err) => {
// Fail silently if the history file cannot be set up
if (err !== null) {
log("Failed to setup REPL history", err);
}
resolveSetupHistory();
});
});
}
// Execute each command in the REPL server
for (const command of commands) {
replServer.write(`${command}\n`);
}
});
}
finally {
hre.hooks.unregisterHandlers("userInterruptions", handlers);
}
};
export default consoleAction;
//# sourceMappingURL=task-action.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"task-action.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/console/task-action.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,67 @@
import type { CoverageData, CoverageManager, CoverageMetadata, Statement } from "./types.js";
type Line = number;
/**
* @private exposed for testing purposes only
*/
export interface FileReport {
lineExecutionCounts: Map<Line, number>;
executedStatementsCount: number;
unexecutedStatementsCount: number;
executedLinesCount: number;
unexecutedLines: Set<Line>;
}
export interface Report {
[relativePath: string]: FileReport;
}
type FilesMetadata = Map<string, // relative path
Map<string, // composite key
Statement>>;
export declare class CoverageManagerImplementation implements CoverageManager {
#private;
/**
* @private exposed for testing purposes only
*/
filesMetadata: FilesMetadata;
/**
* @private exposed for testing purposes only
*/
data: CoverageData;
constructor(coveragePath: string);
addData(data: CoverageData): Promise<void>;
addMetadata(metadata: CoverageMetadata): Promise<void>;
clearData(id: string): Promise<void>;
saveData(id: string): Promise<void>;
report(...ids: string[]): Promise<void>;
enableReport(): void;
disableReport(): void;
/**
* @private exposed for testing purposes only
*/
loadData(...ids: string[]): Promise<void>;
/**
* @private exposed for testing purposes only
*/
getReport(): Promise<Report>;
/**
* @private exposed for testing purposes only
*/
formatLcovReport(report: Report): string;
/**
* @private exposed for testing purposes only
*/
formatRelativePath(relativePath: string): string;
/**
* @private exposed for testing purposes only
*/
formatCoverage(coverage: number): string;
/**
* @private exposed for testing purposes only
*/
formatLines(lines: Set<number>): string;
/**
* @private exposed for testing purposes only
*/
formatMarkdownReport(report: Report): string;
}
export {};
//# sourceMappingURL=coverage-manager.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"coverage-manager.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/coverage-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,SAAS,EACV,MAAM,YAAY,CAAC;AAyBpB,KAAK,IAAI,GAAG,MAAM,CAAC;AAEnB;;GAEG;AACH,MAAM,WAAW,UAAU;IAOzB,mBAAmB,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAEvC,uBAAuB,EAAE,MAAM,CAAC;IAChC,yBAAyB,EAAE,MAAM,CAAC;IAElC,kBAAkB,EAAE,MAAM,CAAC;IAE3B,eAAe,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,MAAM;IACrB,CAAC,YAAY,EAAE,MAAM,GAAG,UAAU,CAAC;CACpC;AAED,KAAK,aAAa,GAAG,GAAG,CACtB,MAAM,EAAE,gBAAgB;AACxB,GAAG,CACD,MAAM,EAAE,gBAAgB;AACxB,SAAS,CACV,CACF,CAAC;AAEF,qBAAa,6BAA8B,YAAW,eAAe;;IACnE;;OAEG;IACI,aAAa,EAAE,aAAa,CAG/B;IAEJ;;OAEG;IACI,IAAI,EAAE,YAAY,CAAM;gBAMnB,YAAY,EAAE,MAAM;IAUnB,OAAO,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ1C,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBtD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOpC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQnC,MAAM,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB7C,YAAY,IAAI,IAAI;IAIpB,aAAa,IAAI,IAAI;IAI5B;;OAEG;IACU,QAAQ,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAetD;;OAEG;IACU,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IA2DzC;;OAEG;IACI,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAkE/C;;OAEG;IACI,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAiCvD;;OAEG;IACI,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAI/C;;OAEG;IACI,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM;IA4D9C;;OAEG;IACI,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;CAqFpD"}

View File

@@ -0,0 +1,345 @@
import path from "node:path";
import { formatTable } from "@nomicfoundation/hardhat-utils/format";
import { ensureDir, getAllFilesMatching, readJsonFile, readUtf8File, remove, writeJsonFile, writeUtf8File, } from "@nomicfoundation/hardhat-utils/fs";
import chalk from "chalk";
import debug from "debug";
import { getProcessedCoverageInfo } from "./process-coverage.js";
import { generateHtmlReport } from "./reports/html.js";
const log = debug("hardhat:core:coverage:coverage-manager");
const MAX_COLUMN_WIDTH = 80;
export class CoverageManagerImplementation {
/**
* @private exposed for testing purposes only
*/
filesMetadata = new Map();
/**
* @private exposed for testing purposes only
*/
data = [];
#coveragePath;
#reportEnabled = true;
constructor(coveragePath) {
this.#coveragePath = coveragePath;
}
async #getDataPath(id) {
const dataPath = path.join(this.#coveragePath, "data", id);
await ensureDir(dataPath);
return dataPath;
}
async addData(data) {
for (const entry of data) {
this.data.push(entry);
}
log("Added data", JSON.stringify(data, null, 2));
}
async addMetadata(metadata) {
for (const entry of metadata) {
log("Added metadata", JSON.stringify(metadata, null, 2));
let fileStatements = this.filesMetadata.get(entry.relativePath);
if (fileStatements === undefined) {
fileStatements = new Map();
this.filesMetadata.set(entry.relativePath, fileStatements);
}
const key = `${entry.relativePath}-${entry.tag}-${entry.startUtf16}-${entry.endUtf16}`;
const existingData = fileStatements.get(key);
if (existingData === undefined) {
fileStatements.set(key, entry);
}
}
}
async clearData(id) {
const dataPath = await this.#getDataPath(id);
await remove(dataPath);
this.data = [];
log("Cleared data from disk and memory");
}
async saveData(id) {
const dataPath = await this.#getDataPath(id);
const filePath = path.join(dataPath, `${crypto.randomUUID()}.json`);
const data = this.data;
await writeJsonFile(filePath, data);
log("Saved data", id, filePath);
}
async report(...ids) {
if (!this.#reportEnabled) {
return;
}
await this.loadData(...ids);
const report = await this.getReport();
const lcovReport = this.formatLcovReport(report);
const markdownReport = this.formatMarkdownReport(report);
const lcovReportPath = path.join(this.#coveragePath, "lcov.info");
await writeUtf8File(lcovReportPath, lcovReport);
log(`Saved lcov report to ${lcovReportPath}`);
const htmlReportPath = path.join(this.#coveragePath, "html");
await generateHtmlReport(report, htmlReportPath);
console.log(`Saved html report to ${htmlReportPath}`);
console.log(markdownReport);
console.log();
log("Printed markdown report");
}
enableReport() {
this.#reportEnabled = true;
}
disableReport() {
this.#reportEnabled = false;
}
/**
* @private exposed for testing purposes only
*/
async loadData(...ids) {
this.data = [];
for (const id of ids) {
const dataPath = await this.#getDataPath(id);
const filePaths = await getAllFilesMatching(dataPath);
for (const filePath of filePaths) {
const entries = await readJsonFile(filePath);
for (const entry of entries) {
this.data.push(entry);
}
log("Loaded data", id, filePath);
}
}
}
/**
* @private exposed for testing purposes only
*/
async getReport() {
const allExecutedTags = new Set(this.data);
const reportPromises = Array.from(this.filesMetadata.entries()).map(async ([fileRelativePath, fileStatements]) => {
const statements = Array.from(fileStatements.values());
const fileContent = await readUtf8File(path.join(process.cwd(), fileRelativePath));
const tags = new Set();
let executedStatementsCount = 0;
let unexecutedStatementsCount = 0;
for (const { tag } of statements) {
if (allExecutedTags.has(tag)) {
tags.add(tag);
executedStatementsCount++;
}
else {
unexecutedStatementsCount++;
}
}
const coverageInfo = getProcessedCoverageInfo(fileContent, statements, tags);
const lineExecutionCounts = new Map();
coverageInfo.lines.executed.forEach((_, line) => lineExecutionCounts.set(line, 1));
coverageInfo.lines.unexecuted.forEach((_, line) => lineExecutionCounts.set(line, 0));
const executedLinesCount = coverageInfo.lines.executed.size;
const unexecutedLines = new Set(coverageInfo.lines.unexecuted.keys());
return {
path: fileRelativePath,
data: {
lineExecutionCounts,
executedStatementsCount,
unexecutedStatementsCount,
executedLinesCount,
unexecutedLines,
},
};
});
const results = await Promise.all(reportPromises);
return Object.fromEntries(results.map((r) => [r.path, r.data]));
}
/**
* @private exposed for testing purposes only
*/
formatLcovReport(report) {
// NOTE: Format follows the guidelines set out in:
// https://github.com/linux-test-project/lcov/blob/df03ba434eee724bfc2b27716f794d0122951404/man/geninfo.1#L1409
let lcov = "";
// A tracefile is made up of several human-readable lines of text, divided
// into sections.
// If available, a tracefile begins with the testname which is stored in the
// following format:
// TN:<test name>
lcov += "TN:\n";
// For each source file referenced in the .gcda file, there is a section
// containing filename and coverage data:
// SF:<path to the source file>
for (const [relativePath, { lineExecutionCounts, executedLinesCount },] of Object.entries(report)) {
lcov += `SF:${relativePath}\n`;
// NOTE: We report statement coverage as branches to get partial line coverage
// data in tools parsing the lcov files. This is because the lcov format
// does not support statement coverage.
// WARN: This feature is highly experimental and should not be relied upon.
// Branch coverage information is stored one line per branch:
// BRDA:<line_number>,[<exception>]<block>,<branch>,<taken>
// Branch coverage summaries are stored in two lines:
// BRF:<number of branches found>
// BRH:<number of branches hit>
// TODO: currently EDR does not provide branch coverage information.
// for (const [[line, tag], executionCount] of branchExecutionCounts) {
// lcov += `BRDA:${line},0,${tag},${executionCount === 0 ? "-" : executionCount}\n`;
// }
// lcov += `BRH:${executedBranchesCount}\n`;
// lcov += `BRF:${branchExecutionCounts.size}\n`;
// Then there is a list of execution counts for each instrumented line
// (i.e. a line which resulted in executable code):
// DA:<line number>,<execution count>[,<checksum>]
// At the end of a section, there is a summary about how many lines
// were found and how many were actually instrumented:
// LH:<number of lines with a non\-zero execution count>
// LF:<number of instrumented lines>
for (const [line, executionCount] of lineExecutionCounts) {
lcov += `DA:${line},${executionCount}\n`;
}
lcov += `LH:${executedLinesCount}\n`;
lcov += `LF:${lineExecutionCounts.size}\n`;
// Each sections ends with:
// end_of_record
lcov += "end_of_record\n";
}
return lcov;
}
/**
* @private exposed for testing purposes only
*/
formatRelativePath(relativePath) {
if (relativePath.length <= MAX_COLUMN_WIDTH) {
return relativePath;
}
const prefix = "…";
const pathParts = relativePath.split(path.sep);
const parts = [pathParts[pathParts.length - 1]];
let partsLength = parts[0].length;
for (let i = pathParts.length - 2; i >= 0; i--) {
const part = pathParts[i];
if (partsLength +
part.length +
prefix.length +
(parts.length + 1) * path.sep.length <=
MAX_COLUMN_WIDTH) {
parts.push(part);
partsLength += part.length;
}
else {
break;
}
}
parts.push(prefix);
return parts.reverse().join(path.sep);
}
/**
* @private exposed for testing purposes only
*/
formatCoverage(coverage) {
return coverage.toFixed(2).toString();
}
/**
* @private exposed for testing purposes only
*/
formatLines(lines) {
if (lines.size === 0) {
return "-";
}
const sortedLines = Array.from(lines).toSorted((a, b) => a - b);
const intervals = [];
let intervalsLength = 0;
let startLine = sortedLines[0];
let endLine = sortedLines[0];
for (let i = 1; i <= sortedLines.length; i++) {
if (i < sortedLines.length && sortedLines[i] === endLine + 1) {
endLine = sortedLines[i];
}
else {
let interval;
if (startLine === endLine) {
interval = startLine.toString();
}
else {
interval = `${startLine}-${endLine}`;
}
intervals.push(interval);
intervalsLength += interval.length;
if (i < sortedLines.length) {
startLine = sortedLines[i];
endLine = sortedLines[i];
}
}
}
const sep = ", ";
const suffixSep = ",";
const suffix = "…";
if (intervalsLength + (intervals.length - 1) * sep.length <=
MAX_COLUMN_WIDTH) {
return intervals.join(sep);
}
while (intervalsLength +
(intervals.length - 1) * sep.length +
suffix.length +
suffixSep.length >
MAX_COLUMN_WIDTH) {
const interval = intervals.pop();
if (interval !== undefined) {
intervalsLength -= interval.length;
}
else {
break;
}
}
return [intervals.join(sep), suffix].join(suffixSep);
}
/**
* @private exposed for testing purposes only
*/
formatMarkdownReport(report) {
let totalExecutedLines = 0;
let totalExecutableLines = 0;
let totalExecutedStatements = 0;
let totalExecutableStatements = 0;
const rows = [];
rows.push({
type: "title",
text: chalk.bold("Coverage Report"),
});
rows.push({
type: "section-header",
text: chalk.bold("File Coverage"),
});
rows.push({
type: "header",
cells: ["File Path", "Line %", "Statement %", "Uncovered Lines"].map((s) => chalk.yellow(s)),
});
for (const [relativePath, { executedStatementsCount, unexecutedStatementsCount, lineExecutionCounts, executedLinesCount, unexecutedLines, },] of Object.entries(report)) {
const lineCoverage = lineExecutionCounts.size === 0
? 0
: (executedLinesCount * 100.0) / lineExecutionCounts.size;
const statementCoverage = executedStatementsCount === 0
? 0
: (executedStatementsCount * 100.0) /
(executedStatementsCount + unexecutedStatementsCount);
totalExecutedLines += executedLinesCount;
totalExecutableLines += lineExecutionCounts.size;
totalExecutedStatements += executedStatementsCount;
totalExecutableStatements +=
executedStatementsCount + unexecutedStatementsCount;
rows.push({
type: "row",
cells: [
this.formatRelativePath(relativePath),
this.formatCoverage(lineCoverage),
this.formatCoverage(statementCoverage),
this.formatLines(unexecutedLines),
],
});
}
const totalLineCoverage = totalExecutableLines === 0
? 0
: (totalExecutedLines * 100.0) / totalExecutableLines;
const totalStatementCoverage = totalExecutableStatements === 0
? 0
: (totalExecutedStatements * 100.0) / totalExecutableStatements;
rows.push({
type: "header",
cells: [
chalk.yellow("Total"),
this.formatCoverage(totalLineCoverage),
this.formatCoverage(totalStatementCoverage),
"",
],
});
return formatTable(rows);
}
}
//# sourceMappingURL=coverage-manager.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
export { markTestRunStart, markTestRunDone, markTestWorkerDone, } from "./helpers.js";
//# sourceMappingURL=exports.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/exports.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,kBAAkB,GACnB,MAAM,cAAc,CAAC"}

View File

@@ -0,0 +1,2 @@
export { markTestRunStart, markTestRunDone, markTestWorkerDone, } from "./helpers.js";
//# sourceMappingURL=exports.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"exports.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/exports.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,kBAAkB,GACnB,MAAM,cAAc,CAAC"}

View File

@@ -0,0 +1,12 @@
export declare function getCoveragePath(rootPath: string): string;
/**
* NOTE: The following helpers interact with the global HRE instance only;
* This is OK because:
* - They are intended for the internal use only. They are exposed via the
* internal public API only.
* - We know the HRE has been initialized by the time they are used.
*/
export declare function markTestRunStart(id: string): Promise<void>;
export declare function markTestWorkerDone(id: string): Promise<void>;
export declare function markTestRunDone(id: string): Promise<void>;
//# sourceMappingURL=helpers.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/helpers.ts"],"names":[],"mappings":"AAMA,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;GAMG;AAEH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAShE;AAED,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASlE;AAED,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS/D"}

View File

@@ -0,0 +1,35 @@
import path from "node:path";
import { assertHardhatInvariant } from "@nomicfoundation/hardhat-errors";
import { HardhatRuntimeEnvironmentImplementation } from "../../core/hre.js";
export function getCoveragePath(rootPath) {
return path.join(rootPath, "coverage");
}
/**
* NOTE: The following helpers interact with the global HRE instance only;
* This is OK because:
* - They are intended for the internal use only. They are exposed via the
* internal public API only.
* - We know the HRE has been initialized by the time they are used.
*/
export async function markTestRunStart(id) {
const { default: hre } = await import("../../../index.js");
if (hre.globalOptions.coverage === true) {
assertHardhatInvariant(hre instanceof HardhatRuntimeEnvironmentImplementation, "Expected HRE to be an instance of HardhatRuntimeEnvironmentImplementation");
await hre._coverage.clearData(id);
}
}
export async function markTestWorkerDone(id) {
const { default: hre } = await import("../../../index.js");
if (hre.globalOptions.coverage === true) {
assertHardhatInvariant(hre instanceof HardhatRuntimeEnvironmentImplementation, "Expected HRE to be an instance of HardhatRuntimeEnvironmentImplementation");
await hre._coverage.saveData(id);
}
}
export async function markTestRunDone(id) {
const { default: hre } = await import("../../../index.js");
if (hre.globalOptions.coverage === true) {
assertHardhatInvariant(hre instanceof HardhatRuntimeEnvironmentImplementation, "Expected HRE to be an instance of HardhatRuntimeEnvironmentImplementation");
await hre._coverage.report(id);
}
}
//# sourceMappingURL=helpers.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,OAAO,EAAE,uCAAuC,EAAE,MAAM,mBAAmB,CAAC;AAE5E,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;GAMG;AAEH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAU;IAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC3D,IAAI,GAAG,CAAC,aAAa,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QACxC,sBAAsB,CACpB,GAAG,YAAY,uCAAuC,EACtD,2EAA2E,CAC5E,CAAC;QACF,MAAM,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EAAU;IACjD,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC3D,IAAI,GAAG,CAAC,aAAa,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QACxC,sBAAsB,CACpB,GAAG,YAAY,uCAAuC,EACtD,2EAA2E,CAC5E,CAAC;QACF,MAAM,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,EAAU;IAC9C,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC3D,IAAI,GAAG,CAAC,aAAa,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QACxC,sBAAsB,CACpB,GAAG,YAAY,uCAAuC,EACtD,2EAA2E,CAC5E,CAAC;QACF,MAAM,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;AACH,CAAC"}

View File

@@ -0,0 +1,4 @@
import type { CleanHooks } from "../../../../types/hooks.js";
declare const _default: () => Promise<Partial<CleanHooks>>;
export default _default;
//# sourceMappingURL=clean.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"clean.d.ts","sourceRoot":"","sources":["../../../../../../src/internal/builtin-plugins/coverage/hook-handlers/clean.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;8BAMpC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAArD,wBAKG"}

View File

@@ -0,0 +1,9 @@
import { remove } from "@nomicfoundation/hardhat-utils/fs";
import { getCoveragePath } from "../helpers.js";
export default async () => ({
onClean: async (context) => {
const coveragePath = getCoveragePath(context.config.paths.root);
await remove(coveragePath);
},
});
//# sourceMappingURL=clean.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"clean.js","sourceRoot":"","sources":["../../../../../../src/internal/builtin-plugins/coverage/hook-handlers/clean.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,eAAe,KAAK,IAAkC,EAAE,CAAC,CAAC;IACxD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACzB,MAAM,YAAY,GAAG,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChE,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;IAC7B,CAAC;CACF,CAAC,CAAC"}

View File

@@ -0,0 +1,4 @@
import type { HardhatRuntimeEnvironmentHooks } from "../../../../types/hooks.js";
declare const _default: () => Promise<Partial<HardhatRuntimeEnvironmentHooks>>;
export default _default;
//# sourceMappingURL=hre.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"hre.d.ts","sourceRoot":"","sources":["../../../../../../src/internal/builtin-plugins/coverage/hook-handlers/hre.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,4BAA4B,CAAC;8BAQxD,OAAO,CAAC,OAAO,CAAC,8BAA8B,CAAC,CAAC;AAAzE,wBAyBG"}

View File

@@ -0,0 +1,24 @@
import { assertHardhatInvariant } from "@nomicfoundation/hardhat-errors";
import { HardhatRuntimeEnvironmentImplementation } from "../../../core/hre.js";
import { CoverageManagerImplementation } from "../coverage-manager.js";
import { getCoveragePath } from "../helpers.js";
export default async () => ({
created: async (context, hre) => {
if (context.globalOptions.coverage) {
const coveragePath = getCoveragePath(hre.config.paths.root);
const coverageManager = new CoverageManagerImplementation(coveragePath);
assertHardhatInvariant(hre instanceof HardhatRuntimeEnvironmentImplementation, "Expected HRE to be an instance of HardhatRuntimeEnvironmentImplementation");
hre._coverage = coverageManager;
// NOTE: We register this hook dynamically because we use the information about
// the existence of onCoverageData hook handlers to determine whether coverage
// is enabled or not.
hre.hooks.registerHandlers("network", {
onCoverageData: async (_context, coverageData) => {
await coverageManager.addData(coverageData);
return;
},
});
}
},
});
//# sourceMappingURL=hre.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"hre.js","sourceRoot":"","sources":["../../../../../../src/internal/builtin-plugins/coverage/hook-handlers/hre.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,OAAO,EAAE,uCAAuC,EAAE,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,eAAe,KAAK,IAAsD,EAAE,CAAC,CAAC;IAC5E,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;QAC9B,IAAI,OAAO,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;YACnC,MAAM,YAAY,GAAG,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5D,MAAM,eAAe,GAAG,IAAI,6BAA6B,CAAC,YAAY,CAAC,CAAC;YAExE,sBAAsB,CACpB,GAAG,YAAY,uCAAuC,EACtD,2EAA2E,CAC5E,CAAC;YAEF,GAAG,CAAC,SAAS,GAAG,eAAe,CAAC;YAEhC,+EAA+E;YAC/E,8EAA8E;YAC9E,qBAAqB;YACrB,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,SAAS,EAAE;gBACpC,cAAc,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE;oBAC/C,MAAM,eAAe,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;oBAE5C,OAAO;gBACT,CAAC;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}

View File

@@ -0,0 +1,4 @@
import type { SolidityHooks } from "../../../../types/hooks.js";
declare const _default: () => Promise<Partial<SolidityHooks>>;
export default _default;
//# sourceMappingURL=solidity.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"solidity.d.ts","sourceRoot":"","sources":["../../../../../../src/internal/builtin-plugins/coverage/hook-handlers/solidity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;8BAsBvC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;AAAxD,wBAgHG"}

View File

@@ -0,0 +1,94 @@
import path from "node:path";
import { assertHardhatInvariant, HardhatError, } from "@nomicfoundation/hardhat-errors";
import { ensureError } from "@nomicfoundation/hardhat-utils/error";
import { readUtf8File } from "@nomicfoundation/hardhat-utils/fs";
import { findClosestPackageRoot } from "@nomicfoundation/hardhat-utils/package";
import debug from "debug";
import { CoverageManagerImplementation } from "../coverage-manager.js";
import { instrumentSolidityFileForCompilationJob } from "../instrumentation.js";
const log = debug("hardhat:core:coverage:hook-handlers:solidity");
const COVERAGE_LIBRARY_PATH = "__hardhat_coverage_library_a3e9cfe2-41b4-4a1f-ad9e-ac62dd82979e.sol";
export default async () => ({
preprocessProjectFileBeforeBuilding: async (context, sourceName, fsPath, fileContent, solcVersion, next) => {
// NOTE: We do not want to instrument the test project files as we don't
// want to report coverage for them.
const isTestSource = (await context.solidity.getScope(fsPath)) === "tests";
if (context.globalOptions.coverage && !isTestSource) {
try {
const { source, metadata } = instrumentSolidityFileForCompilationJob({
compilationJobSolcVersion: solcVersion,
sourceName,
fileContent,
coverageLibraryPath: COVERAGE_LIBRARY_PATH,
});
// TODO: Remove this once EDR starts returning line information as part
// of the metadata.
let lineNumber = 1;
const lineNumbers = [];
for (const character of fileContent) {
lineNumbers.push(lineNumber);
if (character === "\n") {
lineNumber++;
}
}
const coverageMetadata = [];
for (const m of metadata) {
switch (m.kind) {
case "statement":
const relativePath = path.relative(context.config.paths.root, fsPath);
const tag = Buffer.from(m.tag).toString("hex");
coverageMetadata.push({
relativePath,
tag,
startUtf16: m.startUtf16,
endUtf16: m.endUtf16,
});
break;
default:
// NOTE: We don't support other kinds of metadata yet; however,
// we don't want to start throwing errors if/when EDR adds support
// for new kinds of coverage metadata.
log("Unsupported coverage metadata kind", m.kind);
break;
}
}
assertHardhatInvariant("_coverage" in context &&
context._coverage instanceof CoverageManagerImplementation, "Expected _coverage to be defined in the HookContext, as it's should be defined in the HRE");
await context._coverage.addMetadata(coverageMetadata);
return await next(context, sourceName, fsPath, source, solcVersion);
}
catch (e) {
ensureError(e);
// NOTE: These could be raised if the source content we pass to EDR
// cannot be parsed with a version of the parser we provided, for example.
throw new HardhatError(HardhatError.ERRORS.CORE.COVERAGE.SOURCE_NOT_INSTRUMENTED, {
sourceName,
}, e);
}
}
else {
return next(context, sourceName, fsPath, fileContent, solcVersion);
}
},
preprocessSolcInputBeforeBuilding: async (context, solcInput, next) => {
if (context.globalOptions.coverage) {
// NOTE: We check for a source name clash here. It could happen if the user
// wanted to compile a source with our highly unlikely name by chance or
// if we accidentally tried to preprocess the same solc input twice.
if (solcInput.sources[COVERAGE_LIBRARY_PATH] !== undefined) {
throw new HardhatError(HardhatError.ERRORS.CORE.COVERAGE.IMPORT_PATH_ALREADY_DEFINED, {
importPath: COVERAGE_LIBRARY_PATH,
});
}
// NOTE: We add the coverage.sol straight into sources here. The alternative
// would be to do it during the resolution phase. However, we decided this
// is a simpler solution, at least for now.
const packageRoot = await findClosestPackageRoot(import.meta.url);
const coverageSolPath = path.join(packageRoot, "coverage.sol");
const content = await readUtf8File(coverageSolPath);
solcInput.sources[COVERAGE_LIBRARY_PATH] = { content };
}
return next(context, solcInput);
},
});
//# sourceMappingURL=solidity.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"solidity.js","sourceRoot":"","sources":["../../../../../../src/internal/builtin-plugins/coverage/hook-handlers/solidity.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,5 @@
import type { HardhatPlugin } from "../../../types/plugins.js";
import "./type-extensions.js";
declare const hardhatPlugin: HardhatPlugin;
export default hardhatPlugin;
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAI/D,OAAO,sBAAsB,CAAC;AAE9B,QAAA,MAAM,aAAa,EAAE,aAepB,CAAC;AAEF,eAAe,aAAa,CAAC"}

View File

@@ -0,0 +1,20 @@
import { globalFlag } from "../../core/config.js";
import "./type-extensions.js";
const hardhatPlugin = {
id: "builtin:coverage",
tasks: [],
globalOptions: [
globalFlag({
name: "coverage",
description: "Enables code coverage",
}),
],
hookHandlers: {
clean: () => import("./hook-handlers/clean.js"),
hre: () => import("./hook-handlers/hre.js"),
solidity: () => import("./hook-handlers/solidity.js"),
},
npmPackage: "hardhat",
};
export default hardhatPlugin;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,sBAAsB,CAAC;AAE9B,MAAM,aAAa,GAAkB;IACnC,EAAE,EAAE,kBAAkB;IACtB,KAAK,EAAE,EAAE;IACT,aAAa,EAAE;QACb,UAAU,CAAC;YACT,IAAI,EAAE,UAAU;YAChB,WAAW,EAAE,uBAAuB;SACrC,CAAC;KACH;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,0BAA0B,CAAC;QAC/C,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC;QAC3C,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,6BAA6B,CAAC;KACtD;IACD,UAAU,EAAE,SAAS;CACtB,CAAC;AAEF,eAAe,aAAa,CAAC"}

View File

@@ -0,0 +1,27 @@
import type { InstrumentationMetadata } from "@nomicfoundation/edr";
/**
* Instruments a solidity source file as part of a compilation job. i.e. the
* file is about to be compile as either a root file or a transitive dependency
* of one of the root files.
*
* @param compilationJobSolcVersion The solc version that the compilation job
* will use.
* @param sourceName The source name of the file, as present in the compilation
* job.
* @param fileContent The contents of the file.
* @param coverageLibraryPath The path to the coverage library. i.e. where to
* import it from.
* @returns An object with the instrumented source and its metadata, and the
* solidity version used to instrument the sources.
*/
export declare function instrumentSolidityFileForCompilationJob({ compilationJobSolcVersion, sourceName, fileContent, coverageLibraryPath, }: {
compilationJobSolcVersion: string;
sourceName: string;
fileContent: string;
coverageLibraryPath: string;
}): {
source: string;
metadata: InstrumentationMetadata[];
instrumentationVersion: string;
};
//# sourceMappingURL=instrumentation.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/instrumentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAQpE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uCAAuC,CAAC,EACtD,yBAAyB,EACzB,UAAU,EACV,WAAW,EACX,mBAAmB,GACpB,EAAE;IACD,yBAAyB,EAAE,MAAM,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,MAAM,CAAC;CAC7B,GAAG;IACF,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,sBAAsB,EAAE,MAAM,CAAC;CAChC,CAcA"}

View File

@@ -0,0 +1,27 @@
import { addStatementCoverageInstrumentation, latestSupportedSolidityVersion, } from "@nomicfoundation/edr";
import { satisfies } from "semver";
/**
* Instruments a solidity source file as part of a compilation job. i.e. the
* file is about to be compile as either a root file or a transitive dependency
* of one of the root files.
*
* @param compilationJobSolcVersion The solc version that the compilation job
* will use.
* @param sourceName The source name of the file, as present in the compilation
* job.
* @param fileContent The contents of the file.
* @param coverageLibraryPath The path to the coverage library. i.e. where to
* import it from.
* @returns An object with the instrumented source and its metadata, and the
* solidity version used to instrument the sources.
*/
export function instrumentSolidityFileForCompilationJob({ compilationJobSolcVersion, sourceName, fileContent, coverageLibraryPath, }) {
const latestSupportedVersion = latestSupportedSolidityVersion();
let instrumentationVersion = compilationJobSolcVersion;
if (!satisfies(instrumentationVersion, `<=${latestSupportedVersion}`)) {
instrumentationVersion = latestSupportedVersion;
}
const { source, metadata } = addStatementCoverageInstrumentation(fileContent, sourceName, instrumentationVersion, coverageLibraryPath);
return { source, metadata, instrumentationVersion };
}
//# sourceMappingURL=instrumentation.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/instrumentation.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,mCAAmC,EACnC,8BAA8B,GAC/B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,uCAAuC,CAAC,EACtD,yBAAyB,EACzB,UAAU,EACV,WAAW,EACX,mBAAmB,GAMpB;IAKC,MAAM,sBAAsB,GAAG,8BAA8B,EAAE,CAAC;IAChE,IAAI,sBAAsB,GAAG,yBAAyB,CAAC;IACvD,IAAI,CAAC,SAAS,CAAC,sBAAsB,EAAE,KAAK,sBAAsB,EAAE,CAAC,EAAE,CAAC;QACtE,sBAAsB,GAAG,sBAAsB,CAAC;IAClD,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,mCAAmC,CAC9D,WAAW,EACX,UAAU,EACV,sBAAsB,EACtB,mBAAmB,CACpB,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,sBAAsB,EAAE,CAAC;AACtD,CAAC"}

View File

@@ -0,0 +1,21 @@
import type { CoverageMetadata } from "./types.js";
/**
* Processes the raw EDR coverage information for a file and returns the executed and
* non-executed statements and lines.
*
* @param fileContent The original file content being analyzed
* @param metadata Coverage metadata received from EDR for this file
* @param hitTags The coverage tags recorded as executed during the test run
* for this specific file.
*
* @returns An object containing:
* - statements: the executed and not-executed statements
* - lines: the executed and not-executed line numbers
*/
export declare function getProcessedCoverageInfo(fileContent: string, metadata: CoverageMetadata, hitTags: Set<string>): {
lines: {
executed: Map<number, string>;
unexecuted: Map<number, string>;
};
};
//# sourceMappingURL=process-coverage.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"process-coverage.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/process-coverage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AASpB;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,GACnB;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC9B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACjC,CAAC;CACH,CAwBA"}

View File

@@ -0,0 +1,291 @@
import chalk from "chalk";
// Use constants for the Uint8Array to improve memory usage (1 byte vs 8 bytes per item)
const STATUS_NOT_EXECUTED = 0; // equivalent to false
const STATUS_EXECUTED = 1; // equivalent to true
const STATUS_IGNORED = 2; // equivalent to null
/**
* Processes the raw EDR coverage information for a file and returns the executed and
* non-executed statements and lines.
*
* @param fileContent The original file content being analyzed
* @param metadata Coverage metadata received from EDR for this file
* @param hitTags The coverage tags recorded as executed during the test run
* for this specific file.
*
* @returns An object containing:
* - statements: the executed and not-executed statements
* - lines: the executed and not-executed line numbers
*/
export function getProcessedCoverageInfo(fileContent, metadata, hitTags) {
const statementsByExecution = partitionStatementsByExecution(metadata, hitTags);
const { start, end } = getCoverageBounds(statementsByExecution, fileContent.length);
const characterCoverage = buildCharacterCoverage(fileContent, start, end, statementsByExecution.unexecuted);
// printStatementsForDebugging(fileContent, statementsByExecution);
// printCharacterCoverageForDebugging(fileContent, characterCoverage);
return {
lines: partitionLinesByExecution(fileContent, characterCoverage),
};
}
function partitionStatementsByExecution(metadata, hitTags) {
const executed = [];
const unexecuted = [];
for (const node of metadata) {
if (hitTags.has(node.tag)) {
executed.push(node);
}
else {
unexecuted.push(node);
}
}
return {
executed,
unexecuted,
};
}
// Determine the minimum and maximum character indexes that define the range
// where coverage should be calculated, excluding parts that the tests never
// reach. This removes irrelevant sections from coverage analysis, such as the
// beginning or end of each Solidity file.
// Example:
// // SPDX-License-Identifier: MIT
// pragma solidity ^0.8.0;
function getCoverageBounds(statementsByExecution, fileLength) {
let start = fileLength;
let end = 0;
const { executed, unexecuted } = statementsByExecution;
for (const s of [...executed, ...unexecuted]) {
if (s.startUtf16 < start) {
start = s.startUtf16;
}
if (s.endUtf16 > end) {
end = s.endUtf16;
}
}
return { start, end };
}
// Return an array with the same length as the file content. Each position in
// the array corresponds to a character in the file. The value at each position
// indicates whether that character was executed during tests (STATUS_EXECUTED),
// not executed (STATUS_NOT_EXECUTED), or not relevant for coverage
// (STATUS_IGNORED). STATUS_IGNORED is used to indicate characters that are not
// executed during test, such as those found at the start or end of every Solidity file.
// Example:
// // SPDX-License-Identifier: MIT
// pragma solidity ^0.8.0;
function buildCharacterCoverage(fileContent, start, end, unexecutedStatements) {
// Use Uint8Array instead of Array<null | boolean> for memory efficiency.
// We initialize with STATUS_IGNORED (equivalent to filling with null)
const characterCoverage = new Uint8Array(fileContent.length).fill(STATUS_IGNORED);
// Initially mark all characters that may be executed during the tests as
// STATUS_EXECUTED. They will be set to false later if they are not executed.
// The coverage statement received from EDR will provide this information.
// Setting everything to true first simplifies the logic for extracting
// coverage data. Starting with false and toggling only covered characters
// would make statement processing more complex.
for (let i = start; i < end; i++) {
if (charMustBeIgnored(fileContent[i])) {
continue;
}
characterCoverage[i] = STATUS_EXECUTED;
}
for (const statement of unexecutedStatements) {
for (let i = statement.startUtf16; i < statement.endUtf16; i++) {
if (characterCoverage[i] === STATUS_IGNORED) {
continue;
}
characterCoverage[i] = STATUS_NOT_EXECUTED;
}
}
markNonExecutablePatterns(fileContent, characterCoverage);
return characterCoverage;
}
// The following characters are not relevant for the code coverage, so they
// must be ignored when marking characters as executed (true) or not executed (false)
function charMustBeIgnored(char) {
const code = char.charCodeAt(0);
return (code === 32 || // Space
(code >= 9 && code <= 13) || // \t (9), \n (10), \v (11), \f (12), \r (13)
code === 123 || // {
code === 125 // }
);
}
// Mark different types of substrings as not relevant for code coverage (set them to STATUS_IGNORED).
// For example, all "else" substrings or comments are not relevant for code coverage.
function markNonExecutablePatterns(fileContent, characterCoverage) {
// Comments that start with //
markMatchingCharsWithIgnored(fileContent, characterCoverage, /\/\/.*?(?=\n|$)/g);
// Comments wrapped between /* and */
markMatchingCharsWithIgnored(fileContent, characterCoverage, /\/\*[\s\S]*?\*\//g);
// Lines containing `else`, since they do not represent executable code by themselves.
// Keep in mind that `else` is preceded by a closing brace and followed by an opening brace.
// This can span multiple lines.
markMatchingCharsWithIgnored(fileContent, characterCoverage, /\}\s*\belse\b\s*\{/g);
// Lines containing the function signature. This can span multiple lines
markMatchingCharsWithIgnored(fileContent, characterCoverage, /^\s*(function\s+[A-Za-z_$][A-Za-z0-9_$]*\s*\([\s\S]*?\)[^{]*?)(?=\s*\{)/gm);
// Lines containing the catch signature. This can span multiple lines.
markMatchingCharsWithIgnored(fileContent, characterCoverage, /\bcatch\b(?:\s+[A-Za-z_][A-Za-z0-9_]*)?\s*(?:\([\s\S]*?\))?(?=\s*\{)/g);
}
function markMatchingCharsWithIgnored(fileContent, characterCoverage, regex) {
for (const match of fileContent.matchAll(regex)) {
for (let i = match.index; i < match.index + match[0].length; i++) {
characterCoverage[i] = STATUS_IGNORED;
}
}
}
// Generate non overlapping coverage statements. Every character between the start
// and end indexes is guaranteed to be either executed or not executed.
// Unlike the statements received from EDR, which may include nested sub
// statements, these are processed and have no sub statements or overlapping statements.
// Example:
// [
// { start: 12, end: 20, executed: true },
// { start: 15, end: 17, executed: true }
// ]
// ->
// [
// { start: 12, end: 20, executed: true }
// ]
/* eslint-disable-next-line @typescript-eslint/no-unused-vars
-- currently not used, but it will be used to create more detailed.
It will be used to return statements from the function `getProcessedCoverageInfo` */
function getProcessedExecutedStatements(characterCoverage) {
const executed = generateProcessedStatements(characterCoverage, STATUS_EXECUTED);
const unexecuted = generateProcessedStatements(characterCoverage, STATUS_NOT_EXECUTED);
return {
executed,
unexecuted,
};
}
// Based on the marked file, where each character is marked as either
// executed (STATUS_EXECUTED) or not executed (STATUS_NOT_EXECUTED),
// generate non-overlapping statements that indicate the start and
// end indices, along with whether they were executed or not.
function generateProcessedStatements(characterCoverage, targetStatus) {
const ranges = [];
const fileLength = characterCoverage.length;
let start = -1;
for (let i = 0; i < fileLength; i++) {
if (characterCoverage[i] === targetStatus) {
if (start === -1) {
start = i; // begin new range
}
}
else {
if (start !== -1) {
// Map back to boolean for the output object
ranges.push({
startUtf16: start,
endUtf16: i - 1,
executed: targetStatus === STATUS_EXECUTED,
});
start = -1;
}
}
}
// close last range if file ends inside a run
if (start !== -1) {
ranges.push({
startUtf16: start,
endUtf16: fileLength - 1,
executed: targetStatus === STATUS_EXECUTED,
});
}
return ranges;
}
// Return the executed and non executed lines based on the marked file.
// Some lines are excluded from the execution count, for example, comments.
function partitionLinesByExecution(fileContent, characterCoverage) {
const executed = new Map();
const unexecuted = new Map();
let lineStart = 0;
let isLineIgnored = true;
let isLineExecutedOrIgnored = true;
let lineNumber = 1; // File lines start at 1
// Helper to process a line and push to correct map
const processLine = (endIndex) => {
// Only process if the line isn't entirely ignored
if (!isLineIgnored) {
const lineText = fileContent.slice(lineStart, endIndex);
if (isLineExecutedOrIgnored) {
executed.set(lineNumber, lineText);
}
else {
unexecuted.set(lineNumber, lineText);
}
}
// Reset state for the next line
lineStart = endIndex + 1;
isLineIgnored = true;
isLineExecutedOrIgnored = true;
lineNumber++;
};
for (let i = 0; i < fileContent.length; i++) {
const char = fileContent[i];
const status = characterCoverage[i];
if (status !== STATUS_IGNORED) {
isLineIgnored = false;
}
if (status === STATUS_NOT_EXECUTED) {
isLineExecutedOrIgnored = false;
}
if (char === "\n") {
processLine(i);
}
}
// Handle the final line if the file doesn't end in a newline
if (lineStart < fileContent.length) {
processLine(fileContent.length);
}
return { executed, unexecuted };
}
// Enable this function while debugging to display the coverage for a file.
// The file will be printed with green characters when they are executed,
// red characters when they are not executed,
// and gray characters when they are irrelevant for code coverage.
/* eslint-disable-next-line @typescript-eslint/no-unused-vars
-- this function can be enabled for debugging purposes */
function printStatementsForDebugging(fileContent, statementsByExecution) {
const relativePath = statementsByExecution.executed.length > 0
? statementsByExecution.executed[0].relativePath
: statementsByExecution.unexecuted[0].relativePath;
console.debug("Statements for file: " + relativePath);
console.debug("Executed statements:");
let counter = 0;
for (const statement of statementsByExecution.executed) {
console.debug(counter++ + " ---");
for (let i = statement.startUtf16; i < statement.endUtf16; i++) {
process.stdout.write(chalk.gray(fileContent[i]));
}
console.debug();
}
console.debug();
console.debug("Unexecuted statements:");
counter = 0;
for (const statement of statementsByExecution.unexecuted) {
console.debug(counter++ + " ---");
for (let i = statement.startUtf16; i < statement.endUtf16; i++) {
process.stdout.write(chalk.gray(fileContent[i]));
}
console.debug();
}
}
// Enable this function while debugging to display the coverage for a file.
// The file will be printed with green characters when they are executed,
// red characters when they are not executed,
// and gray characters when they are irrelevant for code coverage.
/* eslint-disable-next-line @typescript-eslint/no-unused-vars
-- this function can be enabled for debugging purposes */
function printCharacterCoverageForDebugging(fileContent, characterCoverage) {
for (let i = 0; i < characterCoverage.length; i++) {
if (characterCoverage[i] === STATUS_IGNORED) {
process.stdout.write(chalk.gray(fileContent[i]));
}
else if (characterCoverage[i] === STATUS_EXECUTED) {
process.stdout.write(chalk.green(fileContent[i]));
}
else {
process.stdout.write(chalk.red(fileContent[i]));
}
}
}
//# sourceMappingURL=process-coverage.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
import type { Report } from "../coverage-manager.js";
export declare function generateHtmlReport(report: Report, htmlReportPath: string): Promise<void>;
//# sourceMappingURL=html.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../../../../../src/internal/builtin-plugins/coverage/reports/html.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAWrD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAsCf"}

View File

@@ -0,0 +1,35 @@
import path from "node:path";
import { istanbulLibCoverage, istanbulLibReport, istanbulReports, } from "@nomicfoundation/hardhat-vendored/coverage";
export async function generateHtmlReport(report, htmlReportPath) {
const baseDir = process.cwd();
const coverageMap = istanbulLibCoverage.createCoverageMap({});
// Construct coverage data for each tested file,
// detailing whether each line was executed or not.
for (const [p, coverageInfo] of Object.entries(report)) {
const testedFilePath = path.join(baseDir, p);
const fc = {
path: testedFilePath,
statementMap: {},
fnMap: {}, // Cannot be derived from current report data
branchMap: {}, // Cannot be derived from current report data
s: {},
f: {}, // Cannot be derived from current report data
b: {},
};
for (const [line, count] of coverageInfo.lineExecutionCounts) {
fc.statementMap[line] = {
start: { line, column: 0 },
end: { line, column: 0 },
};
// TODO: currently EDR does not provide per-statement coverage counts
fc.s[line] = count > 0 ? 1 : 0; // mark as covered if hit at least once
}
coverageMap.addFileCoverage(fc);
}
const context = istanbulLibReport.createContext({
dir: htmlReportPath,
coverageMap,
});
istanbulReports.create("html", undefined).execute(context);
}
//# sourceMappingURL=html.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"html.js","sourceRoot":"","sources":["../../../../../../src/internal/builtin-plugins/coverage/reports/html.ts"],"names":[],"mappings":"AAGA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,GAChB,MAAM,4CAA4C,CAAC;AAEpD,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAc,EACd,cAAsB;IAEtB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC9B,MAAM,WAAW,GAAG,mBAAmB,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;IAE9D,gDAAgD;IAChD,mDAAmD;IACnD,KAAK,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACvD,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAE7C,MAAM,EAAE,GAAqB;YAC3B,IAAI,EAAE,cAAc;YACpB,YAAY,EAAE,EAAE;YAChB,KAAK,EAAE,EAAE,EAAE,6CAA6C;YACxD,SAAS,EAAE,EAAE,EAAE,6CAA6C;YAC5D,CAAC,EAAE,EAAE;YACL,CAAC,EAAE,EAAE,EAAE,6CAA6C;YACpD,CAAC,EAAE,EAAE;SACN,CAAC;QAEF,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,YAAY,CAAC,mBAAmB,EAAE,CAAC;YAC7D,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG;gBACtB,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE;gBAC1B,GAAG,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE;aACzB,CAAC;YAEF,qEAAqE;YACrE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,uCAAuC;QACzE,CAAC;QAED,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,OAAO,GAAG,iBAAiB,CAAC,aAAa,CAAC;QAC9C,GAAG,EAAE,cAAc;QACnB,WAAW;KACZ,CAAC,CAAC;IAEH,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAC7D,CAAC"}

View File

@@ -0,0 +1,7 @@
import "../../../types/global-options.js";
declare module "../../../types/global-options.js" {
interface GlobalOptions {
coverage: boolean;
}
}
//# sourceMappingURL=type-extensions.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"type-extensions.d.ts","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/type-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAC;AAC1C,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAiB,aAAa;QAC5B,QAAQ,EAAE,OAAO,CAAC;KACnB;CACF"}

View File

@@ -0,0 +1,2 @@
import "../../../types/global-options.js";
//# sourceMappingURL=type-extensions.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"type-extensions.js","sourceRoot":"","sources":["../../../../../src/internal/builtin-plugins/coverage/type-extensions.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAC"}

Some files were not shown because too many files have changed in this diff Show More