index.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. var through = require('through2');
  2. var shasum = require('shasum-object');
  3. module.exports = function (opts) {
  4. if (!opts) opts = {};
  5. var rows = [];
  6. return through.obj(write, end);
  7. function write (row, enc, next) { rows.push(row); next() }
  8. function end () {
  9. var tr = this;
  10. rows.sort(cmp);
  11. sorter(rows, tr, opts);
  12. }
  13. };
  14. function sorter (rows, tr, opts) {
  15. var expose = opts.expose || {};
  16. if (Array.isArray(expose)) {
  17. expose = expose.reduce(function (acc, key) {
  18. acc[key] = true;
  19. return acc;
  20. }, {});
  21. }
  22. var hashes = {}, deduped = {};
  23. var sameDeps = depCmp();
  24. if (opts.dedupe) {
  25. rows.forEach(function (row) {
  26. var h = shasum(row.source);
  27. sameDeps.add(row, h);
  28. if (hashes[h]) {
  29. hashes[h].push(row);
  30. } else {
  31. hashes[h] = [row];
  32. }
  33. });
  34. Object.keys(hashes).forEach(function (h) {
  35. var rows = hashes[h];
  36. while (rows.length > 1) {
  37. var row = rows.pop();
  38. row.dedupe = rows[0].id;
  39. row.sameDeps = sameDeps.cmp(rows[0].deps, row.deps);
  40. deduped[row.id] = rows[0].id;
  41. }
  42. });
  43. }
  44. if (opts.index) {
  45. var index = {};
  46. var offset = 0;
  47. rows.forEach(function (row, ix) {
  48. if (has(expose, row.id)) {
  49. row.index = row.id;
  50. offset ++;
  51. if (expose[row.id] !== true) {
  52. index[expose[row.id]] = row.index;
  53. }
  54. }
  55. else {
  56. row.index = ix + 1 - offset;
  57. }
  58. index[row.id] = row.index;
  59. });
  60. rows.forEach(function (row) {
  61. row.indexDeps = {};
  62. Object.keys(row.deps).forEach(function (key) {
  63. var id = row.deps[key];
  64. row.indexDeps[key] = index[id];
  65. });
  66. if (row.dedupe) {
  67. row.dedupeIndex = index[row.dedupe];
  68. }
  69. tr.push(row);
  70. });
  71. }
  72. else {
  73. rows.forEach(function (row) { tr.push(row) });
  74. }
  75. tr.push(null);
  76. }
  77. function cmp (a, b) {
  78. return a.id + a.hash < b.id + b.hash ? -1 : 1;
  79. }
  80. function has (obj, key) {
  81. return Object.prototype.hasOwnProperty.call(obj, key);
  82. }
  83. function depCmp () {
  84. var deps = {}, hashes = {};
  85. return { add: add, cmp: cmp }
  86. function add (row, hash) {
  87. deps[row.id] = row.deps;
  88. hashes[row.id] = hash;
  89. }
  90. function cmp (a, b, limit) {
  91. if (!a && !b) return true;
  92. if (!a || !b) return false;
  93. var keys = Object.keys(a);
  94. if (keys.length !== Object.keys(b).length) return false;
  95. for (var i = 0; i < keys.length; i++) {
  96. var k = keys[i], ka = a[k], kb = b[k];
  97. var ha = hashes[ka];
  98. var hb = hashes[kb];
  99. var da = deps[ka];
  100. var db = deps[kb];
  101. if (ka === kb) continue;
  102. if (ha !== hb || (!limit && !cmp(da, db, 1))) {
  103. return false;
  104. }
  105. }
  106. return true;
  107. }
  108. }