index.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. 'use strict';
  2. // Load modules
  3. const Hoek = require('hoek');
  4. // Declare internals
  5. const internals = {};
  6. module.exports = class Topo {
  7. constructor() {
  8. this._items = [];
  9. this.nodes = [];
  10. }
  11. add(nodes, options) {
  12. options = options || {};
  13. // Validate rules
  14. const before = [].concat(options.before || []);
  15. const after = [].concat(options.after || []);
  16. const group = options.group || '?';
  17. const sort = options.sort || 0; // Used for merging only
  18. Hoek.assert(!before.includes(group), `Item cannot come before itself: ${group}`);
  19. Hoek.assert(!before.includes('?'), 'Item cannot come before unassociated items');
  20. Hoek.assert(!after.includes(group), `Item cannot come after itself: ${group}`);
  21. Hoek.assert(!after.includes('?'), 'Item cannot come after unassociated items');
  22. ([].concat(nodes)).forEach((node, i) => {
  23. const item = {
  24. seq: this._items.length,
  25. sort,
  26. before,
  27. after,
  28. group,
  29. node
  30. };
  31. this._items.push(item);
  32. });
  33. // Insert event
  34. const error = this._sort();
  35. Hoek.assert(!error, 'item', (group !== '?' ? `added into group ${group}` : ''), 'created a dependencies error');
  36. return this.nodes;
  37. }
  38. merge(others) {
  39. others = [].concat(others);
  40. for (let i = 0; i < others.length; ++i) {
  41. const other = others[i];
  42. if (other) {
  43. for (let j = 0; j < other._items.length; ++j) {
  44. const item = Object.assign({}, other._items[j]); // Shallow cloned
  45. this._items.push(item);
  46. }
  47. }
  48. }
  49. // Sort items
  50. this._items.sort(internals.mergeSort);
  51. for (let i = 0; i < this._items.length; ++i) {
  52. this._items[i].seq = i;
  53. }
  54. const error = this._sort();
  55. Hoek.assert(!error, 'merge created a dependencies error');
  56. return this.nodes;
  57. }
  58. _sort() {
  59. // Construct graph
  60. const graph = {};
  61. const graphAfters = Object.create(null); // A prototype can bungle lookups w/ false positives
  62. const groups = Object.create(null);
  63. for (let i = 0; i < this._items.length; ++i) {
  64. const item = this._items[i];
  65. const seq = item.seq; // Unique across all items
  66. const group = item.group;
  67. // Determine Groups
  68. groups[group] = groups[group] || [];
  69. groups[group].push(seq);
  70. // Build intermediary graph using 'before'
  71. graph[seq] = item.before;
  72. // Build second intermediary graph with 'after'
  73. const after = item.after;
  74. for (let j = 0; j < after.length; ++j) {
  75. graphAfters[after[j]] = (graphAfters[after[j]] || []).concat(seq);
  76. }
  77. }
  78. // Expand intermediary graph
  79. let graphNodes = Object.keys(graph);
  80. for (let i = 0; i < graphNodes.length; ++i) {
  81. const node = graphNodes[i];
  82. const expandedGroups = [];
  83. const graphNodeItems = Object.keys(graph[node]);
  84. for (let j = 0; j < graphNodeItems.length; ++j) {
  85. const group = graph[node][graphNodeItems[j]];
  86. groups[group] = groups[group] || [];
  87. for (let k = 0; k < groups[group].length; ++k) {
  88. expandedGroups.push(groups[group][k]);
  89. }
  90. }
  91. graph[node] = expandedGroups;
  92. }
  93. // Merge intermediary graph using graphAfters into final graph
  94. const afterNodes = Object.keys(graphAfters);
  95. for (let i = 0; i < afterNodes.length; ++i) {
  96. const group = afterNodes[i];
  97. if (groups[group]) {
  98. for (let j = 0; j < groups[group].length; ++j) {
  99. const node = groups[group][j];
  100. graph[node] = graph[node].concat(graphAfters[group]);
  101. }
  102. }
  103. }
  104. // Compile ancestors
  105. let children;
  106. const ancestors = {};
  107. graphNodes = Object.keys(graph);
  108. for (let i = 0; i < graphNodes.length; ++i) {
  109. const node = graphNodes[i];
  110. children = graph[node];
  111. for (let j = 0; j < children.length; ++j) {
  112. ancestors[children[j]] = (ancestors[children[j]] || []).concat(node);
  113. }
  114. }
  115. // Topo sort
  116. const visited = {};
  117. const sorted = [];
  118. for (let i = 0; i < this._items.length; ++i) { // Really looping thru item.seq values out of order
  119. let next = i;
  120. if (ancestors[i]) {
  121. next = null;
  122. for (let j = 0; j < this._items.length; ++j) { // As above, these are item.seq values
  123. if (visited[j] === true) {
  124. continue;
  125. }
  126. if (!ancestors[j]) {
  127. ancestors[j] = [];
  128. }
  129. const shouldSeeCount = ancestors[j].length;
  130. let seenCount = 0;
  131. for (let k = 0; k < shouldSeeCount; ++k) {
  132. if (visited[ancestors[j][k]]) {
  133. ++seenCount;
  134. }
  135. }
  136. if (seenCount === shouldSeeCount) {
  137. next = j;
  138. break;
  139. }
  140. }
  141. }
  142. if (next !== null) {
  143. visited[next] = true;
  144. sorted.push(next);
  145. }
  146. }
  147. if (sorted.length !== this._items.length) {
  148. return new Error('Invalid dependencies');
  149. }
  150. const seqIndex = {};
  151. for (let i = 0; i < this._items.length; ++i) {
  152. const item = this._items[i];
  153. seqIndex[item.seq] = item;
  154. }
  155. const sortedNodes = [];
  156. this._items = sorted.map((value) => {
  157. const sortedItem = seqIndex[value];
  158. sortedNodes.push(sortedItem.node);
  159. return sortedItem;
  160. });
  161. this.nodes = sortedNodes;
  162. }
  163. };
  164. internals.mergeSort = (a, b) => {
  165. return a.sort === b.sort ? 0 : (a.sort < b.sort ? -1 : 1);
  166. };