UNPKG

hardhat

Version:

Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.

516 lines (515 loc) 22.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createProject = exports.showSoliditySurveyMessage = exports.EMPTY_HARDHAT_CONFIG = void 0; const picocolors_1 = __importDefault(require("picocolors")); const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const constants_1 = require("../constants"); const errors_1 = require("../core/errors"); const errors_list_1 = require("../core/errors-list"); const project_structure_1 = require("../core/project-structure"); const fs_utils_1 = require("../util/fs-utils"); const global_dir_1 = require("../util/global-dir"); const lang_1 = require("../util/lang"); const packageInfo_1 = require("../util/packageInfo"); const strings_1 = require("../util/strings"); const ci_detection_1 = require("../util/ci-detection"); const prompt_1 = require("./prompt"); const emoji_1 = require("./emoji"); const analytics_1 = require("./analytics"); const banner_manager_1 = require("./banner-manager"); var Action; (function (Action) { Action["CREATE_JAVASCRIPT_PROJECT_ACTION"] = "Create a JavaScript project"; Action["CREATE_TYPESCRIPT_PROJECT_ACTION"] = "Create a TypeScript project"; Action["CREATE_TYPESCRIPT_VIEM_PROJECT_ACTION"] = "Create a TypeScript project (with Viem)"; Action["CREATE_EMPTY_HARDHAT_CONFIG_ACTION"] = "Create an empty hardhat.config.js"; Action["QUIT_ACTION"] = "Quit"; })(Action || (Action = {})); const HARDHAT_PACKAGE_NAME = "hardhat"; const PROJECT_DEPENDENCIES = {}; const ETHERS_PROJECT_DEPENDENCIES = { "@nomicfoundation/hardhat-toolbox": "^5.0.0", }; const VIEM_PROJECT_DEPENDENCIES = { "@nomicfoundation/hardhat-toolbox-viem": "^3.0.0", }; const PEER_DEPENDENCIES = { hardhat: "^2.14.0", "@nomicfoundation/hardhat-network-helpers": "^1.0.0", "@nomicfoundation/hardhat-verify": "^2.0.0", chai: "^4.2.0", "hardhat-gas-reporter": "^1.0.8", "solidity-coverage": "^0.8.0", "@nomicfoundation/hardhat-ignition": "^0.15.0", }; const ETHERS_PEER_DEPENDENCIES = { "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", "@nomicfoundation/hardhat-ethers": "^3.0.0", ethers: "^6.4.0", "@typechain/hardhat": "^9.0.0", typechain: "^8.3.0", "@typechain/ethers-v6": "^0.5.0", "@nomicfoundation/hardhat-ignition-ethers": "^0.15.0", }; const VIEM_PEER_DEPENDENCIES = { "@nomicfoundation/hardhat-viem": "^2.0.0", viem: "^2.7.6", "@nomicfoundation/hardhat-ignition-viem": "^0.15.0", }; const TYPESCRIPT_DEPENDENCIES = {}; const TYPESCRIPT_PEER_DEPENDENCIES = { "@types/chai": "^4.2.0", "@types/mocha": ">=9.1.0", "@types/node": ">=18.0.0", "ts-node": ">=8.0.0", typescript: ">=4.5.0", }; const TYPESCRIPT_ETHERS_PEER_DEPENDENCIES = { typescript: ">=4.5.0", }; const TYPESCRIPT_VIEM_PEER_DEPENDENCIES = { "@types/chai-as-promised": "^7.1.6", typescript: "~5.0.4", }; // generated with the "colossal" font function printAsciiLogo() { console.log(picocolors_1.default.blue("888 888 888 888 888")); console.log(picocolors_1.default.blue("888 888 888 888 888")); console.log(picocolors_1.default.blue("888 888 888 888 888")); console.log(picocolors_1.default.blue("8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888")); console.log(picocolors_1.default.blue('888 888 "88b 888P" d88" 888 888 "88b "88b 888')); console.log(picocolors_1.default.blue("888 888 .d888888 888 888 888 888 888 .d888888 888")); console.log(picocolors_1.default.blue("888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.")); console.log(picocolors_1.default.blue('888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888')); console.log(""); } async function printWelcomeMessage() { const packageJson = await (0, packageInfo_1.getPackageJson)(); console.log(picocolors_1.default.cyan(`${(0, emoji_1.emoji)("👷 ")}Welcome to ${constants_1.HARDHAT_NAME} v${packageJson.version}${(0, emoji_1.emoji)(" 👷‍")}\n`)); } async function copySampleProject(projectRoot, projectType, isEsm) { const packageRoot = (0, packageInfo_1.getPackageRoot)(); let sampleProjectName; if (projectType === Action.CREATE_JAVASCRIPT_PROJECT_ACTION) { if (isEsm) { sampleProjectName = "javascript-esm"; } else { sampleProjectName = "javascript"; } } else { if (isEsm) { (0, errors_1.assertHardhatInvariant)(false, "Shouldn't try to create a TypeScript project in an ESM based project"); } else if (projectType === Action.CREATE_TYPESCRIPT_VIEM_PROJECT_ACTION) { sampleProjectName = "typescript-viem"; } else { sampleProjectName = "typescript"; } } await fs_extra_1.default.ensureDir(projectRoot); const sampleProjectPath = path_1.default.join(packageRoot, "sample-projects", sampleProjectName); // relative paths to all the sample project files const sampleProjectFiles = (await (0, fs_utils_1.getAllFilesMatching)(sampleProjectPath)).map((file) => path_1.default.relative(sampleProjectPath, file)); // check if the target directory already has files that clash with the sample // project files const existingFiles = []; for (const file of sampleProjectFiles) { const targetProjectFile = path_1.default.resolve(projectRoot, file); // if the project already has a README.md file, we'll skip it when // we copy the files if (file !== "README.md" && fs_extra_1.default.existsSync(targetProjectFile)) { existingFiles.push(file); } } if (existingFiles.length > 0) { const errorMsg = `We couldn't initialize the sample project because ${(0, strings_1.pluralize)(existingFiles.length, "this file already exists", "these files already exist")}: ${existingFiles.join(", ")} Please delete or rename ${(0, strings_1.pluralize)(existingFiles.length, "it", "them")} and try again.`; console.log(picocolors_1.default.red(errorMsg)); process.exit(1); } // copy the files for (const file of sampleProjectFiles) { const sampleProjectFile = path_1.default.resolve(sampleProjectPath, file); const targetProjectFile = path_1.default.resolve(projectRoot, file); if (file === "README.md" && fs_extra_1.default.existsSync(targetProjectFile)) { // we don't override the readme if it exists continue; } if (file === "LICENSE.md") { // we don't copy the license continue; } fs_extra_1.default.copySync(sampleProjectFile, targetProjectFile); } } async function addGitIgnore(projectRoot) { const gitIgnorePath = path_1.default.join(projectRoot, ".gitignore"); let content = await (0, project_structure_1.getRecommendedGitIgnore)(); if (await fs_extra_1.default.pathExists(gitIgnorePath)) { const existingContent = await fs_extra_1.default.readFile(gitIgnorePath, "utf-8"); content = `${existingContent} ${content}`; } await fs_extra_1.default.writeFile(gitIgnorePath, content); } async function printRecommendedDepsInstallationInstructions(projectType) { console.log(`You need to install these dependencies to run the sample project:`); const cmd = await getRecommendedDependenciesInstallationCommand(await getDependencies(projectType)); console.log(` ${cmd.join(" ")}`); } // exported so we can test that it uses the latest supported version of solidity exports.EMPTY_HARDHAT_CONFIG = `/** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: "0.8.28", }; `; async function writeEmptyHardhatConfig(isEsm) { const hardhatConfigFilename = isEsm ? "hardhat.config.cjs" : "hardhat.config.js"; return fs_extra_1.default.writeFile(hardhatConfigFilename, exports.EMPTY_HARDHAT_CONFIG, "utf-8"); } async function getAction(isEsm) { if (process.env.HARDHAT_CREATE_JAVASCRIPT_PROJECT_WITH_DEFAULTS !== undefined) { return Action.CREATE_JAVASCRIPT_PROJECT_ACTION; } else if (process.env.HARDHAT_CREATE_TYPESCRIPT_PROJECT_WITH_DEFAULTS !== undefined) { return Action.CREATE_TYPESCRIPT_PROJECT_ACTION; } else if (process.env.HARDHAT_CREATE_TYPESCRIPT_VIEM_PROJECT_WITH_DEFAULTS !== undefined) { return Action.CREATE_TYPESCRIPT_VIEM_PROJECT_ACTION; } const { default: enquirer } = await Promise.resolve().then(() => __importStar(require("enquirer"))); try { const actionResponse = await enquirer.prompt([ { name: "action", type: "select", message: "What do you want to do?", initial: 0, choices: Object.values(Action) .filter((a) => { if (isEsm && a === Action.CREATE_TYPESCRIPT_VIEM_PROJECT_ACTION) { // we omit the viem option for ESM projects to avoid showing // two disabled options return false; } return true; }) .map((a) => { let message; if (isEsm) { if (a === Action.CREATE_EMPTY_HARDHAT_CONFIG_ACTION) { message = a.replace(".js", ".cjs"); } else if (a === Action.CREATE_TYPESCRIPT_PROJECT_ACTION) { message = `${a} (not available for ESM projects)`; } else { message = a; } } else { message = a; } return { name: a, message, value: a, }; }), }, ]); if (Object.values(Action).includes(actionResponse.action)) { return actionResponse.action; } else { throw new errors_1.HardhatError(errors_list_1.ERRORS.GENERAL.UNSUPPORTED_OPERATION, { operation: `Responding with "${actionResponse.action}" to the project initialization wizard`, }); } } catch (e) { if (e === "") { return Action.QUIT_ACTION; } // eslint-disable-next-line @nomicfoundation/hardhat-internal-rules/only-hardhat-error throw e; } } async function createPackageJson() { await fs_extra_1.default.writeJson("package.json", { name: "hardhat-project", }, { spaces: 2 }); } function showStarOnGitHubMessage() { console.log(picocolors_1.default.cyan("Give Hardhat a star on Github if you're enjoying it!") + (0, emoji_1.emoji)(" ⭐️✨")); console.log(); console.log(picocolors_1.default.cyan(" https://github.com/NomicFoundation/hardhat")); } function showSoliditySurveyMessage() { if (new Date() > new Date("2025-01-31 23:39")) { // the survey has finished return; } console.log(); console.log(picocolors_1.default.cyan("Please take a moment to complete the 2024 Solidity Survey: https://hardhat.org/solidity-survey-2024")); } exports.showSoliditySurveyMessage = showSoliditySurveyMessage; async function createProject() { printAsciiLogo(); await printWelcomeMessage(); let packageJson; if (await fs_extra_1.default.pathExists("package.json")) { packageJson = await fs_extra_1.default.readJson("package.json"); } const isEsm = packageJson?.type === "module"; const action = await getAction(isEsm); if (action === Action.QUIT_ACTION) { return; } if (isEsm && action === Action.CREATE_TYPESCRIPT_PROJECT_ACTION) { throw new errors_1.HardhatError(errors_list_1.ERRORS.GENERAL.ESM_TYPESCRIPT_PROJECT_CREATION); } if (packageJson === undefined) { await createPackageJson(); } if (action === Action.CREATE_EMPTY_HARDHAT_CONFIG_ACTION) { await writeEmptyHardhatConfig(isEsm); console.log(`${(0, emoji_1.emoji)("✨ ")}${picocolors_1.default.cyan(`Config file created`)}${(0, emoji_1.emoji)(" ✨")}`); if (!isInstalled(HARDHAT_PACKAGE_NAME)) { console.log(""); console.log(`You need to install hardhat locally to use it. Please run:`); const cmd = await getRecommendedDependenciesInstallationCommand({ [HARDHAT_PACKAGE_NAME]: `^${(await (0, packageInfo_1.getPackageJson)()).version}`, }); console.log(""); console.log(cmd.join(" ")); console.log(""); } console.log(); showStarOnGitHubMessage(); showSoliditySurveyMessage(); return; } let responses; const useDefaultPromptResponses = process.env.HARDHAT_CREATE_JAVASCRIPT_PROJECT_WITH_DEFAULTS !== undefined || process.env.HARDHAT_CREATE_TYPESCRIPT_PROJECT_WITH_DEFAULTS !== undefined || process.env.HARDHAT_CREATE_TYPESCRIPT_VIEM_PROJECT_WITH_DEFAULTS !== undefined; if (useDefaultPromptResponses) { responses = { projectRoot: process.cwd(), shouldAddGitIgnore: true, }; } else { try { responses = await (0, prompt_1.confirmProjectCreation)(); } catch (e) { if (e === "") { return; } // eslint-disable-next-line @nomicfoundation/hardhat-internal-rules/only-hardhat-error throw e; } } const { projectRoot, shouldAddGitIgnore } = responses; if (shouldAddGitIgnore) { await addGitIgnore(projectRoot); } if (process.env.HARDHAT_DISABLE_TELEMETRY_PROMPT !== "true" && !(0, ci_detection_1.isRunningOnCiServer)() && (0, global_dir_1.hasConsentedTelemetry)() === undefined) { await (0, analytics_1.requestTelemetryConsent)(); } await copySampleProject(projectRoot, action, isEsm); let shouldShowInstallationInstructions = true; if (await canInstallRecommendedDeps()) { const dependencies = await getDependencies(action); const recommendedDeps = Object.keys(dependencies); const dependenciesToInstall = (0, lang_1.fromEntries)(Object.entries(dependencies).filter(([name]) => !isInstalled(name))); const installedRecommendedDeps = recommendedDeps.filter(isInstalled); const installedExceptHardhat = installedRecommendedDeps.filter((name) => name !== HARDHAT_PACKAGE_NAME); if (installedRecommendedDeps.length === recommendedDeps.length) { shouldShowInstallationInstructions = false; } else if (installedExceptHardhat.length === 0) { const shouldInstall = useDefaultPromptResponses || (await (0, prompt_1.confirmRecommendedDepsInstallation)(dependenciesToInstall, await getProjectPackageManager())); if (shouldInstall) { const installed = await installRecommendedDependencies(dependenciesToInstall); if (!installed) { console.warn(picocolors_1.default.red("Failed to install the sample project's dependencies")); } shouldShowInstallationInstructions = !installed; } } } if (shouldShowInstallationInstructions) { console.log(``); await printRecommendedDepsInstallationInstructions(action); } console.log(`\n${(0, emoji_1.emoji)("✨ ")}${picocolors_1.default.cyan("Project created")}${(0, emoji_1.emoji)(" ✨")}`); console.log(); console.log("See the README.md file for some example tasks you can run"); console.log(); showStarOnGitHubMessage(); showSoliditySurveyMessage(); const hardhat3BannerManager = await banner_manager_1.BannerManager.getInstance(); await hardhat3BannerManager.showBanner(); } exports.createProject = createProject; async function canInstallRecommendedDeps() { return fs_extra_1.default.pathExists("package.json"); } function isInstalled(dep) { const packageJson = fs_extra_1.default.readJSONSync("package.json"); const allDependencies = { ...packageJson.dependencies, ...packageJson.devDependencies, ...packageJson.optionalDependencies, }; return dep in allDependencies; } function getProjectTypeFromUserAgent() { const userAgent = process.env.npm_config_user_agent; // Get first part of user agent string const [projectType] = userAgent?.split("/") ?? []; return projectType; } async function isYarnProject() { return (getProjectTypeFromUserAgent() === "yarn" || fs_extra_1.default.pathExists("yarn.lock")); } async function isPnpmProject() { return (getProjectTypeFromUserAgent() === "pnpm" || fs_extra_1.default.pathExists("pnpm-lock.yaml")); } async function getProjectPackageManager() { if (await isYarnProject()) return "yarn"; if (await isPnpmProject()) return "pnpm"; return "npm"; } async function doesNpmAutoInstallPeerDependencies() { const { execSync } = require("child_process"); try { const version = execSync("npm --version").toString(); return parseInt(version.split(".")[0], 10) >= 7; } catch (_) { return false; } } async function installRecommendedDependencies(dependencies) { console.log(""); const installCmd = await getRecommendedDependenciesInstallationCommand(dependencies); return installDependencies(installCmd[0], installCmd.slice(1)); } async function installDependencies(packageManager, args) { const { spawn } = await Promise.resolve().then(() => __importStar(require("child_process"))); console.log(`${packageManager} ${args.join(" ")}`); const childProcess = spawn(packageManager, args, { stdio: "inherit", shell: true, }); return new Promise((resolve, reject) => { childProcess.once("close", (status) => { childProcess.removeAllListeners("error"); if (status === 0) { resolve(true); return; } reject(false); }); childProcess.once("error", (_status) => { childProcess.removeAllListeners("close"); reject(false); }); }); } async function getRecommendedDependenciesInstallationCommand(dependencies) { const deps = Object.entries(dependencies).map(([name, version]) => `"${name}@${version}"`); if (await isYarnProject()) { return ["yarn", "add", "--dev", ...deps]; } if (await isPnpmProject()) { return ["pnpm", "add", "-D", ...deps]; } return ["npm", "install", "--save-dev", ...deps]; } async function getDependencies(projectType) { const shouldInstallPeerDependencies = (await isYarnProject()) || (await isPnpmProject()) || !(await doesNpmAutoInstallPeerDependencies()); const shouldInstallTypescriptDependencies = projectType === Action.CREATE_TYPESCRIPT_PROJECT_ACTION || projectType === Action.CREATE_TYPESCRIPT_VIEM_PROJECT_ACTION; const shouldInstallTypescriptPeerDependencies = shouldInstallTypescriptDependencies && shouldInstallPeerDependencies; const commonDependencies = { [HARDHAT_PACKAGE_NAME]: `^${(await (0, packageInfo_1.getPackageJson)()).version}`, ...PROJECT_DEPENDENCIES, ...(shouldInstallPeerDependencies ? PEER_DEPENDENCIES : {}), ...(shouldInstallTypescriptDependencies ? TYPESCRIPT_DEPENDENCIES : {}), ...(shouldInstallTypescriptPeerDependencies ? TYPESCRIPT_PEER_DEPENDENCIES : {}), }; // At the moment, the default toolbox is the ethers based toolbox const shouldInstallDefaultToolbox = projectType !== Action.CREATE_TYPESCRIPT_VIEM_PROJECT_ACTION; const ethersToolboxDependencies = { ...ETHERS_PROJECT_DEPENDENCIES, ...(shouldInstallPeerDependencies ? ETHERS_PEER_DEPENDENCIES : {}), ...(shouldInstallTypescriptPeerDependencies ? TYPESCRIPT_ETHERS_PEER_DEPENDENCIES : {}), }; const viemToolboxDependencies = { ...VIEM_PROJECT_DEPENDENCIES, ...(shouldInstallPeerDependencies ? VIEM_PEER_DEPENDENCIES : {}), ...(shouldInstallTypescriptPeerDependencies ? TYPESCRIPT_VIEM_PEER_DEPENDENCIES : {}), }; const toolboxDependencies = shouldInstallDefaultToolbox ? ethersToolboxDependencies : viemToolboxDependencies; return { ...commonDependencies, ...toolboxDependencies, }; } //# sourceMappingURL=project-creation.js.map