diff --git a/.vitepress/utils/archives.ts b/.vitepress/utils/archives.ts index dc075b5da..897dd2ad5 100644 --- a/.vitepress/utils/archives.ts +++ b/.vitepress/utils/archives.ts @@ -1,60 +1,30 @@ -import fs from 'fs' import path from 'path' -import matter from 'gray-matter' +import { BasePost, walkFiles, extractDateFromFilename } from './fileWalker' -interface Post { - title: string - date: string - url: string -} +interface Post extends BasePost {} interface ArchiveData { [year: string]: Post[] } -// 从文件名中提取日期 -function extractDateFromFilename(filename: string): string | null { - const match = filename.match(/^(\d{4}-\d{2}-\d{2})/) - return match ? match[1] : null -} - export function generateArchives(): ArchiveData { - const posts: Post[] = [] const contentDir = path.resolve(__dirname, '../../blog') - function findMarkdownFiles(dir: string) { - const files = fs.readdirSync(dir) - - files.forEach(file => { - const filePath = path.join(dir, file) - const stat = fs.statSync(filePath) - - if (stat.isDirectory()) { - findMarkdownFiles(filePath) - } else if (path.extname(file) === '.md') { - // 从文件名中提取日期 - const dateFromFilename = extractDateFromFilename(file) - - // 如果文件名没有日期前缀,跳过这个文件 - if (!dateFromFilename) return - - const content = fs.readFileSync(filePath, 'utf-8') - const { data } = matter(content) - const url = '/' + path.relative(path.resolve(__dirname, '../..'), filePath).replace(/\.md$/, '') - - const post = { - title: data.title || file.replace(/^\d{4}-\d{2}-\d{2}-/, '').replace(/\.md$/, ''), - date: dateFromFilename, - url - } - - posts.push(post) + const posts = walkFiles({ + contentDir, + fileFilter: (file) => !!extractDateFromFilename(file), + processFile: (filePath, content, { data }) => { + const dateFromFilename = extractDateFromFilename(path.basename(filePath)) + if (!dateFromFilename) return null + + return { + title: data.title || path.basename(filePath).replace(/^\d{4}-\d{2}-\d{2}-/, '').replace(/\.md$/, ''), + date: dateFromFilename, + url: '/' + path.relative(path.resolve(__dirname, '../..'), filePath).replace(/\.md$/, '') } - }) - } - - findMarkdownFiles(contentDir) - + } + }) + // 按日期排序(从新到旧) posts.sort((a, b) => b.date.localeCompare(a.date)) diff --git a/.vitepress/utils/fileWalker.ts b/.vitepress/utils/fileWalker.ts new file mode 100644 index 000000000..76b9824d8 --- /dev/null +++ b/.vitepress/utils/fileWalker.ts @@ -0,0 +1,66 @@ +import fs from 'fs' +import path from 'path' +import matter from 'gray-matter' + +export interface BasePost { + title: string + date: string + url: string +} + +export interface FileProcessorOptions { + contentDir: string + fileFilter?: (file: string) => boolean + processFile: (filePath: string, content: string, matter: matter.GrayMatterFile) => any +} + +export function walkFiles({ contentDir, fileFilter, processFile }: FileProcessorOptions): T[] { + const results: T[] = [] + + function walk(dir: string) { + const files = fs.readdirSync(dir) + + files.forEach(file => { + const filePath = path.join(dir, file) + const stat = fs.statSync(filePath) + + if (stat.isDirectory()) { + walk(filePath) + } else if (path.extname(file) === '.md') { + if (fileFilter && !fileFilter(file)) { + return + } + + const content = fs.readFileSync(filePath, 'utf-8') + const matterResult = matter(content) + const result = processFile(filePath, content, matterResult) + + if (result) { + results.push(result) + } + } + }) + } + + walk(contentDir) + return results +} + +// 辅助函数:从文件名中提取日期 +export function extractDateFromFilename(filename: string): string | null { + const match = filename.match(/^(\d{4}-\d{2}-\d{2})/) + return match ? match[1] : null +} + +// 辅助函数:格式化日期 +export function formatDate(date: string | Date): string { + if (typeof date === 'string') { + date = new Date(date) + } + return date.toISOString().split('T')[0] +} + +// 辅助函数:生成URL +export function generateUrl(filePath: string, baseDir: string): string { + return '/' + path.relative(path.resolve(baseDir), filePath).replace(/\.md$/, '') +} \ No newline at end of file diff --git a/.vitepress/utils/tags.ts b/.vitepress/utils/tags.ts index a9e8b6a95..2e6e7440e 100644 --- a/.vitepress/utils/tags.ts +++ b/.vitepress/utils/tags.ts @@ -1,77 +1,63 @@ -import fs from 'fs' -import path from 'path' -import matter from 'gray-matter' +import fs from "fs"; +import path from "path"; +import { BasePost, walkFiles, formatDate } from "./fileWalker"; -interface Post { - title: string - date: string - url: string - tags: string[] +interface Post extends BasePost { + tags: string[]; } interface TagData { - [key: string]: Post[] -} - -// 格式化日期函数 -function formatDate(date: string | Date): string { - if (typeof date === 'string') { - // 如果是ISO格式的字符串,先转换为Date对象 - date = new Date(date) - } - return date.toISOString().split('T')[0] + [key: string]: Post[]; } export function generateTags(): TagData { - const posts: Post[] = [] - const contentDir = path.resolve(__dirname, '../../blog') - const tags: TagData = {} - - function findMarkdownFiles(dir: string) { - const files = fs.readdirSync(dir) - - files.forEach(file => { - const filePath = path.join(dir, file) - const stat = fs.statSync(filePath) - - if (stat.isDirectory()) { - findMarkdownFiles(filePath) - } else if (path.extname(file) === '.md') { - const content = fs.readFileSync(filePath, 'utf-8') - const { data } = matter(content) - - if (data.tags && Array.isArray(data.tags)) { - const url = '/' + path.relative(path.resolve(__dirname, '../..'), filePath).replace(/\.md$/, '') - const post = { - title: data.title || file.replace(/\.md$/, ''), - date: formatDate(data.date || stat.birthtime), - url, - tags: data.tags - } - - posts.push(post) - - data.tags.forEach((tag: string) => { - if (!tags[tag]) { - tags[tag] = [] - } - tags[tag].push(post) - }) - } + const contentDir = path.resolve(__dirname, "../../blog"); + const tags: TagData = {}; + + const posts = walkFiles({ + contentDir, + processFile: (filePath, content, { data }) => { + if (!data.tags || !Array.isArray(data.tags)) { + return null; } - }) - } - - findMarkdownFiles(contentDir) - - Object.keys(tags).forEach(tag => { - tags[tag].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) - }) - - const sortedTags: TagData = {} - Object.keys(tags).sort().forEach(tag => { - sortedTags[tag] = tags[tag] - }) - - return sortedTags -} \ No newline at end of file + + const post = { + title: data.title || path.basename(filePath).replace(/\.md$/, ""), + date: formatDate(data.date || fs.statSync(filePath).birthtime), + url: + "/" + + path + .relative(path.resolve(__dirname, "../.."), filePath) + .replace(/\.md$/, ""), + tags: data.tags, + }; + + // 将文章添加到对应的标签集合中 + data.tags.forEach((tag: string) => { + if (!tags[tag]) { + tags[tag] = []; + } + tags[tag].push(post); + }); + + return post; + }, + }); + + // 对每个标签下的文章按日期排序 + Object.keys(tags).forEach((tag) => { + tags[tag].sort( + (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime() + ); + }); + + // 对标签进行字母顺序排序 + const sortedTags: TagData = {}; + Object.keys(tags) + .sort() + .forEach((tag) => { + sortedTags[tag] = tags[tag]; + }); + + return sortedTags; +}