From 9dacde6d07332030b3965680f2ffae258b0f3e87 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sat, 6 Jun 2015 03:34:08 +0100 Subject: [PATCH] further implement the concept of a "Hub" that all traversal paths get access to, also add in some assertions to confirm path state when performing manipulation --- src/babel/transformation/file/index.js | 6 +++- src/babel/traversal/hub.js | 5 +++ src/babel/traversal/index.js | 7 ---- src/babel/traversal/path/context.js | 43 +++++++++++++++++------- src/babel/traversal/path/modification.js | 12 ++++++- src/babel/traversal/path/removal.js | 13 +++++-- 6 files changed, 63 insertions(+), 23 deletions(-) create mode 100644 src/babel/traversal/hub.js diff --git a/src/babel/transformation/file/index.js b/src/babel/transformation/file/index.js index 335f4e109f..1a38da9773 100644 --- a/src/babel/transformation/file/index.js +++ b/src/babel/transformation/file/index.js @@ -14,6 +14,7 @@ import codeFrame from "../../helpers/code-frame"; import defaults from "lodash/object/defaults"; import includes from "lodash/collection/includes"; import traverse from "../../traversal"; +import Hub from "../../traversal/hub"; import assign from "lodash/object/assign"; import Logger from "./logger"; import parse from "../../helpers/parse"; @@ -44,6 +45,8 @@ export default class File { this.ast = {}; this.buildTransformers(); + + this.hub = new Hub(this); } static helpers = [ @@ -467,11 +470,12 @@ export default class File { _addAst(ast) { this.path = NodePath.get({ + hub: this.hub, parentPath: null, parent: ast, container: ast, key: "program" - }).setContext(null, this); + }).setContext(); this.scope = this.path.scope; this.ast = ast; } diff --git a/src/babel/traversal/hub.js b/src/babel/traversal/hub.js new file mode 100644 index 0000000000..69f9406fc9 --- /dev/null +++ b/src/babel/traversal/hub.js @@ -0,0 +1,5 @@ +export default class Hub { + constructor(file) { + this.file = file; + } +} diff --git a/src/babel/traversal/index.js b/src/babel/traversal/index.js index d11dbc9a15..506f735049 100644 --- a/src/babel/traversal/index.js +++ b/src/babel/traversal/index.js @@ -54,13 +54,6 @@ function clearNode(node) { let key = CLEAR_KEYS[i]; if (node[key] != null) node[key] = null; } - - for (let key in node) { - var val = node[key]; - if (Array.isArray(val)) { - delete val._paths; - } - } } var clearVisitor = { diff --git a/src/babel/traversal/path/context.js b/src/babel/traversal/path/context.js index 1383949d9a..3a411a64b9 100644 --- a/src/babel/traversal/path/context.js +++ b/src/babel/traversal/path/context.js @@ -1,4 +1,3 @@ -import * as messages from "../../messages"; import NodePath from "./index"; import traverse from "../index"; @@ -111,11 +110,11 @@ export function stop() { * Description */ -export function setScope(file?) { +export function setScope() { if (this.opts && this.opts.noScope) return; var target = this.context || this.parentPath; - this.scope = NodePath.getScope(this, target && target.scope, file); + this.scope = NodePath.getScope(this, target && target.scope, this.hub); if (this.scope) this.scope.init(); } @@ -123,7 +122,7 @@ export function setScope(file?) { * Description */ -export function setContext(context, file) { +export function setContext(context) { this.shouldSkip = false; this.shouldStop = false; this.removed = false; @@ -135,10 +134,7 @@ export function setContext(context, file) { this.opts = context.opts; } - var log = file && this.type === "Program"; - if (log) file.log.debug("Start scope building"); - this.setScope(file); - if (log) file.log.debug("End scope building"); + this.setScope(); return this; } @@ -152,11 +148,21 @@ export function setContext(context, file) { export function resync() { if (this.removed) return; + this._resyncParent(); this._resyncContainer(); this._resyncKey(); + //this._resyncRemoved(); +} + +export function _resyncParent() { + if (this.parentPath) { + this.parent = this.parentPath.node; + } } export function _resyncKey() { + if (!this.container) return; + 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 @@ -176,7 +182,7 @@ export function _resyncKey() { } } - throw new Error(messages.get("lostTrackNodePath")); + this.key = null; } export function _resyncContainer() { @@ -185,11 +191,21 @@ export function _resyncContainer() { if (!containerKey || !parentPath) return; var newContainer = parentPath.node[containerKey]; - if (!newContainer || this.container === newContainer) return; + if (this.container === newContainer) return; // container is out of sync. this is likely the result of it being reassigned - this.container = newContainer; + if (newContainer) { + this.container = newContainer; + } else { + this.container = null; + } +} + +export function _resyncRemoved() { + if (this.key == null || !this.container || this.container[this.key] !== this.node) { + this._markRemoved(); + } } /** @@ -214,7 +230,10 @@ export function unshiftContext(context) { * Description */ -export function setup(parentPath, key) { +export function setup(parentPath, container, containerKey, key) { + this.containerKey = containerKey; + this.container = container; + this.parentPath = parentPath || this.parentPath; this.setKey(key); } diff --git a/src/babel/traversal/path/modification.js b/src/babel/traversal/path/modification.js index 8cc79aa229..22f16a8dbb 100644 --- a/src/babel/traversal/path/modification.js +++ b/src/babel/traversal/path/modification.js @@ -7,6 +7,8 @@ import * as t from "../../types"; */ export function insertBefore(nodes) { + this._assertUnremoved(); + nodes = this._verifyNodeList(nodes); if (this.parentPath.isExpressionStatement() || this.parentPath.isLabeledStatement()) { @@ -27,6 +29,7 @@ export function insertBefore(nodes) { } else { throw new Error("No clue what to do with this node type."); } + return [this]; } @@ -78,6 +81,8 @@ export function _maybePopFromStatements(nodes) { */ export function insertAfter(nodes) { + this._assertUnremoved(); + nodes = this._verifyNodeList(nodes); if (this.parentPath.isExpressionStatement() || this.parentPath.isLabeledStatement()) { @@ -102,6 +107,7 @@ export function insertAfter(nodes) { } else { throw new Error("No clue what to do with this node type."); } + return [this]; } @@ -110,7 +116,7 @@ export function insertAfter(nodes) { */ export function updateSiblingKeys(fromIndex, incrementBy) { - var paths = this.container._paths; + var paths = this.parent._paths; for (var i = 0; i < paths.length; i++) { let path = paths[i]; if (path.key >= fromIndex) { @@ -153,6 +159,8 @@ export function _verifyNodeList(nodes) { */ export function unshiftContainer(containerKey, nodes) { + this._assertUnremoved(); + nodes = this._verifyNodeList(nodes); // get the first path and insert our nodes before it, if it doesn't exist then it @@ -175,6 +183,8 @@ export function unshiftContainer(containerKey, nodes) { */ export function pushContainer(containerKey, nodes) { + this._assertUnremoved(); + nodes = this._verifyNodeList(nodes); // get an invisible path that represents the last node + 1 and replace it with our diff --git a/src/babel/traversal/path/removal.js b/src/babel/traversal/path/removal.js index 843c442726..35b3afcbf1 100644 --- a/src/babel/traversal/path/removal.js +++ b/src/babel/traversal/path/removal.js @@ -15,6 +15,8 @@ export function remove() { */ export function dangerouslyRemove() { + this._assertUnremoved(); + this.resync(); if (this._callRemovalHooks("pre")) { @@ -45,8 +47,15 @@ export function _remove() { } export function _markRemoved() { - this.node = null; - this.removed = true; + this.shouldSkip = true; + this.removed = true; + this.node = null; +} + +export function _assertUnremoved() { + if (this.removed) { + throw this.errorWithNode("NodePath has been removed so is read-only."); + } } /**