feat: v0.4.0 — 安全修复×6 + 增量性能×3 + Playwright E2E + 实例级 i18n
CI / test-parser (push) Successful in 9m27s
CI / test-core (push) Successful in 9m34s
CI / test-rest (push) Successful in 9m27s
CI / e2e (push) Failing after 5m15s
CI / verify (20.x) (push) Successful in 9m54s
CI / verify (18.x) (push) Successful in 9m56s
CI / verify (24.x) (push) Successful in 9m48s

0.3.0 修复版:
- 脚注 id 属性注入 XSS 防护(行内引用 + 脚注区)
- 全局插件注入移至 afterCreate(searchReplace 等依赖 textarea 的插件真正生效)+ use() 同名防重
- 行内渲染缓存附加 refs 指纹,防跨文档引用链接串数据
- 白名单裸标签 <u>/</u> 透传,underline(Ctrl+U)预览可见
- getStatus()/渲染 env 使用实例 locale;replaceAll 替换文本按字面处理
- replaceAllRegex 保留 $1 捕获组语义

0.3.1 性能版:
- 高亮规则按语言缓存(registerLanguage 覆盖失效),bench +33%
- 统计增量计算:字数/词数/行数差异区间,击键零全量扫描
- outline 树构建 O(n²) → 迭代栈 O(n)
- 拖放图片 >500KB 拦截、scrollToLine 真实行高、undo/redo 光标恢复
- selectionChange/cursorMove 与浮动工具栏解耦、unregisterShortcut 大小写归一
- toast 接入 7 种 ANIMATIONS、实例主题订阅随 destroy 断开(dispose)

0.4.0 工程版:
- Playwright 真实浏览器冒烟测试(e2e/,22 项断言)+ CI e2e job
- sideEffects: false 便于 tree-shaking
- MeEditor.destroy() 全面复位(全局钩子清理 + 内部注入钩子重建)
- 实例 locale 统一作用于全部 UI 文案(状态栏/工具栏/右键菜单/大纲)

测试 782 → 826,全绿;typecheck/lint/build 通过
This commit is contained in:
2026-08-09 12:31:14 +08:00
parent 64aedb9546
commit cb0caff4cf
24 changed files with 1122 additions and 129 deletions
+30 -16
View File
@@ -2,9 +2,9 @@
> TypeScript 重构 · 零运行时依赖 · 轻量级桌面端 Markdown Editor 库
[![version](https://img.shields.io/badge/version-0.2.5-blue)](https://git.metona.cn/MetonaTeam/-/packages/npm/@metona-team%2Fmetona-editor)
[![version](https://img.shields.io/badge/version-0.4.0-blue)](https://git.metona.cn/MetonaTeam/-/packages/npm/@metona-team%2Fmetona-editor)
[![license](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
[![tests](https://img.shields.io/badge/tests-780%20passed-brightgreen)](./tests)
[![tests](https://img.shields.io/badge/tests-826%20passed-brightgreen)](./tests)
[![coverage](https://img.shields.io/badge/coverage-parser%2099%25-brightgreen)](./tests)
[![types](https://img.shields.io/badge/types-TypeScript%20strict-blue)](./tsconfig.json)
@@ -18,8 +18,10 @@
- **主题系统** — light / dark / warm / autoCSS 变量可定制,实例级隔离,主题继承,外部跟随
- **国际化** — zh-CN / en-US / ja / ko / fr / de 六种语言完整翻译,实例级语言隔离,远程加载翻译包
- **编辑体验** — 行号装订线、智能 Enter、括号自动闭合、拖放文件、大纲面板、Zen 专注模式、右键上下文菜单
- **内置语法高亮** — 零依赖轻量高亮器,js/ts/python/bash/css/html 等 13 种语言`MeEditor.highlight` 即插即用
- **安全** — HTML 转义,XSS 协议过滤(javascript / vbscript / file / data),属性级注入防护,sanitize 钩子
- **内置语法高亮** — 零依赖轻量高亮器,js/ts/python/bash/css/html 等 16 个语言标识,规则缓存零重建开销`MeEditor.highlight` 即插即用
- **性能** — 字数 / 词数 / 行数差异增量统计,击键零全量扫描;大纲 O(n) 线性构建,长文档即时响应
- **安全** — HTML 转义,XSS 协议过滤(javascript / vbscript / file / data),属性级注入防护(含脚注 id),白名单裸标签透传,sanitize 钩子
- **工程化** — 826 个单元测试 + Playwright 真实浏览器冒烟测试,CI 并行 4 job,`sideEffects: false` 便于 tree-shaking
- **桌面端优先** — 纯电脑端设计,无移动端冗余代码
- **引用链接** — 支持 `[text][ref]` / `![alt][ref]` 引用式链接和图片,含 title 属性
- **RTL 支持** — 完整的从右到左布局适配(阿拉伯语、希伯来语等)
@@ -77,10 +79,10 @@ const editor = MeEditor.create('#editor', { mode: 'split' });
```html
<!-- jsDelivr CDN (推荐) -->
<script src="https://cdn.jsdelivr.net/npm/@metona-team/metona-editor@0.2.5/dist/metona-editor.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@metona-team/metona-editor@0.4.0/dist/metona-editor.min.js"></script>
<!-- unpkg CDN -->
<script src="https://unpkg.com/@metona-team/metona-editor@0.2.5/dist/metona-editor.min.js"></script>
<script src="https://unpkg.com/@metona-team/metona-editor@0.4.0/dist/metona-editor.min.js"></script>
<!-- Gitea 源 -->
<script src="https://git.metona.cn/MetonaTeam/MetonaEditor/raw/branch/master/dist/metona-editor.js"></script>
@@ -122,6 +124,7 @@ MeEditor.create(container, {
// 内容
value: '', // 初始 Markdown 文本
placeholder: '', // 占位符
id: '', // 实例 id(默认自动生成,用于分隔条/草稿等存储键隔离)
// 视图
mode: 'split', // 'edit' | 'split' | 'preview'
@@ -256,6 +259,7 @@ editor.exec(action: string): this
// h1 h2 h3 quote ul ol hr
// link image table
// indent outdent undo redo
// formatTable(光标所在表格按列宽对齐)
// edit split preview fullscreen
// zen wordwrap
```
@@ -352,8 +356,8 @@ editor.isWordWrap(): boolean
```typescript
editor.toast(message: string, opts?: {
type?: 'success' | 'error' | 'warning' | 'info';
duration?: number;
animation?: 'fade' | 'slide' | 'scale';
duration?: number; // ms0 = 不自动消失
animation?: 'fade' | 'slide' | 'scale' | 'bounce' | 'flip' | 'rotate' | 'zoom';
}): this
```
@@ -397,7 +401,7 @@ MeEditor.setLocale('en-US')
MeEditor.getStatus()
// => { version, theme, locale, globalPlugins, presetPlugins }
// 销毁全局资源
// 销毁全局资源(主题/语言监听、全局插件、全局钩子全面复位)
MeEditor.destroy()
// 解析器独立使用
@@ -465,6 +469,7 @@ import { highlight, registerLanguage, getSupportedLanguages } from '@metona-team
| 粗斜体 | `***text***` `___text___` | `<em><strong>` |
| 斜体 | `*italic*` `_italic_` | `<em>` |
| 删除线 | `~~text~~` | `<del>` |
| 下划线 | `<u>text</u>`(裸标签透传) | `<u>` |
| 高亮 | `==text==` | `<mark>` |
| 上标 | `x^2^` | `<sup>` |
| 下标 | `H~2~O` | `<sub>` |
@@ -482,6 +487,7 @@ import { highlight, registerLanguage, getSupportedLanguages } from '@metona-team
| 图片 | `![alt](url)` | `<img>` |
| 引用链接 | `[text][ref]` + `[ref]: url` | `<a>` |
| 引用图片 | `![alt][ref]` + `[ref]: url` | `<img>` |
| 表格格式化 | `exec('formatTable')` | 光标所在表格按列宽对齐 |
| 自动链接 | `<https://...>` | `<a>` |
| 数学公式 | `$E=mc^2$` `$$\int$$` | `<span>` / `<div>` |
| 脚注 | `text[^1]` | `<sup>` + 底部定义 |
@@ -532,7 +538,8 @@ registerBlockHandler({
- 所有文本经 `escapeHTML` 转义
- 链接 URL 过滤 `javascript:` / `vbscript:` / `file:` / 非图片 `data:`
- 属性级注入防护:`href` / `src` / `alt` / `title` / `language-*` 引号与换行均被转义
- 属性级注入防护:`href` / `src` / `alt` / `title` / 脚注 id / `language-*` 引号与换行均被转义
- 白名单裸标签透传:仅 `<u>` / `</u>`(无属性),带属性的标签仍被转义
- `data:image` 限制最大 500KB
- `sanitize` 钩子供外部净化(如 DOMPurify)
- `highlight` 钩子异常自动回退为纯文本
@@ -666,6 +673,9 @@ editor.getThemeContext().adopt();
// 跟随外部元素
editor.getThemeContext().syncWithElement(document.body);
// 手动断开所有外部跟随订阅(实例 destroy 时也会自动断开)
editor.getThemeContext().dispose();
```
---
@@ -749,6 +759,9 @@ npm test
# 测试覆盖率
npm run test -- --coverage
# 浏览器冒烟测试(Playwright + Chromium,需先构建)
npm run build && npm run test:e2e
# 性能基准(先构建再运行)
npm run build && npm run bench
@@ -760,11 +773,11 @@ npm run format
```
dist/
├── metona-editor.js UMD(浏览器直接引入)
├── metona-editor.min.js UMD 压缩版(CDN
├── metona-editor.mjs ES Module
├── metona-editor.cjs CommonJS
└── metona-editor.d.ts TypeScript 类型声明
├── metona-editor.js UMD(浏览器直接引入)
├── metona-editor.min.js UMD 压缩版(CDN
├── metona-editor.mjs ES Module
├── metona-editor.cjs CommonJS
└── metona-editor.d.ts TypeScript 类型声明
```
### 源码结构
@@ -796,7 +809,8 @@ src/
|------|------|
| 语言 | TypeScript 5 (strict) |
| 构建 | Rollup 3 |
| 测试 | Jest 29 + jsdom |
| 测试 | Jest 29 + jsdom826 用例) |
| 浏览器冒烟 | Playwright + Chromium22 项断言) |
| 类型生成 | rollup-plugin-dts |
| 零运行时依赖 | ✅ |