UNPKG

@koroligor/docusaurus-plugin-backlinks

Version:
119 lines (118 loc) 5.05 kB
import { aliasedSitePathToRelativePath, resolveMarkdownLinkPathname } from '@docusaurus/utils'; import fs from 'fs-extra'; import path from 'path'; const plugin = function () { return { name: 'docusaurus-plugin-backlinks', async postBuild({ plugins, outDir }) { const allContent = await getBlogAndDocsContent(plugins); const backlinksMap = await getBacklinksMap(allContent); await saveToFile(backlinksMap, path.join(outDir, 'backlinks.json')); } }; }; export default plugin; async function getBlogAndDocsContent(plugins) { const blogPlugin = plugins.find(plugin => plugin.name === 'docusaurus-plugin-content-blog'); const docsPlugin = plugins.find(plugin => plugin.name === 'docusaurus-plugin-content-docs'); const blogPosts = blogPlugin?.content?.blogPosts || []; const docItems = docsPlugin?.content?.loadedVersions[0]?.docs || []; //blogPosts.map((post: any) => {console.debug('post = ' + post)}) //docItems.map((doc: any) => {console.debug( 'doc.id = ' + doc.id + ' doc.source = ' + doc.source + ' doc.title ' + doc.title + ' doc.description ' + doc.description + ' doc.sidebar_label ' + doc.sidebar_label )}) return [ ...blogPosts.map((post) => ({ content: post.content, metadata: post.metadata })), ...docItems.map((doc) => ({ content: fs.readFileSync(aliasedSitePathToRelativePath(doc.source), 'utf8'), metadata: { source: doc.source, permalink: doc.permalink, description: doc.title } })) ]; } function getMarkdownLinkResolver(pages_with_metadata) { function createResolverContext(file_permalink_dict) { return { siteDir: ".", contentPaths: { contentPath: 'blog', contentPathLocalized: 'docs', }, sourceToPermalink: new Map(Object.entries(file_permalink_dict)), sourceFilePath: "override me", }; } function createSourceToPermalinkDict(pages_with_metadata) { const dict = {}; for (const { metadata } of pages_with_metadata) { dict[metadata.source] = metadata.permalink; } return dict; } const sourceToPermalinkDict = createSourceToPermalinkDict(pages_with_metadata); const resolverContext = createResolverContext(sourceToPermalinkDict); const registeredRoutes = new Set(Object.values(sourceToPermalinkDict)); return (url, sourceFilePath) => { resolverContext.sourceFilePath = aliasedSitePathToRelativePath(sourceFilePath); return resolveMarkdownLinkPathname(url, resolverContext) || (registeredRoutes.has(url) ? url : null); }; } const ignoreRoutes = new Set(['/about', 'about']); function isIgnoredUrl(url) { return url.startsWith('http') || url.startsWith('/tags/') || ignoreRoutes.has(url) || url === '#' || url === '/'; } async function getBacklinksMap(pages_with_metadata) { const resolveMarkdownLink = getMarkdownLinkResolver(pages_with_metadata); const backlinksMap = { links: {}, descriptions: {}, }; for (const { content, metadata } of pages_with_metadata) { if (!content) { console.error("[backlinks-plugin] postBuild, content is undefined", metadata.source); throw new Error("Content is undefined"); } const links = content.match(/(?<!!)\[([^\]]*)\]\(([^)]+)\)/g) || []; for (const linkMarkup of links) { const [_, title, url] = linkMarkup.match(/(?<!!)\[([^\]]*)\]\(([^)]+)\)/); const resolvedUrl = resolveMarkdownLink(url, metadata.source); if (resolvedUrl) { addBacklink(backlinksMap, resolvedUrl, metadata.permalink, metadata.description); } else if (!isIgnoredUrl(url)) { console.warn("🤔 [backlinks-plugin] postBuild hook, NOT resolved Url", url, " in ", metadata.source); } } } const links = backlinksMap.links; const result = { links: Object.fromEntries(Object.entries(links).map(([k, v]) => [k, Array.from(v)])), descriptions: backlinksMap.descriptions }; return result; } function addBacklink(backlinksMap, resolvedUrl, permalink, description) { //console.debug('resolvedUrl =' + resolvedUrl + ' permalink = ' + permalink + ' description = ' + description) if (!backlinksMap.descriptions[permalink]) { backlinksMap.descriptions[permalink] = description || ''; } if (!backlinksMap.links[resolvedUrl]) { backlinksMap.links[resolvedUrl] = new Set(); } backlinksMap.links[resolvedUrl].add(permalink); } async function saveToFile(data, filePath) { try { await fs.outputFile(filePath, JSON.stringify(data, null, 2)); } catch (err) { console.error("[backlinks-plugin] postBuild hook, error writing backlinks.json", err); throw err; } }