UNPKG

2.83 kBPlain TextView Raw
1import { internalTask, task } from "../internal/core/config/config-env";
2import { BuidlerError } from "../internal/core/errors";
3import { ERRORS } from "../internal/core/errors-list";
4import { DependencyGraph } from "../internal/solidity/dependencyGraph";
5import { ResolvedFile, ResolvedFilesMap } from "../internal/solidity/resolver";
6import { getPackageJson } from "../internal/util/packageInfo";
7
8import {
9 TASK_COMPILE_GET_DEPENDENCY_GRAPH,
10 TASK_FLATTEN,
11 TASK_FLATTEN_GET_FLATTENED_SOURCE,
12} from "./task-names";
13
14function getSortedFiles(dependenciesGraph: DependencyGraph) {
15 const tsort = require("tsort");
16 const graph = tsort();
17
18 const filesMap: ResolvedFilesMap = {};
19 const resolvedFiles = dependenciesGraph.getResolvedFiles();
20 resolvedFiles.forEach((f) => (filesMap[f.globalName] = f));
21
22 for (const [from, deps] of dependenciesGraph.dependenciesPerFile.entries()) {
23 for (const to of deps) {
24 graph.add(to.globalName, from.globalName);
25 }
26 }
27
28 try {
29 const topologicalSortedNames: string[] = graph.sort();
30
31 // If an entry has no dependency it won't be included in the graph, so we
32 // add them and then dedup the array
33 const withEntries = topologicalSortedNames.concat(
34 resolvedFiles.map((f) => f.globalName)
35 );
36
37 const sortedNames = [...new Set(withEntries)];
38 return sortedNames.map((n) => filesMap[n]);
39 } catch (error) {
40 if (error.toString().includes("Error: There is a cycle in the graph.")) {
41 throw new BuidlerError(ERRORS.BUILTIN_TASKS.FLATTEN_CYCLE, error);
42 }
43
44 // tslint:disable-next-line only-buidler-error
45 throw error;
46 }
47}
48
49function getFileWithoutImports(resolvedFile: ResolvedFile) {
50 const IMPORT_SOLIDITY_REGEX = /^\s*import(\s+)[\s\S]*?;\s*$/gm;
51 return resolvedFile.content.replace(IMPORT_SOLIDITY_REGEX, "").trim();
52}
53
54export default function () {
55 internalTask(
56 TASK_FLATTEN_GET_FLATTENED_SOURCE,
57 "Returns all contracts and their dependencies flattened",
58 async (_, { run }) => {
59 let flattened = "";
60
61 const graph: DependencyGraph = await run(
62 TASK_COMPILE_GET_DEPENDENCY_GRAPH
63 );
64 if (graph.getResolvedFiles().length === 0) {
65 return flattened;
66 }
67
68 const packageJson = await getPackageJson();
69 flattened += `// Sources flattened with buidler v${packageJson.version} https://buidler.dev`;
70
71 const sortedFiles = getSortedFiles(graph);
72
73 for (const file of sortedFiles) {
74 flattened += `\n\n// File ${file.getVersionedName()}\n`;
75 flattened += `\n${getFileWithoutImports(file)}\n`;
76 }
77
78 return flattened.trim();
79 }
80 );
81
82 task(
83 TASK_FLATTEN,
84 "Flattens and prints all contracts and their dependencies",
85 async (_, { run }) => {
86 console.log(await run(TASK_FLATTEN_GET_FLATTENED_SOURCE));
87 }
88 );
89}