import type { CheerioAPI } from 'cheerio'; import { resolvePageHtmlUrl } from '../utils/pagePaths.js'; export interface ResourceHintResult { readonly added: number; readonly candidates: string[]; readonly missingHead: boolean; } export function injectResourceHints( document: CheerioAPI, currentPage: string, pagesUrlPrefix: string, useRootIndex: boolean, ): ResourceHintResult { const head = document('head').first(); const pages = [...collectInternalPages(document, currentPage, pagesUrlPrefix)]; if (head.length === 0) { return { added: 0, candidates: pages, missingHead: pages.length > 0, }; } if (pages.length === 0) { return { added: 0, candidates: [], missingHead: false }; } for (const page of pages) { const href = resolvePageHtmlUrl(pagesUrlPrefix, page, useRootIndex); head.append(`\n`); } return { added: pages.length, candidates: pages, missingHead: false }; } function collectInternalPages( document: CheerioAPI, currentPage: string, pagesUrlPrefix: string, ): Set { const pages = new Set(); document('a[href]').each((_index, element) => { const href = document(element).attr('href'); const pageName = normalizePageName(href, pagesUrlPrefix); if (!pageName || pageName === currentPage) { return; } pages.add(pageName); }); return pages; } function normalizePageName(href: string | undefined, pagesUrlPrefix: string): string | null { if (!href || href.length === 0) { return null; } const lower = href.toLowerCase(); if ( lower.startsWith('http://') || lower.startsWith('https://') || lower.startsWith('mailto:') || lower.startsWith('#') ) { return null; } let path = href.split('#')[0]?.split('?')[0] ?? ''; if (path.length === 0) { return null; } if (path.startsWith('/')) { path = path.slice(1); } const prefix = trimSlashes(pagesUrlPrefix); if (prefix && path.startsWith(`${prefix}/`)) { path = path.slice(prefix.length + 1); } if (path.endsWith('/')) { path = path.slice(0, -1); } const segments = path.split('/'); const candidate = segments[0]; if (!candidate) { return null; } return candidate; } function trimSlashes(value: string): string { return value.replace(/^\/+|\/+$/g, ''); }