mirror of
https://github.com/bendtherules/react-fiber-traverse.git
synced 2026-08-18 22:01:49 +00:00
158 lines
3.9 KiB
TypeScript
158 lines
3.9 KiB
TypeScript
// import { isNodeSimple } from './utils';
|
|
import { FiberNode } from "./mocked-types";
|
|
|
|
interface TTraverseConfig {
|
|
order?: Array<"self" | "child" | "sibling">;
|
|
skipSiblingForStartNode?: boolean;
|
|
skipSelfForStartNode?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Traverse nodes recursively using generators.
|
|
*
|
|
* This is the advanced traverse function, which can be used used
|
|
* to write other variants of traversal and find.
|
|
*
|
|
* Type signature for generator.next first argument is `{ skipChild?: boolean; skipSibling?: boolean } | void `
|
|
* Throw any error into the generator to finish the generator and let it cleanup its internals.
|
|
*
|
|
* It allows inversion of control -
|
|
* so, application code can decide to
|
|
* 1. change order of traversal,
|
|
* 2. skip some elements,
|
|
* 3. cancel traversal mid-way.
|
|
*
|
|
* @example
|
|
* ```js
|
|
* // Basic use (for-of)
|
|
*
|
|
* const nodeIterator = traverseGenerator(rootNode);
|
|
* for (const node of nodeIterator) {
|
|
* // do something with each node here
|
|
* }
|
|
* ```
|
|
* ------
|
|
*
|
|
* @example
|
|
* ```js
|
|
* // Breadth-first
|
|
*
|
|
* // note the order below
|
|
* const nodeIterator = traverseGenerator(rootNode, ["self", "sibling", "child"]);
|
|
* // rest - same as above
|
|
* ```
|
|
* -----
|
|
*
|
|
* @example
|
|
* ```js
|
|
* // Get first 3 nodes and then stop the generator
|
|
*
|
|
* const nodeIterator = traverseGenerator(rootNode);
|
|
*
|
|
* var count = 0;
|
|
* var next;
|
|
* while (
|
|
* count < 3 &&
|
|
* !(next = nodeIterator.next()).done
|
|
* ) {
|
|
* count++;
|
|
* const node = next.value;
|
|
* // do something with each node here
|
|
* }
|
|
*
|
|
* // Finish generator, to prevent memory leak
|
|
* nodeIterator.throw(new Error());
|
|
* ```
|
|
* -----
|
|
*
|
|
*/
|
|
function* traverseGenerator(
|
|
node: FiberNode,
|
|
{
|
|
order = ["self", "child", "sibling"],
|
|
skipSiblingForStartNode = true,
|
|
skipSelfForStartNode = false
|
|
}: TTraverseConfig = {}
|
|
): IterableIterator<FiberNode> {
|
|
let skipChild = false,
|
|
skipSibling = skipSiblingForStartNode,
|
|
skipSelf = skipSelfForStartNode;
|
|
|
|
function* traverseSelf() {
|
|
if (!skipSelf) {
|
|
const controlInput:
|
|
| { skipChild?: boolean; skipSibling?: boolean }
|
|
| undefined = yield node;
|
|
|
|
if (controlInput !== undefined) {
|
|
({ skipChild = skipChild, skipSibling = skipSibling } = controlInput);
|
|
}
|
|
}
|
|
}
|
|
|
|
function* traverseChild() {
|
|
if (!skipChild && node.child !== null) {
|
|
const nextNode = node.child;
|
|
yield* traverseGenerator(nextNode, {
|
|
order,
|
|
skipSiblingForStartNode: false,
|
|
skipSelfForStartNode: false
|
|
});
|
|
}
|
|
}
|
|
|
|
function* traverseSibling() {
|
|
if (!skipSibling && node.sibling !== null) {
|
|
const nextNode = node.sibling;
|
|
yield* traverseGenerator(nextNode, {
|
|
order,
|
|
skipSiblingForStartNode: false,
|
|
skipSelfForStartNode: false
|
|
});
|
|
}
|
|
}
|
|
|
|
const traverseMap = {
|
|
self: traverseSelf,
|
|
child: traverseChild,
|
|
sibling: traverseSibling
|
|
};
|
|
|
|
// For each item mentioned in order, find generator functions to run
|
|
const orderedGenerators = order
|
|
.map(step => traverseMap[step])
|
|
.filter(tmp => tmp !== undefined);
|
|
|
|
// Now run each generator till end
|
|
for (const eachGen of orderedGenerators) {
|
|
yield* eachGen();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Traverse nodes recursively in depth-first manner, starting from a start node.
|
|
*
|
|
* This is the default and basic traversal function, which covers basic use cases.
|
|
* You can't do advanced things like change the order of traversal, skip or cancel traversal after any node, etc.
|
|
* For more advanced usecases, see {@link traverseGenerator}
|
|
*
|
|
* @example
|
|
* ```js
|
|
* // calls fn for each node inside startNode
|
|
* traverse(startNode, fn);
|
|
* ```
|
|
*
|
|
*/
|
|
function traverse(
|
|
node: FiberNode,
|
|
fn: (node: FiberNode) => any,
|
|
traverseConfig?: TTraverseConfig
|
|
) {
|
|
const nodeIterator = traverseGenerator(node, traverseConfig);
|
|
for (const tmpNode of nodeIterator) {
|
|
fn.call(null, tmpNode);
|
|
}
|
|
}
|
|
|
|
export { traverse, traverseGenerator, TTraverseConfig };
|