UNPKG

7.81 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 RawSource = require("./RawSource");
9const streamChunks = require("./helpers/streamChunks");
10const { getMap, getSourceAndMap } = require("./helpers/getFromStreamChunks");
11
12const stringsAsRawSources = new WeakSet();
13
14class ConcatSource extends Source {
15 constructor() {
16 super();
17 this._children = [];
18 for (let i = 0; i < arguments.length; i++) {
19 const item = arguments[i];
20 if (item instanceof ConcatSource) {
21 for (const child of item._children) {
22 this._children.push(child);
23 }
24 } else {
25 this._children.push(item);
26 }
27 }
28 this._isOptimized = arguments.length === 0;
29 }
30
31 getChildren() {
32 if (!this._isOptimized) this._optimize();
33 return this._children;
34 }
35
36 add(item) {
37 if (item instanceof ConcatSource) {
38 for (const child of item._children) {
39 this._children.push(child);
40 }
41 } else {
42 this._children.push(item);
43 }
44 this._isOptimized = false;
45 }
46
47 addAllSkipOptimizing(items) {
48 for (const item of items) {
49 this._children.push(item);
50 }
51 }
52
53 buffer() {
54 if (!this._isOptimized) this._optimize();
55 const buffers = [];
56 for (const child of this._children) {
57 if (typeof child.buffer === "function") {
58 buffers.push(child.buffer());
59 } else {
60 const bufferOrString = child.source();
61 if (Buffer.isBuffer(bufferOrString)) {
62 buffers.push(bufferOrString);
63 } else {
64 buffers.push(Buffer.from(bufferOrString, "utf-8"));
65 }
66 }
67 }
68 return Buffer.concat(buffers);
69 }
70
71 source() {
72 if (!this._isOptimized) this._optimize();
73 let source = "";
74 for (const child of this._children) {
75 source += child.source();
76 }
77 return source;
78 }
79
80 size() {
81 if (!this._isOptimized) this._optimize();
82 let size = 0;
83 for (const child of this._children) {
84 size += child.size();
85 }
86 return size;
87 }
88
89 map(options) {
90 return getMap(this, options);
91 }
92
93 sourceAndMap(options) {
94 return getSourceAndMap(this, options);
95 }
96
97 streamChunks(options, onChunk, onSource, onName) {
98 if (!this._isOptimized) this._optimize();
99 if (this._children.length === 1)
100 return this._children[0].streamChunks(options, onChunk, onSource, onName);
101 let currentLineOffset = 0;
102 let currentColumnOffset = 0;
103 let sourceMapping = new Map();
104 let nameMapping = new Map();
105 const finalSource = !!(options && options.finalSource);
106 let code = "";
107 let needToCloseMapping = false;
108 for (const item of this._children) {
109 const sourceIndexMapping = [];
110 const nameIndexMapping = [];
111 let lastMappingLine = 0;
112 const { generatedLine, generatedColumn, source } = streamChunks(
113 item,
114 options,
115 // eslint-disable-next-line no-loop-func
116 (
117 chunk,
118 generatedLine,
119 generatedColumn,
120 sourceIndex,
121 originalLine,
122 originalColumn,
123 nameIndex
124 ) => {
125 const line = generatedLine + currentLineOffset;
126 const column =
127 generatedLine === 1
128 ? generatedColumn + currentColumnOffset
129 : generatedColumn;
130 if (needToCloseMapping) {
131 if (generatedLine !== 1 || generatedColumn !== 0) {
132 onChunk(
133 undefined,
134 currentLineOffset + 1,
135 currentColumnOffset,
136 -1,
137 -1,
138 -1,
139 -1
140 );
141 }
142 needToCloseMapping = false;
143 }
144 const resultSourceIndex =
145 sourceIndex < 0 ? -1 : sourceIndexMapping[sourceIndex];
146 const resultNameIndex =
147 nameIndex < 0 ? -1 : nameIndexMapping[nameIndex];
148 lastMappingLine = resultSourceIndex < 0 ? 0 : generatedLine;
149 if (finalSource && chunk !== undefined) {
150 code += chunk;
151 onChunk(
152 undefined,
153 line,
154 column,
155 resultSourceIndex,
156 originalLine,
157 originalColumn,
158 resultNameIndex
159 );
160 } else {
161 onChunk(
162 chunk,
163 line,
164 column,
165 resultSourceIndex,
166 originalLine,
167 originalColumn,
168 resultNameIndex
169 );
170 }
171 },
172 (i, source, sourceContent) => {
173 let globalIndex = sourceMapping.get(source);
174 if (globalIndex === undefined) {
175 sourceMapping.set(source, (globalIndex = sourceMapping.size));
176 onSource(globalIndex, source, sourceContent);
177 }
178 sourceIndexMapping[i] = globalIndex;
179 },
180 (i, name) => {
181 let globalIndex = nameMapping.get(name);
182 if (globalIndex === undefined) {
183 nameMapping.set(name, (globalIndex = nameMapping.size));
184 onName(globalIndex, name);
185 }
186 nameIndexMapping[i] = globalIndex;
187 }
188 );
189 if (source !== undefined) code += source;
190 if (needToCloseMapping) {
191 if (generatedLine !== 1 || generatedColumn !== 0) {
192 onChunk(
193 undefined,
194 currentLineOffset + 1,
195 currentColumnOffset,
196 -1,
197 -1,
198 -1,
199 -1
200 );
201 needToCloseMapping = false;
202 }
203 }
204 if (generatedLine > 1) {
205 currentColumnOffset = generatedColumn;
206 } else {
207 currentColumnOffset += generatedColumn;
208 }
209 needToCloseMapping =
210 needToCloseMapping ||
211 (finalSource && lastMappingLine === generatedLine);
212 currentLineOffset += generatedLine - 1;
213 }
214 if (needToCloseMapping) {
215 onChunk(
216 undefined,
217 currentLineOffset + 1,
218 currentColumnOffset,
219 -1,
220 -1,
221 -1,
222 -1
223 );
224 }
225 return {
226 generatedLine: currentLineOffset + 1,
227 generatedColumn: currentColumnOffset,
228 source: finalSource ? code : undefined
229 };
230 }
231
232 updateHash(hash) {
233 if (!this._isOptimized) this._optimize();
234 hash.update("ConcatSource");
235 for (const item of this._children) {
236 item.updateHash(hash);
237 }
238 }
239
240 _optimize() {
241 const newChildren = [];
242 let currentString = undefined;
243 let currentRawSources = undefined;
244 const addStringToRawSources = string => {
245 if (currentRawSources === undefined) {
246 currentRawSources = string;
247 } else if (Array.isArray(currentRawSources)) {
248 currentRawSources.push(string);
249 } else {
250 currentRawSources = [
251 typeof currentRawSources === "string"
252 ? currentRawSources
253 : currentRawSources.source(),
254 string
255 ];
256 }
257 };
258 const addSourceToRawSources = source => {
259 if (currentRawSources === undefined) {
260 currentRawSources = source;
261 } else if (Array.isArray(currentRawSources)) {
262 currentRawSources.push(source.source());
263 } else {
264 currentRawSources = [
265 typeof currentRawSources === "string"
266 ? currentRawSources
267 : currentRawSources.source(),
268 source.source()
269 ];
270 }
271 };
272 const mergeRawSources = () => {
273 if (Array.isArray(currentRawSources)) {
274 const rawSource = new RawSource(currentRawSources.join(""));
275 stringsAsRawSources.add(rawSource);
276 newChildren.push(rawSource);
277 } else if (typeof currentRawSources === "string") {
278 const rawSource = new RawSource(currentRawSources);
279 stringsAsRawSources.add(rawSource);
280 newChildren.push(rawSource);
281 } else {
282 newChildren.push(currentRawSources);
283 }
284 };
285 for (const child of this._children) {
286 if (typeof child === "string") {
287 if (currentString === undefined) {
288 currentString = child;
289 } else {
290 currentString += child;
291 }
292 } else {
293 if (currentString !== undefined) {
294 addStringToRawSources(currentString);
295 currentString = undefined;
296 }
297 if (stringsAsRawSources.has(child)) {
298 addSourceToRawSources(child);
299 } else {
300 if (currentRawSources !== undefined) {
301 mergeRawSources();
302 currentRawSources = undefined;
303 }
304 newChildren.push(child);
305 }
306 }
307 }
308 if (currentString !== undefined) {
309 addStringToRawSources(currentString);
310 }
311 if (currentRawSources !== undefined) {
312 mergeRawSources();
313 }
314 this._children = newChildren;
315 this._isOptimized = true;
316 }
317}
318
319module.exports = ConcatSource;