1 | ;
|
2 | /**
|
3 | * This source code is from https://github.com/jriecken/dependency-graph
|
4 | * Just added "any" types here, wrapper everything into exported class.
|
5 | * We cant use a package itself because we want to package "everything-in-it" for the frontend users of TypeORM.
|
6 | */
|
7 | Object.defineProperty(exports, "__esModule", { value: true });
|
8 | /**
|
9 | * A simple dependency graph
|
10 | */
|
11 | /**
|
12 | * Helper for creating a Depth-First-Search on
|
13 | * a set of edges.
|
14 | *
|
15 | * Detects cycles and throws an Error if one is detected.
|
16 | *
|
17 | * @param edges The set of edges to DFS through
|
18 | * @param leavesOnly Whether to only return "leaf" nodes (ones who have no edges)
|
19 | * @param result An array in which the results will be populated
|
20 | */
|
21 | function createDFS(edges, leavesOnly, result) {
|
22 | var currentPath = [];
|
23 | var visited = {};
|
24 | return function DFS(currentNode) {
|
25 | visited[currentNode] = true;
|
26 | currentPath.push(currentNode);
|
27 | edges[currentNode].forEach(function (node) {
|
28 | if (!visited[node]) {
|
29 | DFS(node);
|
30 | }
|
31 | else if (currentPath.indexOf(node) >= 0) {
|
32 | currentPath.push(node);
|
33 | throw new Error("Dependency Cycle Found: " + currentPath.join(" -> "));
|
34 | }
|
35 | });
|
36 | currentPath.pop();
|
37 | if ((!leavesOnly || edges[currentNode].length === 0) && result.indexOf(currentNode) === -1) {
|
38 | result.push(currentNode);
|
39 | }
|
40 | };
|
41 | }
|
42 | var DepGraph = /** @class */ (function () {
|
43 | function DepGraph() {
|
44 | this.nodes = {};
|
45 | this.outgoingEdges = {}; // Node -> [Dependency Node]
|
46 | this.incomingEdges = {}; // Node -> [Dependant Node]
|
47 | }
|
48 | /**
|
49 | * Add a node to the dependency graph. If a node already exists, this method will do nothing.
|
50 | */
|
51 | DepGraph.prototype.addNode = function (node, data) {
|
52 | if (!this.hasNode(node)) {
|
53 | // Checking the arguments length allows the user to add a node with undefined data
|
54 | if (arguments.length === 2) {
|
55 | this.nodes[node] = data;
|
56 | }
|
57 | else {
|
58 | this.nodes[node] = node;
|
59 | }
|
60 | this.outgoingEdges[node] = [];
|
61 | this.incomingEdges[node] = [];
|
62 | }
|
63 | };
|
64 | /**
|
65 | * Remove a node from the dependency graph. If a node does not exist, this method will do nothing.
|
66 | */
|
67 | DepGraph.prototype.removeNode = function (node) {
|
68 | if (this.hasNode(node)) {
|
69 | delete this.nodes[node];
|
70 | delete this.outgoingEdges[node];
|
71 | delete this.incomingEdges[node];
|
72 | [this.incomingEdges, this.outgoingEdges].forEach(function (edgeList) {
|
73 | Object.keys(edgeList).forEach(function (key) {
|
74 | var idx = edgeList[key].indexOf(node);
|
75 | if (idx >= 0) {
|
76 | edgeList[key].splice(idx, 1);
|
77 | }
|
78 | }, this);
|
79 | });
|
80 | }
|
81 | };
|
82 | /**
|
83 | * Check if a node exists in the graph
|
84 | */
|
85 | DepGraph.prototype.hasNode = function (node) {
|
86 | return this.nodes.hasOwnProperty(node);
|
87 | };
|
88 | /**
|
89 | * Get the data associated with a node name
|
90 | */
|
91 | DepGraph.prototype.getNodeData = function (node) {
|
92 | if (this.hasNode(node)) {
|
93 | return this.nodes[node];
|
94 | }
|
95 | else {
|
96 | throw new Error("Node does not exist: " + node);
|
97 | }
|
98 | };
|
99 | /**
|
100 | * Set the associated data for a given node name. If the node does not exist, this method will throw an error
|
101 | */
|
102 | DepGraph.prototype.setNodeData = function (node, data) {
|
103 | if (this.hasNode(node)) {
|
104 | this.nodes[node] = data;
|
105 | }
|
106 | else {
|
107 | throw new Error("Node does not exist: " + node);
|
108 | }
|
109 | };
|
110 | /**
|
111 | * Add a dependency between two nodes. If either of the nodes does not exist,
|
112 | * an Error will be thrown.
|
113 | */
|
114 | DepGraph.prototype.addDependency = function (from, to) {
|
115 | if (!this.hasNode(from)) {
|
116 | throw new Error("Node does not exist: " + from);
|
117 | }
|
118 | if (!this.hasNode(to)) {
|
119 | throw new Error("Node does not exist: " + to);
|
120 | }
|
121 | if (this.outgoingEdges[from].indexOf(to) === -1) {
|
122 | this.outgoingEdges[from].push(to);
|
123 | }
|
124 | if (this.incomingEdges[to].indexOf(from) === -1) {
|
125 | this.incomingEdges[to].push(from);
|
126 | }
|
127 | return true;
|
128 | };
|
129 | /**
|
130 | * Remove a dependency between two nodes.
|
131 | */
|
132 | DepGraph.prototype.removeDependency = function (from, to) {
|
133 | var idx;
|
134 | if (this.hasNode(from)) {
|
135 | idx = this.outgoingEdges[from].indexOf(to);
|
136 | if (idx >= 0) {
|
137 | this.outgoingEdges[from].splice(idx, 1);
|
138 | }
|
139 | }
|
140 | if (this.hasNode(to)) {
|
141 | idx = this.incomingEdges[to].indexOf(from);
|
142 | if (idx >= 0) {
|
143 | this.incomingEdges[to].splice(idx, 1);
|
144 | }
|
145 | }
|
146 | };
|
147 | /**
|
148 | * Get an array containing the nodes that the specified node depends on (transitively).
|
149 | *
|
150 | * Throws an Error if the graph has a cycle, or the specified node does not exist.
|
151 | *
|
152 | * If `leavesOnly` is true, only nodes that do not depend on any other nodes will be returned
|
153 | * in the array.
|
154 | */
|
155 | DepGraph.prototype.dependenciesOf = function (node, leavesOnly) {
|
156 | if (this.hasNode(node)) {
|
157 | var result = [];
|
158 | var DFS = createDFS(this.outgoingEdges, leavesOnly, result);
|
159 | DFS(node);
|
160 | var idx = result.indexOf(node);
|
161 | if (idx >= 0) {
|
162 | result.splice(idx, 1);
|
163 | }
|
164 | return result;
|
165 | }
|
166 | else {
|
167 | throw new Error("Node does not exist: " + node);
|
168 | }
|
169 | };
|
170 | /**
|
171 | * get an array containing the nodes that depend on the specified node (transitively).
|
172 | *
|
173 | * Throws an Error if the graph has a cycle, or the specified node does not exist.
|
174 | *
|
175 | * If `leavesOnly` is true, only nodes that do not have any dependants will be returned in the array.
|
176 | */
|
177 | DepGraph.prototype.dependantsOf = function (node, leavesOnly) {
|
178 | if (this.hasNode(node)) {
|
179 | var result = [];
|
180 | var DFS = createDFS(this.incomingEdges, leavesOnly, result);
|
181 | DFS(node);
|
182 | var idx = result.indexOf(node);
|
183 | if (idx >= 0) {
|
184 | result.splice(idx, 1);
|
185 | }
|
186 | return result;
|
187 | }
|
188 | else {
|
189 | throw new Error("Node does not exist: " + node);
|
190 | }
|
191 | };
|
192 | /**
|
193 | * Construct the overall processing order for the dependency graph.
|
194 | *
|
195 | * Throws an Error if the graph has a cycle.
|
196 | *
|
197 | * If `leavesOnly` is true, only nodes that do not depend on any other nodes will be returned.
|
198 | */
|
199 | DepGraph.prototype.overallOrder = function (leavesOnly) {
|
200 | var self = this;
|
201 | var result = [];
|
202 | var keys = Object.keys(this.nodes);
|
203 | if (keys.length === 0) {
|
204 | return result; // Empty graph
|
205 | }
|
206 | else {
|
207 | // Look for cycles - we run the DFS starting at all the nodes in case there
|
208 | // are several disconnected subgraphs inside this dependency graph.
|
209 | var CycleDFS_1 = createDFS(this.outgoingEdges, false, []);
|
210 | keys.forEach(function (n) {
|
211 | CycleDFS_1(n);
|
212 | });
|
213 | var DFS_1 = createDFS(this.outgoingEdges, leavesOnly, result);
|
214 | // Find all potential starting points (nodes with nothing depending on them) an
|
215 | // run a DFS starting at these points to get the order
|
216 | keys.filter(function (node) {
|
217 | return self.incomingEdges[node].length === 0;
|
218 | }).forEach(function (n) {
|
219 | DFS_1(n);
|
220 | });
|
221 | return result;
|
222 | }
|
223 | };
|
224 | return DepGraph;
|
225 | }());
|
226 | exports.DepGraph = DepGraph;
|
227 |
|
228 | //# sourceMappingURL=DepGraph.js.map
|