mirror of
https://github.com/dreammis/social-auto-upload.git
synced 2026-10-31 03:02:39 +08:00
57 lines
2.9 KiB
JavaScript
57 lines
2.9 KiB
JavaScript
/** 只执行本地 Markdown 渲染;stdin/stdout 使用 JSON,正文不经过命令行参数。 */
|
|
import { renderMarkdownDocument, resolveMarkdownStyle } from 'baoyu-md';
|
|
import { Marked } from 'marked';
|
|
import { load } from 'cheerio';
|
|
|
|
let input = '';
|
|
// 按字符解码流,避免长中文稿跨 Buffer 边界时出现替换字符。
|
|
process.stdin.setEncoding('utf8');
|
|
for await (const chunk of process.stdin) input += chunk;
|
|
try {
|
|
const { content, theme } = JSON.parse(input);
|
|
const markdown = new Marked({ gfm: true });
|
|
// 图表扩展会产生 SVG 或远程图片,不作为普通代码块静默降级。
|
|
const tokens = markdown.lexer(content);
|
|
markdown.walkTokens(tokens, token => {
|
|
if (token.type === 'html' && /<\/?(?:script|style|link|iframe|object|embed)\b/i.test(token.raw)) {
|
|
throw Error('Markdown 内嵌 HTML 不支持脚本、外部资源或样式表,请使用内联样式');
|
|
}
|
|
if (token.type === 'code' && /^(mermaid|plantuml|infographic)(\s|$)/i.test(token.lang || '')) {
|
|
throw Error(`暂不支持 ${token.lang} 图表,请先导出为本地图片`);
|
|
}
|
|
});
|
|
if (theme === 'plain') {
|
|
process.stdout.write(JSON.stringify({ html: markdown.parser(tokens), metadata: {} }));
|
|
} else {
|
|
// 显式给出全部选项,避免上游读取用户的 EXTEND.md 后改变本项目的默认排版。
|
|
const style = resolveMarkdownStyle({ theme });
|
|
const result = await renderMarkdownDocument(content, {
|
|
theme, primaryColor: style.primaryColor, fontFamily: style.fontFamily,
|
|
fontSize: style.fontSize, codeTheme: 'github', countStatus: false,
|
|
citeStatus: false, keepTitle: true, legend: 'none',
|
|
isMacCodeBlock: false, isShowLineNumber: false, themeMode: 'light',
|
|
});
|
|
// 上游即使关闭 Mac 代码装饰也保留隐藏 SVG;只移除该装饰,不删除用户正文元素。
|
|
const $ = load(result.html);
|
|
$('pre.code__pre > span.mac-sign').remove();
|
|
// 上游用正文里的“1. / • ”和 display:block 模拟列表;平台清理 CSS 后会显示双重序号。
|
|
// 恢复原生列表语义,保留其余主题样式,使本地和过滤样式后的平台都只显示一次标记。
|
|
$('ol,ul').each((_, list) => {
|
|
const ordered = list.tagName === 'ol';
|
|
$(list).children('li.listitem').each((index, item) => {
|
|
const first = item.children[0];
|
|
if (first?.type !== 'text') return;
|
|
const prefix = first.data.match(ordered ? /^(\d+)\. / : /^• /);
|
|
if (!prefix) return;
|
|
if (ordered && index === 0 && Number(prefix[1]) !== 1) $(list).attr('start', prefix[1]);
|
|
first.data = first.data.slice(prefix[0].length);
|
|
$(item).attr('style', ($(item).attr('style') || '').replace(/display:\s*block\s*;/, 'display: list-item;'));
|
|
});
|
|
});
|
|
process.stdout.write(JSON.stringify({ html: $.html(), metadata: result.meta }));
|
|
}
|
|
} catch (error) {
|
|
process.stderr.write(error.stack || String(error));
|
|
process.exitCode = 1;
|
|
}
|