feat: 新增 Hugo Markdown 头部元数据解析器
This commit is contained in:
1 parent
c8a95499f7
commit
60caa08228
1 file changed
+125
@@ -0,0 +1,125 @@
|
||||
import { parse as parseToml } from 'smol-toml';
|
||||
import { parse as parseYaml } from 'yaml';
|
||||
|
||||
export interface HugoPostData {
|
||||
title: string;
|
||||
slug: string;
|
||||
content: string;
|
||||
cover: string;
|
||||
published_at: string;
|
||||
updated_at: string;
|
||||
categories: string[];
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析并转换时间,一旦解析失败或空缺,严格使用当前系统时间
|
||||
*/
|
||||
function parseOrCurrentDate(val: unknown): string {
|
||||
if (!val) return new Date().toISOString();
|
||||
try {
|
||||
const dateObj = new Date(val as string | number | Date);
|
||||
if (isNaN(dateObj.getTime())) {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
return dateObj.toISOString();
|
||||
} catch {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清洗分类和标签列表(同时支持数组与单字符格式)
|
||||
*/
|
||||
function parseArrayField(val: unknown): string[] {
|
||||
if (Array.isArray(val)) {
|
||||
return val.map((item) => String(item).trim()).filter(Boolean);
|
||||
}
|
||||
if (typeof val === 'string' && val.trim() !== '') {
|
||||
return [val.trim()];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 去掉文件名结尾的格式拓展名,提供纯文件名基础
|
||||
*/
|
||||
function stripExtension(filename: string): string {
|
||||
return filename.replace(/\.(md|markdown|txt)$/i, '').trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hugo Markdown 头部元数据及正文解析器
|
||||
* 同时原生兼容 TOML (+++) 与 YAML (---) 语法,自动过滤并丢弃本项目无端对应参数
|
||||
*/
|
||||
export function parseHugoMarkdown(rawContent: string, fileName = ''): HugoPostData {
|
||||
// 剔除可能存在的 UTF-8 BOM
|
||||
const text = rawContent.replace(/^\uFEFF/, '').trim();
|
||||
const defaultName = fileName ? stripExtension(fileName) : '新文章';
|
||||
|
||||
let meta: Record<string, unknown> = {};
|
||||
let bodyContent = text;
|
||||
|
||||
try {
|
||||
// 1. 优先检识 TOML (以 +++ 开头包络)
|
||||
if (text.startsWith('+++')) {
|
||||
const endIndex = text.indexOf('\n+++', 3);
|
||||
if (endIndex !== -1) {
|
||||
const frontmatter = text.substring(3, endIndex).trim();
|
||||
bodyContent = text.substring(endIndex + 4).trim();
|
||||
meta = parseToml(frontmatter) as Record<string, unknown>;
|
||||
}
|
||||
}
|
||||
// 2. 检识常规 YAML (以 --- 开头包络)
|
||||
else if (text.startsWith('---')) {
|
||||
const endIndex = text.indexOf('\n---', 3);
|
||||
if (endIndex !== -1) {
|
||||
const frontmatter = text.substring(3, endIndex).trim();
|
||||
bodyContent = text.substring(endIndex + 4).trim();
|
||||
meta = parseYaml(frontmatter) as Record<string, unknown>;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('解析 Markdown 头部参数时出现异常,直接回货底稿正文并对时差退回初始系统时刻:', err);
|
||||
// 当解析受卡出错,空字典依然不阻挠对下方日期与稿费正文字面的顺利捕拾
|
||||
}
|
||||
|
||||
// 取值并完成映射:除以下对应参数处,诸如 draft/twemoji/lightgallery 等全在空载转储里随水蒸丢!
|
||||
const title = typeof meta.title === 'string' && meta.title.trim() !== '' ? meta.title.trim() : defaultName;
|
||||
|
||||
let slug = typeof meta.slug === 'string' ? meta.slug.trim() : '';
|
||||
if (!slug) {
|
||||
// 没有声明明确的 slug 时,由名目精简转化出基础链接字符串
|
||||
slug = defaultName
|
||||
.toLowerCase()
|
||||
.replace(/[^\w\u4e00-\u9fa5\s-]/g, '')
|
||||
.replace(/\s+/g, '-');
|
||||
if (!slug) {
|
||||
slug = 'post-' + Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
// 特色图片字段识别(首挑 featuredImage,也兼备处理 cover/image)
|
||||
const cover = typeof meta.featuredImage === 'string'
|
||||
? meta.featuredImage.trim()
|
||||
: typeof meta.cover === 'string'
|
||||
? meta.cover.trim()
|
||||
: typeof meta.image === 'string'
|
||||
? meta.image.trim()
|
||||
: '';
|
||||
|
||||
// 时间字段转化:严格按需求约定“一旦转换错漏或是全无,采用目前时刻填满”
|
||||
const published_at = parseOrCurrentDate(meta.date || meta.published_at);
|
||||
const updated_at = parseOrCurrentDate(meta.lastmod || meta.updated_at || meta.date || meta.published_at);
|
||||
|
||||
return {
|
||||
title: title || '无名小贴',
|
||||
slug,
|
||||
content: bodyContent,
|
||||
cover,
|
||||
published_at,
|
||||
updated_at,
|
||||
categories: parseArrayField(meta.categories || meta.category),
|
||||
tags: parseArrayField(meta.tags || meta.tag)
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user