import { visitParents } from "unist-util-visit-parents"; export function findAndReplace(tree, list) { const pairs = list; let pairIndex = -1; const visitor = (node, parents) => { let index = -1; /** @type {Parents | undefined} */ let grandparent; while (++index < parents.length) { const parent = parents[index]; // const siblings = grandparent ? grandparent.children : undefined; grandparent = parent; } if (grandparent) { return handler(node, parents); } return undefined; }; while (++pairIndex < pairs.length) { visitParents(tree, "text", visitor); } /** * Handle a text node which is not in an ignored parent. * * @param {Text} node * Text node. * @param {Array} parents * Parents. * @returns {VisitorResult} * Result. */ function handler(node, parents) { const parent = parents[parents.length - 1]; const find = pairs[pairIndex][0]; const replace = pairs[pairIndex][1]; let start = 0; const siblings = parent.children; const index = siblings.indexOf(node); let change = false; let nodes = []; find.lastIndex = 0; let match = find.exec(node.value); while (match) { const position = match.index; /** @type {RegExpMatchObject} */ // const matchObject = { // index: match.index, // input: match.input, // stack: [...parents, node], // }; // let value = replace(...match, matchObject); let value = replace(...match); if (typeof value === "string") { value = value.length > 0 ? { type: "text", value } : undefined; } // It wasn’t a match after all. if (value === false) { // False acts as if there was no match. // So we need to reset `lastIndex`, which currently being at the end of // the current match, to the beginning. find.lastIndex = position + 1; } else { if (start !== position) { nodes.push({ type: "text", value: node.value.slice(start, position), }); } if (Array.isArray(value)) { nodes.push(...value); } else if (value) { nodes.push(value); } start = position + match[0].length; change = true; } if (!find.global) { break; } match = find.exec(node.value); } if (change) { if (start < node.value.length) { nodes.push({ type: "text", value: node.value.slice(start) }); } parent.children.splice(index, 1, ...nodes); } else { nodes = [node]; } return index + nodes.length; } }