From 755aaf222396616618415a1e7193168a2214b203 Mon Sep 17 00:00:00 2001 From: xiamidaxia Date: Fri, 23 May 2025 18:21:51 +0800 Subject: [PATCH] fix: FlowNodeRegistry.extendChildRegistries extend nested, add document.isExtend (#269) --- .../__tests__/flow-node-registry.spec.ts | 116 ++++++++++++++++++ .../document/src/flow-document.ts | 13 ++ .../src/typings/flow-node-register.ts | 30 +++++ 3 files changed, 159 insertions(+) create mode 100644 packages/canvas-engine/document/__tests__/flow-node-registry.spec.ts diff --git a/packages/canvas-engine/document/__tests__/flow-node-registry.spec.ts b/packages/canvas-engine/document/__tests__/flow-node-registry.spec.ts new file mode 100644 index 00000000..2be9dbc1 --- /dev/null +++ b/packages/canvas-engine/document/__tests__/flow-node-registry.spec.ts @@ -0,0 +1,116 @@ +import { beforeEach, describe, expect, it } from 'vitest'; + +import { createDocumentContainer } from './flow-document-container.mock'; +import { FlowDocument, FlowNodeRegistry } from '../src'; + +function registerNode(doc: FlowDocument, newRegistry: FlowNodeRegistry): FlowNodeRegistry { + doc.registerFlowNodes(newRegistry); + return doc.getNodeRegistry(newRegistry.type); +} +const mockRegistries: FlowNodeRegistry[] = [ + { + type: 'dynamicSplit', + meta: {}, + onCreate(node, json) { + return node.document.addInlineBlocks(node, json.blocks || []); + }, + extendChildRegistries: [ + { + type: 'blockIcon', + customKey: 'blockIcon_base', + }, + { + type: 'inlineBlocks', + customKey: 'inlineBlocks_base', + }, + ], + onAdd: () => {}, + }, + { + type: 'a', + extend: 'dynamicSplit', + }, + { + type: 'b', + extend: 'a', + extendChildRegistries: [ + { + type: 'blockIcon', + customKey: 'blockIcon_from_b', + }, + ], + }, + { + type: 'c', + extend: 'b', + extendChildRegistries: [ + { + type: 'blockIcon', + customKey: 'blockIcon_from_c', + }, + { + type: 'inlineBlocks', + customKey: 'inlineBlocks_from_c', + }, + ], + }, +]; +describe('flow-node-registry', () => { + let doc: FlowDocument; + beforeEach(() => { + const container = createDocumentContainer(); + doc = container.get(FlowDocument); + doc.registerFlowNodes(...mockRegistries); + }); + it('extend check', () => { + expect(doc.getNodeRegistry('dynamicSplit').__extends__).toEqual(undefined); + expect(doc.getNodeRegistry('a').__extends__).toEqual(['dynamicSplit']); + expect(doc.getNodeRegistry('b').__extends__).toEqual(['a', 'dynamicSplit']); + expect(doc.getNodeRegistry('c').__extends__).toEqual(['b', 'a', 'dynamicSplit']); + expect(doc.isExtend('dynamicSplit', 'dynamicSplit')).toBeFalsy(); + expect(doc.isExtend('a', 'b')).toBeFalsy(); + expect(doc.isExtend('a', 'dynamicSplit')).toBeTruthy(); + expect(doc.isExtend('b', 'dynamicSplit')).toBeTruthy(); + expect(doc.isExtend('b', 'a')).toBeTruthy(); + expect(doc.isExtend('c', 'dynamicSplit')).toBeTruthy(); + expect(doc.isExtend('c', 'b')).toBeTruthy(); + expect(doc.isExtend('c', 'a')).toBeTruthy(); + }); + it('base extend', () => { + expect(doc.getNodeRegistry('a').onAdd).toBeTypeOf('function'); + doc.addNode({ + id: 'a', + type: 'a', + parent: doc.root, + }); + expect(doc.toString()).toEqual(`root +|-- a +|---- $blockIcon$a +|---- $inlineBlocks$a`); + expect(doc.getNode('$blockIcon$a').getNodeRegistry().customKey).toBe('blockIcon_base'); + }); + it('extend nested', () => { + expect(doc.getNodeRegistry('b').onAdd).toBeTypeOf('function'); + doc.addNode({ + id: 'b', + type: 'b', + parent: doc.root, + }); + doc.addNode({ + id: 'c', + type: 'c', + parent: doc.root, + }); + expect(doc.toString()).toEqual(`root +|-- b +|---- $blockIcon$b +|---- $inlineBlocks$b +|-- c +|---- $blockIcon$c +|---- $inlineBlocks$c`); + expect(doc.getNode('$blockIcon$b').getNodeRegistry().customKey).toBe('blockIcon_from_b'); + expect(doc.getNode('$inlineBlocks$b').getNodeRegistry().customKey).toBe('inlineBlocks_base'); + expect(doc.getNode('$blockIcon$c').getNodeRegistry().customKey).toBe('blockIcon_from_c'); + expect(doc.getNode('$inlineBlocks$c').getNodeRegistry().customKey).toBe('inlineBlocks_from_c'); + }); +}); diff --git a/packages/canvas-engine/document/src/flow-document.ts b/packages/canvas-engine/document/src/flow-document.ts index 1f75876e..46645579 100644 --- a/packages/canvas-engine/document/src/flow-document.ts +++ b/packages/canvas-engine/document/src/flow-document.ts @@ -433,10 +433,23 @@ export class FlowDocument implements Disposable { ...preRegistry?.meta, ...newRegistry?.meta, }, + extendChildRegistries: FlowNodeRegistry.mergeChildRegistries( + preRegistry?.extendChildRegistries, + newRegistry?.extendChildRegistries + ), }); }); } + /** + * Check node extend + * @param currentType + * @param parentType + */ + isExtend(currentType: FlowNodeType, parentType: FlowNodeType): boolean { + return (this.getNodeRegistry(currentType).__extends__ || []).includes(parentType); + } + /** * 导出数据,可以重载 */ diff --git a/packages/canvas-engine/document/src/typings/flow-node-register.ts b/packages/canvas-engine/document/src/typings/flow-node-register.ts index d53fdd59..584de43d 100644 --- a/packages/canvas-engine/document/src/typings/flow-node-register.ts +++ b/packages/canvas-engine/document/src/typings/flow-node-register.ts @@ -276,6 +276,10 @@ export interface FlowNodeRegistry { } ) => FlowNodeEntity; + /** + * 内部用于继承逻辑判断,不要使用 + */ + __extends__?: FlowNodeType[]; /** * 扩展注册器 */ @@ -283,17 +287,43 @@ export interface FlowNodeRegistry { } export namespace FlowNodeRegistry { + export function mergeChildRegistries( + r1: FlowNodeRegistry[] = [], + r2: FlowNodeRegistry[] = [] + ): FlowNodeRegistry[] { + if (r1.length === 0 || r2.length === 0) { + return [...r1, ...r2]; + } + const r1Filter = r1.map((r1Current) => { + const r2Current = r2.find((n) => n.type === r1Current.type); + if (r2Current) { + return merge(r1Current, r2Current, r1Current.type); + } + return r1Current; + }); + const r2Filter = r2.filter((n) => !r1.some((r) => r.type === n.type)); + return [...r1Filter, ...r2Filter]; + } export function merge( registry1: FlowNodeRegistry, registry2: FlowNodeRegistry, finalType: FlowNodeType ): FlowNodeRegistry { + const extendKeys = registry1.__extends__ ? registry1.__extends__.slice() : []; + if (registry1.type !== registry2.type) { + extendKeys.unshift(registry1.type); + } return { ...registry1, ...registry2, + extendChildRegistries: mergeChildRegistries( + registry1.extendChildRegistries, + registry2.extendChildRegistries + ), meta: { ...registry1.meta, ...registry2.meta }, extend: undefined, type: finalType, + __extends__: extendKeys, }; }