source: frontend/node_modules/webpack-sources/lib/CachedSource.js

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

Fix frontend appearance

  • Property mode set to 100644
File size: 13.6 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 Source = require("./Source");
9const streamAndGetSourceAndMap = require("./helpers/streamAndGetSourceAndMap");
10const streamChunksOfRawSource = require("./helpers/streamChunksOfRawSource");
11const streamChunksOfSourceMap = require("./helpers/streamChunksOfSourceMap");
12const {
13 isDualStringBufferCachingEnabled,
14} = require("./helpers/stringBufferUtils");
15
16/** @typedef {import("./Source").HashLike} HashLike */
17/** @typedef {import("./Source").MapOptions} MapOptions */
18/** @typedef {import("./Source").RawSourceMap} RawSourceMap */
19/** @typedef {import("./Source").SourceAndMap} SourceAndMap */
20/** @typedef {import("./Source").SourceValue} SourceValue */
21/** @typedef {import("./helpers/getGeneratedSourceInfo").GeneratedSourceInfo} GeneratedSourceInfo */
22/** @typedef {import("./helpers/streamChunks").OnChunk} OnChunk */
23/** @typedef {import("./helpers/streamChunks").OnName} OnName */
24/** @typedef {import("./helpers/streamChunks").OnSource} OnSource */
25/** @typedef {import("./helpers/streamChunks").Options} Options */
26
27/**
28 * @typedef {object} BufferedMap
29 * @property {number} version version
30 * @property {string[]} sources sources
31 * @property {string[]} names name
32 * @property {string=} sourceRoot source root
33 * @property {(Buffer | "")[]=} sourcesContent sources content
34 * @property {Buffer=} mappings mappings
35 * @property {string} file file
36 */
37
38/**
39 * @param {null | RawSourceMap} map map
40 * @returns {null | BufferedMap} buffered map
41 */
42const mapToBufferedMap = (map) => {
43 if (typeof map !== "object" || !map) return map;
44 const bufferedMap =
45 /** @type {BufferedMap} */
46 (/** @type {unknown} */ ({ ...map }));
47 if (map.mappings) {
48 bufferedMap.mappings = Buffer.from(map.mappings, "utf8");
49 }
50 if (map.sourcesContent) {
51 bufferedMap.sourcesContent = map.sourcesContent.map(
52 (str) => str && Buffer.from(str, "utf8"),
53 );
54 }
55 return bufferedMap;
56};
57
58/**
59 * @param {null | BufferedMap} bufferedMap buffered map
60 * @returns {null | RawSourceMap} map
61 */
62const bufferedMapToMap = (bufferedMap) => {
63 if (typeof bufferedMap !== "object" || !bufferedMap) return bufferedMap;
64 const map =
65 /** @type {RawSourceMap} */
66 (/** @type {unknown} */ ({ ...bufferedMap }));
67 if (bufferedMap.mappings) {
68 map.mappings = bufferedMap.mappings.toString("utf8");
69 }
70 if (bufferedMap.sourcesContent) {
71 map.sourcesContent = bufferedMap.sourcesContent.map(
72 (buffer) => buffer && buffer.toString("utf8"),
73 );
74 }
75 return map;
76};
77
78/** @typedef {{ map?: null | RawSourceMap, bufferedMap?: null | BufferedMap }} BufferEntry */
79/** @typedef {Map<string, BufferEntry>} BufferedMaps */
80
81const CACHE_KEY_EMPTY = "{}";
82const CACHE_KEY_COLUMNS_FALSE = '{"columns":false}';
83const CACHE_KEY_COLUMNS_TRUE = '{"columns":true}';
84
85/**
86 * Fast-path replacement for `JSON.stringify(options)` when used as a cache
87 * key. MapOptions / streamChunks Options are both small boolean-only shapes
88 * and the overwhelmingly common shapes (`undefined`, `{}`, `{columns}`) can
89 * be keyed without calling `JSON.stringify`, which dominates short-circuit
90 * cache lookups. Falls back to `JSON.stringify` for any other shape so keys
91 * remain compatible with previously cached `BufferedMaps` entries.
92 * @param {undefined | MapOptions | Options} options options
93 * @returns {string} cache key
94 */
95const getCacheKey = (options) => {
96 if (!options) return CACHE_KEY_EMPTY;
97 const { columns } = options;
98 if (
99 /** @type {Options} */ (options).source === undefined &&
100 /** @type {Options} */ (options).finalSource === undefined &&
101 /** @type {MapOptions} */ (options).module === undefined
102 ) {
103 if (columns === undefined) return CACHE_KEY_EMPTY;
104 return columns ? CACHE_KEY_COLUMNS_TRUE : CACHE_KEY_COLUMNS_FALSE;
105 }
106 return JSON.stringify(options);
107};
108
109/**
110 * @typedef {object} CachedData
111 * @property {boolean=} source source
112 * @property {Buffer} buffer buffer
113 * @property {number=} size size
114 * @property {BufferedMaps} maps maps
115 * @property {(string | Buffer)[]=} hash hash
116 */
117
118class CachedSource extends Source {
119 /**
120 * @param {Source | (() => Source)} source source
121 * @param {CachedData=} cachedData cached data
122 */
123 constructor(source, cachedData) {
124 super();
125 /**
126 * @private
127 * @type {Source | (() => Source)}
128 */
129 this._source = source;
130 /**
131 * @private
132 * @type {undefined | string}
133 */
134 this._cachedSource = undefined;
135 // Split on `cachedData` once instead of re-evaluating the ternary for
136 // every field. Under the interpreter (and CodSpeed's simulation) each
137 // ternary is a separate branch; consolidating cuts the per-instance
138 // branch count roughly in half.
139 if (cachedData) {
140 /**
141 * @private
142 * @type {boolean | undefined}
143 */
144 this._cachedSourceType = cachedData.source;
145 /**
146 * @private
147 * @type {Buffer | undefined}
148 */
149 this._cachedBuffer = cachedData.buffer;
150 /**
151 * @private
152 * @type {number | undefined}
153 */
154 this._cachedSize = cachedData.size;
155 /**
156 * @private
157 * @type {BufferedMaps}
158 */
159 this._cachedMaps = cachedData.maps;
160 /**
161 * @private
162 * @type {(string | Buffer)[] | undefined}
163 */
164 this._cachedHashUpdate = cachedData.hash;
165 } else {
166 this._cachedSourceType = undefined;
167 this._cachedBuffer = undefined;
168 this._cachedSize = undefined;
169 this._cachedMaps = new Map();
170 this._cachedHashUpdate = undefined;
171 }
172 }
173
174 /**
175 * @returns {CachedData} cached data
176 */
177 getCachedData() {
178 /** @type {BufferedMaps} */
179 const bufferedMaps = new Map();
180 for (const pair of this._cachedMaps) {
181 const [, cacheEntry] = pair;
182 if (cacheEntry.bufferedMap === undefined) {
183 cacheEntry.bufferedMap = mapToBufferedMap(
184 this._getMapFromCacheEntry(cacheEntry),
185 );
186 }
187 bufferedMaps.set(pair[0], {
188 map: undefined,
189 bufferedMap: cacheEntry.bufferedMap,
190 });
191 }
192 return {
193 // We don't want to cache strings
194 // So if we have a caches sources
195 // create a buffer from it and only store
196 // if it was a Buffer or string
197 buffer: this._cachedSource
198 ? this.buffer()
199 : /** @type {Buffer} */ (this._cachedBuffer),
200 source:
201 this._cachedSourceType !== undefined
202 ? this._cachedSourceType
203 : typeof this._cachedSource === "string"
204 ? true
205 : Buffer.isBuffer(this._cachedSource)
206 ? false
207 : undefined,
208 size: this._cachedSize,
209 maps: bufferedMaps,
210 hash: this._cachedHashUpdate,
211 };
212 }
213
214 originalLazy() {
215 return this._source;
216 }
217
218 original() {
219 if (typeof this._source === "function") this._source = this._source();
220 return this._source;
221 }
222
223 /**
224 * @returns {SourceValue} source
225 */
226 source() {
227 // Fully inlined _getCachedSource: both warm- and cold-cache paths skip
228 // the prototype method lookup / stack frame the interpreter would
229 // otherwise pay on every call.
230 if (this._cachedSource !== undefined) return this._cachedSource;
231 const cachedBuffer = this._cachedBuffer;
232 const cachedSourceType = this._cachedSourceType;
233 if (cachedBuffer !== undefined && cachedSourceType !== undefined) {
234 const value = cachedSourceType
235 ? cachedBuffer.toString("utf8")
236 : cachedBuffer;
237 if (isDualStringBufferCachingEnabled()) {
238 this._cachedSource = /** @type {string} */ (value);
239 }
240 return /** @type {string} */ (value);
241 }
242 return (this._cachedSource =
243 /** @type {string} */
244 (this.original().source()));
245 }
246
247 /**
248 * @private
249 * @param {BufferEntry} cacheEntry cache entry
250 * @returns {null | RawSourceMap} raw source map
251 */
252 _getMapFromCacheEntry(cacheEntry) {
253 if (cacheEntry.map !== undefined) {
254 return cacheEntry.map;
255 } else if (cacheEntry.bufferedMap !== undefined) {
256 return (cacheEntry.map = bufferedMapToMap(cacheEntry.bufferedMap));
257 }
258
259 return null;
260 }
261
262 /**
263 * @private
264 * @returns {undefined | string} cached source
265 */
266 _getCachedSource() {
267 if (this._cachedSource !== undefined) return this._cachedSource;
268 if (this._cachedBuffer && this._cachedSourceType !== undefined) {
269 const value = this._cachedSourceType
270 ? this._cachedBuffer.toString("utf8")
271 : this._cachedBuffer;
272 if (isDualStringBufferCachingEnabled()) {
273 this._cachedSource = /** @type {string} */ (value);
274 }
275 return /** @type {string} */ (value);
276 }
277 }
278
279 /**
280 * @returns {Buffer} buffer
281 */
282 buffer() {
283 if (this._cachedBuffer !== undefined) return this._cachedBuffer;
284 if (this._cachedBuffers !== undefined) {
285 return (this._cachedBuffer = Buffer.concat(this._cachedBuffers));
286 }
287 if (this._cachedSource !== undefined) {
288 const value = Buffer.isBuffer(this._cachedSource)
289 ? this._cachedSource
290 : Buffer.from(this._cachedSource, "utf8");
291 if (isDualStringBufferCachingEnabled()) {
292 this._cachedBuffer = value;
293 }
294 return value;
295 }
296 if (typeof this.original().buffer === "function") {
297 return (this._cachedBuffer = this.original().buffer());
298 }
299 const bufferOrString = this.source();
300 if (Buffer.isBuffer(bufferOrString)) {
301 return (this._cachedBuffer = bufferOrString);
302 }
303 const value = Buffer.from(bufferOrString, "utf8");
304 if (isDualStringBufferCachingEnabled()) {
305 this._cachedBuffer = value;
306 }
307 return value;
308 }
309
310 /**
311 * @returns {Buffer[]} buffers
312 */
313 buffers() {
314 if (this._cachedBuffers !== undefined) return this._cachedBuffers;
315 if (this._cachedBuffer !== undefined) {
316 return (this._cachedBuffers = [this._cachedBuffer]);
317 }
318 const original = this.original();
319 if (typeof original.buffers === "function") {
320 return (this._cachedBuffers = original.buffers());
321 }
322 return (this._cachedBuffers = [this.buffer()]);
323 }
324
325 /**
326 * @returns {number} size
327 */
328 size() {
329 if (this._cachedSize !== undefined) return this._cachedSize;
330 if (this._cachedBuffer !== undefined) {
331 return (this._cachedSize = this._cachedBuffer.length);
332 }
333 const source = this._getCachedSource();
334 if (source !== undefined) {
335 return (this._cachedSize = Buffer.byteLength(source));
336 }
337 return (this._cachedSize = this.original().size());
338 }
339
340 /**
341 * @param {MapOptions=} options map options
342 * @returns {SourceAndMap} source and map
343 */
344 sourceAndMap(options) {
345 const key = getCacheKey(options);
346 const cacheEntry = this._cachedMaps.get(key);
347 // Look for a cached map
348 if (cacheEntry !== undefined) {
349 // We have a cached map in some representation
350 const map = this._getMapFromCacheEntry(cacheEntry);
351
352 // Either get the cached source or compute it
353 return { source: this.source(), map };
354 }
355 // Look for a cached source
356 let source = this._getCachedSource();
357 // Compute the map
358 let map;
359 if (source !== undefined) {
360 map = this.original().map(options);
361 } else {
362 // Compute the source and map together.
363 const sourceAndMap = this.original().sourceAndMap(options);
364 source = /** @type {string} */ (sourceAndMap.source);
365 map = sourceAndMap.map;
366 this._cachedSource = source;
367 }
368 this._cachedMaps.set(key, {
369 map,
370 bufferedMap: undefined,
371 });
372 return { source, map };
373 }
374
375 /**
376 * @param {Options} options options
377 * @param {OnChunk} onChunk called for each chunk of code
378 * @param {OnSource} onSource called for each source
379 * @param {OnName} onName called for each name
380 * @returns {GeneratedSourceInfo} generated source info
381 */
382 streamChunks(options, onChunk, onSource, onName) {
383 const key = getCacheKey(options);
384 if (
385 this._cachedMaps.has(key) &&
386 (this._cachedBuffer !== undefined || this._cachedSource !== undefined)
387 ) {
388 const { source, map } = this.sourceAndMap(options);
389 if (map) {
390 return streamChunksOfSourceMap(
391 /** @type {string} */
392 (source),
393 map,
394 onChunk,
395 onSource,
396 onName,
397 Boolean(options && options.finalSource),
398 true,
399 );
400 }
401 return streamChunksOfRawSource(
402 /** @type {string} */
403 (source),
404 onChunk,
405 onSource,
406 onName,
407 Boolean(options && options.finalSource),
408 );
409 }
410 const sourceAndMap = streamAndGetSourceAndMap(
411 this.original(),
412 options,
413 onChunk,
414 onSource,
415 onName,
416 );
417 this._cachedSource = sourceAndMap.source;
418 this._cachedMaps.set(key, {
419 map: /** @type {RawSourceMap} */ (sourceAndMap.map),
420 bufferedMap: undefined,
421 });
422 return sourceAndMap.result;
423 }
424
425 /**
426 * @param {MapOptions=} options map options
427 * @returns {RawSourceMap | null} map
428 */
429 map(options) {
430 const key = getCacheKey(options);
431 const cacheEntry = this._cachedMaps.get(key);
432 if (cacheEntry !== undefined) {
433 return this._getMapFromCacheEntry(cacheEntry);
434 }
435 const map = this.original().map(options);
436 this._cachedMaps.set(key, {
437 map,
438 bufferedMap: undefined,
439 });
440 return map;
441 }
442
443 /**
444 * @param {HashLike} hash hash
445 * @returns {void}
446 */
447 updateHash(hash) {
448 if (this._cachedHashUpdate !== undefined) {
449 for (const item of this._cachedHashUpdate) hash.update(item);
450 return;
451 }
452 /** @type {(string | Buffer)[]} */
453 const update = [];
454 /** @type {string | undefined} */
455 let currentString;
456 const tracker = {
457 /**
458 * @param {string | Buffer} item item
459 * @returns {void}
460 */
461 update: (item) => {
462 if (typeof item === "string" && item.length < 10240) {
463 if (currentString === undefined) {
464 currentString = item;
465 } else {
466 currentString += item;
467 if (currentString.length > 102400) {
468 update.push(Buffer.from(currentString));
469 currentString = undefined;
470 }
471 }
472 } else {
473 if (currentString !== undefined) {
474 update.push(Buffer.from(currentString));
475 currentString = undefined;
476 }
477 update.push(item);
478 }
479 },
480 };
481 this.original().updateHash(/** @type {HashLike} */ (tracker));
482 if (currentString !== undefined) {
483 update.push(Buffer.from(currentString));
484 }
485 for (const item of update) hash.update(item);
486 this._cachedHashUpdate = update;
487 }
488}
489
490module.exports = CachedSource;
Note: See TracBrowser for help on using the repository browser.