Unist Node Selector Utility
unist-util-select is a utility designed to query and match nodes within a unist (Universal Syntax Tree) abstract syntax tree using CSS-like selectors. The package is currently at stable version 5.1.0 and maintains an active release cadence, with multiple minor and major updates in the past year, reflecting ongoing development and adherence to modern JavaScript standards. Key differentiators include its ability to work with any unist syntax tree and select all node types, providing equivalents to DOM's `querySelector`, `querySelectorAll`, and `matches`. However, it's important to note that unlike the DOM, unist nodes do not inherently store parent references, meaning certain parent-sensitive selectors (e.g., `:first-child`) will not function as they do in a browser environment. For performance-critical scenarios involving numerous modifications, alternatives like `unist-util-visit` might be more efficient, as `unist-util-select` walks the entire tree on each call. For `hast` (HTML AST) specific element selections, `hast-util-select` is recommended.
Common errors
-
ReferenceError: require is not defined in ES module scope
cause Attempting to use CommonJS `require()` syntax for `unist-util-select` in an environment where it's treated as an ES module.fixChange your import statements to use ES module syntax: `import { select } from 'unist-util-select';` -
TypeError: matches is not a function
cause This error typically occurs if `matches` (or `select`/`selectAll`) is imported incorrectly, such as attempting a default import or incorrectly destructuring named exports from a CommonJS `require()` call.fixEnsure you are using named ES module imports: `import { matches } from 'unist-util-select';` -
Error: Unknown pseudo-class :first-child
cause This error indicates an attempt to use a parent-sensitive CSS pseudo-class (like `:first-child`, `:last-child`, `:nth-child` without context) which is not supported by `unist-util-select` due to the lack of parent references in unist nodes.fixRevise your selector to avoid parent-sensitive pseudo-classes. If parent context is strictly needed, you may need to pre-process your tree (e.g., with `unist-util-parents`) or perform manual checks during traversal.
Warnings
- breaking Version 5.0.0 changed the minimum Node.js requirement to 16. Ensure your environment meets this minimum before upgrading.
- breaking Since version 4.0.0, the package is ESM-only and uses the `exports` map. CommonJS `require()` statements will no longer work and will lead to runtime errors.
- breaking In version 5.0.0, the `select` and `selectAll` functions now yield `undefined` instead of `null` when no matching node is found.
- gotcha Parent-sensitive CSS selectors (e.g., `:first-child`, `:last-child`, `>`) are not supported. unist nodes do not store references to their parents, making these selectors impossible to evaluate directly.
- gotcha Frequent use of `select` or `selectAll` on large unist trees can be inefficient as each call involves walking the entire tree. For scenarios requiring many changes or bulk operations, `unist-util-visit` might offer better performance.
Install
-
npm install unist-util-select -
yarn add unist-util-select -
pnpm add unist-util-select
Imports
- matches
import matches from 'unist-util-select'
import { matches } from 'unist-util-select' - select
const { select } = require('unist-util-select')import { select } from 'unist-util-select' - selectAll
import * as selectAll from 'unist-util-select'
import { selectAll } from 'unist-util-select'
Quickstart
import { u } from 'unist-builder';
import { matches, select, selectAll } from 'unist-util-select';
const tree = u('blockquote', [
u('paragraph', [u('text', 'Alpha')]),
u('paragraph', [u('text', 'Bravo')]),
u('code', 'Charlie'),
u('paragraph', [u('text', 'Delta')]),
u('paragraph', [u('text', 'Echo')]),
u('paragraph', [u('text', 'Foxtrot')]),
u('paragraph', [u('text', 'Golf')])
]);
console.log('Matches blockquote or list:', matches('blockquote, list', tree));
const selectedNode = select('code ~ :nth-child(even)', tree);
console.log('Selected single node (Delta):', selectedNode ? selectedNode.children[0].value : 'None');
const allSelectedNodes = selectAll('code ~ :nth-child(even)', tree);
console.log('Selected all nodes (Delta, Foxtrot):', allSelectedNodes.map(node => node.children[0].value));