hardhat
Version:
Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.
184 lines • 8.12 kB
JavaScript
import { assertHardhatInvariant, HardhatError, } from "@nomicfoundation/hardhat-errors";
import { isCi } from "@nomicfoundation/hardhat-utils/ci";
import { createDebug } from "@nomicfoundation/hardhat-utils/debug";
import { setGlobalOptionsAsEnvVariables } from "@nomicfoundation/hardhat-utils/env";
import { ensureError } from "@nomicfoundation/hardhat-utils/error";
import { isResult } from "../../utils/result.js";
import { generateTasks } from "../builtin-plugins/hhu/tasks/index.js";
import { NETWORK_GLOBAL_OPTION } from "../builtin-plugins/network-manager/network-option.js";
import { globalFlag } from "../core/config.js";
import { TaskManagerImplementation } from "../core/tasks/task-manager.js";
import { getHardhatVersion } from "../utils/package.js";
import { printErrorMessages } from "./error-handler.js";
import { getGlobalHelpString } from "./help/get-global-help-string.js";
import { getHelpString } from "./help/get-help-string.js";
import { parseGlobalOptions, parseRawArguments, parseTask, parseTaskArguments, } from "./parser.js";
import { sendTaskAnalytics } from "./telemetry/analytics/analytics.js";
import { setupGlobalUnhandledErrorHandlers } from "./telemetry/error-reporter/global-error-handlers.js";
import { sendErrorTelemetry } from "./telemetry/error-reporter/reporter.js";
export async function main(rawArguments, options = {}) {
// We set up the global unhandled errors before running any functionality
setupGlobalUnhandledErrorHandlers();
const print = options.print ?? console.log;
const log = createDebug("hardhat:core:hhu:main");
let hhuGlobalOptions;
log("hhu started");
try {
const cliArguments = parseRawArguments(rawArguments);
const usedCliArguments = new Array(cliArguments.length).fill(false);
hhuGlobalOptions = await parseHhuGlobalOptions(cliArguments, usedCliArguments);
// We forward `network` as an env variable so it's available when we lazily
// import the real HRE in utils that need it. We explicitly whitelist it
// (rather than forwarding every hhu global option) so that hhu-only options
// never leak into the real HRE's global-option namespace.
setGlobalOptionsAsEnvVariables({ network: hhuGlobalOptions.network });
log("Parsed hhu global options");
if (hhuGlobalOptions.version) {
return await printHhuVersionMessage(print);
}
const globalOptionDefinitions = new Map(HHU_GLOBAL_OPTIONS_DEFINITIONS);
const rootTasks = taskDefinitionsToTasksMap(generateTasks({ prefixWithUtils: false }));
const taskOrId = parseTask(cliArguments, usedCliArguments, rootTasks);
if (Array.isArray(taskOrId)) {
if (taskOrId.length === 0) {
const globalHelp = await getGlobalHelpString(rootTasks, globalOptionDefinitions, { command: "hhu" });
print(globalHelp);
return;
}
throw new HardhatError(HardhatError.ERRORS.CORE.TASK_DEFINITIONS.TASK_NOT_FOUND, { task: taskOrId.join(" ") });
}
const task = taskOrId;
if (task.isEmpty && usedCliArguments.includes(false)) {
const invalidSubtask = cliArguments[usedCliArguments.indexOf(false)];
throw new HardhatError(HardhatError.ERRORS.CORE.TASK_DEFINITIONS.UNRECOGNIZED_SUBTASK, {
task: task.id.join(" "),
invalidSubtask,
});
}
if (hhuGlobalOptions.help || task.isEmpty) {
const taskHelp = await getHelpString(task, globalOptionDefinitions, {
command: "hhu",
});
print(taskHelp);
return;
}
const taskArguments = parseTaskArguments(cliArguments, usedCliArguments, task);
log(`Running task "${task.id.join(" ")}"`);
const [taskResult] = await Promise.all([
task.run(taskArguments),
sendTaskAnalytics(task.id, "hhu"),
]);
if (isResult(taskResult) && !taskResult.success) {
process.exitCode = 1;
}
}
catch (error) {
ensureError(error);
await printErrorMessages(error, hhuGlobalOptions?.showStackTraces);
try {
await sendErrorTelemetry(error);
}
catch (e) {
log("Couldn't report error to sentry: %O", e);
}
if (options.rethrowErrors) {
throw error;
}
process.exitCode = 1;
}
}
const HHU_GLOBAL_OPTIONS_DEFINITIONS = new Map([
[
"help",
{
pluginId: "builtin",
option: globalFlag({
name: "help",
shortName: "h",
description: "Show this message, or a task's help if its name is provided.",
}),
},
],
[
"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 hhu.",
}),
},
],
[
"network",
{
pluginId: "builtin",
option: NETWORK_GLOBAL_OPTION,
},
],
]);
export async function parseHhuGlobalOptions(cliArguments, usedCliArguments) {
const hhuGlobalOptions = await parseGlobalOptions(HHU_GLOBAL_OPTIONS_DEFINITIONS, cliArguments, usedCliArguments);
return {
help: hhuGlobalOptions.help ?? false,
showStackTraces: hhuGlobalOptions.showStackTraces ?? isCi(),
version: hhuGlobalOptions.version ?? false,
network: hhuGlobalOptions.network,
};
}
export async function printHhuVersionMessage(print = console.log) {
print(await getHardhatVersion());
}
function makeStrictProxy(name, object) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- inaccessible properties will throw an assertion error
return new Proxy(object, {
get(target, property) {
assertHardhatInvariant(Object.hasOwn(object, property), `Unexpected access of property "${String(property)}" in ${name}.`);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- we know this is safe because of the assertion above
return target[property];
},
});
}
function taskDefinitionsToTasksMap(tasks) {
// To be able to parse the hhu arguments, we need to create a tasks map from
// the task definitions. The easiest way to do that is by using
// `TaskManagerImplementation`, but this needs an HRE and we don't want to
// create one just for this. So we use a fake HRE that has the minimum
// properties needed.
//
// The exception is `network.create`: utils that need a network get the real
// implementation, which lazily loads Hardhat (and thus creates the project's
// HRE) only when such a util actually runs.
//
// One downside of this approach is that we _won't_ get a compilation error
// if `TaskManagerImplementation` tries to access a property that doesn't exist in the
// fake HRE, but tests should catch that.
//
// The `satisfies FakeHhuHardhatRuntimeEnvironment` keeps this fake HRE in sync
// with the surface utils actions rely on.
const fakeHre = makeStrictProxy("hhu's proxied HRE", {
config: makeStrictProxy("hhu's proxied config", {
tasks,
plugins: [],
}),
network: makeStrictProxy("hhu's proxied network", {
create: async (networkOrParams) => {
const hre = await import("../../index.js");
return await hre.network.create(networkOrParams);
},
}),
});
const taskManager = new TaskManagerImplementation(fakeHre, new Map(HHU_GLOBAL_OPTIONS_DEFINITIONS));
return taskManager.rootTasks;
}
//# sourceMappingURL=hhu.js.map