refactor: md-table body column gen

This commit is contained in:
hypercross 2026-03-21 18:30:40 +08:00
parent 617fd17def
commit 0261061bcb
2 changed files with 68 additions and 52 deletions

View File

@ -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 `<md-table>${csvData}</md-table>\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;

65
src/markdown/table.ts Normal file
View File

@ -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 `<md-table>${csvData}</md-table>\n`;
}
}
};
}