mirror of https://github.com/webpack/webpack.git
				
				
				
			
		
			
				
	
	
		
			230 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| /*
 | |
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | |
| 	Author Tobias Koppers @sokra
 | |
| */
 | |
| "use strict";
 | |
| 
 | |
| const { RawSource, ReplaceSource } = require("webpack-sources");
 | |
| 
 | |
| // 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();
 | |
| 		if (!originalSource) {
 | |
| 			return new RawSource("throw new Error('No source available');");
 | |
| 		}
 | |
| 
 | |
| 		const source = new ReplaceSource(originalSource);
 | |
| 
 | |
| 		this.sourceBlock(
 | |
| 			module,
 | |
| 			module,
 | |
| 			[],
 | |
| 			dependencyTemplates,
 | |
| 			source,
 | |
| 			runtimeTemplate
 | |
| 		);
 | |
| 
 | |
| 		return source;
 | |
| 	}
 | |
| 
 | |
| 	sourceBlock(
 | |
| 		module,
 | |
| 		block,
 | |
| 		availableVars,
 | |
| 		dependencyTemplates,
 | |
| 		source,
 | |
| 		runtimeTemplate
 | |
| 	) {
 | |
| 		for (const dependency of block.dependencies) {
 | |
| 			this.sourceDependency(
 | |
| 				dependency,
 | |
| 				dependencyTemplates,
 | |
| 				source,
 | |
| 				runtimeTemplate
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		/**
 | |
| 		 * Get the variables of all blocks that we need to inject.
 | |
| 		 * These will contain the variable name and its expression.
 | |
| 		 * The name will be added as a parameter in a IIFE the expression as its value.
 | |
| 		 */
 | |
| 		const vars = block.variables.reduce((result, value) => {
 | |
| 			const variable = this.sourceVariables(
 | |
| 				value,
 | |
| 				availableVars,
 | |
| 				dependencyTemplates,
 | |
| 				runtimeTemplate
 | |
| 			);
 | |
| 
 | |
| 			if (variable) {
 | |
| 				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.
 | |
| 		 */
 | |
| 		if (vars.length > 0) {
 | |
| 			/**
 | |
| 			 * 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){
 | |
| 			 *     …
 | |
| 			 *   }(…));
 | |
| 			 * }(…));
 | |
| 			 *
 | |
| 			 * "splitVariablesInUniqueNamedChunks" splits the variables shown above up to this:
 | |
| 			 * [[foo, bar, baz], [foo, some, more]]
 | |
| 			 */
 | |
| 			const injectionVariableChunks = this.splitVariablesInUniqueNamedChunks(
 | |
| 				vars
 | |
| 			);
 | |
| 
 | |
| 			// create all the beginnings of IIFEs
 | |
| 			const functionWrapperStarts = injectionVariableChunks.map(
 | |
| 				variableChunk => {
 | |
| 					return this.variableInjectionFunctionWrapperStartCode(
 | |
| 						variableChunk.map(variable => variable.name)
 | |
| 					);
 | |
| 				}
 | |
| 			);
 | |
| 
 | |
| 			// and all the ends
 | |
| 			const functionWrapperEnds = injectionVariableChunks.map(variableChunk => {
 | |
| 				return this.variableInjectionFunctionWrapperEndCode(
 | |
| 					module,
 | |
| 					variableChunk.map(variable => variable.expression),
 | |
| 					block
 | |
| 				);
 | |
| 			});
 | |
| 
 | |
| 			// 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
 | |
| 			if (varStartCode && varEndCode) {
 | |
| 				const start = block.range ? block.range[0] : -10;
 | |
| 				const end = block.range
 | |
| 					? block.range[1]
 | |
| 					: module.originalSource().size() + 1;
 | |
| 				source.insert(start + 0.5, varStartCode);
 | |
| 				source.insert(end + 0.5, "\n/* WEBPACK VAR INJECTION */" + varEndCode);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		for (const childBlock of block.blocks) {
 | |
| 			this.sourceBlock(
 | |
| 				module,
 | |
| 				childBlock,
 | |
| 				availableVars.concat(vars),
 | |
| 				dependencyTemplates,
 | |
| 				source,
 | |
| 				runtimeTemplate
 | |
| 			);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	sourceDependency(dependency, dependencyTemplates, source, runtimeTemplate) {
 | |
| 		const template = dependencyTemplates.get(dependency.constructor);
 | |
| 		if (!template) {
 | |
| 			throw new Error(
 | |
| 				"No template for dependency: " + dependency.constructor.name
 | |
| 			);
 | |
| 		}
 | |
| 		template.apply(dependency, source, runtimeTemplate, dependencyTemplates);
 | |
| 	}
 | |
| 
 | |
| 	sourceVariables(
 | |
| 		variable,
 | |
| 		availableVars,
 | |
| 		dependencyTemplates,
 | |
| 		runtimeTemplate
 | |
| 	) {
 | |
| 		const name = variable.name;
 | |
| 		const expr = variable.expressionSource(
 | |
| 			dependencyTemplates,
 | |
| 			runtimeTemplate
 | |
| 		);
 | |
| 
 | |
| 		if (
 | |
| 			availableVars.some(
 | |
| 				v => v.name === name && v.expression.source() === expr.source()
 | |
| 			)
 | |
| 		) {
 | |
| 			return;
 | |
| 		}
 | |
| 		return {
 | |
| 			name: name,
 | |
| 			expression: expr
 | |
| 		};
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * creates the start part of a IIFE around the module to inject a variable name
 | |
| 	 * (function(…){   <- this part
 | |
| 	 * }.call(…))
 | |
| 	 */
 | |
| 	variableInjectionFunctionWrapperStartCode(varNames) {
 | |
| 		const args = varNames.join(", ");
 | |
| 		return `/* WEBPACK VAR INJECTION */(function(${args}) {`;
 | |
| 	}
 | |
| 
 | |
| 	contextArgument(module, block) {
 | |
| 		if (this === block) {
 | |
| 			return module.exportsArgument;
 | |
| 		}
 | |
| 		return "this";
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * creates the end part of a IIFE around the module to inject a variable name
 | |
| 	 * (function(…){
 | |
| 	 * }.call(…))   <- this part
 | |
| 	 */
 | |
| 	variableInjectionFunctionWrapperEndCode(module, varExpressions, block) {
 | |
| 		const firstParam = this.contextArgument(module, block);
 | |
| 		const furtherParams = varExpressions.map(e => e.source()).join(", ");
 | |
| 		return `}.call(${firstParam}, ${furtherParams}))`;
 | |
| 	}
 | |
| 
 | |
| 	splitVariablesInUniqueNamedChunks(vars) {
 | |
| 		const startState = [[]];
 | |
| 		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.
 | |
| 			const variableNameAlreadyExists = current.some(
 | |
| 				v => v.name === variable.name
 | |
| 			);
 | |
| 
 | |
| 			if (variableNameAlreadyExists) {
 | |
| 				// 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;
 |