Files
ink-note-app/electron/frontmatter.cjs
T
Your NameandClaude Sonnet 5.5 313c41f867 feat(electron): main process, vault, search index, links, import, capture
Vault on plain .md files with FTS5 index, link rewriting, canvas files,
history/trash/backup, link previews, image fetch, tasks and unlinked
mentions, Notion/Obsidian importer, quick-capture window and shortcut.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 11:57:09 +02:00

69 lines
2.3 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
// Minimal YAML front matter: flat `key: value`, inline `[a, b]` lists and `- item` lists.
// Unknown keys are kept and written back, so notes from Obsidian survive a round trip.
const FM = /^?---\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/;
function unquote(v) {
v = v.trim();
if (v.length >= 2 && v.startsWith('"') && v.endsWith('"')) {
try { return JSON.parse(v); } catch { return v.slice(1, -1); }
}
if (v.length >= 2 && v.startsWith("'") && v.endsWith("'")) return v.slice(1, -1).replace(/''/g, "'");
return v;
}
function parseFrontMatter(raw) {
const m = raw.match(FM);
if (!m) return { meta: {}, body: raw };
const meta = {};
let last = null;
for (const line of m[1].split(/\r?\n/)) {
const kv = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
if (kv) {
last = kv[1];
const v = kv[2].trim();
if (v.startsWith('[') && v.endsWith(']')) {
meta[last] = v.slice(1, -1).split(',').map(unquote).filter(Boolean);
} else {
meta[last] = v === '' ? [] : unquote(v);
}
continue;
}
const item = line.match(/^\s*-\s+(.*)$/);
if (item && last) {
if (!Array.isArray(meta[last])) meta[last] = [];
meta[last].push(unquote(item[1]));
}
}
// An empty `key:` with no list items below it is just an empty string.
for (const k of Object.keys(meta)) if (Array.isArray(meta[k]) && !meta[k].length && k !== 'tags') meta[k] = '';
return { meta, body: raw.slice(m[0].length).replace(/^\r?\n/, '') };
}
function quote(s) {
s = String(s);
return /^[\p{L}\p{N}_ .\/@#+-]+$/u.test(s) && s.trim() === s && !/^[-#@]/.test(s) ? s : JSON.stringify(s);
}
const ORDER = ['icon', 'cover', 'tags', 'created'];
function serializeFrontMatter(meta = {}) {
const keys = Object.keys(meta).filter((k) => {
const v = meta[k];
return v !== undefined && v !== null && v !== '' && !(Array.isArray(v) && !v.length);
});
if (!keys.length) return '';
keys.sort((a, b) => {
const ia = ORDER.indexOf(a), ib = ORDER.indexOf(b);
return (ia < 0 ? 99 : ia) - (ib < 0 ? 99 : ib);
});
const lines = keys.map((k) => {
const v = meta[k];
return Array.isArray(v) ? `${k}: [${v.map(quote).join(', ')}]` : `${k}: ${quote(v)}`;
});
return `---\n${lines.join('\n')}\n---\n\n`;
}
module.exports = { parseFrontMatter, serializeFrontMatter };