import { parse } from 'csv-parse/browser/esm/sync'; import yaml from 'js-yaml'; import { getIndexedData } from '../../data-loader/file-index'; /** * 解析 front matter * @param content 包含 front matter 的内容 * @returns 解析结果,包含 front matter 和剩余内容 */ function parseFrontMatter(content: string): { frontmatter?: JSONObject; remainingContent: string } { // 分割内容 const parts = content.trim().split(/(?:^|\n)---\s*\n/g); // 至少需要三个部分:空字符串、front matter、剩余内容 if (parts.length !== 3 || parts[0] !== '') { return { remainingContent: content }; } try { // 解析 YAML front matter const frontmatterStr = parts[1].trim(); const frontmatter = yaml.load(frontmatterStr) as JSONObject; // 剩余内容是第三部分及之后的所有内容 const remainingContent = parts.slice(2).join('---\n').trimStart(); return { frontmatter, remainingContent }; } catch (error) { console.warn('Failed to parse front matter:', error); return { remainingContent: content }; } } /** * 检测字符串是否是 CSV 格式 * @param str 待检测的字符串 * @returns 如果是 CSV 格式返回 true */ export function isCSV(str: string): boolean { const trimmed = str.trim(); // 检查是否以 YAML front matter 开头 if (trimmed.startsWith('---\n') || trimmed.startsWith('---\r\n')) { return true; } // 检查是否包含 CSV 特征:多行且有分隔符 const lines = trimmed.split(/\r?\n/).filter(line => line.trim() !== ''); if (lines.length < 2) { return false; } // 检测常见 CSV 分隔符 const separators = [',', '\t', ';', '|']; const firstLine = lines[0]; for (const sep of separators) { if (firstLine.includes(sep)) { // 检查其他行是否也有相同的分隔符 const hasSeparatorInOtherLines = lines.slice(1).some(line => line.includes(sep)); if (hasSeparatorInOtherLines) { return true; } } } return false; } /** * 解析 CSV 字符串 * @template T 返回数据的类型,默认为 Record * @param csvString CSV 字符串内容 * @param sourcePath 源路径标识(用于标记数据来源) * @returns 解析后的 CSV 数据 */ export function parseCSVString>(csvString: string, sourcePath: string = 'inline'): CSV { // 解析 front matter const { frontmatter, remainingContent } = parseFrontMatter(csvString); const records = parse(remainingContent, { columns: true, comment: '#', trim: true, skipEmptyLines: true, bom: true }); const result = records as Record[]; // 添加 front matter 到结果中 const csvResult = result as CSV; if (frontmatter) { csvResult.frontmatter = frontmatter; for(const each of result){ Object.assign(each, frontmatter); } } csvResult.sourcePath = sourcePath; return csvResult; } /** * 加载 CSV 文件 * @template T 返回数据的类型,默认为 Record * @param pathOrContent 文件路径或 inline CSV 字符串 * @returns 解析后的 CSV 数据 */ export async function loadCSV>(pathOrContent: string): Promise> { // 检测是否是 inline CSV 数据 if (isCSV(pathOrContent)) { return parseCSVString(pathOrContent, 'inline'); } // 从索引获取文件内容 const content = await getIndexedData(pathOrContent); return parseCSVString(content, pathOrContent); } type JSONData = JSONArray | JSONObject | string | number | boolean | null; interface JSONArray extends Array {} interface JSONObject extends Record {} export type CSV = T[] & { frontmatter?: JSONObject; sourcePath: string; } export function processVariables (body: string, currentRow: T, csv: CSV, filtered?: T[], remix?: boolean): string { const rolled = filtered || csv; function replaceProp(key: string) { const row = remix ? rolled[Math.floor(Math.random() * rolled.length)] : currentRow; const frontMatter = csv.frontmatter; if(key in row) return row[key]; if(frontMatter && key in frontMatter) return frontMatter[key]; return `{{${key}}}`; } return body?.replace(/\{\{([^}]+)\}\}/g, (_, key) => `${replaceProp(key)}`) || ''; }