@alauda/doom
Version:
Doctor Doom making docs.
108 lines (107 loc) • 3.67 kB
JavaScript
import fs from 'node:fs/promises';
import path from 'node:path';
import { logger } from '@rspress/core';
import { isProduction } from '@rspress/shared';
import { render } from 'ejs';
import { red } from 'yoctocolors';
import { resolveRepo } from '../../utils/index.js';
import { parseToc } from './parse-toc.js';
import { getFrontmatterNode, mdProcessor, mdxProcessor } from './utils.js';
const refCache = new Map();
const resolveReference_ = async (localBasePath, localPublicBase, items, refName, force) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!items[refName]) {
logger.error(`Reference \`${red(refName)}\` not found`);
return;
}
const source = items[refName];
let publicBase;
let sourcePath = source.path;
if (source.repo) {
const repoFolder = await resolveRepo(source.repo, force, source.branch);
if (!repoFolder) {
return;
}
publicBase = path.resolve(repoFolder, source.publicBase ?? 'docs/public');
sourcePath = path.resolve(repoFolder, sourcePath);
}
else {
publicBase = localPublicBase;
sourcePath = path.resolve(localBasePath, sourcePath);
}
let found = false;
try {
const stat = await fs.stat(sourcePath);
found = stat.isFile();
}
catch {
//
}
if (!found) {
logger.error(`Reference path \`${red(source.path)}\` for \`${red(source.name)}\` not found`);
return;
}
let content = await fs.readFile(sourcePath, 'utf8');
for (const processor of source.processors ?? []) {
switch (processor.type) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
case 'ejsTemplate': {
content = await render(content, { data: processor.data }, { async: true });
break;
}
}
}
const processor = (sourcePath.endsWith('.ejs')
? sourcePath.slice(0, -4)
: sourcePath).endsWith('.mdx')
? mdxProcessor
: mdProcessor;
const root = processor.parse(content);
const sourceBase = path.dirname(sourcePath);
if (!source.anchor) {
return {
publicBase,
sourceBase,
contents: root.children,
};
}
const frontmatterNode = getFrontmatterNode(root);
const { toc } = parseToc(root, true);
const active = toc.find((it) => it.id === source.anchor) ??
toc.find((it) => it.text === source.anchor);
if (!active) {
logger.error(`Anchor \`${red(source.anchor)}\` not found in \`${red(source.name)}\``);
return;
}
const nodes = frontmatterNode ? [frontmatterNode] : [];
for (let i = active.index, n = root.children.length; i < n; i++) {
if (i === active.index && (source.ignoreHeading ?? active.depth === 1)) {
continue;
}
const node = root.children[i];
if (node.type === 'heading' &&
node.depth <= active.depth &&
i > active.index) {
break;
}
nodes.push(node);
}
return {
publicBase,
sourceBase,
contents: nodes,
};
};
export const resolveReference = async (localBasePath, localPublicBase, items, refName, force) => {
if (refCache.has(refName)) {
return refCache.get(refName);
}
const resolving = resolveReference_(localBasePath, localPublicBase, items, refName, force);
refCache.set(refName, resolving);
if (!isProduction()) {
setTimeout(() => {
refCache.delete(refName);
}, 5_000);
}
return resolving;
};