diff --git a/src/babel/transformation/transformers/other/regenerator.js b/src/babel/transformation/transformers/other/regenerator.js
index d2a0112459..265bad2876 100644
--- a/src/babel/transformation/transformers/other/regenerator.js
+++ b/src/babel/transformation/transformers/other/regenerator.js
@@ -15,15 +15,15 @@ export function Func/*tion*/(node) {
// but that's a small optimization. Starting here instead of at the
// root of the AST is the key optimization, since huge async/generator
// functions are relatively rare.
- regenerator.transform(convertTraversalPathToNodePath(this));
+ regenerator.transform(convertNodePath(this));
}
}
-// Given a TraversalPath, return a NodePath that includes full ancestry
-// information (up to and including the Program node). This is complicated
-// by having to include intermediate objects like blockStatement.body
+// Given a Babel NodePath, return an ast-types NodePath that includes full
+// ancestry information (up to and including the Program node). This is
+// complicated by having to include intermediate objects like blockStatement.body
// arrays, in addition to Node objects.
-function convertTraversalPathToNodePath(path) {
+function convertNodePath(path) {
var programNode;
var keysAlongPath = [];
diff --git a/src/babel/traversal/context.js b/src/babel/traversal/context.js
index e223d956c2..a3b23acd90 100644
--- a/src/babel/traversal/context.js
+++ b/src/babel/traversal/context.js
@@ -1,4 +1,4 @@
-import TraversalPath from "./path";
+import NodePath from "./path";
import compact from "lodash/array/compact";
import * as t from "../types";
@@ -16,7 +16,7 @@ export default class TraversalContext {
}
create(node, obj, key) {
- var path = TraversalPath.get(this.parentPath, node, obj, key);
+ var path = NodePath.get(this.parentPath, node, obj, key);
path.unshiftContext(this);
return path;
}
diff --git a/src/babel/traversal/path/ancestry.js b/src/babel/traversal/path/ancestry.js
new file mode 100644
index 0000000000..2e3d12a2b6
--- /dev/null
+++ b/src/babel/traversal/path/ancestry.js
@@ -0,0 +1,65 @@
+/**
+ * Description
+ */
+
+export function findParent(callback) {
+ var path = this;
+ while (path) {
+ if (callback(path.node, path)) return path;
+ path = path.parentPath;
+ }
+ return null;
+}
+
+/**
+ * Description
+ */
+
+export function getAncestry() {
+ var ancestry = [];
+
+ var path = this.parentPath;
+ while (path) {
+ ancestry.push(path.node);
+ path = path.parentPath;
+ }
+
+ return ancestry;
+}
+
+/**
+ * Description
+ */
+
+export function inType(types) {
+ if (!Array.isArray(types)) types = [types];
+
+ var path = this;
+ while (path) {
+ for (var type of (types: Array)) {
+ if (path.node.type === type) return true;
+ }
+ path = path.parentPath;
+ }
+
+ return false;
+}
+
+/**
+ * Description
+ */
+
+export function inShadow() {
+ var path = this;
+ while (path) {
+ if (path.isFunction()) {
+ if (path.node.shadow) {
+ return path;
+ } else {
+ return null;
+ }
+ }
+ path = path.parentPath;
+ }
+ return null;
+}
diff --git a/src/babel/traversal/path/evaluation.js b/src/babel/traversal/path/evaluation.js
index 73d5346eba..ee382dbbbe 100644
--- a/src/babel/traversal/path/evaluation.js
+++ b/src/babel/traversal/path/evaluation.js
@@ -1,3 +1,5 @@
+/* eslint eqeqeq: 0 */
+
/**
* Walk the input `node` and statically evaluate if it's truthy.
*
diff --git a/src/babel/traversal/path/hoister.js b/src/babel/traversal/path/hoister.js
deleted file mode 100644
index de68729b55..0000000000
--- a/src/babel/traversal/path/hoister.js
+++ /dev/null
@@ -1,130 +0,0 @@
-import * as react from "../../transformation/helpers/react";
-import * as t from "../../types";
-
-var referenceVisitor = {
- ReferencedIdentifier(node, parent, scope, state) {
- if (this.isJSXIdentifier() && react.isCompatTag(node.name)) {
- return;
- }
-
- // direct references that we need to track to hoist this to the highest scope we can
- var bindingInfo = scope.getBinding(node.name);
- if (!bindingInfo) return;
-
- // this binding isn't accessible from the parent scope so we can safely ignore it
- // eg. it's in a closure etc
- if (bindingInfo !== state.scope.getBinding(node.name)) return;
-
- if (bindingInfo.constant) {
- state.bindings[node.name] = bindingInfo;
- } else {
- for (var violationPath of (bindingInfo.constantViolations: Array)) {
- state.breakOnScopePaths.push(violationPath.scope.path);
- }
- }
- }
-};
-
-export default class PathHoister {
- constructor(path, scope) {
- this.breakOnScopePaths = [];
- this.bindings = {};
- this.scopes = [];
- this.scope = scope;
- this.path = path;
- }
-
- isCompatibleScope(scope) {
- for (var key in this.bindings) {
- var binding = this.bindings[key];
- if (!scope.bindingIdentifierEquals(key, binding.identifier)) {
- return false;
- }
- }
-
- return true;
- }
-
- getCompatibleScopes() {
- var scope = this.path.scope;
- do {
- if (this.isCompatibleScope(scope)) {
- this.scopes.push(scope);
- } else {
- break;
- }
-
- if (this.breakOnScopePaths.indexOf(scope.path) >= 0) {
- break;
- }
- } while(scope = scope.parent);
- }
-
- getAttachmentPath() {
- var scopes = this.scopes;
-
- var scope = scopes.pop();
- if (!scope) return;
-
- if (scope.path.isFunction()) {
- if (this.hasOwnParamBindings(scope)) {
- // should ignore this scope since it's ourselves
- if (this.scope.is(scope)) return;
-
- // needs to be attached to the body
- return scope.path.get("body").get("body")[0];
- } else {
- // doesn't need to be be attached to this scope
- return this.getNextScopeStatementParent();
- }
- } else if (scope.path.isProgram()) {
- return this.getNextScopeStatementParent();
- }
- }
-
- getNextScopeStatementParent() {
- var scope = this.scopes.pop();
- if (scope) return scope.path.getStatementParent();
- }
-
- hasOwnParamBindings(scope) {
- for (var name in this.bindings) {
- if (!scope.hasOwnBinding(name)) continue
-
- var binding = this.bindings[name];
- if (binding.kind === "param") return true;
- }
- return false;
- }
-
- run() {
- var node = this.path.node;
- if (node._hoisted) return;
- node._hoisted = true;
-
- this.path.traverse(referenceVisitor, this);
-
- this.getCompatibleScopes();
-
- var path = this.getAttachmentPath();
- if (!path) return;
-
- var uid = path.scope.generateUidIdentifier("ref");
-
- path.insertBefore([
- t.variableDeclaration("var", [
- t.variableDeclarator(uid, this.path.node)
- ])
- ]);
-
- var parent = this.path.parentPath;
-
- if (parent.isJSXElement() && this.path.container === parent.node.children) {
- // turning the `span` in `
` to an expression so we need to wrap it with
- // an expression container
- uid = t.jSXExpressionContainer(uid);
- }
-
- this.path.replaceWith(uid);
- }
-}
diff --git a/src/babel/traversal/path/index.js b/src/babel/traversal/path/index.js
index 3e965e0b1d..a79969cccd 100644
--- a/src/babel/traversal/path/index.js
+++ b/src/babel/traversal/path/index.js
@@ -1,6 +1,7 @@
-import PathHoister from "./hoister";
-import * as virtualTypes from "./virtual-types";
+import PathHoister from "./lib/hoister";
+import * as virtualTypes from "./lib/virtual-types";
import * as messages from "../../messages";
+import * as contextual from "./lib/contextual";
import isBoolean from "lodash/lang/isBoolean";
import isNumber from "lodash/lang/isNumber";
import isRegExp from "lodash/lang/isRegExp";
@@ -15,35 +16,7 @@ import extend from "lodash/object/extend";
import Scope from "../scope";
import * as t from "../../types";
-var hoistVariablesVisitor = explode({
- Function() {
- this.skip();
- },
-
- VariableDeclaration(node, parent, scope) {
- if (node.kind !== "var") return;
-
- var bindings = this.getBindingIdentifiers();
- for (var key in bindings) {
- scope.push({ id: bindings[key] });
- }
-
- var exprs = [];
-
- for (var declar of (node.declarations: Array)) {
- var declar = node.declarations[i];
- if (declar.init) {
- exprs.push(t.expressionStatement(
- t.assignmentExpression("=", declar.id, declar.init)
- ));
- }
- }
-
- return exprs;
- }
-});
-
-export default class TraversalPath {
+export default class NodePath {
constructor(parent, container) {
this.container = container;
this.contexts = [];
@@ -55,7 +28,7 @@ export default class TraversalPath {
* Description
*/
- static get(parentPath: TraversalPath, parent, container, key) {
+ static get(parentPath: NodePath, parent, container, key) {
var targetNode = container[key];
var paths = container._paths = container._paths || [];
var path;
@@ -69,7 +42,7 @@ export default class TraversalPath {
}
if (!path) {
- path = new TraversalPath(parent, container);
+ path = new NodePath(parent, container);
paths.push(path);
}
@@ -82,7 +55,7 @@ export default class TraversalPath {
* Description
*/
- static getScope(path: TraversalPath, scope: Scope, file?: File) {
+ static getScope(path: NodePath, scope: Scope, file?: File) {
var ourScope = scope;
// we're entering a new scope so let's construct it!
@@ -93,59 +66,6 @@ export default class TraversalPath {
return ourScope;
}
- /**
- * Description
- */
-
- getAncestry() {
- var ancestry = [];
-
- var path = this.parentPath;
- while (path) {
- ancestry.push(path.node);
- path = path.parentPath;
- }
-
- return ancestry;
- }
-
- /**
- * Description
- */
-
- inType(types) {
- if (!Array.isArray(types)) types = [types];
-
- var path = this;
- while (path) {
- for (var type of (types: Array)) {
- if (path.node.type === type) return true;
- }
- path = path.parentPath;
- }
-
- return false;
- }
-
- /**
- * Description
- */
-
- inShadow() {
- var path = this;
- while (path) {
- if (path.isFunction()) {
- if (path.node.shadow) {
- return path;
- } else {
- return null;
- }
- }
- path = path.parentPath;
- }
- return null;
- }
-
/**
* Check whether this node was a part of the original AST.
*/
@@ -162,19 +82,6 @@ export default class TraversalPath {
return !this.isUser();
}
- /**
- * Description
- */
-
- findParent(callback) {
- var path = this;
- while (path) {
- if (callback(path.node, path)) return path;
- path = path.parentPath;
- }
- return null;
- }
-
/**
* Description
*/
@@ -229,7 +136,7 @@ export default class TraversalPath {
paths.push(path);
this.queueNode(path);
} else {
- paths.push(TraversalPath.get(this, node, this.container, to));
+ paths.push(NodePath.get(this, node, this.container, to));
}
}
@@ -356,7 +263,7 @@ export default class TraversalPath {
if (this.opts && this.opts.noScope) return;
var target = this.context || this.parentPath;
- this.scope = TraversalPath.getScope(this, target && target.scope, file);
+ this.scope = NodePath.getScope(this, target && target.scope, file);
}
/**
@@ -492,38 +399,18 @@ export default class TraversalPath {
*/
remove() {
+ if (this._contextualCall("removers", "pre")) return;
+
this.shareCommentsWithSiblings();
this._remove();
this.removed = true;
- var parentPath = this.parentPath;
- var parent = this.parent;
- if (!parentPath) return;
+ this._contextualCall("removers", "post");
+ }
- // we've just removed the last declarator of a variable declaration so there's no point in
- // keeping it
- if (parentPath.isVariableDeclaration() && parent.declarations.length === 0) {
- return parentPath.remove();
- }
-
- // we're the child of an expression statement so we should remove the parent
- if (parentPath.isExpressionStatement()) {
- return parentPath.remove();
- }
-
- // we've just removed the second element of a sequence expression so let's turn that sequence
- // expression into a regular expression
- if (parentPath.isSequenceExpression() && parent.expressions.length === 1) {
- parentPath.replaceWith(parent.expressions[0]);
- }
-
- // we're in a binary expression, better remove it and replace it with the last expression
- if (parentPath.isBinary()) {
- if (this.key === "left") {
- parentPath.replaceWith(parent.right);
- } else { // key === "right"
- parentPath.replaceWith(parent.left);
- }
+ _contextualCall(type, position) {
+ for (var fn of (contextual[type][position]: Array)) {
+ if (fn(this, this.parentPath)) return;
}
}
@@ -565,24 +452,6 @@ export default class TraversalPath {
return err;
}
- /**
- * Description
- */
-
- replaceInline(nodes) {
- if (Array.isArray(nodes)) {
- if (Array.isArray(this.container)) {
- nodes = this._verifyNodeList(nodes);
- this._containerInsertAfter(nodes);
- return this.remove();
- } else {
- return this.replaceWithMultiple(nodes);
- }
- } else {
- return this.replaceWith(nodes);
- }
- }
-
/**
* Description
*/
@@ -617,7 +486,7 @@ export default class TraversalPath {
// doesn't matter, our nodes will be inserted anyway
var container = this.node[containerKey];
- var path = TraversalPath.get(this, this.node, container, 0);
+ var path = NodePath.get(this, this.node, container, 0);
return path.insertBefore(nodes);
}
@@ -634,105 +503,11 @@ export default class TraversalPath {
var container = this.node[containerKey];
var i = container.length;
- var path = TraversalPath.get(this, this.node, container, i);
+ var path = NodePath.get(this, this.node, container, i);
return path.replaceWith(nodes, true);
}
- /**
- * Description
- */
-
- replaceWithMultiple(nodes: Array