UNPKG

hast-util-excerpt

Version:
107 lines (93 loc) 2.97 kB
/** * @typedef {import('hast').Nodes} Nodes * @typedef {import('hast').RootContent} RootContent */ /** * @typedef Options * Configuration. * @property {string | null | undefined} [comment='more'] * Comment value to search for (default: `'more'`). * @property {Array<RootContent> | null | undefined} [ignore=[]] * Nodes to exclude from the resulting tree (default: `[]`). * * These are not counted towards `size`. * @property {number | null | undefined} [maxSearchSize=2048] * How far to search for the comment before bailing (default: `2048`). * * The goal of this project is to find user-defined explicit excerpts, that * are assumed to be somewhat reasonably placed. * This option prevents searching giant documents for some comment that * probably won’t be found at the end. */ import {truncate} from 'hast-util-truncate' /** @type {Readonly<Options>} */ const emptyOptions = {} /** * Truncate `tree` to a certain comment. * * @template {Nodes} Tree * Tree kind. * @param {Tree} tree * Tree to truncate. * @param {Readonly<Options> | null | undefined} [options] * Configuration (optional). * @returns {Tree | undefined} * Truncated clone of `tree` when a comment is found, `undefined` otherwise. */ export function excerpt(tree, options) { const config = options || emptyOptions const comment = config.comment || 'more' const maxSearchSize = typeof config.maxSearchSize === 'number' ? config.maxSearchSize : 2048 let found = false // Note: `truncate` returns a deep clone. const result = preorder( truncate(tree, {ignore: config.ignore, size: maxSearchSize}) ) return found ? result : undefined /** * Truncate `node`. * * @template {Nodes} Kind * Node kind. * @param {Kind} node * Node to truncate. * @returns {Kind | undefined} * Copy of `node` or `undefined` when done. */ function preorder(node) { /** @typedef {Kind extends import('unist').Parent ? Kind['children'][number] : never} Child */ if (node.type === 'comment' && node.value.trim() === comment) { found = true return } // Support MDX. if ( (node.type === 'mdxFlowExpression' || node.type === 'mdxTextExpression') && node.data && node.data.estree && node.data.estree.comments && node.data.estree.comments.some(function (node) { return node.value.trim() === comment }) ) { found = true return } /** @type {Kind} */ const replacement = {...node} if ('children' in replacement) { /** @type {Array<Child>} */ const children = [] let index = -1 while (++index < replacement.children.length && !found) { const child = /** @type {Child} */ (replacement.children[index]) const result = preorder(child) if (result) children.push(result) } replacement.children = children } return replacement } }