| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 19:05:58 +08:00
										 |  |  | const { RawSource, ReplaceSource } = require("webpack-sources"); | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TODO: clean up this file
 | 
					
						
							|  |  |  | // replace with newer constructs
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TODO: remove DependencyVariables and replace them with something better
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class JavascriptGenerator { | 
					
						
							|  |  |  | 	generate(module, dependencyTemplates, runtimeTemplate) { | 
					
						
							|  |  |  | 		const originalSource = module.originalSource(); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (!originalSource) { | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 			return new RawSource("throw new Error('No source available');"); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 00:57:02 +08:00
										 |  |  | 		const source = new ReplaceSource(originalSource, null); | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		this.sourceBlock( | 
					
						
							|  |  |  | 			module, | 
					
						
							|  |  |  | 			module, | 
					
						
							|  |  |  | 			[], | 
					
						
							|  |  |  | 			dependencyTemplates, | 
					
						
							|  |  |  | 			source, | 
					
						
							|  |  |  | 			runtimeTemplate | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return source; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	sourceBlock( | 
					
						
							|  |  |  | 		module, | 
					
						
							|  |  |  | 		block, | 
					
						
							|  |  |  | 		availableVars, | 
					
						
							|  |  |  | 		dependencyTemplates, | 
					
						
							|  |  |  | 		source, | 
					
						
							|  |  |  | 		runtimeTemplate | 
					
						
							|  |  |  | 	) { | 
					
						
							|  |  |  | 		for (const dependency of block.dependencies) { | 
					
						
							|  |  |  | 			this.sourceDependency( | 
					
						
							|  |  |  | 				dependency, | 
					
						
							|  |  |  | 				dependencyTemplates, | 
					
						
							|  |  |  | 				source, | 
					
						
							|  |  |  | 				runtimeTemplate | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * Get the variables of all blocks that we need to inject. | 
					
						
							|  |  |  | 		 * These will contain the variable name and its expression. | 
					
						
							| 
									
										
										
										
											2018-02-26 10:38:26 +08:00
										 |  |  | 		 * The name will be added as a parameter in a IIFE the expression as its value. | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 		 */ | 
					
						
							|  |  |  | 		const vars = block.variables.reduce((result, value) => { | 
					
						
							|  |  |  | 			const variable = this.sourceVariables( | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				value, | 
					
						
							|  |  |  | 				availableVars, | 
					
						
							|  |  |  | 				dependencyTemplates, | 
					
						
							|  |  |  | 				runtimeTemplate | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			if (variable) { | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 				result.push(variable); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return result; | 
					
						
							|  |  |  | 		}, []); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * if we actually have variables | 
					
						
							|  |  |  | 		 * this is important as how #splitVariablesInUniqueNamedChunks works | 
					
						
							|  |  |  | 		 * it will always return an array in an array which would lead to a IIFE wrapper around | 
					
						
							|  |  |  | 		 * a module if we do this with an empty vars array. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (vars.length > 0) { | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 			/** | 
					
						
							|  |  |  | 			 * Split all variables up into chunks of unique names. | 
					
						
							|  |  |  | 			 * e.g. imagine you have the following variable names that need to be injected: | 
					
						
							|  |  |  | 			 * [foo, bar, baz, foo, some, more] | 
					
						
							|  |  |  | 			 * we can not inject "foo" twice, therefore we just make two IIFEs like so: | 
					
						
							|  |  |  | 			 * (function(foo, bar, baz){ | 
					
						
							|  |  |  | 			 *   (function(foo, some, more){ | 
					
						
							| 
									
										
										
										
											2018-03-22 17:54:18 +08:00
										 |  |  | 			 *     … | 
					
						
							|  |  |  | 			 *   }(…)); | 
					
						
							|  |  |  | 			 * }(…)); | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 			 * | 
					
						
							|  |  |  | 			 * "splitVariablesInUniqueNamedChunks" splits the variables shown above up to this: | 
					
						
							|  |  |  | 			 * [[foo, bar, baz], [foo, some, more]] | 
					
						
							|  |  |  | 			 */ | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			const injectionVariableChunks = this.splitVariablesInUniqueNamedChunks( | 
					
						
							|  |  |  | 				vars | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// create all the beginnings of IIFEs
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			const functionWrapperStarts = injectionVariableChunks.map( | 
					
						
							|  |  |  | 				variableChunk => { | 
					
						
							|  |  |  | 					return this.variableInjectionFunctionWrapperStartCode( | 
					
						
							|  |  |  | 						variableChunk.map(variable => variable.name) | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// and all the ends
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			const functionWrapperEnds = injectionVariableChunks.map(variableChunk => { | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 				return this.variableInjectionFunctionWrapperEndCode( | 
					
						
							|  |  |  | 					module, | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					variableChunk.map(variable => variable.expression), | 
					
						
							|  |  |  | 					block | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 				); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// join them to one big string
 | 
					
						
							|  |  |  | 			const varStartCode = functionWrapperStarts.join(""); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// reverse the ends first before joining them, as the last added must be the inner most
 | 
					
						
							|  |  |  | 			const varEndCode = functionWrapperEnds.reverse().join(""); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// if we have anything, add it to the source
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			if (varStartCode && varEndCode) { | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 				const start = block.range ? block.range[0] : -10; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				const end = block.range | 
					
						
							|  |  |  | 					? block.range[1] | 
					
						
							|  |  |  | 					: module.originalSource().size() + 1; | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 				source.insert(start + 0.5, varStartCode); | 
					
						
							|  |  |  | 				source.insert(end + 0.5, "\n/* WEBPACK VAR INJECTION */" + varEndCode); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		for (const childBlock of block.blocks) { | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 			this.sourceBlock( | 
					
						
							|  |  |  | 				module, | 
					
						
							|  |  |  | 				childBlock, | 
					
						
							|  |  |  | 				availableVars.concat(vars), | 
					
						
							|  |  |  | 				dependencyTemplates, | 
					
						
							|  |  |  | 				source, | 
					
						
							|  |  |  | 				runtimeTemplate | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sourceDependency(dependency, dependencyTemplates, source, runtimeTemplate) { | 
					
						
							|  |  |  | 		const template = dependencyTemplates.get(dependency.constructor); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (!template) | 
					
						
							|  |  |  | 			throw new Error( | 
					
						
							|  |  |  | 				"No template for dependency: " + dependency.constructor.name | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 		template.apply(dependency, source, runtimeTemplate, dependencyTemplates); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	sourceVariables( | 
					
						
							|  |  |  | 		variable, | 
					
						
							|  |  |  | 		availableVars, | 
					
						
							|  |  |  | 		dependencyTemplates, | 
					
						
							|  |  |  | 		runtimeTemplate | 
					
						
							|  |  |  | 	) { | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 		const name = variable.name; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		const expr = variable.expressionSource( | 
					
						
							|  |  |  | 			dependencyTemplates, | 
					
						
							|  |  |  | 			runtimeTemplate | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ( | 
					
						
							|  |  |  | 			availableVars.some( | 
					
						
							|  |  |  | 				v => v.name === name && v.expression.source() === expr.source() | 
					
						
							|  |  |  | 			) | 
					
						
							|  |  |  | 		) { | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return { | 
					
						
							|  |  |  | 			name: name, | 
					
						
							|  |  |  | 			expression: expr | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* | 
					
						
							|  |  |  | 	 * creates the start part of a IIFE around the module to inject a variable name | 
					
						
							| 
									
										
										
										
											2018-03-22 17:54:18 +08:00
										 |  |  | 	 * (function(…){   <- this part | 
					
						
							|  |  |  | 	 * }.call(…)) | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	variableInjectionFunctionWrapperStartCode(varNames) { | 
					
						
							|  |  |  | 		const args = varNames.join(", "); | 
					
						
							|  |  |  | 		return `/* WEBPACK VAR INJECTION */(function(${args}) {`; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	contextArgument(module, block) { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (this === block) { | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 			return module.exportsArgument; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return "this"; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* | 
					
						
							|  |  |  | 	 * creates the end part of a IIFE around the module to inject a variable name | 
					
						
							| 
									
										
										
										
											2018-03-22 17:54:18 +08:00
										 |  |  | 	 * (function(…){ | 
					
						
							|  |  |  | 	 * }.call(…))   <- this part | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	variableInjectionFunctionWrapperEndCode(module, varExpressions, block) { | 
					
						
							|  |  |  | 		const firstParam = this.contextArgument(module, block); | 
					
						
							|  |  |  | 		const furtherParams = varExpressions.map(e => e.source()).join(", "); | 
					
						
							|  |  |  | 		return `}.call(${firstParam}, ${furtherParams}))`; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	splitVariablesInUniqueNamedChunks(vars) { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		const startState = [[]]; | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 		return vars.reduce((chunks, variable) => { | 
					
						
							|  |  |  | 			const current = chunks[chunks.length - 1]; | 
					
						
							|  |  |  | 			// check if variable with same name exists already
 | 
					
						
							|  |  |  | 			// if so create a new chunk of variables.
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			const variableNameAlreadyExists = current.some( | 
					
						
							|  |  |  | 				v => v.name === variable.name | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			if (variableNameAlreadyExists) { | 
					
						
							| 
									
										
										
										
											2018-01-24 06:09:26 +08:00
										 |  |  | 				// start new chunk with current variable
 | 
					
						
							|  |  |  | 				chunks.push([variable]); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// else add it to current chunk
 | 
					
						
							|  |  |  | 				current.push(variable); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return chunks; | 
					
						
							|  |  |  | 		}, startState); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = JavascriptGenerator; |