diff --git a/estraverse.js b/estraverse.js index 6e28ca0..b7db020 100644 --- a/estraverse.js +++ b/estraverse.js @@ -388,6 +388,15 @@ function isProperty(nodeType, key) { return (nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === key; } + + function candidateExistsInLeaveList(leavelist, candidate) { + for (var i = leavelist.length - 1; i >= 0; --i) { + if (leavelist[i].node === candidate) { + return true; + } + } + return false; + } Controller.prototype.traverse = function traverse(root, visitor) { var worklist, @@ -469,6 +478,11 @@ if (!candidate[current2]) { continue; } + + if (candidateExistsInLeaveList(leavelist, candidate[current2])) { + continue; + } + if (isProperty(nodeType, candidates[current])) { element = new Element(candidate[current2], [key, current2], 'Property', null); } else if (isNode(candidate[current2])) { @@ -479,6 +493,10 @@ worklist.push(element); } } else if (isNode(candidate)) { + if (candidateExistsInLeaveList(leavelist, candidate)) { + continue; + } + worklist.push(new Element(candidate, key, null, null)); } } diff --git a/test/traverse.js b/test/traverse.js index 4782220..2aed36f 100644 --- a/test/traverse.js +++ b/test/traverse.js @@ -559,4 +559,28 @@ describe('no listed keys fallback', function() { () => traverse(tree, { enter(node) {} }) ).to.throw('Unknown node type XXXExpression.'); }); + + it('break loop', function () { + const children = { + type: 'Children', + name: 'div' + }; + + const parent = { + type: 'Parent', + name: children, + parent: null, + }; + + children.parent = parent; + + const tree = parent; + + checkDump(Dumper.dump(tree, null, 'iteration'), ` + enter - Parent + enter - Children + leave - Children + leave - Parent + `); + }) });