const DEFAULT_HEADER_SELECTOR = [...new Array(6)] .map((_, i) => `[vp-content] h${i + 1}`) .join(','); export const resolveHeaders = (headers, levels = 2) => { if (levels === false) { return []; } const [high, low] = typeof levels === 'number' ? [levels, levels] : levels === 'deep' ? [2, 6] : levels; const allowedHeaders = headers.filter((header) => header.level >= high && header.level <= low); const result = []; // eslint-disable-next-line no-restricted-syntax outer: for (let i = 0; i < allowedHeaders.length; i++) { const current = allowedHeaders[i]; if (i === 0) { result.push(current); } else { for (let j = i - 1; j >= 0; j--) { const prev = allowedHeaders[j]; if (prev.level < current.level) { prev.children.push(current); continue outer; } } result.push(current); } } return result; }; const serializeHeader = (h, ignore = []) => { let text; if (ignore.length) { const clone = h.cloneNode(true); clone.querySelectorAll(ignore.join(',')).forEach((el) => { el.remove(); }); text = clone.textContent || ''; } else { text = h.textContent || ''; } return text.trim(); }; export const getHeadersFromDom = (selector = DEFAULT_HEADER_SELECTOR, ignore = []) => Array.from(document.querySelectorAll(selector)) .filter((el) => el.id && el.hasChildNodes()) .map((el) => ({ element: el, title: serializeHeader(el, ignore), link: `#${el.id}`, slug: el.id, level: Number(el.tagName[1]), children: [], })); /** * Get headers of current page. * * 获取当前页面的标题 * * @param options - Options for getting headers / 获取标题的选项 * * @returns Array of header items / 标题项数组 */ export const getHeaders = ({ selector = DEFAULT_HEADER_SELECTOR, levels = 2, ignore = [], } = {}) => resolveHeaders(getHeadersFromDom(selector, ignore), levels);