2025-03-03 21:16:16 +08:00
|
|
|
|
// 1. 从 `astro:content` 导入工具函数
|
2025-03-10 13:56:56 +08:00
|
|
|
|
import { defineCollection, z, getCollection, type CollectionEntry } from 'astro:content';
|
2025-03-03 21:16:16 +08:00
|
|
|
|
import { glob } from 'astro/loaders';
|
|
|
|
|
|
|
|
|
|
// 2. 定义内容结构接口
|
|
|
|
|
export interface ContentStructure {
|
|
|
|
|
articles: string[];
|
|
|
|
|
sections: SectionStructure[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface SectionStructure {
|
|
|
|
|
name: string;
|
|
|
|
|
path: string;
|
|
|
|
|
articles: string[];
|
|
|
|
|
sections: SectionStructure[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 辅助函数:获取相对于content目录的路径
|
|
|
|
|
export function getRelativePath(fullPath: string, basePath = './src/content'): string {
|
|
|
|
|
const normalizedPath = fullPath.replace(/\\/g, '/');
|
|
|
|
|
const normalizedBasePath = basePath.replace(/\\/g, '/');
|
|
|
|
|
|
|
|
|
|
let relativePath = normalizedPath;
|
|
|
|
|
if (normalizedPath.includes(normalizedBasePath)) {
|
|
|
|
|
relativePath = normalizedPath.replace(normalizedBasePath, '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
relativePath = relativePath.startsWith('/') ? relativePath.substring(1) : relativePath;
|
|
|
|
|
if (relativePath.startsWith('articles/')) {
|
|
|
|
|
relativePath = relativePath.substring('articles/'.length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return relativePath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 辅助函数:从文件路径中提取文件名(不带扩展名)
|
|
|
|
|
export function getBasename(filePath: string): string {
|
|
|
|
|
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
|
|
|
const parts = normalizedPath.split('/');
|
|
|
|
|
const fileName = parts[parts.length - 1];
|
2025-03-10 13:56:56 +08:00
|
|
|
|
return fileName.replace(/\.(md|mdx)$/, '');
|
2025-03-03 21:16:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 辅助函数:从文件路径中提取目录路径
|
|
|
|
|
export function getDirPath(filePath: string, basePath = './src/content'): string {
|
|
|
|
|
const basename = getBasename(filePath);
|
|
|
|
|
const relativePath = getRelativePath(filePath, basePath);
|
2025-03-10 15:15:30 +08:00
|
|
|
|
return relativePath.replace(`${basename}.(md|mdx)`, '').replace(/\/$/, '');
|
2025-03-03 21:16:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-03-10 13:56:56 +08:00
|
|
|
|
// 辅助函数:获取特殊文件路径
|
|
|
|
|
export function getSpecialPath(originalPath: string): string {
|
|
|
|
|
const parts = originalPath.split('/');
|
2025-03-09 01:11:43 +08:00
|
|
|
|
const fileName = parts[parts.length - 1];
|
2025-03-10 13:56:56 +08:00
|
|
|
|
const dirName = parts.length > 1 ? parts[parts.length - 2] : '';
|
2025-03-09 01:11:43 +08:00
|
|
|
|
|
2025-03-10 13:56:56 +08:00
|
|
|
|
// 如果文件名与目录名相同,添加下划线前缀
|
|
|
|
|
if (dirName && fileName.toLowerCase() === dirName.toLowerCase()) {
|
|
|
|
|
const newFileName = fileName.startsWith('_') ? fileName : `_${fileName}`;
|
|
|
|
|
return [...parts.slice(0, -1), newFileName].join('/');
|
2025-03-09 01:11:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-03-10 13:56:56 +08:00
|
|
|
|
return originalPath;
|
2025-03-09 01:11:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
2025-03-03 21:16:16 +08:00
|
|
|
|
// 3. 定义目录结构处理函数
|
2025-03-10 13:56:56 +08:00
|
|
|
|
async function getContentStructure(): Promise<ContentStructure> {
|
|
|
|
|
// 获取所有文章
|
|
|
|
|
const allArticles = await getCollection('articles');
|
|
|
|
|
const articlePaths = allArticles.map((entry: CollectionEntry<'articles'>) => entry.id);
|
|
|
|
|
|
|
|
|
|
// 构建目录树
|
|
|
|
|
const sections = new Map<string, SectionStructure>();
|
|
|
|
|
|
|
|
|
|
// 处理每个文章路径
|
|
|
|
|
for (const articlePath of articlePaths) {
|
|
|
|
|
const parts = articlePath.split('/');
|
|
|
|
|
const dirPath = parts.slice(0, -1);
|
|
|
|
|
|
|
|
|
|
// 为每一级目录创建或更新节点
|
|
|
|
|
let currentPath = '';
|
|
|
|
|
for (const part of dirPath) {
|
|
|
|
|
const parentPath = currentPath;
|
|
|
|
|
currentPath = currentPath ? `${currentPath}/${part}` : part;
|
2025-03-09 01:11:43 +08:00
|
|
|
|
|
2025-03-10 13:56:56 +08:00
|
|
|
|
if (!sections.has(currentPath)) {
|
|
|
|
|
sections.set(currentPath, {
|
|
|
|
|
name: part,
|
|
|
|
|
path: currentPath,
|
|
|
|
|
articles: [],
|
|
|
|
|
sections: []
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-03-09 01:11:43 +08:00
|
|
|
|
|
2025-03-10 13:56:56 +08:00
|
|
|
|
// 将当前节点添加到父节点的子节点列表中
|
|
|
|
|
if (parentPath) {
|
|
|
|
|
const parentSection = sections.get(parentPath);
|
|
|
|
|
if (parentSection && !parentSection.sections.find(s => s.path === currentPath)) {
|
|
|
|
|
parentSection.sections.push(sections.get(currentPath)!);
|
2025-03-09 01:11:43 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-10 13:56:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将文章添加到其所在目录
|
|
|
|
|
if (dirPath.length > 0) {
|
|
|
|
|
const dirFullPath = dirPath.join('/');
|
|
|
|
|
const section = sections.get(dirFullPath);
|
|
|
|
|
if (section) {
|
|
|
|
|
section.articles.push(articlePath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-03 21:16:16 +08:00
|
|
|
|
|
2025-03-10 13:56:56 +08:00
|
|
|
|
// 获取顶级目录
|
|
|
|
|
const topLevelSections = Array.from(sections.values())
|
|
|
|
|
.filter(section => !section.path.includes('/'));
|
|
|
|
|
|
|
|
|
|
// 获取顶级文章(不在任何子目录中的文章)
|
|
|
|
|
const topLevelArticles = articlePaths.filter((path: string) => !path.includes('/'));
|
2025-03-03 21:16:16 +08:00
|
|
|
|
|
2025-03-10 13:56:56 +08:00
|
|
|
|
return {
|
|
|
|
|
articles: topLevelArticles,
|
|
|
|
|
sections: topLevelSections
|
|
|
|
|
};
|
2025-03-03 21:16:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. 定义你的集合
|
|
|
|
|
const articles = defineCollection({
|
2025-03-09 01:11:43 +08:00
|
|
|
|
loader: glob({
|
2025-03-10 15:15:30 +08:00
|
|
|
|
pattern: "**/*.{md,mdx}",
|
2025-03-09 01:11:43 +08:00
|
|
|
|
base: "./src/content"
|
|
|
|
|
}),
|
2025-03-03 21:16:16 +08:00
|
|
|
|
schema: z.object({
|
|
|
|
|
title: z.string(),
|
|
|
|
|
date: z.date(),
|
|
|
|
|
tags: z.array(z.string()).optional(),
|
|
|
|
|
summary: z.string().optional(),
|
|
|
|
|
image: z.string().optional(),
|
|
|
|
|
author: z.string().optional(),
|
|
|
|
|
draft: z.boolean().optional().default(false),
|
|
|
|
|
section: z.string().optional(),
|
|
|
|
|
weight: z.number().optional(),
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
|
2025-03-10 13:56:56 +08:00
|
|
|
|
// 5. 导出一个 `collections` 对象来注册你的集合
|
2025-03-09 01:11:43 +08:00
|
|
|
|
export const collections = { articles };
|
2025-03-03 21:16:16 +08:00
|
|
|
|
|
2025-03-10 13:56:56 +08:00
|
|
|
|
// 6. 导出内容结构
|
|
|
|
|
export const contentStructure = await getContentStructure();
|