index.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. /**
  2. * @typedef {import('unist').Node} UnistNode
  3. * @typedef {import('unist').Parent} UnistParent
  4. */
  5. /**
  6. * @typedef {Exclude<import('unist-util-is').Test, undefined> | undefined} Test
  7. * Test from `unist-util-is`.
  8. *
  9. * Note: we have remove and add `undefined`, because otherwise when generating
  10. * automatic `.d.ts` files, TS tries to flatten paths from a local perspective,
  11. * which doesn’t work when publishing on npm.
  12. */
  13. /**
  14. * @typedef {(
  15. * Fn extends (value: any) => value is infer Thing
  16. * ? Thing
  17. * : Fallback
  18. * )} Predicate
  19. * Get the value of a type guard `Fn`.
  20. * @template Fn
  21. * Value; typically function that is a type guard (such as `(x): x is Y`).
  22. * @template Fallback
  23. * Value to yield if `Fn` is not a type guard.
  24. */
  25. /**
  26. * @typedef {(
  27. * Check extends null | undefined // No test.
  28. * ? Value
  29. * : Value extends {type: Check} // String (type) test.
  30. * ? Value
  31. * : Value extends Check // Partial test.
  32. * ? Value
  33. * : Check extends Function // Function test.
  34. * ? Predicate<Check, Value> extends Value
  35. * ? Predicate<Check, Value>
  36. * : never
  37. * : never // Some other test?
  38. * )} MatchesOne
  39. * Check whether a node matches a primitive check in the type system.
  40. * @template Value
  41. * Value; typically unist `Node`.
  42. * @template Check
  43. * Value; typically `unist-util-is`-compatible test, but not arrays.
  44. */
  45. /**
  46. * @typedef {(
  47. * Check extends Array<any>
  48. * ? MatchesOne<Value, Check[keyof Check]>
  49. * : MatchesOne<Value, Check>
  50. * )} Matches
  51. * Check whether a node matches a check in the type system.
  52. * @template Value
  53. * Value; typically unist `Node`.
  54. * @template Check
  55. * Value; typically `unist-util-is`-compatible test.
  56. */
  57. /**
  58. * @typedef {(
  59. * Kind extends {children: Array<infer Child>}
  60. * ? Child
  61. * : never
  62. * )} Child
  63. * Collect nodes that can be parents of `Child`.
  64. * @template {UnistNode} Kind
  65. * All node types.
  66. */
  67. import {convert} from 'unist-util-is'
  68. /**
  69. * Find the first node in `parent` after another `node` or after an index,
  70. * that passes `test`.
  71. *
  72. * @param parent
  73. * Parent node.
  74. * @param index
  75. * Child node or index.
  76. * @param [test=undefined]
  77. * Test for child to look for (optional).
  78. * @returns
  79. * A child (matching `test`, if given) or `undefined`.
  80. */
  81. export const findAfter =
  82. // Note: overloads like this are needed to support optional generics.
  83. /**
  84. * @type {(
  85. * (<Kind extends UnistParent, Check extends Test>(parent: Kind, index: Child<Kind> | number, test: Check) => Matches<Child<Kind>, Check> | undefined) &
  86. * (<Kind extends UnistParent>(parent: Kind, index: Child<Kind> | number, test?: null | undefined) => Child<Kind> | undefined)
  87. * )}
  88. */
  89. (
  90. /**
  91. * @param {UnistParent} parent
  92. * @param {UnistNode | number} index
  93. * @param {Test} [test]
  94. * @returns {UnistNode | undefined}
  95. */
  96. function (parent, index, test) {
  97. const is = convert(test)
  98. if (!parent || !parent.type || !parent.children) {
  99. throw new Error('Expected parent node')
  100. }
  101. if (typeof index === 'number') {
  102. if (index < 0 || index === Number.POSITIVE_INFINITY) {
  103. throw new Error('Expected positive finite number as index')
  104. }
  105. } else {
  106. index = parent.children.indexOf(index)
  107. if (index < 0) {
  108. throw new Error('Expected child node or index')
  109. }
  110. }
  111. while (++index < parent.children.length) {
  112. if (is(parent.children[index], index, parent)) {
  113. return parent.children[index]
  114. }
  115. }
  116. return undefined
  117. }
  118. )