资讯动态

Plate 引用块自动格式化 `> ` 回归修复:从 localhost:3000 复现到 createRuleFactory 配置默认值合并

发布时间:2026/9/16 12:02:53 来源:尧图企业网站定制
Plate 引用块自动格式化回归修复从 localhost:3000 复现到 createRuleFactory 配置默认值合并【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate导读本文以 Plate 仓库中一次真实的 bug 修复实战为主线在localhost:3000上输入后段落不再自动提升为引用块blockquote。从用户复现、逐层排查最终定位到createRuleFactory构建对象式配置规则时未把配置默认值如marker: 合并进运行时解析器输入这一深层根因并通过包级单测、core 回归单测与 app 级集成测试三层防线完成闭环。读完本文你将掌握 Plate 输入规则input rules工厂的配置合并机制、引用块作为容器节点时的wrapNodes自动格式化写法以及先写失败测试、再修根因、最后浏览器验证的完整调试方法论。问题背景在 localhost:3000 上不再提升为引用块用户明确要求在localhost:3000上做真实复现在段落开头输入时期望当前段落被自动包裹成 blockquote但实际表现是以纯文本形式留在原地段落没有被提升。这一现象与 2026-04-17-blockquote-autoformat-port-3000.md 中记录的排查结论一致这不是单纯的块类型设置问题而是横跨 core 输入规则工厂与 app 层 kit 装配两层的系统性缺陷。排查工作的三个关键约束是必须在真实浏览器环境验证不能仅凭代码推断下结论必须先补上缺失的测试覆盖再动手修复引用块此前已有一次嵌套自动格式化 bug 的历史教训见下文本次排查需要把嵌套场景一并锁定。前置知识引用块是容器自动格式化必须走 wrapNodes在分析本次根因之前必须先理解 Plate 中引用块的特殊性——它是容器节点而非可重标记retaggable的扁平块。此前的 2026-04-02-blockquote-autoformat-must-wrap-nested-quotes.md 已经记录了同样的教训当 blockquote 从扁平块演变为容器元素后通用块自动格式化路径假设目标是一个可以改type字段的块因此在根级仍然碰巧能工作依赖归一化但在已有引用块内部再输入时期望的是嵌套包装blockquote blockquote p通用路径却只生成了blockquote p加文本 hello的错误形状。当时总结的三条不可行方案至今仍有参考价值尝试过的方案为什么不行在packages/autoformat里当作通用自动格式化 bug 修包级块变换对扁平块类型的行为完全符合设计不是包的问题仅用type: KEYS.blockquote走setNodes对包装元素而言setNodes是错误操作用toggleBlock(..., { wrap: true })处理该接缝已处于引用块内部时toggle 语义可能取消包装而非嵌套最终确认的正确方案是显式声明引用块的包装语义{ allowSameTypeAbove: true, // 允许光标已在引用块内部时继续触发 format: (editor) { editor.tf.wrapNodes({ children: [], type: KEYS.blockquote }); }, match: , mode: block, type: KEYS.blockquote, }核心要点有二wrapNodes保留容器关系嵌套引用需要一个引用块包裹另一个引用块而不是一个块改变 type 字段wrapNodes(...)直接表达这种父子关系allowSameTypeAbove: true解除同类型守卫默认情况下规则在光标已经位于同类元素内部时会被拦截这个开关让根级与嵌套级都能触发。本次在 3000 端口的回归正是在这段历史教训的背景下被排查的不能想当然认为又是 blockquote 规则本身的问题需要先验证规则配置是否被正确传递。排查路径从包级单测缺口到 app 级集成测试本次排查首先明确了已有的覆盖边界包级单测已存在packages/basic-nodes/src/lib/BaseBlockquoteInputRules.spec.tsx中已有两条用例——根级包裹段落为 blockquote以及已处于引用块内部时嵌套包装为blockquote blockquote p且断言了光标最终落在新引用块内的hello文本开头selection 为path: [0, 0, 0, 0]。这说明 basic-nodes 包层面的自动格式化行为本身是经过验证的。真正缺失的是 app 级shipped kit覆盖BasicBlocksKit是apps/www中把BlockquotePlugin、HeadingRules、HorizontalRuleRules等装配起来的真实分发面见 basic-blocks-kit.tsx而当时没有任何测试在该 kit 表面上验证提升行为。因此本次新增了 app 级集成测试 basic-blocks-kit.slow.tsx通过createSlateEditor({ plugins: BasicBlocksKit, ... })直接装配完整 kit模拟hello文本后插入空格 断言根级场景下editor.children[0]变为{ children: [{ children: [{ text: hello }], type: p }], type: blockquote }光标被移到新引用块内hello文本的起点{ offset: 0, path: [0, 0, 0] }。这条测试先把根级提升行为锁定在 kit 分发面上而嵌套场景由包级单测继续守护形成互补。真正的根因createRuleFactory 对象配置未合并默认值在补上 app 级失败测试后真正的缺陷浮出水面——它比 blockquote 本身更深。Plate 的自动格式化规则允许两种定义方式函数式 buildercreateRuleFactory(configBuilder)builder 接收运行时 options 并返回配置对象式配置createRuleFactory(config)直接传入配置对象。问题出在对象式路径上。createRuleFactory.ts 的实现中return (options: Recordstring, unknown {}) { const factoryOptions typeof configOrBuilder function ? options : { ...(configOrBuilder as Recordstring, unknown), ...options }; // ... const getFactoryInput TContext extends object(context: TContext) getMergedInput(context, factoryOptions);修复后factoryOptions会把对象式配置configOrBuilder整体并入{ ...config, ...options }随后所有resolveFactoryValue与回调如match、apply、enabled都能通过getFactoryInput(context)拿到合并后的输入。而在修复前对象式配置里的默认字段比如BlockquoteRules.markdown中定义的marker: 没有进入运行时输入导致({ marker }) marker这类依赖默认值的回调在真实编辑器流程中解析出undefinedmatch匹配失败自然不会被提升为引用块。以 BasicBlockRules.ts 中的BlockquoteRules为例其完整定义如下export const BlockquoteRules { markdown: createRuleFactory{}, { marker: string }({ type: blockStart, marker: , // 配置默认值曾被吞掉 trigger: , enabled: ({ editor }) !editor.api.some({ match: { type: [editor.getType(KEYS.codeBlock)], }, }), match: ({ marker }) marker, // 修复前 marker 为 undefined apply: ({ editor }, match) { editor.tf.delete({ at: match.range }); editor.tf.wrapNodes( { children: [], type: editor.getType(KEYS.blockquote) }, { match: (node) editor.api.isBlock(node), } ); return true; }, }), };可以看到marker是作为配置默认值声明的match回调依赖它。当createRuleFactory没有把默认值合并进运行时输入时这条规则在编辑器里就会静默失效——包级单测之所以通过是因为单测环境与真实编辑器流程传入的上下文形态不同这正好解释了代码看起来没问题、浏览器里却复现的割裂感。修复落地与 core 级回归单测修复本身收敛在createRuleFactory的输入合并逻辑上一行核心变更见 createRuleFactory.ts对象式配置不再被当作仅静态配置而是与运行时options一起合并为factoryOptions随后所有resolveFactoryValue求值和回调调用都通过getFactoryInput(context)获得完整输入。配套的 core 回归单测 createRuleFactory.spec.ts 锁定两条契约默认值必须传入解析器createRuleFactory{}, { marker: string }({ type: blockStart, marker: , trigger: , match: ({ marker }) marker })()在无任何公共 options 的情况下调用rule.resolve(...)断言解析结果为{ range, text: }—— 证明marker默认值生效resolveMatch 扩展数据与基础 match 数据合并createRuleFactory{}, {}, { start: number }使用正则match与自定义resolveMatch断言最终 match 同时包含基础range/text与扩展的start字段。这两条测试分别守护配置默认值注入与match 数据合并两个行为面防止未来重构再次破坏对象式配置路径。浏览器验证在 localhost:3000 上用真实编辑器变换取证由于/blocks/basic-blocks-demo页面上的原始按键模拟raw keystroke typing噪音较大、不够稳定验证阶段采用了更精准的方案从页面中拉取 live editor 实例直接调用真实的编辑器变换transforms。在localhost:3000上依次执行insertBreak()—— 新建段落模拟回车后的起始状态输入与后续文本断言编辑器树中产生尾部引用块trailing blockquote。该验证路径证明修复后的规则在真实编辑器运行时上下文中marker默认值能正确注入解析器match命中后apply中的wrapNodes按预期把段落包装为引用块。这也再次印证了复盘结论——仅靠包级单测无法覆盖真实编辑器的上下文形态浏览器级的 live-editor 验证是此类输入规则回归的必要手段。三层回归防线与防复发建议本次修复最终形成如下三层防线层级测试文件守护内容包级单测BaseBlockquoteInputRules.spec.tsx根级与嵌套级提升、光标落点core 级单测createRuleFactory.spec.ts配置默认值注入、match 数据合并app 级集成测试basic-blocks-kit.slow.tsxBasicBlocksKit真实分发面上的提升结合本次实战与历史教训可沉淀出以下防复发经验对象式createRuleFactory配置的默认值必须进入运行时输入。凡是配置里声明了marker、variant等字段并在match/apply/enabled回调中消费的规则都应有一条无公共 options 调用的 core 单测兜底容器类节点的自动格式化要独立审计。引用块是包装元素根级setNodes路径失效不代表规则逻辑错误要检查是否应走wrapNodes并配合allowSameTypeAbove包级单测 ≠ app 级可用性。BasicBlocksKit这类 shipped kit 的装配面需要专门的集成测试防止包内正确、装配后失效的断层浏览器验证优先使用 live editor 实例与真实 transforms。原始按键模拟受 IME、浏览器事件时序影响噪音大直接调用编辑器变换能更稳定地验证修复路径。延伸阅读引用块容器化后的首次嵌套自动格式化修复2026-04-02-blockquote-autoformat-must-wrap-nested-quotes.md输入规则最佳实践——围栏匹配与功能应用分离block-fence-input-rules-should-split-fence-matching-from-feature-apply.md输入规则最佳实践——显式注册规则实例与包导出 markdown 家族input-rules-should-register-explicit-rule-instances-while-packages-export-markdown-families.md引用块插件本体归一化、break/delete 提升、Tab 缩进/反缩进BaseBlockquotePlugin.ts输入规则工厂类型定义与实现createRuleFactory.ts【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价