hardhat
Version:
Hardhat is an extensible developer tool that helps smart contract developers increase productivity by reliably bringing together the tools they want.
261 lines (224 loc) • 7.63 kB
text/typescript
import type { DependencyGraph } from "../../../../types/solidity/dependency-graph.js";
import type { ResolvedFile } from "../../../../types/solidity/resolved-file.js";
import { assertHardhatInvariant } from "@nomicfoundation/hardhat-errors";
export interface DependencyGraphImplementationJson {
readonly fileByInputSourceName: Record<string, ResolvedFile>;
readonly rootByUserSourceName: Record<
string /* user source name */,
string /* input source name */
>;
readonly dependencies: Record<
string /* from input source name */,
Record<string /* to input source name */, string[] /* remappings */>
>;
}
export class DependencyGraphImplementation implements DependencyGraph {
readonly
readonly
readonly
ResolvedFile,
Map<ResolvedFile, Set<string>>
>();
/**
* 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.
*/
public addRootFile(userSourceName: string, root: ResolvedFile): void {
this.
this.
}
/**
* 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.
*/
public addDependency(
from: ResolvedFile,
to: ResolvedFile,
remapping?: string,
): void {
const dependencies = this.
assertHardhatInvariant(
dependencies !== undefined,
"File `from` from not present",
);
if (!this.hasFile(to)) {
this.
}
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.
*/
public getRoots(): ReadonlyMap<string, ResolvedFile> {
return this.
}
/**
* Returns a sorted map of userSourceName to inputSourceName for every
* root of the graph.
*/
public getRootsUserSourceNameMap(): Record<string, string> {
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.
*/
public getAllFiles(): Iterable<ResolvedFile> {
return this.
}
public hasFile(file: ResolvedFile): boolean {
return this.
}
public getDependencies(file: ResolvedFile): ReadonlySet<{
file: ResolvedFile;
remappings: ReadonlySet<string>;
}> {
const dependencies = this.
if (dependencies === undefined) {
return new Set();
}
return new Set(
Array.from(dependencies.entries()).map(([to, remappings]) => ({
file: to,
remappings,
})),
);
}
public getFileByInputSourceName(
inputSourceName: string,
): ResolvedFile | undefined {
return this.
}
public getSubgraph(
...rootUserSourceNames: string[]
): DependencyGraphImplementation {
const subgraph = new DependencyGraphImplementation();
const filesToTraverse: ResolvedFile[] = [];
for (const rootUserSourceName of rootUserSourceNames) {
const root = this.
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;
}
public merge(
other: DependencyGraphImplementation,
): DependencyGraphImplementation {
const merged = new DependencyGraphImplementation();
for (const [userSourceName, root] of this.
merged.addRootFile(userSourceName, root);
}
for (const [userSourceName, root] of other.
if (merged.hasFile(root)) {
continue;
}
merged.addRootFile(userSourceName, root);
}
for (const [from, dependencies] of this.
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.
for (const [to, remappings] of dependencies) {
merged.addDependency(from, to);
for (const remapping of remappings) {
merged.addDependency(from, to, remapping);
}
}
}
return merged;
}
public getAllRemappings(): readonly string[] {
return this.
.values()
.flatMap((dependencies) =>
dependencies.values().flatMap((remappings) => remappings.values()),
)
.toArray()
.sort();
}
public toJSON(): DependencyGraphImplementationJson {
return {
fileByInputSourceName: Object.fromEntries(this.
rootByUserSourceName: Object.fromEntries(
this.
.entries()
.map(([userSourceName, file]) => [
userSourceName,
file.inputSourceName,
]),
),
dependencies: Object.fromEntries(
this.
.entries()
.map(([from, dependencies]) => [
from.inputSourceName,
Object.fromEntries(
dependencies
.entries()
.map(([to, remappings]) => [
to.inputSourceName,
[...remappings].sort(),
]),
),
]),
),
};
}
assertHardhatInvariant(
!this.hasFile(file),
`File ${file.inputSourceName} already present`,
);
assertHardhatInvariant(
this.
`File "${file.inputSourceName}" already present`,
);
this.
this.
}
}