import { randomUUID } from 'node:crypto';
import parseSrcset from 'parse-srcset';
import GithubSlugger from 'github-slugger';
import { Marked, Tokenizer } from 'marked';
import { parseFragment } from 'parse5';
export const DOCUMENT_EXTENSION =
/\.(?:md|markdown|mdown|mkd|mdx|txt|json|ya?ml|html?|rst|rest|adoc|asciidoc|pdf|doc[xm]?|dot[xm]?|od[tspgfbm]|ot[tspg]|fod[tspg]|rtf|org|tex|latex)$/iu;
// Inspection only: generated HTML is parsed in memory, never executed or emitted.
const markdownLexer = new Marked({
tokenizer: {
reflink(source, links) {
const token = Tokenizer.prototype.reflink.call(this, source, links);
if (token?.type !== 'text') return token;
// Marked otherwise turns unresolved references into ordinary text.
// Retain explicit full/collapsed forms and shortcut images;
// a bare [word] without a definition remains ordinary prose.
const full = this.rules.inline.reflink.exec(source);
const collapsed = this.rules.inline.nolink.exec(source);
const match =
full ??
(collapsed?.[0].endsWith('[]') ||
collapsed?.[0].startsWith('![')
? collapsed
: undefined);
if (!match) return token;
return {
type: 'unresolved-reference',
raw: match[0],
text: match[0],
label: match[2] || match[1],
};
},
},
});
function inlineText(tokens) {
return tokens
.map((token) => {
if (token.type === 'html') return '';
if (token.tokens) return inlineText(token.tokens);
return token.type === 'text'
? decodeEntities(token.text ?? '')
: (token.text ?? '');
})
.join('');
}
function decodeEntities(text, attribute = false) {
if (attribute) {
const html = ``;
return parseFragment(html).childNodes[0].attrs[0].value;
}
// RCDATA decodes the full HTML character-reference grammar without
// interpreting literal tags. The prefix preserves an initial newline.
const html = `