UNPKG

hardhat

Version:

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

246 lines (243 loc) 10.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getConfiguredCompilers = exports.analyzeModuleNotFoundError = exports.loadConfigAndTasks = exports.resolveConfigPath = exports.importCsjOrEsModule = void 0; const chalk_1 = __importDefault(require("chalk")); const debug_1 = __importDefault(require("debug")); const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const semver_1 = __importDefault(require("semver")); const context_1 = require("../../context"); const packageInfo_1 = require("../../util/packageInfo"); const errors_1 = require("../errors"); const errors_list_1 = require("../errors-list"); const project_structure_1 = require("../project-structure"); const constants_1 = require("../../hardhat-network/stack-traces/constants"); const config_resolution_1 = require("./config-resolution"); const default_config_1 = require("./default-config"); const log = (0, debug_1.default)("hardhat:core:config"); function importCsjOrEsModule(filePath) { try { const imported = require(filePath); return imported.default !== undefined ? imported.default : imported; } catch (e) { if (e.code === "ERR_REQUIRE_ESM") { throw new errors_1.HardhatError(errors_list_1.ERRORS.GENERAL.ESM_PROJECT_WITHOUT_CJS_CONFIG, {}, e); } // eslint-disable-next-line @nomicfoundation/hardhat-internal-rules/only-hardhat-error throw e; } } exports.importCsjOrEsModule = importCsjOrEsModule; function resolveConfigPath(configPath) { if (configPath === undefined) { configPath = (0, project_structure_1.getUserConfigPath)(); } else { if (!path_1.default.isAbsolute(configPath)) { configPath = path_1.default.join(process.cwd(), configPath); configPath = path_1.default.normalize(configPath); } } return configPath; } exports.resolveConfigPath = resolveConfigPath; function loadConfigAndTasks(hardhatArguments, { showEmptyConfigWarning = false, showSolidityConfigWarnings = false, } = { showEmptyConfigWarning: false, showSolidityConfigWarnings: false, }) { const { validateConfig, validateResolvedConfig } = require("./config-validation"); let configPath = hardhatArguments !== undefined ? hardhatArguments.config : undefined; configPath = resolveConfigPath(configPath); log(`Loading Hardhat config from ${configPath}`); // Before loading the builtin tasks, the default and user's config we expose // the config env in the global object. const configEnv = require("./config-env"); const globalAsAny = global; Object.entries(configEnv).forEach(([key, value]) => (globalAsAny[key] = value)); const ctx = context_1.HardhatContext.getHardhatContext(); ctx.setConfigLoadingAsStarted(); let userConfig; try { require("../tasks/builtin-tasks"); userConfig = importCsjOrEsModule(configPath); } catch (e) { analyzeModuleNotFoundError(e, configPath); // eslint-disable-next-line @nomicfoundation/hardhat-internal-rules/only-hardhat-error throw e; } finally { ctx.setConfigLoadingAsFinished(); } if (showEmptyConfigWarning) { checkEmptyConfig(userConfig, { showSolidityConfigWarnings }); } validateConfig(userConfig); if (showSolidityConfigWarnings) { checkMissingSolidityConfig(userConfig); } // To avoid bad practices we remove the previously exported stuff Object.keys(configEnv).forEach((key) => (globalAsAny[key] = undefined)); const frozenUserConfig = deepFreezeUserConfig(userConfig); const resolved = (0, config_resolution_1.resolveConfig)(configPath, userConfig); for (const extender of context_1.HardhatContext.getHardhatContext().configExtenders) { extender(resolved, frozenUserConfig); } validateResolvedConfig(resolved); if (showSolidityConfigWarnings) { checkUnsupportedSolidityConfig(resolved); checkUnsupportedRemappings(resolved); } return { resolvedConfig: resolved, userConfig: frozenUserConfig }; } exports.loadConfigAndTasks = loadConfigAndTasks; function deepFreezeUserConfig(config, propertyPath = []) { if (typeof config !== "object" || config === null) { return config; } return new Proxy(config, { get(target, property, receiver) { return deepFreezeUserConfig(Reflect.get(target, property, receiver), [ ...propertyPath, property, ]); }, set(target, property, _value, _receiver) { throw new errors_1.HardhatError(errors_list_1.ERRORS.GENERAL.USER_CONFIG_MODIFIED, { path: [...propertyPath, property] .map((pathPart) => pathPart.toString()) .join("."), }); }, }); } /** * Receives an Error and checks if it's a MODULE_NOT_FOUND and the reason that * caused it. * * If it can infer the reason, it throws an appropiate error. Otherwise it does * nothing. */ function analyzeModuleNotFoundError(error, configPath) { const stackTraceParser = require("stacktrace-parser"); if (error.code !== "MODULE_NOT_FOUND") { return; } const stackTrace = stackTraceParser.parse(error.stack); const throwingFile = stackTrace .filter((x) => x.file !== null) .map((x) => x.file) // ignore frames related to source map support .filter((x) => !x.includes(path_1.default.join("@cspotcode", "source-map-support"))) .find((x) => path_1.default.isAbsolute(x)); if (throwingFile === null || throwingFile === undefined) { return; } // if the error comes from the config file, we ignore it because we know it's // a direct import that's missing if (throwingFile === configPath) { return; } const packageJsonPath = (0, packageInfo_1.findClosestPackageJson)(throwingFile); if (packageJsonPath === null) { return; } const packageJson = fs_extra_1.default.readJsonSync(packageJsonPath); const peerDependencies = packageJson.peerDependencies ?? {}; if (peerDependencies["@nomiclabs/buidler"] !== undefined) { throw new errors_1.HardhatError(errors_list_1.ERRORS.PLUGINS.BUIDLER_PLUGIN, { plugin: packageJson.name, }); } // if the problem doesn't come from a hardhat plugin, we ignore it if (peerDependencies.hardhat === undefined) { return; } const missingPeerDependencies = {}; for (const [peerDependency, version] of Object.entries(peerDependencies)) { const peerDependencyPackageJson = readPackageJson(peerDependency, configPath); if (peerDependencyPackageJson === undefined) { missingPeerDependencies[peerDependency] = version; } } const missingPeerDependenciesNames = Object.keys(missingPeerDependencies); if (missingPeerDependenciesNames.length > 0) { throw new errors_1.HardhatError(errors_list_1.ERRORS.PLUGINS.MISSING_DEPENDENCIES, { plugin: packageJson.name, missingDependencies: missingPeerDependenciesNames.join(", "), missingDependenciesVersions: Object.entries(missingPeerDependencies) .map(([name, version]) => `"${name}@${version}"`) .join(" "), }); } } exports.analyzeModuleNotFoundError = analyzeModuleNotFoundError; function readPackageJson(packageName, configPath) { const resolve = require("resolve"); try { const packageJsonPath = resolve.sync(path_1.default.join(packageName, "package.json"), { basedir: path_1.default.dirname(configPath), }); return require(packageJsonPath); } catch { return undefined; } } function checkEmptyConfig(userConfig, { showSolidityConfigWarnings }) { if (userConfig === undefined || Object.keys(userConfig).length === 0) { let warning = `Hardhat config is returning an empty config object, check the export from the config file if this is unexpected.\n`; // This 'learn more' section is also printed by the solidity config warning, // so we need to check to avoid printing it twice if (!showSolidityConfigWarnings) { warning += `\nLearn more about configuring Hardhat at https://hardhat.org/config\n`; } console.warn(chalk_1.default.yellow(warning)); } } function checkMissingSolidityConfig(userConfig) { if (userConfig.solidity === undefined) { console.warn(chalk_1.default.yellow(`Solidity compiler is not configured. Version ${default_config_1.DEFAULT_SOLC_VERSION} will be used by default. Add a 'solidity' entry to your configuration to suppress this warning. Learn more about compiler configuration at https://hardhat.org/config `)); } } function checkUnsupportedSolidityConfig(resolvedConfig) { const configuredCompilers = getConfiguredCompilers(resolvedConfig.solidity); const solcVersions = configuredCompilers.map((x) => x.version); const unsupportedVersions = []; for (const solcVersion of solcVersions) { if (!semver_1.default.satisfies(solcVersion, constants_1.SUPPORTED_SOLIDITY_VERSION_RANGE) && !unsupportedVersions.includes(solcVersion)) { unsupportedVersions.push(solcVersion); } } if (unsupportedVersions.length > 0) { console.warn(chalk_1.default.yellow(`Solidity ${unsupportedVersions.join(", ")} ${unsupportedVersions.length === 1 ? "is" : "are"} not fully supported yet. You can still use Hardhat, but some features, like stack traces, might not work correctly. Learn more at https://hardhat.org/hardhat-runner/docs/reference/solidity-support `)); } } function checkUnsupportedRemappings({ solidity }) { const solcConfigs = [ ...solidity.compilers, ...Object.values(solidity.overrides), ]; const remappings = solcConfigs.filter(({ settings }) => settings.remappings !== undefined); if (remappings.length > 0) { console.warn(chalk_1.default.yellow(`Solidity remappings are not currently supported; you may experience unexpected compilation results. Remove any 'remappings' fields from your configuration to suppress this warning. Learn more about compiler configuration at https://hardhat.org/config `)); } } function getConfiguredCompilers(solidityConfig) { const compilerVersions = solidityConfig.compilers; const overrideVersions = Object.values(solidityConfig.overrides); return [...compilerVersions, ...overrideVersions]; } exports.getConfiguredCompilers = getConfiguredCompilers; //# sourceMappingURL=config-loading.js.map