131 lines
3.5 KiB
JavaScript
131 lines
3.5 KiB
JavaScript
/**
|
||
* @typedef {import('unist').Node} UnistNode
|
||
* @typedef {import('unist').Parent} UnistParent
|
||
*/
|
||
|
||
/**
|
||
* @typedef {Exclude<import('unist-util-is').Test, undefined> | undefined} Test
|
||
* Test from `unist-util-is`.
|
||
*
|
||
* Note: we have remove and add `undefined`, because otherwise when generating
|
||
* automatic `.d.ts` files, TS tries to flatten paths from a local perspective,
|
||
* which doesn’t work when publishing on npm.
|
||
*/
|
||
|
||
/**
|
||
* @typedef {(
|
||
* Fn extends (value: any) => value is infer Thing
|
||
* ? Thing
|
||
* : Fallback
|
||
* )} Predicate
|
||
* Get the value of a type guard `Fn`.
|
||
* @template Fn
|
||
* Value; typically function that is a type guard (such as `(x): x is Y`).
|
||
* @template Fallback
|
||
* Value to yield if `Fn` is not a type guard.
|
||
*/
|
||
|
||
/**
|
||
* @typedef {(
|
||
* Check extends null | undefined // No test.
|
||
* ? Value
|
||
* : Value extends {type: Check} // String (type) test.
|
||
* ? Value
|
||
* : Value extends Check // Partial test.
|
||
* ? Value
|
||
* : Check extends Function // Function test.
|
||
* ? Predicate<Check, Value> extends Value
|
||
* ? Predicate<Check, Value>
|
||
* : never
|
||
* : never // Some other test?
|
||
* )} MatchesOne
|
||
* Check whether a node matches a primitive check in the type system.
|
||
* @template Value
|
||
* Value; typically unist `Node`.
|
||
* @template Check
|
||
* Value; typically `unist-util-is`-compatible test, but not arrays.
|
||
*/
|
||
|
||
/**
|
||
* @typedef {(
|
||
* Check extends Array<any>
|
||
* ? MatchesOne<Value, Check[keyof Check]>
|
||
* : MatchesOne<Value, Check>
|
||
* )} Matches
|
||
* Check whether a node matches a check in the type system.
|
||
* @template Value
|
||
* Value; typically unist `Node`.
|
||
* @template Check
|
||
* Value; typically `unist-util-is`-compatible test.
|
||
*/
|
||
|
||
/**
|
||
* @typedef {(
|
||
* Kind extends {children: Array<infer Child>}
|
||
* ? Child
|
||
* : never
|
||
* )} Child
|
||
* Collect nodes that can be parents of `Child`.
|
||
* @template {UnistNode} Kind
|
||
* All node types.
|
||
*/
|
||
|
||
import {convert} from 'unist-util-is'
|
||
|
||
/**
|
||
* Find the first node in `parent` after another `node` or after an index,
|
||
* that passes `test`.
|
||
*
|
||
* @param parent
|
||
* Parent node.
|
||
* @param index
|
||
* Child node or index.
|
||
* @param [test=undefined]
|
||
* Test for child to look for (optional).
|
||
* @returns
|
||
* A child (matching `test`, if given) or `undefined`.
|
||
*/
|
||
export const findAfter =
|
||
// Note: overloads like this are needed to support optional generics.
|
||
/**
|
||
* @type {(
|
||
* (<Kind extends UnistParent, Check extends Test>(parent: Kind, index: Child<Kind> | number, test: Check) => Matches<Child<Kind>, Check> | undefined) &
|
||
* (<Kind extends UnistParent>(parent: Kind, index: Child<Kind> | number, test?: null | undefined) => Child<Kind> | undefined)
|
||
* )}
|
||
*/
|
||
(
|
||
/**
|
||
* @param {UnistParent} parent
|
||
* @param {UnistNode | number} index
|
||
* @param {Test} [test]
|
||
* @returns {UnistNode | undefined}
|
||
*/
|
||
function (parent, index, test) {
|
||
const is = convert(test)
|
||
|
||
if (!parent || !parent.type || !parent.children) {
|
||
throw new Error('Expected parent node')
|
||
}
|
||
|
||
if (typeof index === 'number') {
|
||
if (index < 0 || index === Number.POSITIVE_INFINITY) {
|
||
throw new Error('Expected positive finite number as index')
|
||
}
|
||
} else {
|
||
index = parent.children.indexOf(index)
|
||
|
||
if (index < 0) {
|
||
throw new Error('Expected child node or index')
|
||
}
|
||
}
|
||
|
||
while (++index < parent.children.length) {
|
||
if (is(parent.children[index], index, parent)) {
|
||
return parent.children[index]
|
||
}
|
||
}
|
||
|
||
return undefined
|
||
}
|
||
)
|