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