Hexo文章头的识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/**
* 解析 Hexo Front-Matter YAML 字符串为 JavaScript 对象
* 严格遵循 Hexo 官方 Front-Matter 规范
* @param {string} yamlStr - 纯 YAML 字符串(不含 --- 分隔符)
* @returns {object} 解析后的 JS 对象
*/
function hexoFrontMatterToJSON(yamlStr) {
// 处理空值
if (!yamlStr || typeof yamlStr !== 'string') {
return {};
}

const result = {};
// 按行拆分,过滤空行、注释行
const lines = yamlStr
.split(/\r?\n/)
.map(line => line.trimEnd()) // 保留行前缩进,只去掉末尾空格
.filter(line => {
const trimmed = line.trim();
return trimmed !== '' && !trimmed.startsWith('#');
});

let currentKey = null;
let currentIndent = 0;

for (const line of lines) {
// 匹配缩进(用于多行/数组)
const indentMatch = line.match(/^(\s+)/);
const indent = indentMatch ? indentMatch[1].length : 0;

// 匹配 key: value 格式
const keyValueMatch = line.match(/^\s*([^:#]+?)\s*:\s*(.*)/);

if (keyValueMatch) {
const key = keyValueMatch[1].trim();
let value = keyValueMatch[2].trim();

// —————————————————— 类型解析 ——————————————————
// 布尔值
if (value === 'true' || value === 'false') {
value = value === 'true';
}
// 数字
else if (!isNaN(value) && value !== '') {
value = Number(value);
}
// 空值
else if (value === '' || value === 'null') {
value = null;
}
// 字符串(带引号自动去除)
else {
value = value.replace(/^["']|["']$/g, '');
}

result[key] = value;
currentKey = key;
currentIndent = indent;
}

// —————————————————— 数组解析(- 开头)——————————————————
else if (line.trim().startsWith('-')) {
const arrayValue = line.replace(/^\s*-\s*/, '').replace(/^["']|["']$/g, '');

if (!Array.isArray(result[currentKey])) {
result[currentKey] = [];
}
result[currentKey].push(arrayValue);
}

// —————————————————— 多行文本解析(缩进 > 当前)——————————————————
else if (indent > currentIndent && currentKey) {
const text = line.trim();
if (text) {
result[currentKey] = (result[currentKey] || '') + ' ' + text;
}
}
}

return result;
}