You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					162 lines
				
				4.7 KiB
			
		
		
			
		
	
	
					162 lines
				
				4.7 KiB
			| 
											3 years ago
										 | /** | ||
|  |  * Copyright (c) 2014-present, Facebook, Inc. | ||
|  |  * | ||
|  |  * This source code is licensed under the MIT license found in the | ||
|  |  * LICENSE file in the root directory of this source tree. | ||
|  |  */ | ||
|  | 
 | ||
|  | import * as util from "./util"; | ||
|  | let hasOwn = Object.prototype.hasOwnProperty; | ||
|  | 
 | ||
|  | // The hoist function takes a FunctionExpression or FunctionDeclaration
 | ||
|  | // and replaces any Declaration nodes in its body with assignments, then
 | ||
|  | // returns a VariableDeclaration containing just the names of the removed
 | ||
|  | // declarations.
 | ||
|  | exports.hoist = function(funPath) { | ||
|  |   const t = util.getTypes(); | ||
|  |   t.assertFunction(funPath.node); | ||
|  | 
 | ||
|  |   let vars = {}; | ||
|  | 
 | ||
|  |   function varDeclToExpr({ node: vdec, scope }, includeIdentifiers) { | ||
|  |     t.assertVariableDeclaration(vdec); | ||
|  |     // TODO assert.equal(vdec.kind, "var");
 | ||
|  |     let exprs = []; | ||
|  | 
 | ||
|  |     vdec.declarations.forEach(function(dec) { | ||
|  |       // Note: We duplicate 'dec.id' here to ensure that the variable declaration IDs don't
 | ||
|  |       // have the same 'loc' value, since that can make sourcemaps and retainLines behave poorly.
 | ||
|  |       vars[dec.id.name] = t.identifier(dec.id.name); | ||
|  | 
 | ||
|  |       // Remove the binding, to avoid "duplicate declaration" errors when it will
 | ||
|  |       // be injected again.
 | ||
|  |       scope.removeBinding(dec.id.name); | ||
|  | 
 | ||
|  |       if (dec.init) { | ||
|  |         exprs.push(t.assignmentExpression( | ||
|  |           "=", dec.id, dec.init | ||
|  |         )); | ||
|  |       } else if (includeIdentifiers) { | ||
|  |         exprs.push(dec.id); | ||
|  |       } | ||
|  |     }); | ||
|  | 
 | ||
|  |     if (exprs.length === 0) | ||
|  |       return null; | ||
|  | 
 | ||
|  |     if (exprs.length === 1) | ||
|  |       return exprs[0]; | ||
|  | 
 | ||
|  |     return t.sequenceExpression(exprs); | ||
|  |   } | ||
|  | 
 | ||
|  |   funPath.get("body").traverse({ | ||
|  |     VariableDeclaration: { | ||
|  |       exit: function(path) { | ||
|  |         let expr = varDeclToExpr(path, false); | ||
|  |         if (expr === null) { | ||
|  |           path.remove(); | ||
|  |         } else { | ||
|  |           // We don't need to traverse this expression any further because
 | ||
|  |           // there can't be any new declarations inside an expression.
 | ||
|  |           util.replaceWithOrRemove(path, t.expressionStatement(expr)); | ||
|  |         } | ||
|  | 
 | ||
|  |         // Since the original node has been either removed or replaced,
 | ||
|  |         // avoid traversing it any further.
 | ||
|  |         path.skip(); | ||
|  |       } | ||
|  |     }, | ||
|  | 
 | ||
|  |     ForStatement: function(path) { | ||
|  |       let init = path.get("init"); | ||
|  |       if (init.isVariableDeclaration()) { | ||
|  |         util.replaceWithOrRemove(init, varDeclToExpr(init, false)); | ||
|  |       } | ||
|  |     }, | ||
|  | 
 | ||
|  |     ForXStatement: function(path) { | ||
|  |       let left = path.get("left"); | ||
|  |       if (left.isVariableDeclaration()) { | ||
|  |         util.replaceWithOrRemove(left, varDeclToExpr(left, true)); | ||
|  |       } | ||
|  |     }, | ||
|  | 
 | ||
|  |     FunctionDeclaration: function(path) { | ||
|  |       let node = path.node; | ||
|  |       vars[node.id.name] = node.id; | ||
|  | 
 | ||
|  |       let assignment = t.expressionStatement( | ||
|  |         t.assignmentExpression( | ||
|  |           "=", | ||
|  |           t.clone(node.id), | ||
|  |           t.functionExpression( | ||
|  |             path.scope.generateUidIdentifierBasedOnNode(node), | ||
|  |             node.params, | ||
|  |             node.body, | ||
|  |             node.generator, | ||
|  |             node.expression | ||
|  |           ) | ||
|  |         ) | ||
|  |       ); | ||
|  | 
 | ||
|  |       if (path.parentPath.isBlockStatement()) { | ||
|  |         // Insert the assignment form before the first statement in the
 | ||
|  |         // enclosing block.
 | ||
|  |         path.parentPath.unshiftContainer("body", assignment); | ||
|  | 
 | ||
|  |         // Remove the function declaration now that we've inserted the
 | ||
|  |         // equivalent assignment form at the beginning of the block.
 | ||
|  |         path.remove(); | ||
|  |       } else { | ||
|  |         // If the parent node is not a block statement, then we can just
 | ||
|  |         // replace the declaration with the equivalent assignment form
 | ||
|  |         // without worrying about hoisting it.
 | ||
|  |         util.replaceWithOrRemove(path, assignment); | ||
|  |       } | ||
|  | 
 | ||
|  |       // Remove the binding, to avoid "duplicate declaration" errors when it will
 | ||
|  |       // be injected again.
 | ||
|  |       path.scope.removeBinding(node.id.name); | ||
|  | 
 | ||
|  |       // Don't hoist variables out of inner functions.
 | ||
|  |       path.skip(); | ||
|  |     }, | ||
|  | 
 | ||
|  |     FunctionExpression: function(path) { | ||
|  |       // Don't descend into nested function expressions.
 | ||
|  |       path.skip(); | ||
|  |     }, | ||
|  | 
 | ||
|  |     ArrowFunctionExpression: function(path) { | ||
|  |       // Don't descend into nested function expressions.
 | ||
|  |       path.skip(); | ||
|  |     } | ||
|  |   }); | ||
|  | 
 | ||
|  |   let paramNames = {}; | ||
|  |   funPath.get("params").forEach(function(paramPath) { | ||
|  |     let param = paramPath.node; | ||
|  |     if (t.isIdentifier(param)) { | ||
|  |       paramNames[param.name] = param; | ||
|  |     } else { | ||
|  |       // Variables declared by destructuring parameter patterns will be
 | ||
|  |       // harmlessly re-declared.
 | ||
|  |     } | ||
|  |   }); | ||
|  | 
 | ||
|  |   let declarations = []; | ||
|  | 
 | ||
|  |   Object.keys(vars).forEach(function(name) { | ||
|  |     if (!hasOwn.call(paramNames, name)) { | ||
|  |       declarations.push(t.variableDeclarator(vars[name], null)); | ||
|  |     } | ||
|  |   }); | ||
|  | 
 | ||
|  |   if (declarations.length === 0) { | ||
|  |     return null; // Be sure to handle this case!
 | ||
|  |   } | ||
|  | 
 | ||
|  |   return t.variableDeclaration("var", declarations); | ||
|  | }; |