source: frontend/node_modules/webpack/lib/util/StackedMap.js@ cdcff72

Last change on this file since cdcff72 was 9af201e, checked in by MBK <marija.karapandzova@…>, 13 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 5.1 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const TOMBSTONE = Symbol("tombstone");
9const UNDEFINED_MARKER = Symbol("undefined");
10
11/**
12 * Public cell value exposed by `StackedMap`, where `undefined` is preserved as
13 * a valid stored result.
14 * @template T
15 * @typedef {T | undefined} Cell<T>
16 */
17
18/**
19 * Internal cell value used to distinguish deleted entries and explicit
20 * `undefined` assignments while traversing stacked scopes.
21 * @template T
22 * @typedef {T | typeof TOMBSTONE | typeof UNDEFINED_MARKER} InternalCell<T>
23 */
24
25/**
26 * Converts an internal key/value pair into the external representation returned
27 * by iteration helpers.
28 * @template K
29 * @template V
30 * @param {[K, InternalCell<V>]} pair the internal cell
31 * @returns {[K, Cell<V>]} its “safe” representation
32 */
33const extractPair = (pair) => {
34 const key = pair[0];
35 const val = pair[1];
36 if (val === UNDEFINED_MARKER || val === TOMBSTONE) {
37 return [key, undefined];
38 }
39 return /** @type {[K, Cell<V>]} */ (pair);
40};
41
42/**
43 * Layered map that supports child scopes while memoizing lookups from parent
44 * scopes into the current layer.
45 * @template K
46 * @template V
47 */
48class StackedMap {
49 /**
50 * Creates a new map layer on top of an optional parent stack.
51 * @param {Map<K, InternalCell<V>>[]=} parentStack an optional parent
52 */
53 constructor(parentStack) {
54 /** @type {Map<K, InternalCell<V>>} */
55 this.map = new Map();
56 /** @type {Map<K, InternalCell<V>>[]} */
57 this.stack = parentStack === undefined ? [] : [...parentStack];
58 this.stack.push(this.map);
59 }
60
61 /**
62 * Stores a value in the current layer, preserving explicit `undefined`
63 * values with an internal marker.
64 * @param {K} item the key of the element to add
65 * @param {V} value the value of the element to add
66 * @returns {void}
67 */
68 set(item, value) {
69 this.map.set(item, value === undefined ? UNDEFINED_MARKER : value);
70 }
71
72 /**
73 * Deletes a key from the current view, either by removing it outright in the
74 * root layer or by recording a tombstone in child layers.
75 * @param {K} item the item to delete
76 * @returns {void}
77 */
78 delete(item) {
79 if (this.stack.length > 1) {
80 this.map.set(item, TOMBSTONE);
81 } else {
82 this.map.delete(item);
83 }
84 }
85
86 /**
87 * Checks whether a key exists in the current scope chain, caching any parent
88 * lookup result in the current layer.
89 * @param {K} item the item to test
90 * @returns {boolean} true if the item exists in this set
91 */
92 has(item) {
93 const topValue = this.map.get(item);
94 if (topValue !== undefined) {
95 return topValue !== TOMBSTONE;
96 }
97 if (this.stack.length > 1) {
98 for (let i = this.stack.length - 2; i >= 0; i--) {
99 const value = this.stack[i].get(item);
100 if (value !== undefined) {
101 this.map.set(item, value);
102 return value !== TOMBSTONE;
103 }
104 }
105 this.map.set(item, TOMBSTONE);
106 }
107 return false;
108 }
109
110 /**
111 * Returns the visible value for a key, caching parent hits and misses in the
112 * current layer.
113 * @param {K} item the key of the element to return
114 * @returns {Cell<V>} the value of the element
115 */
116 get(item) {
117 const topValue = this.map.get(item);
118 if (topValue !== undefined) {
119 return topValue === TOMBSTONE || topValue === UNDEFINED_MARKER
120 ? undefined
121 : topValue;
122 }
123 if (this.stack.length > 1) {
124 for (let i = this.stack.length - 2; i >= 0; i--) {
125 const value = this.stack[i].get(item);
126 if (value !== undefined) {
127 this.map.set(item, value);
128 return value === TOMBSTONE || value === UNDEFINED_MARKER
129 ? undefined
130 : value;
131 }
132 }
133 this.map.set(item, TOMBSTONE);
134 }
135 }
136
137 /**
138 * Collapses the stacked layers into a single concrete map.
139 */
140 _compress() {
141 if (this.stack.length === 1) return;
142 this.map = new Map();
143 for (const data of this.stack) {
144 for (const pair of data) {
145 if (pair[1] === TOMBSTONE) {
146 this.map.delete(pair[0]);
147 } else {
148 this.map.set(pair[0], pair[1]);
149 }
150 }
151 }
152 this.stack = [this.map];
153 }
154
155 /**
156 * Returns the visible keys as an array after collapsing the stack.
157 * @returns {K[]} array of keys
158 */
159 asArray() {
160 this._compress();
161 return [...this.map.keys()];
162 }
163
164 /**
165 * Returns the visible keys as a `Set` after collapsing the stack.
166 * @returns {Set<K>} set of keys
167 */
168 asSet() {
169 this._compress();
170 return new Set(this.map.keys());
171 }
172
173 /**
174 * Returns visible key/value pairs using the external representation.
175 * @returns {[K, Cell<V>][]} array of key/value pairs
176 */
177 asPairArray() {
178 this._compress();
179 return Array.from(this.map.entries(), extractPair);
180 }
181
182 /**
183 * Returns the visible contents as a plain `Map`.
184 * @returns {Map<K, Cell<V>>} materialized map
185 */
186 asMap() {
187 return new Map(this.asPairArray());
188 }
189
190 /**
191 * Returns the number of visible keys after collapsing the stack.
192 * @returns {number} number of keys
193 */
194 get size() {
195 this._compress();
196 return this.map.size;
197 }
198
199 /**
200 * Creates a child `StackedMap` that sees the current layers as its parent
201 * scope.
202 * @returns {StackedMap<K, V>} child map
203 */
204 createChild() {
205 return new StackedMap(this.stack);
206 }
207}
208
209module.exports = StackedMap;
Note: See TracBrowser for help on using the repository browser.