unist-utils-core
Version:
A collection of commonly used (albeit enhanced) algorithms based on unist and unist-util-xxx
98 lines • 3.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.visit = exports.vistorResultToTuple = exports.visitorResultToTuple = exports.PostTraversalIndex = exports.PreTraversalIndex = exports.EXIT = exports.SKIP = exports.CONTINUE = void 0;
const test_1 = require("./test");
/**
* Continue traversing as normal.
*/
exports.CONTINUE = true;
/**
* Do not traverse this node’s children
*/
exports.SKIP = 'skip';
/**
* Stop traversing immediately
*/
exports.EXIT = false;
exports.PreTraversalIndex = Number.MIN_SAFE_INTEGER;
exports.PostTraversalIndex = Number.MAX_SAFE_INTEGER;
function visitorResultToTuple(value) {
if (value !== null && typeof value === 'object' && 'length' in value)
return value;
if (typeof value === 'number')
return [exports.CONTINUE, value];
return [value];
}
exports.visitorResultToTuple = visitorResultToTuple;
// Backwards compatible fix of spelling error.
exports.vistorResultToTuple = visitorResultToTuple;
/**
* Visit nodes (inclusive descendants of tree), with ancestral information. Optionally filtering nodes. Optionally in reverse, Optionally wrapping child visitation with pre/post visits.
* This algorithm performs depth-first tree traversal in preorder (NLR), or if reverse is given, in reverse preorder (NRL).
*
* @param tree Tree to traverse (this may be a Node, or an array of Nodes).
* @param tst (optional) 'is-compatible' test (such as a type).
* @param visitor Function invoked when a node is found that passes test @see Visitor
* @param options Options for controlling the visit @see VisitOptions. If you pass a boolean, it is used for {VisitOptions.reverse}. default is false
*/
function visit(tree, tst, visitor, options) {
if (typeof tst === 'function' && typeof visitor !== 'function') {
options = visitor;
visitor = tst;
tst = null;
}
let opts;
if (typeof options === 'boolean')
opts = { reverse: options };
else if (!options)
opts = {};
else
opts = options;
let is = (0, test_1.test)(tst);
// Visit a single node.
function one(node, index, parents) {
let result = [];
let subResult;
if (!tst || is(node, index, parents[parents.length - 1] || null)) {
let aResult = visitor(node, index, parents, opts.context);
result = visitorResultToTuple(aResult);
if (result[0] === exports.EXIT)
return result;
}
if (result[0] !== exports.SKIP) {
let c = node.children;
if (c) {
let aSubResult = all(c, parents.concat(node));
subResult = visitorResultToTuple(aSubResult);
return subResult[0] === exports.EXIT ? subResult : result;
}
}
return result;
}
// Visit children in `parent`.
function all(children, parents) {
let min = -1;
let step = opts.reverse ? -1 : 1;
let index = (opts.reverse ? children.length : min) + step;
let result;
if (opts.preTraverse)
visitor(undefined, exports.PreTraversalIndex, parents, opts.context);
while (index > min && index < children.length) {
result = one(children[index], index, parents);
if (result[0] === exports.EXIT)
break;
index = typeof result[1] === 'number' ? result[1] : index + step;
}
if (opts.postTraverse)
visitor(undefined, exports.PostTraversalIndex, parents, opts.context);
if (result)
return result;
}
// Begin
if (Array.isArray(tree))
return all(tree, []);
else
return one(tree, null, []);
}
exports.visit = visit;
//# sourceMappingURL=visit.js.map