Unist Utility for Ancestral Node Traversal
unist-util-visit-parents is a robust utility within the unist (Universal Syntax Tree) ecosystem designed for deeply traversing ASTs (Abstract Syntax Trees) while providing a full lineage of parent nodes for each visited node. This functionality is crucial for transformations or analyses that require contextual information about a node's position within the tree. The current stable version is 6.0.2, with active development evidenced by frequent minor and major releases, particularly focusing on TypeScript type improvements and ESM compatibility. It differentiates itself from `unist-util-visit` by offering an array of parent nodes, making it indispensable for scenarios where ancestral context is necessary, such as scope analysis or complex rewrite operations. The library is ESM-only and requires Node.js 16 or higher, adhering to modern JavaScript module standards.
Common errors
-
ERR_REQUIRE_ESM
cause Attempting to `require()` an ESM-only package like `unist-util-visit-parents`.fixChange `const { visitParents } = require('unist-util-visit-parents');` to `import { visitParents } from 'unist-util-visit-parents';` and ensure your environment supports ES modules (e.g., Node.js 16+ or a bundler). -
TypeError: Cannot read properties of undefined (reading 'exports')
cause This error often occurs in older Node.js versions or build environments when an ESM-only package (like `unist-util-visit-parents@6`) is incorrectly treated as a CommonJS module, particularly when there's an `exports` map involved.fixEnsure you are running Node.js 16 or newer. If using a bundler (e.g., Webpack, Rollup), update it to a version that fully supports ESM and `exports` maps. Verify your `tsconfig.json` (if applicable) is configured for a modern module system (e.g., `"module": "Node16"` or `"ES2022"`). -
TypeError: visitParents is not a function
cause This usually indicates that the import failed to correctly resolve `visitParents`. This can happen if you're using CommonJS `require()` or if there's a mismatch between how the module is exported and imported (e.g., trying to import a default export when only named exports exist).fixConfirm your import statement is `import { visitParents } from 'unist-util-visit-parents';`. If you're in a CommonJS context, you cannot use this package directly; you must transition to ESM or use an older version if available (though not recommended). -
Argument of type '...' is not assignable to parameter of type 'Test'.
cause TypeScript error related to the `test` argument of `visitParents` or the `visitor` function's node types not matching the expected `Node` interface or a more specific unist node type.fixEnsure you are using a compatible version of `@types/unist` for your unist package. For specific node types, use type parameters with `visitParents<SpecificNodeType>(tree, 'type', visitor)` or `is()` from `unist-util-is` for more robust type checking within the visitor. Update `@types/unist` if needed, as `unist-util-visit-parents` v6.0.0 updated its dependency.
Warnings
- breaking Version 6.0.0 changed the package to be ESM-only, removing CommonJS support. It also updated the required Node.js version to 16 or higher.
- breaking With version 6.0.0, the package now uses an `exports` map in `package.json`. This may affect how the package is resolved in older bundlers or Node.js environments that do not fully support `exports` maps, or if you were relying on undocumented deep imports.
- breaking Version 6.0.0 removed the `complex-types.d.ts` file, consolidating types into the main export. TypeScript projects relying on this specific file for type imports will break.
- breaking Version 5.0.0 introduced a breaking change to TypeScript types, specifically how the `visitor` function's arguments are typed, basing them on the `tree` type. This might cause type errors in existing TypeScript projects.
- gotcha The `reverse` option (the fourth argument to `visitParents`) changes the traversal order from preorder (NLR) to reverse preorder (NRL). Using this incorrectly can lead to unexpected processing order for nodes.
- gotcha This utility is a high-level abstraction for AST traversal. For optimal performance in complex scenarios, avoid walking the tree multiple times. Instead, perform a single walk and use `unist-util-is` inside your visitor function to test for different node types and apply multiple operations.
Install
-
npm install unist-util-visit-parents -
yarn add unist-util-visit-parents -
pnpm add unist-util-visit-parents
Imports
- visitParents
const visitParents = require('unist-util-visit-parents').visitParentsimport { visitParents } from 'unist-util-visit-parents' - CONTINUE
import { Action } from 'unist-util-visit-parents'import { CONTINUE } from 'unist-util-visit-parents' - Visitor
import { Visitor } from 'unist-util-visit-parents'import type { Visitor } from 'unist-util-visit-parents'
Quickstart
import { visitParents, SKIP } from 'unist-util-visit-parents';
import { fromMarkdown } from 'mdast-util-from-markdown';
import type { Root, Paragraph, Strong, PhrasingContent } from 'mdast';
const markdownInput = 'This is a *test* with **strong** emphasis and `code` blocks.';
const tree: Root = fromMarkdown(markdownInput);
console.log('Original Tree:');
console.log(JSON.stringify(tree, null, 2));
// Example 1: Log all nodes and their direct parent types
visitParents(tree, (node, ancestors) => {
const parentTypes = ancestors.length > 0 ? ancestors.map(p => p.type).join(' > ') : 'Root';
console.log(`- Node Type: ${node.type}, Parents: ${parentTypes}`);
});
// Example 2: Skip children of 'strong' nodes and modify content
visitParents<Strong>(tree, 'strong', (node, ancestors) => {
console.log(`\nVisiting strong node: ${node.value || ''}`);
if (node.children && node.children.length > 0 && node.children[0].type === 'text') {
node.children[0].value = (node.children[0].value || '') + ' (MODIFIED)';
console.log(` Modified strong text. Skipping its children for further traversal.`);
}
return SKIP; // Do not traverse children of this 'strong' node
});
console.log('\nModified Tree (strong nodes updated and their children skipped):');
console.log(JSON.stringify(tree, null, 2));
// Example 3: Find a specific node type and its ancestors, then exit
let foundCode = false;
visitParents<PhrasingContent>(tree, 'inlineCode', (node, ancestors) => {
console.log(`\nFound inlineCode: ${node.value}`);
console.log(' Ancestors:', ancestors.map(a => a.type));
foundCode = true;
return true; // Use true (CONTINUE) or EXIT to stop, depending on requirement
});
if (!foundCode) {
console.log('\nNo inlineCode nodes found.');
}