UNPKG

7.46 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7const Source = require("./Source");
8const streamChunksOfSourceMap = require("./helpers/streamChunksOfSourceMap");
9const streamChunksOfRawSource = require("./helpers/streamChunksOfRawSource");
10const streamAndGetSourceAndMap = require("./helpers/streamAndGetSourceAndMap");
11
12const mapToBufferedMap = map => {
13 if (typeof map !== "object" || !map) return map;
14 const bufferedMap = Object.assign({}, map);
15 if (map.mappings) {
16 bufferedMap.mappings = Buffer.from(map.mappings, "utf-8");
17 }
18 if (map.sourcesContent) {
19 bufferedMap.sourcesContent = map.sourcesContent.map(
20 str => str && Buffer.from(str, "utf-8")
21 );
22 }
23 return bufferedMap;
24};
25
26const bufferedMapToMap = bufferedMap => {
27 if (typeof bufferedMap !== "object" || !bufferedMap) return bufferedMap;
28 const map = Object.assign({}, bufferedMap);
29 if (bufferedMap.mappings) {
30 map.mappings = bufferedMap.mappings.toString("utf-8");
31 }
32 if (bufferedMap.sourcesContent) {
33 map.sourcesContent = bufferedMap.sourcesContent.map(
34 buffer => buffer && buffer.toString("utf-8")
35 );
36 }
37 return map;
38};
39
40class CachedSource extends Source {
41 constructor(source, cachedData) {
42 super();
43 this._source = source;
44 this._cachedSourceType = cachedData ? cachedData.source : undefined;
45 this._cachedSource = undefined;
46 this._cachedBuffer = cachedData ? cachedData.buffer : undefined;
47 this._cachedSize = cachedData ? cachedData.size : undefined;
48 this._cachedMaps = cachedData ? cachedData.maps : new Map();
49 this._cachedHashUpdate = cachedData ? cachedData.hash : undefined;
50 }
51
52 getCachedData() {
53 const bufferedMaps = new Map();
54 for (const pair of this._cachedMaps) {
55 let cacheEntry = pair[1];
56 if (cacheEntry.bufferedMap === undefined) {
57 cacheEntry.bufferedMap = mapToBufferedMap(
58 this._getMapFromCacheEntry(cacheEntry)
59 );
60 }
61 bufferedMaps.set(pair[0], {
62 map: undefined,
63 bufferedMap: cacheEntry.bufferedMap
64 });
65 }
66 // We don't want to cache strings
67 // So if we have a caches sources
68 // create a buffer from it and only store
69 // if it was a Buffer or string
70 if (this._cachedSource) {
71 this.buffer();
72 }
73 return {
74 buffer: this._cachedBuffer,
75 source:
76 this._cachedSourceType !== undefined
77 ? this._cachedSourceType
78 : typeof this._cachedSource === "string"
79 ? true
80 : Buffer.isBuffer(this._cachedSource)
81 ? false
82 : undefined,
83 size: this._cachedSize,
84 maps: bufferedMaps,
85 hash: this._cachedHashUpdate
86 };
87 }
88
89 originalLazy() {
90 return this._source;
91 }
92
93 original() {
94 if (typeof this._source === "function") this._source = this._source();
95 return this._source;
96 }
97
98 source() {
99 const source = this._getCachedSource();
100 if (source !== undefined) return source;
101 return (this._cachedSource = this.original().source());
102 }
103
104 _getMapFromCacheEntry(cacheEntry) {
105 if (cacheEntry.map !== undefined) {
106 return cacheEntry.map;
107 } else if (cacheEntry.bufferedMap !== undefined) {
108 return (cacheEntry.map = bufferedMapToMap(cacheEntry.bufferedMap));
109 }
110 }
111
112 _getCachedSource() {
113 if (this._cachedSource !== undefined) return this._cachedSource;
114 if (this._cachedBuffer && this._cachedSourceType !== undefined) {
115 return (this._cachedSource = this._cachedSourceType
116 ? this._cachedBuffer.toString("utf-8")
117 : this._cachedBuffer);
118 }
119 }
120
121 buffer() {
122 if (this._cachedBuffer !== undefined) return this._cachedBuffer;
123 if (this._cachedSource !== undefined) {
124 if (Buffer.isBuffer(this._cachedSource)) {
125 return (this._cachedBuffer = this._cachedSource);
126 }
127 return (this._cachedBuffer = Buffer.from(this._cachedSource, "utf-8"));
128 }
129 if (typeof this.original().buffer === "function") {
130 return (this._cachedBuffer = this.original().buffer());
131 }
132 const bufferOrString = this.source();
133 if (Buffer.isBuffer(bufferOrString)) {
134 return (this._cachedBuffer = bufferOrString);
135 }
136 return (this._cachedBuffer = Buffer.from(bufferOrString, "utf-8"));
137 }
138
139 size() {
140 if (this._cachedSize !== undefined) return this._cachedSize;
141 if (this._cachedBuffer !== undefined) {
142 return (this._cachedSize = this._cachedBuffer.length);
143 }
144 const source = this._getCachedSource();
145 if (source !== undefined) {
146 return (this._cachedSize = Buffer.byteLength(source));
147 }
148 return (this._cachedSize = this.original().size());
149 }
150
151 sourceAndMap(options) {
152 const key = options ? JSON.stringify(options) : "{}";
153 const cacheEntry = this._cachedMaps.get(key);
154 // Look for a cached map
155 if (cacheEntry !== undefined) {
156 // We have a cached map in some representation
157 const map = this._getMapFromCacheEntry(cacheEntry);
158 // Either get the cached source or compute it
159 return { source: this.source(), map };
160 }
161 // Look for a cached source
162 let source = this._getCachedSource();
163 // Compute the map
164 let map;
165 if (source !== undefined) {
166 map = this.original().map(options);
167 } else {
168 // Compute the source and map together.
169 const sourceAndMap = this.original().sourceAndMap(options);
170 source = sourceAndMap.source;
171 map = sourceAndMap.map;
172 this._cachedSource = source;
173 }
174 this._cachedMaps.set(key, {
175 map,
176 bufferedMap: undefined
177 });
178 return { source, map };
179 }
180
181 streamChunks(options, onChunk, onSource, onName) {
182 const key = options ? JSON.stringify(options) : "{}";
183 if (
184 this._cachedMaps.has(key) &&
185 (this._cachedBuffer !== undefined || this._cachedSource !== undefined)
186 ) {
187 const { source, map } = this.sourceAndMap(options);
188 if (map) {
189 return streamChunksOfSourceMap(
190 source,
191 map,
192 onChunk,
193 onSource,
194 onName,
195 !!(options && options.finalSource)
196 );
197 } else {
198 return streamChunksOfRawSource(
199 source,
200 onChunk,
201 onSource,
202 onName,
203 !!(options && options.finalSource)
204 );
205 }
206 }
207 const { result, source, map } = streamAndGetSourceAndMap(
208 this.original(),
209 options,
210 onChunk,
211 onSource,
212 onName
213 );
214 this._cachedSource = source;
215 this._cachedMaps.set(key, {
216 map,
217 bufferedMap: undefined
218 });
219 return result;
220 }
221
222 map(options) {
223 const key = options ? JSON.stringify(options) : "{}";
224 const cacheEntry = this._cachedMaps.get(key);
225 if (cacheEntry !== undefined) {
226 return this._getMapFromCacheEntry(cacheEntry);
227 }
228 const map = this.original().map(options);
229 this._cachedMaps.set(key, {
230 map,
231 bufferedMap: undefined
232 });
233 return map;
234 }
235
236 updateHash(hash) {
237 if (this._cachedHashUpdate !== undefined) {
238 for (const item of this._cachedHashUpdate) hash.update(item);
239 return;
240 }
241 const update = [];
242 let currentString = undefined;
243 const tracker = {
244 update: item => {
245 if (typeof item === "string" && item.length < 10240) {
246 if (currentString === undefined) {
247 currentString = item;
248 } else {
249 currentString += item;
250 if (currentString.length > 102400) {
251 update.push(Buffer.from(currentString));
252 currentString = undefined;
253 }
254 }
255 } else {
256 if (currentString !== undefined) {
257 update.push(Buffer.from(currentString));
258 currentString = undefined;
259 }
260 update.push(item);
261 }
262 }
263 };
264 this.original().updateHash(tracker);
265 if (currentString !== undefined) {
266 update.push(Buffer.from(currentString));
267 }
268 for (const item of update) hash.update(item);
269 this._cachedHashUpdate = update;
270 }
271}
272
273module.exports = CachedSource;