| 
									
										
										
										
											2012-03-12 04:50:55 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | var esprima = require("esprima"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function walkStatements(context, statements) { | 
					
						
							|  |  |  | 	statements.forEach(function(statement) { | 
					
						
							|  |  |  | 		walkStatement(context, statement); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function walkStatement(context, statement) { | 
					
						
							|  |  |  | 	switch(statement.type) { | 
					
						
							|  |  |  | 	// Real Statements
 | 
					
						
							|  |  |  | 	case "BlockStatement": | 
					
						
							|  |  |  | 		walkStatements(context, statement.body); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "ExpressionStatement": | 
					
						
							|  |  |  | 		walkExpression(context, statement.expression); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "IfStatement": | 
					
						
							|  |  |  | 		walkExpression(context, statement.test); | 
					
						
							|  |  |  | 		walkStatement(context, statement.consequent); | 
					
						
							|  |  |  | 		if(statement.alternate) | 
					
						
							|  |  |  | 			walkStatement(context, statement.alternate); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "LabeledStatement": | 
					
						
							|  |  |  | 		walkStatement(context, statement.body); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "WithStatement": | 
					
						
							|  |  |  | 		walkExpression(context, statement.object); | 
					
						
							|  |  |  | 		walkStatement(context, statement.body); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "SwitchStatement": | 
					
						
							|  |  |  | 		walkExpression(context, statement.discriminant); | 
					
						
							|  |  |  | 		walkSwitchCases(context, statement.cases); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "ReturnStatement": | 
					
						
							|  |  |  | 	case "ThrowStatement": | 
					
						
							|  |  |  | 		if(statement.argument) | 
					
						
							|  |  |  | 			walkExpression(context, statement.argument); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "TryStatement": | 
					
						
							|  |  |  | 		walkStatement(context, statement.block); | 
					
						
							|  |  |  | 		walkCatchClauses(context, statement.handlers); | 
					
						
							|  |  |  | 		if(statement.finalizer) | 
					
						
							|  |  |  | 			walkStatement(context, statement.finalizer); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "WhileStatement": | 
					
						
							|  |  |  | 	case "DoWhileStatement": | 
					
						
							|  |  |  | 		walkExpression(context, statement.test); | 
					
						
							|  |  |  | 		walkStatement(context, statement.body); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "ForStatement": | 
					
						
							|  |  |  | 		if(statement.init) { | 
					
						
							|  |  |  | 			if(statement.init.type === "VariableDeclaration") | 
					
						
							|  |  |  | 				walkStatement(context, statement.init); | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				walkExpression(context, statement.init); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if(statement.test) | 
					
						
							|  |  |  | 			walkExpression(context, statement.test); | 
					
						
							|  |  |  | 		if(statement.update) | 
					
						
							|  |  |  | 			walkExpression(context, statement.update); | 
					
						
							|  |  |  | 		walkStatement(context, statement.body); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "ForInStatement": | 
					
						
							|  |  |  | 		if(statement.left.type === "VariableDeclaration") | 
					
						
							|  |  |  | 			walkStatement(context, statement.left); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			walkExpression(context, statement.left); | 
					
						
							|  |  |  | 		walkExpression(context, statement.right); | 
					
						
							|  |  |  | 		walkStatement(context, statement.body); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Declarations
 | 
					
						
							|  |  |  | 	case "FunctionDeclaration": | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 		if(statement.name in context.options.overwrites) { | 
					
						
							|  |  |  | 			context.overwrite.push(statement.name); | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 		var old = addOverwrites(context, statement.params); | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 		if(statement.body.type === "BlockStatement") | 
					
						
							|  |  |  | 			walkStatement(context, statement.body); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			walkExpression(context, statement.body); | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 		context.overwrite.length = old; | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case "VariableDeclaration": | 
					
						
							|  |  |  | 		if(statement.declarations) | 
					
						
							|  |  |  | 			walkVariableDeclarators(context, statement.declarations); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function walkSwitchCases(context, switchCases) { | 
					
						
							|  |  |  | 	switchCases.forEach(function(switchCase) { | 
					
						
							|  |  |  | 		if(switchCase.test) | 
					
						
							|  |  |  | 			walkExpression(context, switchCase.test); | 
					
						
							|  |  |  | 		walkStatements(context, switchCase.consequent); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function walkCatchClauses(context, catchClauses) { | 
					
						
							|  |  |  | 	catchClauses.forEach(function(catchClause) { | 
					
						
							|  |  |  | 		if(catchClause.guard) | 
					
						
							|  |  |  | 			walkExpression(context, catchClause.guard); | 
					
						
							|  |  |  | 		walkStatement(context, catchClause.body); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function walkVariableDeclarators(context, declarators) { | 
					
						
							|  |  |  | 	declarators.forEach(function(declarator) { | 
					
						
							|  |  |  | 		switch(declarator.type) { | 
					
						
							|  |  |  | 		case "VariableDeclarator": | 
					
						
							| 
									
										
										
										
											2012-03-16 06:01:42 +08:00
										 |  |  | 			if(declarator.id.type === "Identifier" && | 
					
						
							|  |  |  | 				declarator.id.name in context.options.overwrites) { | 
					
						
							|  |  |  | 				context.overwrite.push(declarator.id.name); | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 			if(declarator.init) | 
					
						
							|  |  |  | 				walkExpression(context, declarator.init); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function walkExpressions(context, expressions) { | 
					
						
							|  |  |  | 	expressions.forEach(function(expression) { | 
					
						
							|  |  |  | 		walkExpression(context, expression); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function walkExpression(context, expression) { | 
					
						
							|  |  |  | 	switch(expression.type) { | 
					
						
							|  |  |  | 	case "ArrayExpression": | 
					
						
							|  |  |  | 		if(expression.elements) | 
					
						
							|  |  |  | 			walkExpressions(context, expression.elements); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "ObjectExpression": | 
					
						
							|  |  |  | 		expression.properties.forEach(function(prop) { | 
					
						
							|  |  |  | 			walkExpression(context, prop.value); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "FunctionExpression": | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 		var old = addOverwrites(context, expression.params); | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 		if(expression.body.type === "BlockStatement") | 
					
						
							|  |  |  | 			walkStatement(context, expression.body); | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			walkExpression(context, expression.body); | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 		context.overwrite.length = old; | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case "SequenceExpression": | 
					
						
							|  |  |  | 		if(expression.expressions) | 
					
						
							|  |  |  | 			walkExpressions(context, expression.expressions); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "UpdateExpression": | 
					
						
							|  |  |  | 		walkExpression(context, expression.argument); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2012-03-26 20:49:03 +08:00
										 |  |  | 	case "UnaryExpression": | 
					
						
							|  |  |  | 		if(expression.operator === "typeof" && | 
					
						
							|  |  |  | 			expression.argument && | 
					
						
							|  |  |  | 			expression.argument.type === "Identifier" && | 
					
						
							|  |  |  | 			expression.argument.name === "require") | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		walkExpression(context, expression.argument); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 	case "BinaryExpression": | 
					
						
							|  |  |  | 	case "LogicalExpression": | 
					
						
							|  |  |  | 		walkExpression(context, expression.left); | 
					
						
							|  |  |  | 		walkExpression(context, expression.right); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2012-03-21 19:41:03 +08:00
										 |  |  | 	case "AssignmentExpression": | 
					
						
							|  |  |  | 		if(expression.left.type !== "Identifier" || | 
					
						
							|  |  |  | 			expression.left.name !== "require") | 
					
						
							|  |  |  | 			walkExpression(context, expression.left); | 
					
						
							|  |  |  | 		walkExpression(context, expression.right); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 	case "ConditionalExpression": | 
					
						
							|  |  |  | 		walkExpression(context, expression.test); | 
					
						
							|  |  |  | 		walkExpression(context, expression.alternate); | 
					
						
							|  |  |  | 		walkExpression(context, expression.consequent); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "NewExpression": | 
					
						
							| 
									
										
										
										
											2012-03-12 05:45:21 +08:00
										 |  |  | 		walkExpression(context, expression.callee); | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 		if(expression.arguments) | 
					
						
							|  |  |  | 			walkExpressions(context, expression.arguments); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "CallExpression": | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 		var noCallee = false; | 
					
						
							| 
									
										
										
										
											2012-03-20 02:59:38 +08:00
										 |  |  | 		if(context.overwrite.indexOf("require") === -1 && | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 			expression.callee && expression.arguments && | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 			expression.arguments.length == 1 && | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 			expression.callee.type === "Identifier" && | 
					
						
							|  |  |  | 			expression.callee.name === "require") { | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 			var param = parseCalculatedString(expression.arguments[0]); | 
					
						
							| 
									
										
										
										
											2012-03-20 02:59:38 +08:00
										 |  |  | 			if(param.conditional) { | 
					
						
							|  |  |  | 				context.requires = context.requires || []; | 
					
						
							|  |  |  | 				param.conditional.forEach(function(paramItem) { | 
					
						
							|  |  |  | 					context.requires.push({ | 
					
						
							|  |  |  | 						name: paramItem.value, | 
					
						
							|  |  |  | 						valueRange: paramItem.range, | 
					
						
							|  |  |  | 						line: expression.loc.start.line, | 
					
						
							|  |  |  | 						column: expression.loc.start.column | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			} else if(param.code) { | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 				// make context
 | 
					
						
							|  |  |  | 				var pos = param.value.indexOf("/"); | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 				context.contexts = context.contexts || []; | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 				if(pos === -1) { | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 					var newContext = { | 
					
						
							|  |  |  | 						name: ".", | 
					
						
							|  |  |  | 						require: true, | 
					
						
							|  |  |  | 						calleeRange: expression.callee.range, | 
					
						
							|  |  |  | 						line: expression.loc.start.line, | 
					
						
							|  |  |  | 						column: expression.loc.start.column | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 					context.contexts.push(newContext); | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					var match = /\/[^\/]*$/.exec(param.value); | 
					
						
							|  |  |  | 					var dirname = param.value.substring(0, match.index); | 
					
						
							|  |  |  | 					var remainder = "." + param.value.substring(match.index); | 
					
						
							|  |  |  | 					var newContext = { | 
					
						
							|  |  |  | 						name: dirname, | 
					
						
							|  |  |  | 						require: true, | 
					
						
							|  |  |  | 						replace: [param.range, remainder], | 
					
						
							|  |  |  | 						calleeRange: expression.callee.range, | 
					
						
							|  |  |  | 						line: expression.loc.start.line, | 
					
						
							|  |  |  | 						column: expression.loc.start.column | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 					context.contexts.push(newContext); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// normal require
 | 
					
						
							|  |  |  | 				context.requires = context.requires || []; | 
					
						
							|  |  |  | 				context.requires.push({ | 
					
						
							|  |  |  | 					name: param.value, | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 					expressionRange: [expression.callee.range[0], expression.range[1]], | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 					line: expression.loc.start.line, | 
					
						
							|  |  |  | 					column: expression.loc.start.column | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 			noCallee = true; | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-03-20 02:59:38 +08:00
										 |  |  | 		if(context.overwrite.indexOf("require") === -1 && | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 			expression.callee && expression.arguments && | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 			expression.arguments.length >= 1 && | 
					
						
							|  |  |  | 			expression.callee.type === "MemberExpression" && | 
					
						
							|  |  |  | 			expression.callee.object.type === "Identifier" && | 
					
						
							|  |  |  | 			expression.callee.object.name === "require" && | 
					
						
							|  |  |  | 			expression.callee.property.type === "Identifier" && | 
					
						
							|  |  |  | 			expression.callee.property.name in {async:1, ensure:1}) { | 
					
						
							|  |  |  | 			var param = parseStringArray(expression.arguments[0]); | 
					
						
							|  |  |  | 			context.asyncs = context.asyncs || []; | 
					
						
							|  |  |  | 			var newContext = { | 
					
						
							|  |  |  | 				requires: [], | 
					
						
							|  |  |  | 				namesRange: expression.arguments[0].range, | 
					
						
							|  |  |  | 				line: expression.loc.start.line, | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 				column: expression.loc.start.column, | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 				ignoreOverride: true, | 
					
						
							|  |  |  | 				overwrite: context.overwrite.slice(), | 
					
						
							|  |  |  | 				options: context.options | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 			}; | 
					
						
							|  |  |  | 			param.forEach(function(r) { | 
					
						
							|  |  |  | 				newContext.requires.push({name: r}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			context.asyncs.push(newContext); | 
					
						
							|  |  |  | 			context = newContext; | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 			noCallee = true; | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-03-20 02:59:38 +08:00
										 |  |  | 		if(context.overwrite.indexOf("require") === -1 && | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 			expression.callee && expression.arguments && | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 			expression.arguments.length == 1 && | 
					
						
							|  |  |  | 			expression.callee.type === "MemberExpression" && | 
					
						
							|  |  |  | 			expression.callee.object.type === "Identifier" && | 
					
						
							|  |  |  | 			expression.callee.object.name === "require" && | 
					
						
							|  |  |  | 			expression.callee.property.type === "Identifier" && | 
					
						
							|  |  |  | 			expression.callee.property.name in {context:1}) { | 
					
						
							|  |  |  | 			var param = parseString(expression.arguments[0]); | 
					
						
							|  |  |  | 			context.contexts = context.contexts || []; | 
					
						
							|  |  |  | 			var newContext = { | 
					
						
							|  |  |  | 				name: param, | 
					
						
							|  |  |  | 				expressionRange: [expression.callee.range[0], expression.range[1]], | 
					
						
							|  |  |  | 				line: expression.loc.start.line, | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 				column: expression.loc.start.column | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 			}; | 
					
						
							|  |  |  | 			context.contexts.push(newContext); | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 			noCallee = true; | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-03-21 19:41:03 +08:00
										 |  |  | 		if(context.overwrite.indexOf("require") === -1 && | 
					
						
							|  |  |  | 			expression.callee && | 
					
						
							|  |  |  | 			expression.callee.type === "MemberExpression" && | 
					
						
							|  |  |  | 			expression.callee.object.type === "Identifier" && | 
					
						
							|  |  |  | 			expression.callee.object.name === "require" && | 
					
						
							|  |  |  | 			expression.callee.property.type === "Identifier" && | 
					
						
							|  |  |  | 			expression.callee.property.name in {valueOf:1}) { | 
					
						
							|  |  |  | 			noCallee = true; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 		if(expression.callee && !noCallee) | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 			walkExpression(context, expression.callee); | 
					
						
							|  |  |  | 		if(expression.arguments) | 
					
						
							|  |  |  | 			walkExpressions(context, expression.arguments); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case "MemberExpression": | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 		if(expression.object.type === "Identifier" && | 
					
						
							|  |  |  | 			expression.object.name === "module" && | 
					
						
							|  |  |  | 			expression.property.type === "Identifier" && | 
					
						
							|  |  |  | 			expression.property.name === "exports") | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 		walkExpression(context, expression.object); | 
					
						
							|  |  |  | 		if(expression.property.type !== "Identifier") | 
					
						
							|  |  |  | 			walkExpression(context, expression.property); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 	case "Identifier": | 
					
						
							| 
									
										
										
										
											2012-03-20 02:59:38 +08:00
										 |  |  | 		if(context.overwrite.indexOf("require") === -1 && | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 			expression.name === "require") { | 
					
						
							|  |  |  | 			context.contexts = context.contexts || []; | 
					
						
							|  |  |  | 			var newContext = { | 
					
						
							|  |  |  | 				name: ".", | 
					
						
							|  |  |  | 				warn: "Identifier", | 
					
						
							|  |  |  | 				require: true, | 
					
						
							|  |  |  | 				calleeRange: [expression.range[0], expression.range[1]], | 
					
						
							|  |  |  | 				line: expression.loc.start.line, | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 				column: expression.loc.start.column | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 			}; | 
					
						
							|  |  |  | 			context.contexts.push(newContext); | 
					
						
							| 
									
										
										
										
											2012-03-20 02:59:38 +08:00
										 |  |  | 		} else if(context.overwrite.indexOf(expression.name) === -1 && | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 			expression.name in context.options.overwrites) { | 
					
						
							|  |  |  | 			context.requires = context.requires || []; | 
					
						
							|  |  |  | 			context.requires.push({ | 
					
						
							|  |  |  | 				name: context.options.overwrites[expression.name], | 
					
						
							|  |  |  | 				expressionRange: expression.range, | 
					
						
							|  |  |  | 				line: expression.loc.start.line, | 
					
						
							|  |  |  | 				column: expression.loc.start.column | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2012-03-14 23:33:46 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | function addOverwrites(context, params) { | 
					
						
							|  |  |  | 	var l = context.overwrite.length; | 
					
						
							|  |  |  | 	if(!params) return l; | 
					
						
							|  |  |  | 	params.forEach(function(param) { | 
					
						
							|  |  |  | 		if(context.ignoreOverride) { | 
					
						
							|  |  |  | 			context.ignoreOverride = false; | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-03-20 02:59:38 +08:00
										 |  |  | 		if(param.type === "Identifier" && | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 			param.name in context.options.overwrites) | 
					
						
							|  |  |  | 			context.overwrite.push(param.name); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 	return l; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | function parseString(expression) { | 
					
						
							|  |  |  | 	switch(expression.type) { | 
					
						
							|  |  |  | 	case "BinaryExpression": | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 		if(expression.operator == "+") | 
					
						
							|  |  |  | 			return parseString(expression.left) + parseString(expression.right); | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 	case "Literal": | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 		return expression.value+""; | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	throw new Error(expression.type + " is not supported as parameter for require"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | function parseCalculatedString(expression) { | 
					
						
							|  |  |  | 	switch(expression.type) { | 
					
						
							|  |  |  | 	case "BinaryExpression": | 
					
						
							|  |  |  | 		if(expression.operator == "+") { | 
					
						
							|  |  |  | 			var left = parseCalculatedString(expression.left); | 
					
						
							|  |  |  | 			var right = parseCalculatedString(expression.right); | 
					
						
							|  |  |  | 			if(left.code) { | 
					
						
							|  |  |  | 				return {range: left.range, value: left.value, code: true}; | 
					
						
							|  |  |  | 			} else if(right.code) { | 
					
						
							|  |  |  | 				return {range: [left.range[0], right.range ? right.range[1] : left.range[1]], value: left.value + right.value, code: true}; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				return {range: [left.range[0], right.range[1]], value: left.value + right.value}; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2012-03-20 02:59:38 +08:00
										 |  |  | 	case "ConditionalExpression": | 
					
						
							|  |  |  | 		var consequent = parseCalculatedString(expression.consequent); | 
					
						
							|  |  |  | 		var alternate = parseCalculatedString(expression.alternate); | 
					
						
							|  |  |  | 		var items = []; | 
					
						
							|  |  |  | 		if(consequent.conditional) | 
					
						
							|  |  |  | 			Array.prototype.push.apply(items, consequent.conditional); | 
					
						
							|  |  |  | 		else if(!consequent.code) | 
					
						
							|  |  |  | 			items.push(consequent); | 
					
						
							|  |  |  | 		else break; | 
					
						
							|  |  |  | 		if(alternate.conditional) | 
					
						
							|  |  |  | 			Array.prototype.push.apply(items, alternate.conditional); | 
					
						
							|  |  |  | 		else if(!alternate.code) | 
					
						
							|  |  |  | 			items.push(alternate); | 
					
						
							|  |  |  | 		else break; | 
					
						
							|  |  |  | 		return {value: "", code: true, conditional: items}; | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 	case "Literal": | 
					
						
							|  |  |  | 		return {range: expression.range, value: expression.value+""}; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return {value: "", code: true}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | function parseStringArray(expression) { | 
					
						
							|  |  |  | 	switch(expression.type) { | 
					
						
							|  |  |  | 	case "ArrayExpression": | 
					
						
							|  |  |  | 		var arr = []; | 
					
						
							|  |  |  | 		if(expression.elements) | 
					
						
							|  |  |  | 			expression.elements.forEach(function(expr) { | 
					
						
							|  |  |  | 				arr.push(parseString(expr)); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		return arr; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return [parseString(expression)]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = function parse(source, options) { | 
					
						
							| 
									
										
										
										
											2012-03-12 04:37:18 +08:00
										 |  |  | 	var ast = esprima.parse(source, {range: true, loc: true, raw: true}); | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 	if(!ast || typeof ast != "object") | 
					
						
							|  |  |  | 		throw new Error("Source couldn't be parsed"); | 
					
						
							| 
									
										
										
										
											2012-03-15 21:38:55 +08:00
										 |  |  | 	options = options || {}; | 
					
						
							|  |  |  | 	options.overwrites = options.overwrites || {}; | 
					
						
							|  |  |  | 	options.overwrites.require = true; | 
					
						
							|  |  |  | 	var context = { | 
					
						
							|  |  |  | 		options: options, | 
					
						
							|  |  |  | 		overwrite: [] | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2012-03-10 20:11:23 +08:00
										 |  |  | 	walkStatements(context, ast.body); | 
					
						
							|  |  |  | 	return context; | 
					
						
							|  |  |  | } |