hardhat
Version:
Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.
135 lines (111 loc) • 3.69 kB
text/typescript
import type { HardhatPlugin } from "../../../types/plugins.js";
import { HardhatError } from "@nomicfoundation/hardhat-errors";
import { ensureError } from "@nomicfoundation/hardhat-utils/error";
import { detectPluginNpmDependencyProblems } from "./detect-plugin-npm-dependency-problems.js";
/**
* Resolves the plugin list, returning them in the right order.
*/
export async function resolvePluginList(
projectRoot: string,
userConfigPluginList: HardhatPlugin[] = [],
): Promise<HardhatPlugin[]> {
return reverseTopologicalSort(projectRoot, userConfigPluginList);
}
/**
* Returns an array of reverse topological order of the plugins and their dependencies.
*
* If two plugins have no order between them, their relative order should be that of the input array.
*
* @param plugins The plugins.
* @returns The ordered plugins.
*/
async function reverseTopologicalSort(
projectRoot: string,
plugins: HardhatPlugin[],
): Promise<HardhatPlugin[]> {
const visitedPlugins: Map<string, HardhatPlugin> = new Map();
const resolvedPlugins: HardhatPlugin[] = [];
async function dfs(plugin: HardhatPlugin) {
const visited = visitedPlugins.get(plugin.id);
if (visited !== undefined) {
if (visited !== plugin) {
throw new HardhatError(
HardhatError.ERRORS.CORE.GENERAL.DUPLICATED_PLUGIN_ID,
{ id: plugin.id },
);
}
return;
}
visitedPlugins.set(plugin.id, plugin);
if (plugin.dependencies !== undefined) {
let dependencyModules: Array<{ default: HardhatPlugin }>;
try {
dependencyModules = await Promise.all(plugin.dependencies());
} catch (error) {
ensureError(error);
await detectPluginNpmDependencyProblems(projectRoot, plugin, error);
throw new HardhatError(
HardhatError.ERRORS.CORE.PLUGINS.PLUGIN_DEPENDENCY_FAILED_LOAD,
{
pluginId: plugin.id,
},
error,
);
}
for (const dependencyModule of dependencyModules) {
await dfs(dependencyModule.default);
}
}
resolvedPlugins.push(plugin);
}
for (const plugin of plugins) {
await dfs(plugin);
}
// Resolve conditional dependencies iteratively
let lastResolvedCount = -1;
while (resolvedPlugins.length !== lastResolvedCount) {
lastResolvedCount = resolvedPlugins.length;
for (const plugin of resolvedPlugins) {
if (plugin.conditionalDependencies === undefined) {
continue;
}
for (const conditionalDependency of plugin.conditionalDependencies) {
// Check all condition plugins are installed
let conditionModules;
try {
conditionModules = await Promise.all(
conditionalDependency.condition(),
);
} catch (_error) {
continue;
}
// Check all condition plugins are loaded
if (
conditionModules.some(
(conditionPlugin) =>
!resolvedPlugins.includes(conditionPlugin.default),
)
) {
continue;
}
// Load the conditional dependency
let pluginModule;
try {
pluginModule = await conditionalDependency.plugin();
} catch (error) {
ensureError(error);
await detectPluginNpmDependencyProblems(projectRoot, plugin, error);
throw new HardhatError(
HardhatError.ERRORS.CORE.PLUGINS.PLUGIN_DEPENDENCY_FAILED_LOAD,
{
pluginId: plugin.id,
},
error,
);
}
await dfs(pluginModule.default);
}
}
}
return resolvedPlugins;
}