diff --git a/src/markdown/index.ts b/src/markdown/index.ts index 8072063..af78697 100644 --- a/src/markdown/index.ts +++ b/src/markdown/index.ts @@ -1,8 +1,9 @@ -import { Marked, type MarkedExtension, type Tokens } from 'marked'; +import { Marked, type MarkedExtension } from 'marked'; import {createDirectives, presetDirectiveConfigs} from 'marked-directive'; import yaml from 'js-yaml'; import markedAlert from "marked-alert"; import markedMermaid from "./mermaid"; +import markedTable from "./table"; import {gfmHeadingId} from "marked-gfm-heading-id"; let globalIconPrefix: string | undefined = undefined; @@ -15,32 +16,12 @@ function overrideIconPrefix(path?: string){ } } -/** - * 将表格数据转换为 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('"')) { - 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'); -} - // 使用 marked-directive 来支持指令语法 const marked = new Marked() .use(gfmHeadingId()) .use(markedAlert()) .use(markedMermaid()) + .use(markedTable()) .use(createDirectives([ ...presetDirectiveConfigs, { @@ -121,36 +102,6 @@ const marked = new Marked() }] }); -// 覆盖默认的 table renderer 以支持自动转换 -marked.use({ - renderer: { - table(token: Tokens.Table) { - // 检查表头是否包含 md-table-label - const header = token.header; - const labelIndex = header.findIndex(cell => cell.text === 'md-table-label'); - - if (labelIndex !== -1) { - // 将 md-table-label 列转换为 label - const headers = header.map(cell => cell.text === 'md-table-label' ? 'label' : cell.text); - - // 转换所有行 - const rows = token.rows.map(row => - row.map(cell => cell.text) - ); - - // 生成 CSV 数据 - const csvData = tableToCSV(headers, rows); - - // 渲染为 md-table 组件,内联 CSV 数据 - return `${csvData}\n`; - } - - // 默认表格渲染 - 使用 marked 默认行为 - return false; - } - } -} as MarkedExtension); - export function parseMarkdown(content: string, iconPrefix?: string): string { using prefix = overrideIconPrefix(iconPrefix); return marked.parse(content.trimStart()) as string; diff --git a/src/markdown/table.ts b/src/markdown/table.ts new file mode 100644 index 0000000..effbd7a --- /dev/null +++ b/src/markdown/table.ts @@ -0,0 +1,65 @@ +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('"')) { + 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) { + // 检查表头是否包含 md-table-label + const header = token.header; + const labelIndex = header.findIndex(cell => cell.text === 'md-table-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 = header + .filter(cell => cell.text !== 'md-table-label') + .map(cell => cell.text); + + // 构建 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 数据 + console.log(csvData) + return `${csvData}\n`; + } + } + }; +}