/** * merge-estraverse-visitors * merge multiple estraverse visitors into one * * https://github.com/twada/merge-estraverse-visitors * * Copyright (c) 2016 Takuto Wada * Licensed under the MIT license. * https://twada.mit-license.org/ */ 'use strict'; var estraverse = require('estraverse'); function SubVisitor () { this.skipStartNode = null; this.broken = false; } SubVisitor.prototype.isBroken = function () { return !!this.broken; }; SubVisitor.prototype.markBroken = function () { return this.broken = true; }; SubVisitor.prototype.isSkipping = function (controller) { return this.skipStartNode && (this.skipStartNode !== controller.current()); }; SubVisitor.prototype.startSkipping = function (controller) { this.skipStartNode = controller.current(); }; SubVisitor.prototype.finishSkippingIfLeavingFrom = function (controller) { if (this.skipStartNode === controller.current()) { this.skipStartNode = null; } }; function noop () { } function createSubVisitors (visitors) { var enters = []; var leaves = []; var subVisitor, i, v, len = visitors.length; for(i = 0; i < len; i += 1) { v = visitors[i]; subVisitor = new SubVisitor(); subVisitor.enter = (typeof v.enter === 'function') ? v.enter : noop; subVisitor.leave = (typeof v.leave === 'function') ? v.leave : noop; enters.push(subVisitor); leaves.unshift(subVisitor); } return { enters: enters, leaves: leaves }; } module.exports = function mergeVisitors (visitors) { var subVisitors = createSubVisitors(visitors); return { enter: function (currentNode, parentNode) { var orig = this; subVisitors.enters.forEach(function (subVisitor) { var controller = Object.create(orig); if (subVisitor.isBroken()) { return; } if (subVisitor.isSkipping(controller)) { return; } controller.notify = function notify (flag) { switch (flag) { case estraverse.VisitorOption.Skip: subVisitor.startSkipping(controller); return; case estraverse.VisitorOption.Break: subVisitor.markBroken(); return; default: orig.notify.call(orig, flag); } }; var ret = subVisitor.enter.call(controller, currentNode, parentNode); switch (ret) { case estraverse.VisitorOption.Skip: subVisitor.startSkipping(controller); break; case estraverse.VisitorOption.Break: subVisitor.markBroken(); break; } }); }, leave: function (currentNode, parentNode) { var orig = this; var replacements = []; subVisitors.leaves.forEach(function (subVisitor) { var controller = Object.create(orig); if (subVisitor.isBroken()) { return; } if (subVisitor.isSkipping(controller)) { return; } subVisitor.finishSkippingIfLeavingFrom(controller); controller.notify = function notify (flag) { switch (flag) { case estraverse.VisitorOption.Skip: // subVisitor.startSkipping(controller); // meaningless return; case estraverse.VisitorOption.Break: subVisitor.markBroken(); return; default: orig.notify.call(orig, flag); } }; var ret = subVisitor.leave.call(controller, currentNode, parentNode); switch (ret) { case estraverse.VisitorOption.Skip: // subVisitor.startSkipping(controller); // meaningless return; case estraverse.VisitorOption.Break: subVisitor.markBroken(); return; } if (typeof ret === 'object' && ret !== null && typeof ret.type === 'string') { replacements.push(ret); } }); if (replacements.length === 1) { return replacements[0]; } return undefined; } }; };