import type { MarkedExtension, Tokens } from "marked"; /** * 将表格数据转换为 CSV 格式字符串 * @param headers 表头数组 * @param rows 表格数据行 * @returns CSV 格式字符串 */ function tableToCSV(headers: string[], rows: string[][]): string { const escapeCell = (cell: string) => { // 如果单元格包含逗号、换行或引号,需要转义 if ( cell.includes(",") || cell.includes("\n") || cell.includes('"') || cell.includes("#") ) { return `"${cell.replace(/"/g, '""')}"`; } return cell; }; const headerLine = headers.map(escapeCell).join(","); const dataLines = rows.map((row) => row.map(escapeCell).join(",")); return [headerLine, ...dataLines].join("\n"); } export default function markedTable(): MarkedExtension { return { renderer: { table(token: Tokens.Table) { const header = token.header; let roll = ""; let remix = ""; // Spark tables (dice-formula first column) are handled upstream by // `coerceSparkTables` in the content registry — they're rewritten to // `:md-table` directives before rendering. This renderer only handles // label-based tables (md-table-label / md-roll-label / md-remix-label). const labelIndex = header.findIndex((cell) => { if (cell.text === "md-roll-label") { roll = " roll=true"; return true; } else if (cell.text === "md-remix-label") { roll = " roll=true remix=true"; return true; } return cell.text === "md-table-label" || cell.text === "label"; }); // 默认表格渲染 - 使用 marked 默认行为 if (labelIndex === -1) return false; const headers = token.header.map((cell) => cell.text); headers[labelIndex] = "label"; const rows = token.rows.map((row) => row.map((cell) => cell.text)); if (header.findIndex((header) => header.text === "body") < 0) { // 收集所有非 label 列的表头 const bodyColumns = headers.filter((cell) => cell !== "label"); // 构建 body 列的模板:**列名**:{{列名}}\n\n const bodyTemplate = bodyColumns .map((col) => `**${col}**:{{${col}}}`) .join("\n\n"); headers.push("body"); rows.forEach((row) => { row.push(bodyTemplate); }); } // 生成 CSV 数据 const csvData = tableToCSV(headers, rows); // 渲染为 md-table 组件,内联 CSV 数据 // data-spark attribute is injected by the CLI directive scanner, // not computed here. return `${csvData}\n`; }, }, }; }