You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			320 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			JavaScript
		
	
			
		
		
	
	
			320 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			JavaScript
		
	
| /*
 | |
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | |
| 	Author Tobias Koppers @sokra
 | |
| */
 | |
| "use strict";
 | |
| 
 | |
| const Source = require("./Source");
 | |
| const RawSource = require("./RawSource");
 | |
| const streamChunks = require("./helpers/streamChunks");
 | |
| const { getMap, getSourceAndMap } = require("./helpers/getFromStreamChunks");
 | |
| 
 | |
| const stringsAsRawSources = new WeakSet();
 | |
| 
 | |
| class ConcatSource extends Source {
 | |
| 	constructor() {
 | |
| 		super();
 | |
| 		this._children = [];
 | |
| 		for (let i = 0; i < arguments.length; i++) {
 | |
| 			const item = arguments[i];
 | |
| 			if (item instanceof ConcatSource) {
 | |
| 				for (const child of item._children) {
 | |
| 					this._children.push(child);
 | |
| 				}
 | |
| 			} else {
 | |
| 				this._children.push(item);
 | |
| 			}
 | |
| 		}
 | |
| 		this._isOptimized = arguments.length === 0;
 | |
| 	}
 | |
| 
 | |
| 	getChildren() {
 | |
| 		if (!this._isOptimized) this._optimize();
 | |
| 		return this._children;
 | |
| 	}
 | |
| 
 | |
| 	add(item) {
 | |
| 		if (item instanceof ConcatSource) {
 | |
| 			for (const child of item._children) {
 | |
| 				this._children.push(child);
 | |
| 			}
 | |
| 		} else {
 | |
| 			this._children.push(item);
 | |
| 		}
 | |
| 		this._isOptimized = false;
 | |
| 	}
 | |
| 
 | |
| 	addAllSkipOptimizing(items) {
 | |
| 		for (const item of items) {
 | |
| 			this._children.push(item);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	buffer() {
 | |
| 		if (!this._isOptimized) this._optimize();
 | |
| 		const buffers = [];
 | |
| 		for (const child of this._children) {
 | |
| 			if (typeof child.buffer === "function") {
 | |
| 				buffers.push(child.buffer());
 | |
| 			} else {
 | |
| 				const bufferOrString = child.source();
 | |
| 				if (Buffer.isBuffer(bufferOrString)) {
 | |
| 					buffers.push(bufferOrString);
 | |
| 				} else {
 | |
| 					// This will not happen
 | |
| 					buffers.push(Buffer.from(bufferOrString, "utf-8"));
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return Buffer.concat(buffers);
 | |
| 	}
 | |
| 
 | |
| 	source() {
 | |
| 		if (!this._isOptimized) this._optimize();
 | |
| 		let source = "";
 | |
| 		for (const child of this._children) {
 | |
| 			source += child.source();
 | |
| 		}
 | |
| 		return source;
 | |
| 	}
 | |
| 
 | |
| 	size() {
 | |
| 		if (!this._isOptimized) this._optimize();
 | |
| 		let size = 0;
 | |
| 		for (const child of this._children) {
 | |
| 			size += child.size();
 | |
| 		}
 | |
| 		return size;
 | |
| 	}
 | |
| 
 | |
| 	map(options) {
 | |
| 		return getMap(this, options);
 | |
| 	}
 | |
| 
 | |
| 	sourceAndMap(options) {
 | |
| 		return getSourceAndMap(this, options);
 | |
| 	}
 | |
| 
 | |
| 	streamChunks(options, onChunk, onSource, onName) {
 | |
| 		if (!this._isOptimized) this._optimize();
 | |
| 		if (this._children.length === 1)
 | |
| 			return this._children[0].streamChunks(options, onChunk, onSource, onName);
 | |
| 		let currentLineOffset = 0;
 | |
| 		let currentColumnOffset = 0;
 | |
| 		let sourceMapping = new Map();
 | |
| 		let nameMapping = new Map();
 | |
| 		const finalSource = !!(options && options.finalSource);
 | |
| 		let code = "";
 | |
| 		let needToCloseMapping = false;
 | |
| 		for (const item of this._children) {
 | |
| 			const sourceIndexMapping = [];
 | |
| 			const nameIndexMapping = [];
 | |
| 			let lastMappingLine = 0;
 | |
| 			const { generatedLine, generatedColumn, source } = streamChunks(
 | |
| 				item,
 | |
| 				options,
 | |
| 				// eslint-disable-next-line no-loop-func
 | |
| 				(
 | |
| 					chunk,
 | |
| 					generatedLine,
 | |
| 					generatedColumn,
 | |
| 					sourceIndex,
 | |
| 					originalLine,
 | |
| 					originalColumn,
 | |
| 					nameIndex
 | |
| 				) => {
 | |
| 					const line = generatedLine + currentLineOffset;
 | |
| 					const column =
 | |
| 						generatedLine === 1
 | |
| 							? generatedColumn + currentColumnOffset
 | |
| 							: generatedColumn;
 | |
| 					if (needToCloseMapping) {
 | |
| 						if (generatedLine !== 1 || generatedColumn !== 0) {
 | |
| 							onChunk(
 | |
| 								undefined,
 | |
| 								currentLineOffset + 1,
 | |
| 								currentColumnOffset,
 | |
| 								-1,
 | |
| 								-1,
 | |
| 								-1,
 | |
| 								-1
 | |
| 							);
 | |
| 						}
 | |
| 						needToCloseMapping = false;
 | |
| 					}
 | |
| 					const resultSourceIndex =
 | |
| 						sourceIndex < 0 || sourceIndex >= sourceIndexMapping.length
 | |
| 							? -1
 | |
| 							: sourceIndexMapping[sourceIndex];
 | |
| 					const resultNameIndex =
 | |
| 						nameIndex < 0 || nameIndex >= nameIndexMapping.length
 | |
| 							? -1
 | |
| 							: nameIndexMapping[nameIndex];
 | |
| 					lastMappingLine = resultSourceIndex < 0 ? 0 : generatedLine;
 | |
| 					if (finalSource) {
 | |
| 						if (chunk !== undefined) code += chunk;
 | |
| 						if (resultSourceIndex >= 0) {
 | |
| 							onChunk(
 | |
| 								undefined,
 | |
| 								line,
 | |
| 								column,
 | |
| 								resultSourceIndex,
 | |
| 								originalLine,
 | |
| 								originalColumn,
 | |
| 								resultNameIndex
 | |
| 							);
 | |
| 						}
 | |
| 					} else {
 | |
| 						if (resultSourceIndex < 0) {
 | |
| 							onChunk(chunk, line, column, -1, -1, -1, -1);
 | |
| 						} else {
 | |
| 							onChunk(
 | |
| 								chunk,
 | |
| 								line,
 | |
| 								column,
 | |
| 								resultSourceIndex,
 | |
| 								originalLine,
 | |
| 								originalColumn,
 | |
| 								resultNameIndex
 | |
| 							);
 | |
| 						}
 | |
| 					}
 | |
| 				},
 | |
| 				(i, source, sourceContent) => {
 | |
| 					let globalIndex = sourceMapping.get(source);
 | |
| 					if (globalIndex === undefined) {
 | |
| 						sourceMapping.set(source, (globalIndex = sourceMapping.size));
 | |
| 						onSource(globalIndex, source, sourceContent);
 | |
| 					}
 | |
| 					sourceIndexMapping[i] = globalIndex;
 | |
| 				},
 | |
| 				(i, name) => {
 | |
| 					let globalIndex = nameMapping.get(name);
 | |
| 					if (globalIndex === undefined) {
 | |
| 						nameMapping.set(name, (globalIndex = nameMapping.size));
 | |
| 						onName(globalIndex, name);
 | |
| 					}
 | |
| 					nameIndexMapping[i] = globalIndex;
 | |
| 				}
 | |
| 			);
 | |
| 			if (source !== undefined) code += source;
 | |
| 			if (needToCloseMapping) {
 | |
| 				if (generatedLine !== 1 || generatedColumn !== 0) {
 | |
| 					onChunk(
 | |
| 						undefined,
 | |
| 						currentLineOffset + 1,
 | |
| 						currentColumnOffset,
 | |
| 						-1,
 | |
| 						-1,
 | |
| 						-1,
 | |
| 						-1
 | |
| 					);
 | |
| 					needToCloseMapping = false;
 | |
| 				}
 | |
| 			}
 | |
| 			if (generatedLine > 1) {
 | |
| 				currentColumnOffset = generatedColumn;
 | |
| 			} else {
 | |
| 				currentColumnOffset += generatedColumn;
 | |
| 			}
 | |
| 			needToCloseMapping =
 | |
| 				needToCloseMapping ||
 | |
| 				(finalSource && lastMappingLine === generatedLine);
 | |
| 			currentLineOffset += generatedLine - 1;
 | |
| 		}
 | |
| 		return {
 | |
| 			generatedLine: currentLineOffset + 1,
 | |
| 			generatedColumn: currentColumnOffset,
 | |
| 			source: finalSource ? code : undefined
 | |
| 		};
 | |
| 	}
 | |
| 
 | |
| 	updateHash(hash) {
 | |
| 		if (!this._isOptimized) this._optimize();
 | |
| 		hash.update("ConcatSource");
 | |
| 		for (const item of this._children) {
 | |
| 			item.updateHash(hash);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	_optimize() {
 | |
| 		const newChildren = [];
 | |
| 		let currentString = undefined;
 | |
| 		let currentRawSources = undefined;
 | |
| 		const addStringToRawSources = string => {
 | |
| 			if (currentRawSources === undefined) {
 | |
| 				currentRawSources = string;
 | |
| 			} else if (Array.isArray(currentRawSources)) {
 | |
| 				currentRawSources.push(string);
 | |
| 			} else {
 | |
| 				currentRawSources = [
 | |
| 					typeof currentRawSources === "string"
 | |
| 						? currentRawSources
 | |
| 						: currentRawSources.source(),
 | |
| 					string
 | |
| 				];
 | |
| 			}
 | |
| 		};
 | |
| 		const addSourceToRawSources = source => {
 | |
| 			if (currentRawSources === undefined) {
 | |
| 				currentRawSources = source;
 | |
| 			} else if (Array.isArray(currentRawSources)) {
 | |
| 				currentRawSources.push(source.source());
 | |
| 			} else {
 | |
| 				currentRawSources = [
 | |
| 					typeof currentRawSources === "string"
 | |
| 						? currentRawSources
 | |
| 						: currentRawSources.source(),
 | |
| 					source.source()
 | |
| 				];
 | |
| 			}
 | |
| 		};
 | |
| 		const mergeRawSources = () => {
 | |
| 			if (Array.isArray(currentRawSources)) {
 | |
| 				const rawSource = new RawSource(currentRawSources.join(""));
 | |
| 				stringsAsRawSources.add(rawSource);
 | |
| 				newChildren.push(rawSource);
 | |
| 			} else if (typeof currentRawSources === "string") {
 | |
| 				const rawSource = new RawSource(currentRawSources);
 | |
| 				stringsAsRawSources.add(rawSource);
 | |
| 				newChildren.push(rawSource);
 | |
| 			} else {
 | |
| 				newChildren.push(currentRawSources);
 | |
| 			}
 | |
| 		};
 | |
| 		for (const child of this._children) {
 | |
| 			if (typeof child === "string") {
 | |
| 				if (currentString === undefined) {
 | |
| 					currentString = child;
 | |
| 				} else {
 | |
| 					currentString += child;
 | |
| 				}
 | |
| 			} else {
 | |
| 				if (currentString !== undefined) {
 | |
| 					addStringToRawSources(currentString);
 | |
| 					currentString = undefined;
 | |
| 				}
 | |
| 				if (stringsAsRawSources.has(child)) {
 | |
| 					addSourceToRawSources(child);
 | |
| 				} else {
 | |
| 					if (currentRawSources !== undefined) {
 | |
| 						mergeRawSources();
 | |
| 						currentRawSources = undefined;
 | |
| 					}
 | |
| 					newChildren.push(child);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		if (currentString !== undefined) {
 | |
| 			addStringToRawSources(currentString);
 | |
| 		}
 | |
| 		if (currentRawSources !== undefined) {
 | |
| 			mergeRawSources();
 | |
| 		}
 | |
| 		this._children = newChildren;
 | |
| 		this._isOptimized = true;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| module.exports = ConcatSource;
 |