diff --git a/src/babel/transformation/file/index.js b/src/babel/transformation/file/index.js index 1ea0385b03..a16be564ee 100644 --- a/src/babel/transformation/file/index.js +++ b/src/babel/transformation/file/index.js @@ -462,7 +462,12 @@ export default class File { } _addAst(ast) { - this.path = NodePath.get(null, ast, ast, "program", this).setContext(null, this); + this.path = NodePath.get({ + parentPath: null, + parent: ast, + container: ast, + key: "program" + }).setContext(null, this); this.scope = this.path.scope; this.ast = ast; } diff --git a/src/babel/traversal/context.js b/src/babel/traversal/context.js index c15004a9f6..9ed3481b5f 100644 --- a/src/babel/traversal/context.js +++ b/src/babel/traversal/context.js @@ -14,15 +14,21 @@ export default class TraversalContext { return !!(this.opts.enter || this.opts.exit || this.opts[node.type] || (keys && keys.length)); } - create(node, obj, key) { - var path = NodePath.get(this.parentPath, node, obj, key); + create(node, obj, key, containerKey) { + var path = NodePath.get({ + parentPath: this.parentPath, + parent: node, + container: obj, + key: key, + containerKey: containerKey + }); path.unshiftContext(this); return path; } - visitMultiple(nodes, node) { + visitMultiple(container, parent, containerKey) { // nothing to traverse! - if (nodes.length === 0) return false; + if (container.length === 0) return false; var visited = []; @@ -30,16 +36,16 @@ export default class TraversalContext { var stop = false; // build up initial queue - for (let i = 0; i < nodes.length; i++) { - var self = nodes[i]; + for (let key = 0; key < container.length; key++) { + var self = container[key]; if (self && this.shouldVisit(self)) { - queue.push(this.create(node, nodes, i)); + queue.push(this.create(parent, container, key, containerKey)); } } // visit the queue for (let path of (queue: Array)) { - path.update(); + path.resync(); if (visited.indexOf(path.node) >= 0) continue; visited.push(path.node); diff --git a/src/babel/traversal/path/context.js b/src/babel/traversal/path/context.js index ba19552d1c..41e18b6870 100644 --- a/src/babel/traversal/path/context.js +++ b/src/babel/traversal/path/context.js @@ -108,6 +108,7 @@ export function setScope(file?) { var target = this.context || this.parentPath; this.scope = NodePath.getScope(this, target && target.scope, file); + if (this.scope) this.scope.init(); } /** @@ -137,7 +138,14 @@ export function setContext(context, file) { * Description */ -export function update() { +export function resync() { + if (this.removed) return; + + this._resyncContainer(); + this._resyncKey(); +} + +export function _resyncKey() { if (this.node === this.container[this.key]) return; // grrr, path key is out of sync. this is likely due to a modification to the AST @@ -160,6 +168,17 @@ export function update() { throw new Error(messages.get("lostTrackNodePath")); } +export function _resyncContainer() { + var containerKey = this.containerKey; + var parentPath = this.parentPath; + if (!containerKey || !parentPath) return; + + var newContainer = parentPath.node[containerKey]; + if (!newContainer || this.container === newContainer) return; + + this.container = newContainer; +} + /** * Description */ diff --git a/src/babel/traversal/path/family.js b/src/babel/traversal/path/family.js index 023da87e9f..cfba7de496 100644 --- a/src/babel/traversal/path/family.js +++ b/src/babel/traversal/path/family.js @@ -55,7 +55,13 @@ export function getCompletionRecords(): Array { */ export function getSibling(key) { - return NodePath.get(this.parentPath, this.parent, this.container, key, this.file); + return NodePath.get({ + parentPath: this.parentPath, + parent: this.parent, + container: this.container, + containerKey: this.containerKey, + key: key + }); } /** @@ -82,10 +88,21 @@ export function _getKey(key) { if (Array.isArray(container)) { // requested a container so give them all the paths return container.map((_, i) => { - return NodePath.get(this, node, container, i).setContext(); + return NodePath.get({ + containerKey: key, + parentPath: this, + parent: node, + container: container, + key: i + }).setContext(); }); } else { - return NodePath.get(this, node, node, key).setContext(); + return NodePath.get({ + parentPath: this, + parent: node, + container: node, + key: key + }).setContext(); } } diff --git a/src/babel/traversal/path/index.js b/src/babel/traversal/path/index.js index 5044ded011..601cbb1832 100644 --- a/src/babel/traversal/path/index.js +++ b/src/babel/traversal/path/index.js @@ -5,18 +5,19 @@ import Scope from "../scope"; import * as t from "../../types"; export default class NodePath { - constructor(parent, container) { - this.container = container; - this.contexts = []; - this.parent = parent; - this.data = {}; + constructor(parent, container, containerKey) { + this.containerKey = containerKey; + this.container = container; + this.contexts = []; + this.parent = parent; + this.data = {}; } /** * Description */ - static get(parentPath: NodePath, parent, container, key) { + static get({ parentPath, parent, container, containerKey, key }) { var targetNode = container[key]; var paths = container._paths = container._paths || []; var path; @@ -30,7 +31,7 @@ export default class NodePath { } if (!path) { - path = new NodePath(parent, container); + path = new NodePath(parent, container, containerKey); paths.push(path); } diff --git a/src/babel/traversal/path/modification.js b/src/babel/traversal/path/modification.js index c3bf91fca6..466d15d73a 100644 --- a/src/babel/traversal/path/modification.js +++ b/src/babel/traversal/path/modification.js @@ -40,11 +40,17 @@ export function _containerInsert(from, nodes) { this.container.splice(to, 0, node); if (this.context) { - var path = this.context.create(this.parent, this.container, to); + var path = this.context.create(this.parent, this.container, to, this.containerKey); paths.push(path); this.queueNode(path); } else { - paths.push(NodePath.get(this, node, this.container, to)); + paths.push(NodePath.get({ + parentPath: this, + parent: node, + container: this.container, + containerKey: this.containerKey, + key: to + })); } } @@ -145,7 +151,13 @@ export function unshiftContainer(containerKey, nodes) { // doesn't matter, our nodes will be inserted anyway var container = this.node[containerKey]; - var path = NodePath.get(this, this.node, container, 0); + var path = NodePath.get({ + parentPath: this, + parent: this.node, + container: container, + containerKey, + key: 0 + }); return path.insertBefore(nodes); } @@ -162,7 +174,13 @@ export function pushContainer(containerKey, nodes) { var container = this.node[containerKey]; var i = container.length; - var path = NodePath.get(this, this.node, container, i); + var path = NodePath.get({ + parentPath: this, + parent: this.node, + container: container, + containerKey, + key: i + }); return path.replaceWith(nodes, true); } diff --git a/src/babel/traversal/path/removal.js b/src/babel/traversal/path/removal.js index be7b9ba55e..6e832f0cb8 100644 --- a/src/babel/traversal/path/removal.js +++ b/src/babel/traversal/path/removal.js @@ -5,18 +5,23 @@ import * as removalHooks from "./lib/removal-hooks"; */ export function remove() { - if (this._callRemovalHooks("pre")) return; + this.resync(); + + if (this._callRemovalHooks("pre")) { + this._markRemoved(); + return; + } this.shareCommentsWithSiblings(); this._remove(); - this.removed = true; + this._markRemoved(); this._callRemovalHooks("post"); } export function _callRemovalHooks(position) { for (var fn of (removalHooks[position]: Array)) { - if (fn(this, this.parentPath)) return; + if (fn(this, this.parentPath)) return true; } } @@ -27,7 +32,11 @@ export function _remove() { } else { this.container[this.key] = null; } +} + +export function _markRemoved() { this.node = null; + this.removed = true; } /** diff --git a/src/babel/traversal/path/replacement.js b/src/babel/traversal/path/replacement.js index ef7069cd5b..4e2e7c8ff7 100644 --- a/src/babel/traversal/path/replacement.js +++ b/src/babel/traversal/path/replacement.js @@ -35,6 +35,8 @@ var hoistVariablesVisitor = { */ export function replaceWithMultiple(nodes: Array) { + this.resync(); + nodes = this._verifyNodeList(nodes); t.inheritsComments(nodes[0], this.node); this.node = this.container[this.key] = null; @@ -47,6 +49,8 @@ export function replaceWithMultiple(nodes: Array) { */ export function replaceWithSourceString(replacement) { + this.resync(); + try { replacement = `(${replacement})`; replacement = parse(replacement); @@ -69,6 +73,8 @@ export function replaceWithSourceString(replacement) { */ export function replaceWith(replacement, whateverAllowed) { + this.resync(); + if (this.removed) { throw new Error("You can't replace this node, we've already removed it"); } @@ -129,6 +135,8 @@ export function replaceWith(replacement, whateverAllowed) { */ export function replaceExpressionWithStatements(nodes: Array) { + this.resync(); + var toSequenceExpression = t.toSequenceExpression(nodes, this.scope); if (toSequenceExpression) { @@ -167,6 +175,8 @@ export function replaceExpressionWithStatements(nodes: Array) { */ export function replaceInline(nodes) { + this.resync(); + if (Array.isArray(nodes)) { if (Array.isArray(this.container)) { nodes = this._verifyNodeList(nodes); diff --git a/src/babel/traversal/scope/index.js b/src/babel/traversal/scope/index.js index 0f7e80e551..15c2c8b64d 100644 --- a/src/babel/traversal/scope/index.js +++ b/src/babel/traversal/scope/index.js @@ -146,8 +146,6 @@ export default class Scope { this.parentBlock = path.parent; this.block = path.node; this.path = path; - - this.crawl(); } static globals = flatten([globals.builtin, globals.browser, globals.node].map(Object.keys)); @@ -552,11 +550,21 @@ export default class Scope { if (t.isIdentifier(node)) { var bindingInfo = this.getBinding(node.name); return bindingInfo && bindingInfo.constant; + } else if (t.isClass(node)) { + return !node.superClass; } else { return t.isPure(node); } } + /** + * Description + */ + + init() { + if (!this.references) this.crawl(); + } + /** * Description */ @@ -613,7 +621,7 @@ export default class Scope { for (let param of (params: Array)) { this.registerBinding("param", param); } - this.traverse(path.get("body").node, blockVariableVisitor, this); + path.get("body").traverse(blockVariableVisitor, this); } // Program, Function - var variables diff --git a/src/babel/types/alias-keys.json b/src/babel/types/alias-keys.json index 620f8903b5..07fc4000e9 100644 --- a/src/babel/types/alias-keys.json +++ b/src/babel/types/alias-keys.json @@ -41,8 +41,8 @@ "SpreadProperty": ["UnaryLike"], "SpreadElement": ["UnaryLike"], - "ClassDeclaration": ["Scopable", "Class", "Pure", "Statement", "Declaration"], - "ClassExpression": ["Scopable", "Class", "Pure", "Expression"], + "ClassDeclaration": ["Scopable", "Class", "Statement", "Declaration"], + "ClassExpression": ["Scopable", "Class", "Expression"], "ForOfStatement": ["Scopable", "Statement", "For", "Loop"], "ForInStatement": ["Scopable", "Statement", "For", "Loop"],