- 新增 src/highlight.ts 零依赖轻量高亮器:13 种语言,先转义后 着色保证安全,输出 me-hl-* 类,MeEditor.highlight 即插即用 - exportTool 新增 editor.exportPDF()(隐藏 iframe + window.print), exportHTML/exportPDF 共用 HTML 构建逻辑 - imagePaste 支持 maxSizeKB 限制,超限 toast 警告 - npm run bench 性能基准:parseMarkdown 1MB ≈ 51ms - CI 测试拆分为 parser/core/rest 三个并行 job + 跨版本 verify job - docs.html/README 补齐新 API、事件、高亮文档;取消跟踪 dist 产物; 版本 0.2.4 → 0.2.5,测试 725 → 768
112 lines
2.8 KiB
JavaScript
112 lines
2.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* MetonaEditor 性能基准 — 解析器吞吐测试
|
|
* 用法: npm run build && npm run bench
|
|
*/
|
|
|
|
const MeEditor = require('../dist/metona-editor.cjs');
|
|
const { parseMarkdown } = MeEditor;
|
|
|
|
const buildSample = (kb) => {
|
|
const chunk = [
|
|
'# 性能基准文档',
|
|
'',
|
|
'这是一个 **粗体** 与 *斜体* 的段落,包含 `inline code` 和 [链接](https://example.com)。',
|
|
'',
|
|
'- 列表项一',
|
|
'- 列表项二',
|
|
' - 嵌套项',
|
|
' - 嵌套项二',
|
|
'- 列表项三',
|
|
'',
|
|
'1. 有序一',
|
|
'2. 有序二',
|
|
'',
|
|
'> 引用内容',
|
|
'> 继续引用',
|
|
'',
|
|
'```js',
|
|
'function hello(name) {',
|
|
' const msg = `Hello, ${name}!`;',
|
|
' return msg;',
|
|
'}',
|
|
'```',
|
|
'',
|
|
'| 列一 | 列二 | 列三 |',
|
|
'| --- | :---: | ---: |',
|
|
'| A | B | C |',
|
|
'| D | E | F |',
|
|
'',
|
|
'---',
|
|
'',
|
|
'引用链接:[文档][ref]',
|
|
'',
|
|
'[ref]: https://example.com/docs "参考"',
|
|
'',
|
|
'脚注示例[^1]',
|
|
'',
|
|
'[^1]: 脚注内容',
|
|
'',
|
|
'## 二级标题',
|
|
'',
|
|
'$$E = mc^2$$',
|
|
'',
|
|
'H~2~O 与 x^2^,还有 :rocket: emoji。',
|
|
'',
|
|
].join('\n');
|
|
const repeat = Math.max(1, Math.ceil((kb * 1024) / chunk.length));
|
|
return chunk.repeat(repeat);
|
|
};
|
|
|
|
const median = (arr) => {
|
|
const s = [...arr].sort((a, b) => a - b);
|
|
const mid = Math.floor(s.length / 2);
|
|
return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
|
|
};
|
|
|
|
const benchmark = (label, fn, iterations = 5) => {
|
|
// warmup
|
|
fn();
|
|
const times = [];
|
|
for (let i = 0; i < iterations; i++) {
|
|
const t0 = process.hrtime.bigint();
|
|
fn();
|
|
const t1 = process.hrtime.bigint();
|
|
times.push(Number(t1 - t0) / 1e6);
|
|
}
|
|
const ms = median(times);
|
|
return { label, ms: ms.toFixed(1), opsPerSec: (1000 / ms).toFixed(1) };
|
|
};
|
|
|
|
console.log('MetonaEditor 性能基准');
|
|
console.log('=====================');
|
|
console.log(`版本: ${MeEditor.VERSION}`);
|
|
console.log('');
|
|
|
|
for (const size of [64, 256, 1024]) {
|
|
const sample = buildSample(size);
|
|
const r = benchmark(`parseMarkdown ${size}KB (${sample.length.toLocaleString()} chars)`, () => {
|
|
const html = parseMarkdown(sample);
|
|
if (!html) throw new Error('empty output');
|
|
});
|
|
console.log(` ${r.label}: ${r.ms} ms (${r.opsPerSec} ops/s)`);
|
|
}
|
|
|
|
console.log('');
|
|
|
|
// 语法高亮基准
|
|
try {
|
|
const code = Array.from({ length: 200 }, (_, i) =>
|
|
`function f${i}(a, b) { const s = "str${i}"; // comment\n return a + b + ${i}; }`,
|
|
).join('\n');
|
|
const r = benchmark('highlight 200 functions (js)', () => {
|
|
MeEditor.highlight(code, 'js');
|
|
});
|
|
console.log(` ${r.label}: ${r.ms} ms (${r.opsPerSec} ops/s)`);
|
|
} catch (_) {
|
|
console.log(' (highlight benchmark skipped)');
|
|
}
|
|
|
|
console.log('');
|
|
console.log('完成。');
|