1 | import { internalTask, task } from "../internal/core/config/config-env";
|
2 | import { BuidlerError } from "../internal/core/errors";
|
3 | import { ERRORS } from "../internal/core/errors-list";
|
4 | import { DependencyGraph } from "../internal/solidity/dependencyGraph";
|
5 | import { ResolvedFile, ResolvedFilesMap } from "../internal/solidity/resolver";
|
6 | import { getPackageJson } from "../internal/util/packageInfo";
|
7 |
|
8 | import {
|
9 | TASK_COMPILE_GET_DEPENDENCY_GRAPH,
|
10 | TASK_FLATTEN,
|
11 | TASK_FLATTEN_GET_FLATTENED_SOURCE,
|
12 | } from "./task-names";
|
13 |
|
14 | function 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 |
|
32 |
|
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 |
|
45 | throw error;
|
46 | }
|
47 | }
|
48 |
|
49 | function 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 |
|
54 | export 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 | }
|