UNPKG

hardhat

Version:

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

170 lines 6.75 kB
import { assertHardhatInvariant } from "@nomicfoundation/hardhat-errors"; export class DependencyGraphImplementation { #fileByInputSourceName = new Map(); #rootByUserSourceName = new Map(); #dependenciesMap = new Map(); /** * Adds a root file to the graph. All the roots of the dependency graph must * be added before any dependencry. * * @param userSourceName The source name used to identify the file, as it * would appear in the artifacts and used by the user. This is not always the * same as the source name used by solc, as it differs when an npm file is * acting as a root. * @param root The root file. */ addRootFile(userSourceName, root) { this.#addFile(root); this.#rootByUserSourceName.set(userSourceName, root); } /** * Adds a dependency from a file to another one. * * @param from The file that depends on another one, which must be already * present in the graph. * @param to The dependency, which will be added to the list of dependencies * of the file, and added to the graph if needed. * @param remapping The remapping that was used to resolve this dependency, if * any. */ addDependency(from, to, remapping) { const dependencies = this.#dependenciesMap.get(from); assertHardhatInvariant(dependencies !== undefined, "File `from` from not present"); if (!this.hasFile(to)) { this.#addFile(to); } let edgeRemappings = dependencies.get(to); if (edgeRemappings === undefined) { edgeRemappings = new Set(); dependencies.set(to, edgeRemappings); } if (remapping !== undefined) { edgeRemappings.add(remapping); } } /** * Returns a map of user source names to root files. */ getRoots() { return this.#rootByUserSourceName; } /** * Returns a sorted map of userSourceName to inputSourceName for every * root of the graph. */ getRootsUserSourceNameMap() { return Object.fromEntries([...this.getRoots().entries()] .map(([userSourceName, root]) => [userSourceName, root.inputSourceName]) .sort(([userSourceName1, _], [userSourceName2, __]) => userSourceName1.localeCompare(userSourceName2))); } /** * Returns a set of all the files in the graph. */ getAllFiles() { return this.#dependenciesMap.keys(); } hasFile(file) { return this.#dependenciesMap.has(file); } getDependencies(file) { const dependencies = this.#dependenciesMap.get(file); if (dependencies === undefined) { return new Set(); } return new Set(Array.from(dependencies.entries()).map(([to, remappings]) => ({ file: to, remappings, }))); } getFileByInputSourceName(inputSourceName) { return this.#fileByInputSourceName.get(inputSourceName); } getSubgraph(...rootUserSourceNames) { const subgraph = new DependencyGraphImplementation(); const filesToTraverse = []; for (const rootUserSourceName of rootUserSourceNames) { const root = this.#rootByUserSourceName.get(rootUserSourceName); assertHardhatInvariant(root !== undefined, "We should have a root for every root user source name"); subgraph.addRootFile(rootUserSourceName, root); filesToTraverse.push(root); } let fileToAnalyze; while ((fileToAnalyze = filesToTraverse.pop()) !== undefined) { for (const dependency of this.getDependencies(fileToAnalyze)) { if (!subgraph.hasFile(dependency.file)) { filesToTraverse.push(dependency.file); } subgraph.addDependency(fileToAnalyze, dependency.file); for (const remapping of dependency.remappings) { subgraph.addDependency(fileToAnalyze, dependency.file, remapping); } } } return subgraph; } merge(other) { const merged = new DependencyGraphImplementation(); for (const [userSourceName, root] of this.#rootByUserSourceName) { merged.addRootFile(userSourceName, root); } for (const [userSourceName, root] of other.#rootByUserSourceName) { if (merged.hasFile(root)) { continue; } merged.addRootFile(userSourceName, root); } for (const [from, dependencies] of this.#dependenciesMap) { for (const [to, remappings] of dependencies) { merged.addDependency(from, to); for (const remapping of remappings) { merged.addDependency(from, to, remapping); } } } for (const [from, dependencies] of other.#dependenciesMap) { for (const [to, remappings] of dependencies) { merged.addDependency(from, to); for (const remapping of remappings) { merged.addDependency(from, to, remapping); } } } return merged; } getAllRemappings() { return this.#dependenciesMap .values() .flatMap((dependencies) => dependencies.values().flatMap((remappings) => remappings.values())) .toArray() .sort(); } toJSON() { return { fileByInputSourceName: Object.fromEntries(this.#fileByInputSourceName), rootByUserSourceName: Object.fromEntries(this.#rootByUserSourceName .entries() .map(([userSourceName, file]) => [ userSourceName, file.inputSourceName, ])), dependencies: Object.fromEntries(this.#dependenciesMap .entries() .map(([from, dependencies]) => [ from.inputSourceName, Object.fromEntries(dependencies .entries() .map(([to, remappings]) => [ to.inputSourceName, [...remappings].sort(), ])), ])), }; } #addFile(file) { assertHardhatInvariant(!this.hasFile(file), `File ${file.inputSourceName} already present`); assertHardhatInvariant(this.#fileByInputSourceName.get(file.inputSourceName) === undefined, `File "${file.inputSourceName}" already present`); this.#fileByInputSourceName.set(file.inputSourceName, file); this.#dependenciesMap.set(file, new Map()); } } //# sourceMappingURL=dependency-graph.js.map