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
		
	
| 
											9 months ago
										 | /* | ||
|  | 	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; |