UNPKG

hardhat

Version:

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

243 lines (229 loc) 7.39 kB
import { execSync } from "node:child_process"; import semver from "semver"; type PackageManager = "npm" | "yarn" | "pnpm" | "bun" | "deno"; /** * getPackageManager returns the name of the package manager used to run Hardhat * * This logic is based on the env variable `npm_config_user_agent`, which is set * by all major package manager, both when running a package that has been * installed, and when it hasn't. * * Here's how to reproduce it, with the value of the env var: * * npm: * * uninstalled: npx -y print-environment * "npm/11.6.1 node/v24.10.0 linux arm64 workspaces/false" * * installed: npm init -y && npm i print-environment && npx print-environment * "npm/11.6.1 node/v24.10.0 linux arm64 workspaces/false" * * * pnpm: * * uninstalled: pnpm dlx print-environment * "pnpm/10.18.3 npm/? node/v24.10.0 linux arm64" * * installed: pnpm init && pnpm add print-environment && pnpm print-environment * "pnpm/10.18.3 npm/? node/v24.10.0 linux arm64" * * * yarn classic: * uninstalled: unsupported * * installed: yarn init -y && yarn add print-environment && yarn print-environment * "yarn/1.22.22 npm/? node/v24.10.0 linux arm64" * * yarn berry: * * uninstalled: yarn set version berry && yarn dlx print-environment * "yarn/4.10.3 npm/? node/v24.10.0 linux arm64" * * installed: yarn set version berry && yarn add print-environment && yarn print-environment * "yarn/4.10.3 npm/? node/v24.10.0 linux arm64" * * bun: * * uninstalled: bunx print-environment * "bun/1.3.1 npm/? node/v24.3.0 linux arm64" * * installed: bun init -y && bun add print-environment && bun print-environment * "bun/1.3.1 npm/? node/v24.3.0 linux arm64" * * deno: * * uninstalled: deno run -A npm:print-environment * "deno/2.5.6 npm/? deno/2.5.6 linux aarch64" * * installed: deno init && deno add npm:print-environment && deno --allow-env print-environment * "deno/2.5.6 npm/? deno/2.5.6 linux aarch64" * * @returns The name of the package manager used to run hardhat. */ export function getPackageManager(): PackageManager { const DEFAULT = "npm"; const userAgent = process.env.npm_config_user_agent; if (userAgent === undefined) { return DEFAULT; } const firstSlashIndex = userAgent.indexOf("/"); if (firstSlashIndex === -1) { return DEFAULT; } const packageManager = userAgent.substring(0, firstSlashIndex); switch (packageManager) { case "npm": return "npm"; case "yarn": return "yarn"; case "pnpm": return "pnpm"; case "bun": return "bun"; case "deno": return "deno"; default: return DEFAULT; } } /** * getDevDependenciesInstallationCommand returns the command to install the given dependencies * as dev dependencies using the given package manager. The returned command should * be safe to run on the command line. * * @param packageManager The package manager to use. * @param dependencies The dependencies to install. * @returns The installation command. */ export function getDevDependenciesInstallationCommand( packageManager: PackageManager, dependencies: string[], ): string[] { const packageManagerToCommand: Record<PackageManager, string[]> = { npm: ["npm", "install", "--save-dev"], yarn: ["yarn", "add", "--dev"], pnpm: ["pnpm", "add", "--save-dev"], deno: ["deno", "add"], bun: ["bun", "add", "--dev"], }; const command = packageManagerToCommand[packageManager]; // We quote all the dependency identifiers so that they can be run on a shell // without semver symbols interfering with the command command.push( ...dependencies.map((d) => { if (packageManager === "deno") { return `"npm:${d}"`; } return `"${d}"`; }), ); return command; } /** * installsPeerDependenciesByDefault returns true if the package manager * installs peer dependencies by default. * * @param workspace The path to the workspace where the package manager will operate. * @param packageManager The package manager to use. * @param version The version of the package manager to use. This parameter is used only for testing. * @param config The configuration of the package manager to use. This parameter is used only for testing. * @returns True if the package manager installs peer dependencies by default, false otherwise. */ export async function installsPeerDependenciesByDefault( workspace: string, packageManager: PackageManager, version?: string, config?: Record<string, string>, ): Promise<boolean> { switch (packageManager) { case "npm": const npmVersion = await getVersion(workspace, "npm", version); const legacyPeerDeps = await getFromConfig( workspace, "npm", "legacy-peer-deps", config, ); // If we couldn't retrieve the npm version, we assume it is higher than 7 if (npmVersion === undefined || npmVersion.major >= 7) { // If legacy-peer-deps hasn't been explicitly set to true, // peer dependencies are installed by default if (legacyPeerDeps !== "true") { return true; } } return false; case "yarn": // https://github.com/yarnpkg/yarn/issues/1503 return false; case "pnpm": // https://github.com/pnpm/pnpm/releases/tag/v8.0.0 const pnpmVersion = await getVersion(workspace, "pnpm", version); const autoInstallPeers = await getFromConfig( workspace, "pnpm", "auto-install-peers", config, ); // If we couldn't retrieve the pnpm version, we assume it is higher than 8 if (pnpmVersion === undefined || pnpmVersion.major >= 8) { // If auto-install-peers hasn't been explicitly set to false, // peer dependencies are installed by default if (autoInstallPeers !== "false") { return true; } } else { // If auto-install-peers has been explicitly set to true, // peer dependencies are installed if (autoInstallPeers === "true") { return true; } } return false; case "bun": // Bun has installed peer dependencies for over 2 years, so that's fine // https://github.com/oven-sh/bun/releases/tag/bun-v1.0.5 // This can be disabled, and there's no easy way to check that, so we // assume true for now return true; case "deno": // Deno doesn't autoinstall peers return false; } } async function getVersion( workspace: string, packageManager: PackageManager, version?: string, ): Promise<semver.SemVer | undefined> { if (version !== undefined) { return semver.parse(version) ?? undefined; } try { const versionString = execSync(`${packageManager} --version`, { cwd: workspace, encoding: "utf8", }); return semver.parse(versionString) ?? undefined; } catch (_error) { return undefined; } } async function getFromConfig( workspace: string, packageManager: PackageManager, key: string, config?: Record<string, string>, ): Promise<string | undefined> { if (config !== undefined) { return config[key]; } try { return execSync(`${packageManager} config get ${key}`, { cwd: workspace, encoding: "utf8", }); } catch (_error) { return undefined; } }