/** * Build-time projection from canonical repository Markdown into VitePress. * * The generated tree is disposable: sources stay in their owning `docs/` * tier, while this adapter rewrites cross-source links for the public site. */ import { existsSync, lstatSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs' import { dirname, extname, posix, relative, resolve, sep } from 'node:path' import { fromMarkdown } from 'mdast-util-from-markdown' import { gfmFromMarkdown } from 'mdast-util-gfm' import { gfm } from 'micromark-extension-gfm' import type { Nodes } from 'mdast' import { docsPages, type DocsLocale, type DocsPage } from '../website/docs.ts' const REPOSITORY_URL = 'https://github.com/deepseek-harness/deepseek-harness' const root = resolve(import.meta.dirname, '..') const generatedRoot = resolve(root, 'website/.generated') interface Replacement { start: number end: number value: string } interface DestinationRange { start: number end: number } type RewritableNode = Extract /** Inputs for rewriting one canonical Markdown page. */ export interface RewriteMarkdownOptions { locale: DocsLocale sourcePath: string route: string pages: DocsPage[] repoRoot: string repositoryRef: string } function repoPath(absPath: string, repoRoot: string): string { return relative(repoRoot, absPath).split(sep).join('/') } function isExternalOrSiteAbsolute(url: string): boolean { return url.startsWith('#') || url.startsWith('//') || url.startsWith('/') || /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url) } function skipWhitespace(source: string, start: number): number { let index = start while (/\s/.test(source[index] ?? '')) index += 1 return index } function labelEnd(source: string): number { const first = source.indexOf('[') if (first === -1) return -1 let depth = 0 for (let index = first; index < source.length; index += 1) { const char = source[index] if (char === '\\') { index += 1 } else if (char === '[') { depth += 1 } else if (char === ']') { depth -= 1 if (depth === 0) return index } } return -1 } function destinationRange(rawNode: string, type: 'link' | 'image' | 'definition'): DestinationRange { const endOfLabel = labelEnd(rawNode) if (endOfLabel === -1) { throw new Error(`project-doc-site: cannot locate label end in ${JSON.stringify(rawNode)}.`) } let start: number if (type === 'definition') { const colon = rawNode.indexOf(':', endOfLabel + 1) if (colon === -1) { throw new Error(`project-doc-site: cannot locate definition separator in ${JSON.stringify(rawNode)}.`) } start = skipWhitespace(rawNode, colon + 1) } else { if (rawNode[endOfLabel + 1] !== '(') { throw new Error(`project-doc-site: cannot locate inline destination in ${JSON.stringify(rawNode)}.`) } start = skipWhitespace(rawNode, endOfLabel + 2) } if (rawNode[start] === '<') { for (let index = start + 1; index < rawNode.length; index += 1) { if (rawNode[index] === '\\') index += 1 else if (rawNode[index] === '>') return { start: start + 1, end: index } } throw new Error(`project-doc-site: cannot locate angle-bracket destination end in ${JSON.stringify(rawNode)}.`) } let depth = 0 for (let index = start; index < rawNode.length; index += 1) { const char = rawNode[index] if (char === '\\') { index += 1 } else if (char === '(') { depth += 1 } else if (char === ')') { if (depth === 0) return { start, end: index } depth -= 1 } else if (/\s/.test(char ?? '') && depth === 0) { return { start, end: index } } } return { start, end: rawNode.length } } function splitTarget(url: string): { path: string; suffix: string } { const boundary = url.search(/[?#]/) if (boundary === -1) return { path: url, suffix: '' } return { path: url.slice(0, boundary), suffix: url.slice(boundary) } } function decodePath(path: string): string { try { return decodeURIComponent(path) } catch { throw new Error(`project-doc-site: malformed percent escape in ${JSON.stringify(path)}.`) } } function routeTarget(fromRoute: string, toRoute: string, suffix: string): string { const target = posix.relative(posix.dirname(fromRoute), toRoute) return `${target.startsWith('.') ? target : `./${target}`}${suffix}` } function sourceMap(pages: DocsPage[]): Map> { const map = new Map>() for (const page of pages) { for (const source of [page.source, ...(page.sourceAliases ?? [])]) { const localized = map.get(source) ?? new Map() if (localized.has(page.locale)) { throw new Error(`project-doc-site: duplicate source or alias ${JSON.stringify(source)} for locale ${JSON.stringify(page.locale)}.`) } localized.set(page.locale, page) map.set(source, localized) } } return map } function counterpartSource(source: string): string { return source.endsWith('.zh.md') ? source.replace(/\.zh\.md$/, '.md') : source.replace(/\.md$/, '.zh.md') } function resolveRepositoryTarget(sourceAbs: string, rawPath: string, repoRoot: string): { absPath: string; line?: number } { const decoded = decodePath(rawPath) let absPath = resolve(dirname(sourceAbs), decoded) if (existsSync(absPath)) return { absPath } const lineMatch = decoded.match(/:(\d+)$/) if (lineMatch !== null) { const lineText = lineMatch[1] if (lineText === undefined) throw new Error('project-doc-site: line suffix matched without a line number.') absPath = resolve(dirname(sourceAbs), decoded.slice(0, -lineMatch[0].length)) if (existsSync(absPath)) return { absPath, line: Number.parseInt(lineText, 10) } } if (extname(decoded) === '') { const markdown = resolve(dirname(sourceAbs), `${decoded}.md`) if (existsSync(markdown)) return { absPath: markdown } const index = resolve(dirname(sourceAbs), decoded, 'index.md') if (existsSync(index)) return { absPath: index } } throw new Error(`project-doc-site: ${repoPath(sourceAbs, repoRoot)} links to missing path ${JSON.stringify(rawPath)}.`) } function githubTarget( absPath: string, line: number | undefined, suffix: string, repositoryRef: string, repoRoot: string, image: boolean, ): string { const path = repoPath(absPath, repoRoot) if (image) return `https://raw.githubusercontent.com/deepseek-harness/deepseek-harness/${repositoryRef}/${path}${suffix}` const kind = lstatSync(absPath).isDirectory() ? 'tree' : 'blob' const lineSuffix = line === undefined ? suffix : `#L${line}` return `${REPOSITORY_URL}/${kind}/${repositoryRef}/${path}${lineSuffix}` } /** * Rewrite repository-relative links without reserializing Markdown. * * @param source Markdown text from the canonical file. * @param options Source, route, manifest, and repository context. * @returns Markdown whose published links resolve inside the site or to GitHub. */ export function rewriteMarkdown(source: string, options: RewriteMarkdownOptions): string { const sourceAbs = resolve(options.repoRoot, options.sourcePath) const published = sourceMap(options.pages) const tree = fromMarkdown(source, { extensions: [gfm()], mdastExtensions: [gfmFromMarkdown()] }) const replacements: Replacement[] = [] const rewrite = (node: RewritableNode): void => { if (isExternalOrSiteAbsolute(node.url)) return const { path, suffix } = splitTarget(node.url) if (path === '') return const { absPath, line } = resolveRepositoryTarget(sourceAbs, path, options.repoRoot) const targetPath = repoPath(absPath, options.repoRoot) const isLanguageSwitcher = targetPath === counterpartSource(options.sourcePath) const targetLocale: DocsLocale = isLanguageSwitcher ? options.locale === 'root' ? 'en' : 'root' : options.locale const page = published.get(targetPath)?.get(targetLocale) const nextUrl = page === undefined ? githubTarget(absPath, line, suffix, options.repositoryRef, options.repoRoot, node.type === 'image') : routeTarget(options.route, page.route, suffix) const start = node.position?.start.offset const end = node.position?.end.offset if (start === undefined || end === undefined) { throw new Error(`project-doc-site: link ${JSON.stringify(node.url)} has no source offsets.`) } const rawNode = source.slice(start, end) const rawDestination = destinationRange(rawNode, node.type) replacements.push({ start: start + rawDestination.start, end: start + rawDestination.end, value: nextUrl, }) } const visit = (node: Nodes): void => { if ((node.type === 'link' || node.type === 'image' || node.type === 'definition') && 'url' in node) rewrite(node) if ('children' in node) { for (const child of node.children) visit(child) } } visit(tree) let projected = source for (const replacement of replacements.sort((a, b) => b.start - a.start)) { projected = projected.slice(0, replacement.start) + replacement.value + projected.slice(replacement.end) } return projected } /** * Record the canonical edit target in VitePress frontmatter. * * @param markdown Projected Markdown content. * @param sourcePath Repository-relative canonical source path. * @returns Markdown with an `editSource` frontmatter field. */ export function addProjectionFrontmatter(markdown: string, sourcePath: string): string { const field = `editSource: ${JSON.stringify(sourcePath)}` if (markdown.startsWith('---\n')) return markdown.replace('---\n', `---\n${field}\n`) return `---\n${field}\n---\n\n${markdown}` } /** * Select the Markdown rendered for one published page. * * @param markdown Rewritten canonical Markdown content. * @param page Publication manifest entry for the content. * @returns Full Markdown for ordinary pages or frontmatter-only Markdown for a locale home page. */ export function projectedPageContent(markdown: string, page: DocsPage): string { if (page.sidebar !== null) return markdown if (!markdown.startsWith('---\n')) { throw new Error(`project-doc-site: locale home source ${JSON.stringify(page.source)} must start with YAML frontmatter.`) } const closingDelimiter = '\n---\n' const closing = markdown.indexOf(closingDelimiter, 4) if (closing === -1) { throw new Error(`project-doc-site: locale home source ${JSON.stringify(page.source)} has unclosed YAML frontmatter.`) } return markdown.slice(0, closing + closingDelimiter.length) } /** Canonical Markdown files watched by the local VitePress dev server. */ export function docsSourceFiles(): string[] { return [...new Set(docsPages.map(page => resolve(root, page.source)))] } /** Rebuild the disposable VitePress source tree from the publication manifest. */ export function projectDocs(): void { const routes = new Set() const repositoryRef = process.env.GITHUB_SHA ?? 'master' rmSync(generatedRoot, { recursive: true, force: true }) for (const page of docsPages) { if (routes.has(page.route)) throw new Error(`project-doc-site: duplicate route ${JSON.stringify(page.route)}.`) routes.add(page.route) const sourceAbs = resolve(root, page.source) if (!existsSync(sourceAbs) || !lstatSync(sourceAbs).isFile()) { throw new Error(`project-doc-site: source ${JSON.stringify(page.source)} does not exist or is not a file.`) } const output = resolve(generatedRoot, page.route) mkdirSync(dirname(output), { recursive: true }) const markdown = readFileSync(sourceAbs, 'utf8') const projected = rewriteMarkdown(markdown, { sourcePath: page.source, locale: page.locale, route: page.route, pages: docsPages, repoRoot: root, repositoryRef, }) writeFileSync(output, addProjectionFrontmatter(projectedPageContent(projected, page), page.source)) } }