Просмотр исходного кода

Merge pull request #39 from sanmaopep/feat/custom-type

feat(variable): add new variable AST CustomType
YiWei Mao 10 месяцев назад
Родитель
Сommit
ce1444a011

+ 2 - 3
apps/demo-fixed-layout/src/plugins/sync-variable-plugin/utils.ts

@@ -59,8 +59,7 @@ export function createASTFromJSONSchema(jsonSchema: JsonSchema): ASTNodeJSON | u
       return ASTFactory.createInteger();
 
     default:
-      // If the type is not recognized, return undefined.
-      // You can extend this function to handle custom types if needed.
-      return;
+      // If the type is not recognized, return CustomType
+      return ASTFactory.createCustomType({ typeName: type });
   }
 }

+ 9 - 2
apps/demo-fixed-layout/src/plugins/sync-variable-plugin/variable-selector/use-variable-tree.ts

@@ -5,6 +5,7 @@ import {
   ASTFactory,
   ASTKind,
   type BaseType,
+  CustomType,
   isMatchAST,
   ObjectType,
   type UnionJSON,
@@ -44,13 +45,19 @@ export function useVariableTree<TreeData>({
   const available = useScopeAvailable();
 
   const getVariableTypeIcon = useCallback((variable: VariableField) => {
-    if (isMatchAST(variable.type, ArrayType)) {
+    const _type = variable.type;
+
+    if (isMatchAST(_type, ArrayType)) {
       return (
-        (ArrayIcons as any)[variable.type.items?.kind.toLowerCase()] ||
+        (ArrayIcons as any)[_type.items?.kind.toLowerCase()] ||
         VariableTypeIcons[ASTKind.Array.toLowerCase()]
       );
     }
 
+    if (isMatchAST(_type, CustomType)) {
+      return VariableTypeIcons[_type.typeName.toLowerCase()];
+    }
+
     return (VariableTypeIcons as any)[variable.type?.kind.toLowerCase()];
   }, []);
 

+ 2 - 3
apps/demo-free-layout/src/plugins/sync-variable-plugin/utils.ts

@@ -59,8 +59,7 @@ export function createASTFromJSONSchema(jsonSchema: JsonSchema): ASTNodeJSON | u
       return ASTFactory.createInteger();
 
     default:
-      // If the type is not recognized, return undefined.
-      // You can extend this function to handle custom types if needed.
-      return;
+      // If the type is not recognized, return CustomType
+      return ASTFactory.createCustomType({ typeName: type });
   }
 }

+ 9 - 2
apps/demo-free-layout/src/plugins/sync-variable-plugin/variable-selector/use-variable-tree.ts

@@ -5,6 +5,7 @@ import {
   ASTFactory,
   ASTKind,
   type BaseType,
+  CustomType,
   isMatchAST,
   ObjectType,
   type UnionJSON,
@@ -44,13 +45,19 @@ export function useVariableTree<TreeData>({
   const available = useScopeAvailable();
 
   const getVariableTypeIcon = useCallback((variable: VariableField) => {
-    if (isMatchAST(variable.type, ArrayType)) {
+    const _type = variable.type;
+
+    if (isMatchAST(_type, ArrayType)) {
       return (
-        (ArrayIcons as any)[variable.type.items?.kind.toLowerCase()] ||
+        (ArrayIcons as any)[_type.items?.kind.toLowerCase()] ||
         VariableTypeIcons[ASTKind.Array.toLowerCase()]
       );
     }
 
+    if (isMatchAST(_type, CustomType)) {
+      return VariableTypeIcons[_type.typeName.toLowerCase()];
+    }
+
     return (VariableTypeIcons as any)[variable.type?.kind.toLowerCase()];
   }, []);
 

+ 31 - 12
packages/variable-engine/variable-core/__tests__/ast/variable-type-equal.test.ts

@@ -1,7 +1,7 @@
 import { describe, expect, test } from 'vitest';
 
 import { VariableEngine } from '../../src/variable-engine';
-import { ASTFactory, ASTKind, VariableDeclaration } from '../../src/ast';
+import { ASTFactory, ASTKind, CustomType, VariableDeclaration } from '../../src/ast';
 import { getContainer } from '../../__mocks__/container';
 
 const {
@@ -11,9 +11,11 @@ const {
   createBoolean,
   createString,
   createArray,
+  createCustomType,
   createMap,
   createProperty,
   createUnion,
+  create,
 } = ASTFactory;
 
 describe('Test Variable Type Equal', () => {
@@ -29,6 +31,7 @@ describe('Test Variable Type Equal', () => {
       createProperty({ key: 'd', type: createObject({}) }),
       createProperty({ key: 'e', type: createArray({}) }),
       createProperty({ key: 'f', type: createMap({}) }),
+      createProperty({ key: 'g', type: createCustomType({ typeName: 'Custom' }) }),
     ],
   });
 
@@ -101,24 +104,24 @@ describe('Test Variable Type Equal', () => {
       variable1.type.isTypeEqual(
         createUnion({
           types: [testObject1, testObject2, testObject3],
-        }),
-      ),
+        })
+      )
     ).toBeTruthy();
 
     expect(
       variable2.type.isTypeEqual(
         createUnion({
           types: [testObject1, testObject2, testObject3],
-        }),
-      ),
+        })
+      )
     ).toBeTruthy();
 
     expect(
       variable3.type.isTypeEqual(
         createUnion({
           types: [testObject1, testObject2, testObject3],
-        }),
-      ),
+        })
+      )
     ).toBeTruthy();
 
     expect(variable1.type.isTypeEqual({ kind: ASTKind.Union })).toBeFalsy();
@@ -146,7 +149,7 @@ describe('Test Variable Type Equal', () => {
             type: UnionBasicTypes,
           },
         ],
-      }),
+      })
     );
   });
 
@@ -156,16 +159,32 @@ describe('Test Variable Type Equal', () => {
     expect(variable3.type.isTypeEqual({ kind: ASTKind.Object, weak: true })).toBeTruthy();
 
     expect(
-      variable3.getByKeyPath(['c'])?.type.isTypeEqual({ kind: ASTKind.Array, weak: true }),
+      variable3.getByKeyPath(['c'])?.type.isTypeEqual({ kind: ASTKind.Array, weak: true })
     ).toBeTruthy();
     expect(
-      variable3.getByKeyPath(['d'])?.type.isTypeEqual({ kind: ASTKind.Array, weak: true }),
+      variable3.getByKeyPath(['d'])?.type.isTypeEqual({ kind: ASTKind.Array, weak: true })
     ).toBeTruthy();
     expect(
-      variable3.getByKeyPath(['e'])?.type.isTypeEqual({ kind: ASTKind.Map, weak: true }),
+      variable3.getByKeyPath(['e'])?.type.isTypeEqual({ kind: ASTKind.Map, weak: true })
     ).toBeTruthy();
     expect(
-      variable3.getByKeyPath(['f'])?.type.isTypeEqual({ kind: ASTKind.Map, weak: true }),
+      variable3.getByKeyPath(['f'])?.type.isTypeEqual({ kind: ASTKind.Map, weak: true })
     ).toBeTruthy();
   });
+
+  test('CustomType Equal', () => {
+    const customType1 = createCustomType({ typeName: 'Custom' });
+    const customType2 = createCustomType({ typeName: 'Custom2' });
+    const customType3 = create(CustomType, { typeName: 'Custom' });
+    const unionCustomTypes = createUnion({
+      types: [customType1, customType2],
+    });
+
+    const customTypeProperty = variable1.getByKeyPath(['g'])?.type;
+
+    expect(customTypeProperty?.isTypeEqual(customType1)).toBeTruthy();
+    expect(customTypeProperty?.isTypeEqual(customType2)).toBeFalsy();
+    expect(customTypeProperty?.isTypeEqual(customType3)).toBeTruthy();
+    expect(customTypeProperty?.isTypeEqual(unionCustomTypes)).toBeTruthy();
+  });
 });

+ 12 - 3
packages/variable-engine/variable-core/src/ast/ast-registers.ts

@@ -4,7 +4,15 @@ import { injectable } from 'inversify';
 import { POST_CONSTRUCT_AST_SYMBOL } from './utils/inversify';
 import { ASTKindType, ASTNodeJSON, CreateASTParams } from './types';
 import { ArrayType } from './type/array';
-import { BooleanType, IntegerType, MapType, NumberType, ObjectType, StringType } from './type';
+import {
+  BooleanType,
+  CustomType,
+  IntegerType,
+  MapType,
+  NumberType,
+  ObjectType,
+  StringType,
+} from './type';
 import { EnumerateExpression, ExpressionList, KeyPathExpression } from './expression';
 import { Property, VariableDeclaration, VariableDeclarationList } from './declaration';
 import { DataNode, MapNode } from './common';
@@ -29,6 +37,7 @@ export class ASTRegisters {
     this.registerAST(ObjectType);
     this.registerAST(ArrayType);
     this.registerAST(MapType);
+    this.registerAST(CustomType);
     this.registerAST(Property);
     this.registerAST(VariableDeclaration);
     this.registerAST(VariableDeclarationList);
@@ -46,7 +55,7 @@ export class ASTRegisters {
    */
   createAST<ReturnNode extends ASTNode = ASTNode>(
     json: ASTNodeJSON,
-    { parent, scope }: CreateASTParams,
+    { parent, scope }: CreateASTParams
   ): ReturnNode {
     const Registry = this.astMap.get(json.kind!);
 
@@ -62,7 +71,7 @@ export class ASTRegisters {
         scope,
         parent,
       },
-      injector?.() || {},
+      injector?.() || {}
     ) as ReturnNode;
 
     // 初始化创建不触发 fireChange

+ 16 - 3
packages/variable-engine/variable-core/src/ast/factory.ts

@@ -1,9 +1,10 @@
-import { ASTKind } from './types';
+import { ASTKind, ASTNodeJSON } from './types';
 import { MapJSON } from './type/map';
 import { ArrayJSON } from './type/array';
-import { ObjectJSON, UnionJSON } from './type';
+import { CustomTypeJSON, ObjectJSON, UnionJSON } from './type';
 import { EnumerateExpressionJSON, KeyPathExpressionJSON } from './expression';
 import { PropertyJSON, VariableDeclarationJSON, VariableDeclarationListJSON } from './declaration';
+import { ASTNode } from './ast-node';
 
 export namespace ASTFactory {
   /**
@@ -30,12 +31,16 @@ export namespace ASTFactory {
     kind: ASTKind.Union,
     ...json,
   });
+  export const createCustomType = (json: CustomTypeJSON) => ({
+    kind: ASTKind.CustomType,
+    ...json,
+  });
 
   /**
    * 声明相关
    */
   export const createVariableDeclaration = <VariableMeta = any>(
-    json: VariableDeclarationJSON<VariableMeta>,
+    json: VariableDeclarationJSON<VariableMeta>
   ) => ({
     kind: ASTKind.VariableDeclaration,
     ...json,
@@ -60,4 +65,12 @@ export namespace ASTFactory {
     kind: ASTKind.KeyPathExpression,
     ...json,
   });
+
+  /**
+   * 通过 AST Class 创建
+   */
+  export const create = <JSON extends ASTNodeJSON>(
+    targetType: { kind: string; new (...args: any[]): ASTNode<JSON> },
+    json: JSON
+  ) => ({ kind: targetType.kind, ...json });
 }

+ 38 - 0
packages/variable-engine/variable-core/src/ast/type/custom-type.ts

@@ -0,0 +1,38 @@
+import { parseTypeJsonOrKind } from '../utils/helpers';
+import { ASTKind, ASTNodeJSONOrKind } from '../types';
+import { type UnionJSON } from './union';
+import { BaseType } from './base-type';
+
+export interface CustomTypeJSON {
+  typeName: string;
+}
+
+export class CustomType extends BaseType<CustomTypeJSON> {
+  static kind: string = ASTKind.CustomType;
+
+  protected _typeName: string;
+
+  get typeName(): string {
+    return this._typeName;
+  }
+
+  fromJSON(json: CustomTypeJSON): void {
+    if (this._typeName !== json.typeName) {
+      this._typeName = json.typeName;
+      this.fireChange();
+    }
+  }
+
+  public isTypeEqual(targetTypeJSONOrKind?: ASTNodeJSONOrKind): boolean {
+    const targetTypeJSON = parseTypeJsonOrKind(targetTypeJSONOrKind);
+
+    // 如果是 Union 类型,有一个子类型保持相等即可
+    if (targetTypeJSON?.kind === ASTKind.Union) {
+      return ((targetTypeJSON as UnionJSON)?.types || [])?.some((_subType) =>
+        this.isTypeEqual(_subType)
+      );
+    }
+
+    return targetTypeJSON?.kind === this.kind && targetTypeJSON?.typeName === this.typeName;
+  }
+}

+ 1 - 0
packages/variable-engine/variable-core/src/ast/type/index.ts

@@ -11,3 +11,4 @@ export {
 } from './object';
 export { BaseType } from './base-type';
 export { type UnionJSON } from './union';
+export { CustomType, type CustomTypeJSON } from './custom-type';

+ 2 - 1
packages/variable-engine/variable-core/src/ast/types.ts

@@ -29,6 +29,7 @@ export enum ASTKind {
   Map = 'Map', // Map
   Union = 'Union', // 联合类型,常用于类型判断,一般不对业务透出
   Any = 'Any', // 任意类型,常用于业务判断
+  CustomType = 'CustomType', // 自定义类型,用于业务自定义类型
 
   /**
    * 声明
@@ -85,7 +86,7 @@ export type GetKindJSONOrKind<KindType extends string, JSON extends ASTNodeJSON>
 export interface GlobalEventActionType<
   Type = string,
   Payload = any,
-  AST extends ASTNode = ASTNode,
+  AST extends ASTNode = ASTNode
 > {
   type: Type;
   payload?: Payload;