diff --git a/docusaurus.config.js b/docusaurus.config.js index 78ec860b6..256186664 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -5,6 +5,17 @@ // See: https://docusaurus.io/docs/api/docusaurus-config import { themes as prismThemes } from "prism-react-renderer"; +import { + AUTHOR_FALLBACK, + AuthorData, + commitCache, + cacheAuthorData, + getFileCommitHashSafe, +} from "./src/utils/authorUtils"; +import { preview, deploymentID } from "./src/utils/pagesUtils"; +import {env} from "process"; + +cacheAuthorData(preview || env.NODE_ENV === "development"); /** @type {import('@docusaurus/types').Config} */ const config = { @@ -17,6 +28,30 @@ const config = { markdown: { mermaid: true, + parseFrontMatter: async (params) => { + const result = await params.defaultParseFrontMatter(params); + let author = { + ...AUTHOR_FALLBACK, + }; + if (process.env.NODE_ENV !== "development") { + const data = await getFileCommitHashSafe(params.filePath); + if (data) { + const username = commitCache.get(data.commit); + author = { + commit: data.commit, + username: username ?? AUTHOR_FALLBACK.username, + }; + } + } + + return { + ...result, + frontMatter: { + ...result.frontMatter, + author: author, + }, + }; + } }, title: '笨蛋 MC 开服教程', diff --git a/src/utils/authorUtils.ts b/src/utils/authorUtils.ts new file mode 100644 index 000000000..1ddd845f9 --- /dev/null +++ b/src/utils/authorUtils.ts @@ -0,0 +1,79 @@ +import fs from "fs-extra"; +import path from "path"; +import { Endpoints } from "@octokit/types"; + +import { getFileCommitHash, FileNotTrackedError } from "@docusaurus/utils/src/gitUtils"; +import { Globby } from "@docusaurus/utils/src/globUtils"; + +type endpoint = Endpoints["GET /repos/{owner}/{repo}/commits/{ref}"]; + +const headers = { + Accept: "application/vnd.github.v3+json", + "User-Agent": "NitWikit", +}; + +if (process.env.GITHUB_TOKEN !== undefined) { + headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`; +} + +export type AuthorData = { + username: string; + commit: string; +}; + +export const AUTHOR_FALLBACK: AuthorData = { + commit: "1b3d5f7", + username: "ghost", +}; + +export const commitCache: Map = new Map(); + +async function cacheUsernameFromCommit(commit: string) { + try { + const response = await fetch(`https://api.github.com/repos/postyizhan/NitWikit/commits/${commit}`, { + headers, + }); + if (!response.ok) { + console.error(await response.text()); + throw new Error(`Received error status code ${response.status} (${response.statusText})`); + } + + const body = (await response.json()) as endpoint["response"]; + const username = body.author.login; + + commitCache.set(commit, username); + } catch (error) { + // silent + console.error(error); + } +} + +export const getFileCommitHashSafe = async (file: string): Promise<{ commit: string } | null> => { + try { + return await getFileCommitHash(file); + } catch (e) { + if (e instanceof FileNotTrackedError) { + return null; + } + + throw e; // different error, rethrow + } +}; + +export async function cacheAuthorData(isPreview: boolean) { + // Only Render in Production and not cache in every invocation of importing docusaurus.config.ts + if (isPreview || !new Error().stack.includes("async loadSite")) { + return; + } + const docPath = path.resolve("docs/"); + + if (!(await fs.pathExists(docPath))) { + return null; + } + + const pagesFiles = await Globby("docs/**/*.md*"); + const commits = await Promise.all(pagesFiles.map(getFileCommitHashSafe)); + const commitsSet = new Set(commits.filter(Boolean).map((value) => value.commit)); + + await Promise.all(Array.from(commitsSet).map(cacheUsernameFromCommit)); +} diff --git a/src/utils/pagesUtils.ts b/src/utils/pagesUtils.ts new file mode 100644 index 000000000..44d892c3e --- /dev/null +++ b/src/utils/pagesUtils.ts @@ -0,0 +1,9 @@ +import { env } from "process"; + +export const preview = env.CI_ENV === "preview"; + +export const deploymentID: string = + `${env.GITHUB_PR_HEAD_OWNER}-${env.GITHUB_HEAD_REF || env.GITHUB_REF_NAME}` // - + .substring(0, 28) // capped to 28 characters + .toLowerCase() // lowercase + .replaceAll(/[/_.]/g, "-"); // sanitize characters in branch names