newechoes/src/content.config.ts

194 lines
6.7 KiB
TypeScript
Raw Normal View History

2025-03-03 21:16:16 +08:00
// 1. 从 `astro:content` 导入工具函数
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
import fs from 'node:fs';
import path from 'node:path';
// 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;
// 如果路径以articles/开头移除它适配Astro内容集合
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];
// 移除扩展名
const basename = fileName.replace(/\.(md|mdx)$/, '');
return basename;
}
// 辅助函数:从文件路径中提取目录路径
export function getDirPath(filePath: string, basePath = './src/content'): string {
const basename = getBasename(filePath);
const relativePath = getRelativePath(filePath, basePath);
// 移除文件名部分,获取目录路径
const dirPath = relativePath.replace(`${basename}.md`, '').replace(/\/$/, '');
return dirPath;
}
// 辅助函数:获取原始文件路径(移除特殊前缀)
export function getOriginalPath(specialPath: string): string {
// 检查路径是否包含特殊前缀
const parts = specialPath.split('/');
const fileName = parts[parts.length - 1];
// 如果文件名以下划线开头,移除它
if (fileName.startsWith('_')) {
const originalFileName = fileName.substring(1);
const newParts = [...parts.slice(0, -1), originalFileName];
return newParts.join('/');
}
return specialPath;
}
// 辅助函数:获取特殊文件路径(添加特殊前缀)
export function getSpecialPath(originalPath: string): string {
// 检查文件名是否与其所在目录名相同或包含目录名
const parts = originalPath.split('/');
const fileName = parts[parts.length - 1].replace(/\.md$/, '');
// 如果文件名与目录名相同或以目录名开头,则在文件名前添加特殊前缀
if (parts.length > 1) {
const dirName = parts[parts.length - 2];
if (fileName === dirName || fileName.startsWith(dirName)) {
// 创建一个新的路径,在文件名前添加下划线前缀
const newFileName = fileName.startsWith('_') ? fileName : `_${fileName}`;
const fileExt = originalPath.endsWith('.md') ? '.md' : '';
const newParts = [...parts.slice(0, -1), newFileName + fileExt];
return newParts.join('/');
}
}
return originalPath;
}
2025-03-03 21:16:16 +08:00
// 3. 定义目录结构处理函数
function getContentStructure(contentDir = './src/content', basePath = './src/content'): ContentStructure {
// 检查目录是否存在
if (!fs.existsSync(contentDir)) {
return { articles: [], sections: [] };
}
// 获取目录下的所有文件和文件夹
const items = fs.readdirSync(contentDir, { withFileTypes: true });
// 分离文章和目录
const articles = items
.filter(item => item.isFile() && item.name.endsWith('.md'))
.map(item => {
// 生成相对于content目录的路径用于在页面中查找文章
const fullPath = path.join(contentDir, item.name);
// 将路径转换为相对于content目录的格式并移除basePath
const relativePath = fullPath.replace(basePath, '').replace(/^[\/\\]/, '');
// 检查文件名是否与其所在目录名相同或包含目录名
const pathParts = relativePath.split(/[\/\\]/);
const fileName = pathParts[pathParts.length - 1].replace(/\.md$/, '');
// 如果文件名与目录名相同或以目录名开头,则在文件名前添加特殊前缀
if (pathParts.length > 1) {
const dirName = pathParts[pathParts.length - 2];
if (fileName === dirName || fileName.startsWith(dirName)) {
// 创建一个新的路径,在文件名前添加下划线前缀
const newFileName = `_${fileName}.md`;
const newPathParts = [...pathParts.slice(0, -1), newFileName];
return newPathParts.join('/');
}
}
return relativePath.replace(/\\/g, '/');
2025-03-03 21:16:16 +08:00
});
// 获取子目录(作为章节)
const sections: SectionStructure[] = items
.filter(item => item.isDirectory())
.map(item => {
const sectionPath = path.join(contentDir, item.name);
// 递归获取子目录的结构
const sectionContent: ContentStructure = getContentStructure(sectionPath, basePath);
// 确保路径格式正确并移除basePath
const relativePath = sectionPath.replace(basePath, '').replace(/^[\/\\]/, '');
const normalizedPath = relativePath.replace(/\\/g, '/');
2025-03-03 21:16:16 +08:00
return {
name: item.name,
path: normalizedPath,
articles: sectionContent.articles,
sections: sectionContent.sections
};
});
return { articles, sections };
}
// 4. 定义你的集合
const articles = defineCollection({
// 使用glob加载器从content目录加载所有markdown文件
loader: glob({
pattern: "**/*.md",
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字段用于标识文章所属的目录
section: z.string().optional(),
// 添加weight字段用于排序
weight: z.number().optional(),
}),
});
// 6. 导出一个 `collections` 对象来注册你的集合
export const collections = { articles };
2025-03-03 21:16:16 +08:00
// 7. 导出内容结构,可以在构建时使用
export const contentStructure = getContentStructure();