From ddeac7c826d800153bca370257654d9680dde287 Mon Sep 17 00:00:00 2001 From: Northword Date: Thu, 20 Jun 2024 22:14:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=BE=E5=BC=83=E4=BD=BF=E7=94=A8=E6=8F=92?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vitepress/config.ts | 2 +- .vitepress/config/buildEnd.config.ts | 64 ++++++++++++---- .vitepress/config/vite.config.ts | 10 +-- .vitepress/utils/getGitTimestamp.ts | 27 +++++++ .vitepress/utils/markdown.ts | 33 ++++++++ package.json | 2 + pnpm-lock.yaml | 108 ++++++++++++++++++++++++--- 7 files changed, 215 insertions(+), 31 deletions(-) create mode 100644 .vitepress/utils/getGitTimestamp.ts create mode 100644 .vitepress/utils/markdown.ts diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 6d38555..7b5b84a 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -106,7 +106,7 @@ export default defineConfig({ markdown, - // buildEnd, + buildEnd, vite, metaChunk: true, diff --git a/.vitepress/config/buildEnd.config.ts b/.vitepress/config/buildEnd.config.ts index b5c81d2..530c2f3 100644 --- a/.vitepress/config/buildEnd.config.ts +++ b/.vitepress/config/buildEnd.config.ts @@ -1,7 +1,14 @@ -import path from "path"; +import path, { join } from "path"; import { writeFileSync } from "fs"; import { Feed } from "feed"; -import { createContentLoader, type SiteConfig } from "vitepress"; +import { + createContentLoader, + createMarkdownRenderer, + type SiteConfig, +} from "vitepress"; +import { getGitTimestamp } from ".vitepress/utils/getGitTimestamp"; +import { getDefaultTitle, getTextSummary } from ".vitepress/utils/markdown"; +import FastGlob from "fast-glob"; const siteUrl = "https://zotero-chinese.com"; @@ -13,36 +20,63 @@ export const buildEnd = async (config: SiteConfig) => { link: siteUrl, language: "zh", image: "https://zotero-chinese/logo.png", - favicon: "https://vitejs.dev/logo.svg", + favicon: "https://zotero-chinese.com/logo.png", copyright: "Copyright © 2018-present Zotero 中文社区及贡献者", }); - const posts = await createContentLoader("**/*.md", { + const paths = await FastGlob.glob("src/wiki/**/*.md", { + ignore: ["README.md", "node_modules"], + }); + + // 获取每一条路径的 Git 时间 + const updatedDates = await Promise.all( + paths.map(async (path) => { + return { + router: path + .replace("src", "") + .replace("index.md", "") + .replace(".md", ""), + updated: await getGitTimestamp(path), + }; + }), + ); + + // 渲染 md + const posts = await createContentLoader("wiki/**/*.md", { + includeSrc: true, excerpt: true, - render: true, + // render: true, }).load(); + // 匹配时间 + posts.map((post) => { + post.frontmatter.updated = updatedDates.find( + (v) => v.router === post.url, + )?.updated; + return post; + }); + posts.sort( (a, b) => - +new Date(b.frontmatter.date as string) - - +new Date(a.frontmatter.date as string), + +new Date(b.frontmatter.updated) - +new Date(a.frontmatter.updated), ); - for (const { url, excerpt, frontmatter, html } of posts) { + for (const { url, excerpt, frontmatter, html, src } of posts) { feed.addItem({ - title: frontmatter.title, + title: frontmatter.title || getDefaultTitle(src!), id: `${siteUrl}${url}`, - link: `${siteUrl}${url}`, - description: excerpt, - content: html, + link: `${siteUrl}${url.replace("wiki/", "")}`, + description: excerpt || getTextSummary(src!), + // content: html, author: [ { - name: frontmatter.author?.name, + name: "Zotero 中文社区", }, ], - date: frontmatter.date, + date: new Date(frontmatter.updated || frontmatter.date) || new Date(), }); } - writeFileSync(path.join(config.outDir, "blog.rss"), feed.rss2()); + writeFileSync(path.join(config.outDir, "rss.rss"), feed.rss2()); + console.log("🎉 RSS generated"); }; diff --git a/.vitepress/config/vite.config.ts b/.vitepress/config/vite.config.ts index 9bec1dd..5019a87 100644 --- a/.vitepress/config/vite.config.ts +++ b/.vitepress/config/vite.config.ts @@ -32,11 +32,11 @@ export default defineConfig({ }), MarkdownTransform(), - RssPlugin({ - title: "Zotero 中文社区", - baseUrl: "https://zotero-chinese.com", - copyright: "Copyright (c) 2018-present, Zotero 中文社区", - }), + // RssPlugin({ + // title: "Zotero 中文社区", + // baseUrl: "https://zotero-chinese.com", + // copyright: "Copyright (c) 2018-present, Zotero 中文社区", + // }), // Git Changelog GitChangelog({ diff --git a/.vitepress/utils/getGitTimestamp.ts b/.vitepress/utils/getGitTimestamp.ts new file mode 100644 index 0000000..6da6711 --- /dev/null +++ b/.vitepress/utils/getGitTimestamp.ts @@ -0,0 +1,27 @@ +import { execa } from "execa"; +import fs from "fs-extra"; +import { basename, dirname } from "path"; + +const cache = new Map(); + +export function getGitTimestamp(file: string) { + const cached = cache.get(file); + if (cached) return cached; + + return new Promise((resolve, reject) => { + const cwd = dirname(file); + if (!fs.existsSync(cwd)) return resolve(0); + const fileName = basename(file); + const child = execa("git", ["log", "-1", '--pretty="%ai"', fileName], { + cwd, + }); + let output = ""; + child.stdout.on("data", (d) => (output += String(d))); + child.on("close", () => { + const timestamp = +new Date(output); + cache.set(file, timestamp); + resolve(timestamp); + }); + child.on("error", reject); + }); +} diff --git a/.vitepress/utils/markdown.ts b/.vitepress/utils/markdown.ts new file mode 100644 index 0000000..5f31658 --- /dev/null +++ b/.vitepress/utils/markdown.ts @@ -0,0 +1,33 @@ +/** + * 获取 markdown 内容中的标题 + */ +export function getDefaultTitle(content: string) { + const match = content.match(/^(#+)\s+(.+)/m); + return match?.[2] || ""; +} + +export function getTextSummary(text: string, count = 100) { + return ( + text + // 去除frontmatter + ?.replace(/^---[\s\S]*?---/, "") + // 首个标题 + ?.replace(/^#+\s+.*/, "") + // 除去标题 + ?.replace(/#/g, "") + // 除去图片 + ?.replace(/!\[.*?\]\(.*?\)/g, "") + // 除去链接 + ?.replace(/\[(.*?)\]\(.*?\)/g, "$1") + // 除去加粗 + ?.replace(/\*\*(.*?)\*\*/g, "$1") + ?.split("\n") + ?.filter((v) => !!v) + ?.join("\n") + ?.replace(/>(.*)/, "") + ?.replace(//g, ">") + ?.trim() + ?.slice(0, count) + ); +} diff --git a/package.json b/package.json index 97bb445..c53b965 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,8 @@ "@vue/tsconfig": "0.5.1", "eslint": "8.57.0", "eslint-plugin-vue": "9.26.0", + "execa": "^9.2.0", + "fast-glob": "^3.3.2", "fs-extra": "11.2.0", "husky": "9.0.11", "lint-staged": "15.2.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63dbd74..b7d1502 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,6 +75,12 @@ importers: eslint-plugin-vue: specifier: 9.26.0 version: 9.26.0(eslint@8.57.0) + execa: + specifier: ^9.2.0 + version: 9.2.0 + fast-glob: + specifier: ^3.3.2 + version: 3.3.2 fs-extra: specifier: 11.2.0 version: 11.2.0 @@ -747,6 +753,9 @@ packages: cpu: [x64] os: [win32] + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@shikijs/core@1.7.0': resolution: {integrity: sha512-O6j27b7dGmJbR3mjwh/aHH8Ld+GQvA0OQsNO43wKWnqbAae3AYXrhFyScHGX8hXZD6vX2ngjzDFkZY5srtIJbQ==} @@ -757,6 +766,10 @@ packages: resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@sugarat/theme-shared@0.0.1': resolution: {integrity: sha512-9QLUTnxUburhGU5/MBSyaTbwaiV9meB0h8m+XJi94R09OS/+mXeUGYPEIL7VQz0V2M266kkTMeJqnNQ/LcCU0Q==} @@ -1596,6 +1609,10 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + execa@9.2.0: + resolution: {integrity: sha512-vpOyYg7UAVKLAWWtRS2gAdgkT7oJbCn0me3gmUmxZih4kd3MF/oo8kNTBTIbkO3yuuF5uB4ZCZfn8BOolITYhg==} + engines: {node: ^18.19.0 || >=20.5.0} + extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} @@ -1623,6 +1640,10 @@ packages: resolution: {integrity: sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==} engines: {node: '>=0.4.0'} + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -1676,6 +1697,10 @@ packages: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + giscus@1.5.0: resolution: {integrity: sha512-t3LL0qbSO3JXq3uyQeKpF5CegstGfKX/0gI6eDe1cmnI7D56R7j52yLdzw4pdKrg3VnufwCgCM3FDz7G1Qr6lg==} @@ -1753,6 +1778,10 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + human-signals@7.0.0: + resolution: {integrity: sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==} + engines: {node: '>=18.18.0'} + husky@9.0.11: resolution: {integrity: sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==} engines: {node: '>=18'} @@ -1842,10 +1871,18 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} engines: {node: '>=12'} @@ -2097,10 +2134,6 @@ packages: micromark@3.2.0: resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==} - micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} - engines: {node: '>=8.6'} - micromatch@4.0.7: resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} engines: {node: '>=8.6'} @@ -2216,6 +2249,10 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + parse-node-version@1.0.1: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} engines: {node: '>= 0.10'} @@ -2299,6 +2336,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-ms@9.0.0: + resolution: {integrity: sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==} + engines: {node: '>=18'} + prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} @@ -2477,6 +2518,10 @@ packages: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -2783,6 +2828,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoctocolors@2.0.2: + resolution: {integrity: sha512-Ct97huExsu7cWeEjmrXlofevF8CvzUglJ4iGUet5B8xn1oumtAZBpHU4GzYuoE6PVqcZ5hghtBrSlhwHuR1Jmw==} + engines: {node: '>=18'} + snapshots: '@aashutoshrathi/word-wrap@1.2.6': {} @@ -3418,6 +3467,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.16.4': optional: true + '@sec-ant/readable-stream@0.4.1': {} + '@shikijs/core@1.7.0': {} '@shikijs/transformers@1.7.0': @@ -3426,6 +3477,8 @@ snapshots: '@sindresorhus/merge-streams@2.3.0': {} + '@sindresorhus/merge-streams@4.0.0': {} + '@sugarat/theme-shared@0.0.1': dependencies: cross-spawn: 7.0.3 @@ -4412,6 +4465,21 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + execa@9.2.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.3 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 7.0.0 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 5.3.0 + pretty-ms: 9.0.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.0.2 + extend-shallow@2.0.1: dependencies: is-extendable: 0.1.1 @@ -4426,7 +4494,7 @@ snapshots: '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.5 + micromatch: 4.0.7 fast-json-stable-stringify@2.1.0: {} @@ -4440,6 +4508,10 @@ snapshots: dependencies: xml-js: 1.6.11 + figures@6.1.0: + dependencies: + is-unicode-supported: 2.0.0 + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -4488,6 +4560,11 @@ snapshots: get-stream@8.0.1: {} + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + giscus@1.5.0: dependencies: lit: 3.1.3 @@ -4567,6 +4644,8 @@ snapshots: human-signals@5.0.0: {} + human-signals@7.0.0: {} + husky@9.0.11: {} iconv-lite@0.6.3: @@ -4630,8 +4709,12 @@ snapshots: is-path-inside@3.0.3: {} + is-plain-obj@4.1.0: {} + is-stream@3.0.0: {} + is-stream@4.0.1: {} + is-unicode-supported@1.3.0: {} is-unicode-supported@2.0.0: {} @@ -5006,11 +5089,6 @@ snapshots: transitivePeerDependencies: - supports-color - micromatch@4.0.5: - dependencies: - braces: 3.0.2 - picomatch: 2.3.1 - micromatch@4.0.7: dependencies: braces: 3.0.3 @@ -5128,6 +5206,8 @@ snapshots: dependencies: callsites: 3.1.0 + parse-ms@4.0.0: {} + parse-node-version@1.0.1: {} path-browserify@1.0.1: {} @@ -5186,6 +5266,10 @@ snapshots: prettier@3.3.2: {} + pretty-ms@9.0.0: + dependencies: + parse-ms: 4.0.0 + prr@1.0.1: optional: true @@ -5345,6 +5429,8 @@ snapshots: strip-final-newline@3.0.0: {} + strip-final-newline@4.0.0: {} + strip-json-comments@3.1.1: {} strip-literal@1.3.0: @@ -5685,3 +5771,5 @@ snapshots: yaml@2.4.5: {} yocto-queue@0.1.0: {} + + yoctocolors@2.0.2: {}