unist-util-select
Version:
unist utility to select nodes with CSS-like selectors
110 lines (103 loc) • 2.95 kB
JavaScript
/**
* @typedef {import('unist').Position} Position
* @typedef {import('unist').Node} Node
* @typedef {import('./lib/types.js').SelectState} SelectState
*/
/**
* @typedef {Record<string, unknown> & {type: string, position?: Position | undefined}} NodeLike
*/
import {parse} from './lib/parse.js'
import {parent} from './lib/util.js'
import {walk} from './lib/walk.js'
/**
* Check that the given `node` matches `selector`.
*
* This only checks the node itself, not the surrounding tree.
* Thus, nesting in selectors is not supported (`paragraph strong`,
* `paragraph > strong`), neither are selectors like `:first-child`, etc.
* This only checks that the given node matches the selector.
*
* @param {string} selector
* CSS selector, such as (`heading`, `link, linkReference`).
* @param {Node | NodeLike | null | undefined} [node]
* Node that might match `selector`.
* @returns {boolean}
* Whether `node` matches `selector`.
*/
export function matches(selector, node) {
const state = createState(selector, node)
state.one = true
state.shallow = true
walk(state, node || undefined)
return state.results.length > 0
}
/**
* Select the first node that matches `selector` in the given `tree`.
*
* Searches the tree in *preorder*.
*
* @param {string} selector
* CSS selector, such as (`heading`, `link, linkReference`).
* @param {Node | NodeLike | null | undefined} [tree]
* Tree to search.
* @returns {Node | undefined}
* First node in `tree` that matches `selector` or `null` if nothing is
* found.
*
* This could be `tree` itself.
*/
export function select(selector, tree) {
const state = createState(selector, tree)
state.one = true
walk(state, tree || undefined)
return state.results[0]
}
/**
* Select all nodes that match `selector` in the given `tree`.
*
* Searches the tree in *preorder*.
*
* @param {string} selector
* CSS selector, such as (`heading`, `link, linkReference`).
* @param {Node | NodeLike | null | undefined} [tree]
* Tree to search.
* @returns {Array<Node>}
* Nodes in `tree` that match `selector`.
*
* This could include `tree` itself.
*/
export function selectAll(selector, tree) {
const state = createState(selector, tree)
walk(state, tree || undefined)
return state.results
}
/**
* @param {string} selector
* Selector to parse.
* @param {Node | null | undefined} tree
* Tree to search.
* @returns {SelectState}
* State.
*/
function createState(selector, tree) {
return {
// State of the query.
rootQuery: parse(selector),
results: [],
scopeNodes: tree
? parent(tree) &&
// Root in nlcst.
(tree.type === 'RootNode' || tree.type === 'root')
? tree.children
: [tree]
: [],
one: false,
shallow: false,
found: false,
// State in the tree.
typeIndex: undefined,
nodeIndex: undefined,
typeCount: undefined,
nodeCount: undefined
}
}