StackedSetMap.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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(
  88. this.map.entries(),
  89. pair =>
  90. /** @type {[TODO, TODO]} */ (pair[1] === UNDEFINED_MARKER
  91. ? [pair[0], undefined]
  92. : pair)
  93. );
  94. }
  95. asMap() {
  96. return new Map(this.asPairArray());
  97. }
  98. get size() {
  99. this._compress();
  100. return this.map.size;
  101. }
  102. createChild() {
  103. return new StackedSetMap(this.stack);
  104. }
  105. get length() {
  106. throw new Error("This is no longer an Array");
  107. }
  108. set length(value) {
  109. throw new Error("This is no longer an Array");
  110. }
  111. }
  112. // TODO remove in webpack 5
  113. StackedSetMap.prototype.push = util.deprecate(
  114. /**
  115. * @deprecated
  116. * @this {StackedSetMap}
  117. * @param {any} item Item to add
  118. * @returns {void}
  119. */
  120. function(item) {
  121. this.add(item);
  122. },
  123. "This is no longer an Array: Use add instead."
  124. );
  125. module.exports = StackedSetMap;