UNPKG

@alauda/doom

Version:

Doctor Doom making docs.

172 lines (171 loc) 7.8 kB
/* eslint-disable regexp/optimal-quantifier-concatenation, regexp/no-misleading-capturing-group, regexp/no-super-linear-backtracking */ import fs from 'node:fs/promises'; import path from 'node:path'; import { logger } from '@rspress/core'; import { isProduction } from '@rspress/shared'; import { parse, stringify } from 'yaml'; import { cyan, red } from 'yoctocolors'; import { normalizeImgSrc } from "./normalize-img-src.js"; import { resolveReference } from "./resolve-reference.js"; import { resolveRelease } from "./resolve-release.js"; import { getFrontmatterNode, isCI, mdProcessor, mdxProcessor } from "./utils.js"; const MD_REF_START_COMMENT_PATTERN = /<!-{2,} *reference-start#(.+) *-{2,}>/; const MDX_REF_START_COMMENT_PATTERN = /\{\/\*+ *reference-start#(.+) *\*+\/\}/; const MDX_REF_START_PATTERN = /\/\*+ *reference-start#(.+) *\*+\//; const MD_REF_END_COMMENT_PATTERN = /<!-{2,} *reference-end *-{2,}>/; const MDX_REF_END_PATTERN = /\/\*+ *reference-end *\*+\//; export const MD_RELEASE_COMMENT_PATTERN = /<!-{2,} *release-notes-for-bugs\?(.+) *-{2,}>/; export const MDX_RELEASE_COMMENT_PATTERN = /\{\/\*+ *release-notes-for-bugs\?(.+) *\*+\/\}/; const MDX_RELEASE_PATTERN = /\/\*+ *release-notes-for-bugs\?(.+) *\*+\//; export const maybeHaveRef = (filepath, content) => { if (!/\.mdx?$/.test(filepath)) { return; } return (filepath.endsWith('.mdx') ? [MDX_REF_START_COMMENT_PATTERN, MDX_RELEASE_COMMENT_PATTERN] : [MD_REF_START_COMMENT_PATTERN, MD_RELEASE_COMMENT_PATTERN]).some((p) => p.test(content)); }; export const remarkReplace = function ({ lang, localBasePath, root, items, force, releaseNotes }) { return async (ast, vfile) => { const filepath = vfile.path; let content = vfile.toString(); if (!maybeHaveRef(filepath, content)) { return; } const localPublicBase = path.resolve(root, 'public'); const targetBase = path.dirname(filepath); const isMdx = filepath.endsWith('.mdx'); const processor = isMdx ? mdxProcessor : mdProcessor; const originalAst = vfile.data.original ? ast : processor.parse((content = await fs.readFile(filepath, 'utf8'))); const frontmatterNode = getFrontmatterNode(originalAst); let frontmatter = frontmatterNode && parse(frontmatterNode.value); const newContentChildren = []; let start = false; let refName = ''; let matched; let checkContent = false; const relativePath = path.relative(root, filepath); for (const node of originalAst.children) { if (node.type !== (isMdx ? 'mdxFlowExpression' : 'html')) { if (node.type === 'yaml') { continue; } if (!start) { newContentChildren.push(node); } continue; } if ((matched = node.value.match(isMdx ? MDX_REF_START_PATTERN : MD_REF_START_COMMENT_PATTERN))) { if (start) { logger.warn(`Invalid reference start block ${red(node.value)}, nested reference blocks are not allowed`); } else { checkContent = true; start = true; refName = matched[1].trim(); } newContentChildren.push(node); continue; } if ((isMdx ? MDX_REF_END_PATTERN : MD_REF_END_COMMENT_PATTERN).test(node.value)) { if (start) { start = false; } else { logger.warn(`Invalid reference end block ${red(node.value)}, no matching start block found`); } const resolved = await resolveReference(localBasePath, localPublicBase, items, refName, force); if (resolved) { const refSource = items[refName]; const { frontmatterMode } = refSource; for (const nodeItem of resolved.contents) { if (nodeItem.type !== 'yaml') { newContentChildren.push(normalizeImgSrc(nodeItem, { refSource, localPublicBase, targetBase, publicBase: resolved.publicBase, sourceBase: resolved.sourceBase, force, })); continue; } if (!frontmatterMode || frontmatterMode === 'ignore') { continue; } if (frontmatterMode === 'remove') { frontmatter = null; continue; } const data = parse(nodeItem.value); if (frontmatterMode === 'merge') { if (data) { frontmatter = { ...frontmatter, ...data, }; } // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } else if (frontmatterMode === 'replace') { frontmatter = data; } } } newContentChildren.push(node); continue; } if (!start) { newContentChildren.push(node); } } if (start) { logger.warn(`Invalid reference start block ${red(refName)}, no matching end block found, adding for you`); newContentChildren.push({ type: isMdx ? 'mdxFlowExpression' : 'html', value: isMdx ? '/* reference-end */' : '<!-- reference-end -->', }); } if (frontmatter) { newContentChildren.unshift({ type: 'yaml', value: stringify(frontmatter).trim(), }); } if (checkContent) { const newContent = processor.stringify({ ...ast, children: newContentChildren.flat(), }); if (content !== newContent) { await fs.writeFile(filepath, newContent); if (!vfile.data.original && isProduction()) { const message = `Reference block in \`${cyan(relativePath)}\` has been updated, please commit the changes`; if (isCI) { process.env.__DOOM_REBUILD__ = 'true'; } logger.warn(message); } return; } } const newAstChildren = []; const currLang = lang === null ? 'zh' : relativePath.split(/[\\/]/)[0]; for (const node of ast.children) { if (node.type !== (isMdx ? 'mdxFlowExpression' : 'html')) { newAstChildren.push(node); continue; } if ((matched = node.value.match(isMdx ? MDX_RELEASE_PATTERN : MD_RELEASE_COMMENT_PATTERN))) { const releaseContent = await resolveRelease(releaseNotes?.queryTemplates ?? {}, matched[1].trim(), currLang, isMdx); newAstChildren.push(releaseContent ?? node); continue; } newAstChildren.push(node); } ast.children = newAstChildren.flat(); }; };