add support for resyncing container on changes
This commit is contained in:
parent
a533042503
commit
8e2b743f7e
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -55,7 +55,13 @@ export function getCompletionRecords(): Array<NodePath> {
|
||||
*/
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -35,6 +35,8 @@ var hoistVariablesVisitor = {
|
||||
*/
|
||||
|
||||
export function replaceWithMultiple(nodes: Array<Object>) {
|
||||
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<Object>) {
|
||||
*/
|
||||
|
||||
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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user