StackedSetMap.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const util = require("util");
  7. const TOMBSTONE = {};
  8. const UNDEFINED_MARKER = {};
  9. class StackedSetMap {
  10. constructor(parentStack) {
  11. this.stack = parentStack === undefined ? [] : parentStack.slice();
  12. this.map = new Map();
  13. this.stack.push(this.map);
  14. }
  15. add(item) {
  16. this.map.set(item, true);
  17. }
  18. set(item, value) {
  19. this.map.set(item, value === undefined ? UNDEFINED_MARKER : value);
  20. }
  21. delete(item) {
  22. if (this.stack.length > 1) {
  23. this.map.set(item, TOMBSTONE);
  24. } else {
  25. this.map.delete(item);
  26. }
  27. }
  28. has(item) {
  29. const topValue = this.map.get(item);
  30. if (topValue !== undefined) return topValue !== TOMBSTONE;
  31. if (this.stack.length > 1) {
  32. for (var i = this.stack.length - 2; i >= 0; i--) {
  33. const value = this.stack[i].get(item);
  34. if (value !== undefined) {
  35. this.map.set(item, value);
  36. return value !== TOMBSTONE;
  37. }
  38. }
  39. this.map.set(item, TOMBSTONE);
  40. }
  41. return false;
  42. }
  43. get(item) {
  44. const topValue = this.map.get(item);
  45. if (topValue !== undefined) {
  46. return topValue === TOMBSTONE || topValue === UNDEFINED_MARKER
  47. ? undefined
  48. : topValue;
  49. }
  50. if (this.stack.length > 1) {
  51. for (var i = this.stack.length - 2; i >= 0; i--) {
  52. const value = this.stack[i].get(item);
  53. if (value !== undefined) {
  54. this.map.set(item, value);
  55. return value === TOMBSTONE || value === UNDEFINED_MARKER
  56. ? undefined
  57. : value;
  58. }
  59. }
  60. this.map.set(item, TOMBSTONE);
  61. }
  62. return undefined;
  63. }
  64. _compress() {
  65. if (this.stack.length === 1) return;
  66. this.map = new Map();
  67. for (const data of this.stack) {
  68. for (const pair of data) {
  69. if (pair[1] === TOMBSTONE) {
  70. this.map.delete(pair[0]);
  71. } else {
  72. this.map.set(pair[0], pair[1]);
  73. }
  74. }
  75. }
  76. this.stack = [this.map];
  77. }
  78. asArray() {
  79. this._compress();
  80. return Array.from(this.map.entries(), pair => pair[0]);
  81. }
  82. asSet() {
  83. return new Set(this.asArray());
  84. }
  85. asPairArray() {
  86. this._compress();
  87. return Array.from(this.map.entries(), pair =>
  88. /** @type {[TODO, TODO]} */ (pair[1] === UNDEFINED_MARKER
  89. ? [pair[0], undefined]
  90. : pair)
  91. );
  92. }
  93. asMap() {
  94. return new Map(this.asPairArray());
  95. }
  96. get size() {
  97. this._compress();
  98. return this.map.size;
  99. }
  100. createChild() {
  101. return new StackedSetMap(this.stack);
  102. }
  103. get length() {
  104. throw new Error("This is no longer an Array");
  105. }
  106. set length(value) {
  107. throw new Error("This is no longer an Array");
  108. }
  109. }
  110. // TODO remove in webpack 5
  111. StackedSetMap.prototype.push = util.deprecate(
  112. /**
  113. * @deprecated
  114. * @this {StackedSetMap}
  115. * @param {any} item Item to add
  116. * @returns {void}
  117. */
  118. function(item) {
  119. this.add(item);
  120. },
  121. "This is no longer an Array: Use add instead."
  122. );
  123. module.exports = StackedSetMap;