mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
feat: add ASTMatch API in variable-core (#127)
This commit is contained in:
parent
52bbf2794d
commit
20b1dc1ae9
@ -1,15 +1,12 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import {
|
||||
ArrayType,
|
||||
ASTFactory,
|
||||
ASTKind,
|
||||
type BaseType,
|
||||
CustomType,
|
||||
isMatchAST,
|
||||
ObjectType,
|
||||
type UnionJSON,
|
||||
useScopeAvailable,
|
||||
ASTMatch,
|
||||
} from '@flowgram.ai/fixed-layout-editor';
|
||||
|
||||
import { createASTFromJSONSchema } from '../utils';
|
||||
@ -47,14 +44,14 @@ export function useVariableTree<TreeData>({
|
||||
const getVariableTypeIcon = useCallback((variable: VariableField) => {
|
||||
const _type = variable.type;
|
||||
|
||||
if (isMatchAST(_type, ArrayType)) {
|
||||
if (ASTMatch.isArray(_type)) {
|
||||
return (
|
||||
(ArrayIcons as any)[_type.items?.kind.toLowerCase()] ||
|
||||
VariableTypeIcons[ASTKind.Array.toLowerCase()]
|
||||
);
|
||||
}
|
||||
|
||||
if (isMatchAST(_type, CustomType)) {
|
||||
if (ASTMatch.isCustomType(_type)) {
|
||||
return VariableTypeIcons[_type.typeName.toLowerCase()];
|
||||
}
|
||||
|
||||
@ -96,7 +93,7 @@ export function useVariableTree<TreeData>({
|
||||
const isTypeFiltered = checkTypeFiltered(type);
|
||||
|
||||
let children: TreeData[] | undefined;
|
||||
if (isMatchAST(type, ObjectType)) {
|
||||
if (ASTMatch.isObject(type)) {
|
||||
children = (type.properties || [])
|
||||
.map((_property) => renderVariable(_property as VariableField, [...parentFields, variable]))
|
||||
.filter(Boolean) as TreeData[];
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import {
|
||||
ArrayType,
|
||||
ASTFactory,
|
||||
ASTKind,
|
||||
type BaseType,
|
||||
CustomType,
|
||||
isMatchAST,
|
||||
ObjectType,
|
||||
type UnionJSON,
|
||||
useScopeAvailable,
|
||||
ASTMatch,
|
||||
} from '@flowgram.ai/free-layout-editor';
|
||||
|
||||
import { createASTFromJSONSchema } from '../utils';
|
||||
@ -47,14 +44,14 @@ export function useVariableTree<TreeData>({
|
||||
const getVariableTypeIcon = useCallback((variable: VariableField) => {
|
||||
const _type = variable.type;
|
||||
|
||||
if (isMatchAST(_type, ArrayType)) {
|
||||
if (ASTMatch.isArray(_type)) {
|
||||
return (
|
||||
(ArrayIcons as any)[_type.items?.kind.toLowerCase()] ||
|
||||
VariableTypeIcons[ASTKind.Array.toLowerCase()]
|
||||
);
|
||||
}
|
||||
|
||||
if (isMatchAST(_type, CustomType)) {
|
||||
if (ASTMatch.isCustomType(_type)) {
|
||||
return VariableTypeIcons[_type.typeName.toLowerCase()];
|
||||
}
|
||||
|
||||
@ -96,7 +93,7 @@ export function useVariableTree<TreeData>({
|
||||
const isTypeFiltered = checkTypeFiltered(type);
|
||||
|
||||
let children: TreeData[] | undefined;
|
||||
if (isMatchAST(type, ObjectType)) {
|
||||
if (ASTMatch.isObject(type)) {
|
||||
children = (type.properties || [])
|
||||
.map((_property) => renderVariable(_property as VariableField, [...parentFields, variable]))
|
||||
.filter(Boolean) as TreeData[];
|
||||
|
||||
@ -233,8 +233,7 @@ return available.variables.map(renderVariable)
|
||||
```tsx pure title="use-variable-tree.tsx"
|
||||
import {
|
||||
type BaseVariableField,
|
||||
isMatchAST,
|
||||
ObjectType,
|
||||
ASTMatch,
|
||||
} from '@flowgram.ai/fixed-layout-editor';
|
||||
|
||||
// ....
|
||||
@ -243,7 +242,7 @@ const renderVariable = (variable: BaseVariableField) => ({
|
||||
title: variable.meta?.title,
|
||||
key: variable.key,
|
||||
// Only Object Type can drilldown
|
||||
children: isMatchAST(type, ObjectType) ? type.properties.map(renderVariable) : [],
|
||||
children: ASTMatch.isObject(type) ? type.properties.map(renderVariable) : [],
|
||||
});
|
||||
|
||||
// ....
|
||||
@ -256,9 +255,7 @@ const renderVariable = (variable: BaseVariableField) => ({
|
||||
import {
|
||||
type BaseVariableField,
|
||||
type BaseType,
|
||||
isMatchAST,
|
||||
ObjectType,
|
||||
ArrayType,
|
||||
ASTMatch,
|
||||
} from '@flowgram.ai/fixed-layout-editor';
|
||||
|
||||
// ....
|
||||
@ -267,10 +264,10 @@ const getTypeChildren = (type?: BaseType): BaseVariableField[] => {
|
||||
if (!type) return [];
|
||||
|
||||
// get properties of Object
|
||||
if (isMatchAST(type, ObjectType)) return type.properties;
|
||||
if (ASTMatch.isObject(type)) return type.properties;
|
||||
|
||||
// get items type of Array
|
||||
if (isMatchAST(type, ArrayType)) return getTypeChildren(type.items);
|
||||
if (ASTMatch.isArray(type)) return getTypeChildren(type.items);
|
||||
};
|
||||
|
||||
const renderVariable = (variable: BaseVariableField) => ({
|
||||
|
||||
@ -235,8 +235,7 @@ return available.variables.map(renderVariable)
|
||||
```tsx pure title="use-variable-tree.tsx"
|
||||
import {
|
||||
type BaseVariableField,
|
||||
isMatchAST,
|
||||
ObjectType,
|
||||
ASTMatch,
|
||||
} from '@flowgram.ai/fixed-layout-editor';
|
||||
|
||||
// ....
|
||||
@ -245,7 +244,7 @@ const renderVariable = (variable: BaseVariableField) => ({
|
||||
title: variable.meta?.title,
|
||||
key: variable.key,
|
||||
// Only Object Type can drilldown
|
||||
children: isMatchAST(type, ObjectType) ? type.properties.map(renderVariable) : [],
|
||||
children: ASTMatch.isObject(type) ? type.properties.map(renderVariable) : [],
|
||||
});
|
||||
|
||||
// ....
|
||||
@ -258,9 +257,7 @@ const renderVariable = (variable: BaseVariableField) => ({
|
||||
import {
|
||||
type BaseVariableField,
|
||||
type BaseType,
|
||||
isMatchAST,
|
||||
ObjectType,
|
||||
ArrayType,
|
||||
ASTMatch,
|
||||
} from '@flowgram.ai/fixed-layout-editor';
|
||||
|
||||
// ....
|
||||
@ -269,10 +266,10 @@ const getTypeChildren = (type?: BaseType): BaseVariableField[] => {
|
||||
if (!type) return [];
|
||||
|
||||
// get properties of Object
|
||||
if (isMatchAST(type, ObjectType)) return type.properties;
|
||||
if (ASTMatch.isObject(type)) return type.properties;
|
||||
|
||||
// get items type of Array
|
||||
if (isMatchAST(type, ArrayType)) return getTypeChildren(type.items);
|
||||
if (ASTMatch.isArray(type)) return getTypeChildren(type.items);
|
||||
};
|
||||
|
||||
const renderVariable = (variable: BaseVariableField) => ({
|
||||
|
||||
@ -2,11 +2,11 @@ import { vi, describe, test, expect } from 'vitest';
|
||||
|
||||
import {
|
||||
ASTKind,
|
||||
ASTMatch,
|
||||
ObjectType,
|
||||
NumberType,
|
||||
VariableEngine,
|
||||
VariableDeclaration,
|
||||
isMatchAST,
|
||||
} from '../../src';
|
||||
import { simpleVariableList } from '../../__mocks__/variables';
|
||||
import { getContainer } from '../../__mocks__/container';
|
||||
@ -167,7 +167,7 @@ describe('test Basic Variable Declaration', () => {
|
||||
type: ASTKind.Number,
|
||||
meta: { label: 'test Label' },
|
||||
});
|
||||
expect(isMatchAST(declaration.type, NumberType)).toBeTruthy();
|
||||
expect(ASTMatch.is(declaration.type, NumberType)).toBeTruthy();
|
||||
expect(declarationChangeTimes).toBe(2);
|
||||
expect(anyVariableChangeTimes).toBe(2);
|
||||
expect(typeChangeTimes).toBe(1);
|
||||
@ -186,7 +186,7 @@ describe('test Basic Variable Declaration', () => {
|
||||
},
|
||||
meta: { label: 'test Label' },
|
||||
});
|
||||
expect(isMatchAST(declaration.type, ObjectType)).toBeTruthy();
|
||||
expect(ASTMatch.is(declaration.type, ObjectType)).toBeTruthy();
|
||||
expect(declarationChangeTimes).toBe(3);
|
||||
expect(anyVariableChangeTimes).toBe(3);
|
||||
expect(typeChangeTimes).toBe(2);
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
import { vi, describe, test, expect } from 'vitest';
|
||||
|
||||
import {
|
||||
ASTMatch,
|
||||
ObjectType,
|
||||
NumberType,
|
||||
VariableEngine,
|
||||
StringType,
|
||||
VariableDeclarationList,
|
||||
BooleanType,
|
||||
IntegerType,
|
||||
MapType,
|
||||
ArrayType,
|
||||
} from '../../src';
|
||||
import { simpleVariableList } from '../../__mocks__/variables';
|
||||
import { getContainer } from '../../__mocks__/container';
|
||||
|
||||
vi.mock('nanoid', () => {
|
||||
let mockId = 0;
|
||||
return {
|
||||
nanoid: () => 'mocked-id-' + mockId++,
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* 测试基本的变量声明场景
|
||||
*/
|
||||
describe('test Basic Variable Declaration', () => {
|
||||
const container = getContainer();
|
||||
const variableEngine = container.get(VariableEngine);
|
||||
const testScope = variableEngine.createScope('test');
|
||||
|
||||
test('test simple variable match', () => {
|
||||
const simpleCase = testScope.ast.set('simple case', simpleVariableList);
|
||||
|
||||
if (!ASTMatch.isVariableDeclarationList(simpleCase)) {
|
||||
throw new Error('simpleCase is not a VariableDeclarationList');
|
||||
}
|
||||
expect(ASTMatch.isVariableDeclarationList(simpleCase)).toBeTruthy();
|
||||
expect(ASTMatch.is(simpleCase, VariableDeclarationList)).toBeTruthy();
|
||||
|
||||
const stringDeclaration = simpleCase.declarations[0];
|
||||
const booleanDeclaration = simpleCase.declarations[1];
|
||||
const numberDeclaration = simpleCase.declarations[2];
|
||||
const integerDeclaration = simpleCase.declarations[3];
|
||||
const objectDeclaration = simpleCase.declarations[4];
|
||||
const mapDeclaration = simpleCase.declarations[5];
|
||||
const arrayProperty = testScope.output.globalVariableTable.getByKeyPath(['object', 'key4']);
|
||||
|
||||
expect(ASTMatch.isString(stringDeclaration.type)).toBeTruthy();
|
||||
expect(ASTMatch.is(stringDeclaration.type, StringType)).toBeTruthy();
|
||||
|
||||
expect(ASTMatch.isBoolean(booleanDeclaration.type)).toBeTruthy();
|
||||
expect(ASTMatch.is(booleanDeclaration.type, BooleanType)).toBeTruthy();
|
||||
|
||||
expect(ASTMatch.isNumber(numberDeclaration.type)).toBeTruthy();
|
||||
expect(ASTMatch.is(numberDeclaration.type, NumberType)).toBeTruthy();
|
||||
|
||||
expect(ASTMatch.isInteger(integerDeclaration.type)).toBeTruthy();
|
||||
expect(ASTMatch.is(integerDeclaration.type, IntegerType)).toBeTruthy();
|
||||
|
||||
expect(ASTMatch.isObject(objectDeclaration.type)).toBeTruthy();
|
||||
expect(ASTMatch.is(objectDeclaration.type, ObjectType)).toBeTruthy();
|
||||
|
||||
expect(ASTMatch.isMap(mapDeclaration.type)).toBeTruthy();
|
||||
expect(ASTMatch.is(mapDeclaration.type, MapType)).toBeTruthy();
|
||||
|
||||
if (!ASTMatch.isProperty(arrayProperty)) {
|
||||
throw new Error('arrayProperty is not a Property');
|
||||
}
|
||||
expect(ASTMatch.isArray(arrayProperty.type)).toBeTruthy();
|
||||
expect(ASTMatch.is(arrayProperty.type, ArrayType)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -16,5 +16,6 @@ export * from './type';
|
||||
export * from './expression';
|
||||
|
||||
export { ASTFactory } from './factory';
|
||||
export { ASTMatch } from './match';
|
||||
export { injectToAST, postConstructAST } from './utils/inversify';
|
||||
export { isMatchAST } from './utils/helpers';
|
||||
|
||||
73
packages/variable-engine/variable-core/src/ast/match.ts
Normal file
73
packages/variable-engine/variable-core/src/ast/match.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { ASTKind } from './types';
|
||||
import {
|
||||
type StringType,
|
||||
type NumberType,
|
||||
type BooleanType,
|
||||
type IntegerType,
|
||||
type ObjectType,
|
||||
type ArrayType,
|
||||
type MapType,
|
||||
type CustomType,
|
||||
} from './type';
|
||||
import { type EnumerateExpression, type KeyPathExpression } from './expression';
|
||||
import {
|
||||
type Property,
|
||||
type VariableDeclaration,
|
||||
type VariableDeclarationList,
|
||||
} from './declaration';
|
||||
import { type ASTNode } from './ast-node';
|
||||
|
||||
export namespace ASTMatch {
|
||||
/**
|
||||
* 类型相关
|
||||
* @returns
|
||||
*/
|
||||
export const isString = (node?: ASTNode): node is StringType => node?.kind === ASTKind.String;
|
||||
|
||||
export const isNumber = (node?: ASTNode): node is NumberType => node?.kind === ASTKind.Number;
|
||||
|
||||
export const isBoolean = (node?: ASTNode): node is BooleanType => node?.kind === ASTKind.Boolean;
|
||||
|
||||
export const isInteger = (node?: ASTNode): node is IntegerType => node?.kind === ASTKind.Integer;
|
||||
|
||||
export const isObject = (node?: ASTNode): node is ObjectType => node?.kind === ASTKind.Object;
|
||||
|
||||
export const isArray = (node?: ASTNode): node is ArrayType => node?.kind === ASTKind.Array;
|
||||
|
||||
export const isMap = (node?: ASTNode): node is MapType => node?.kind === ASTKind.Map;
|
||||
|
||||
export const isCustomType = (node?: ASTNode): node is CustomType =>
|
||||
node?.kind === ASTKind.CustomType;
|
||||
|
||||
/**
|
||||
* 声明相关
|
||||
*/
|
||||
export const isVariableDeclaration = <VariableMeta = any>(
|
||||
node?: ASTNode
|
||||
): node is VariableDeclaration<VariableMeta> => node?.kind === ASTKind.VariableDeclaration;
|
||||
|
||||
export const isProperty = <VariableMeta = any>(node?: ASTNode): node is Property<VariableMeta> =>
|
||||
node?.kind === ASTKind.Property;
|
||||
|
||||
export const isVariableDeclarationList = (node?: ASTNode): node is VariableDeclarationList =>
|
||||
node?.kind === ASTKind.VariableDeclarationList;
|
||||
|
||||
/**
|
||||
* 表达式相关
|
||||
*/
|
||||
export const isEnumerateExpression = (node?: ASTNode): node is EnumerateExpression =>
|
||||
node?.kind === ASTKind.EnumerateExpression;
|
||||
|
||||
export const isKeyPathExpression = (node?: ASTNode): node is KeyPathExpression =>
|
||||
node?.kind === ASTKind.KeyPathExpression;
|
||||
|
||||
/**
|
||||
* Check AST Match by ASTClass
|
||||
*/
|
||||
export function is<TargetASTNode extends ASTNode>(
|
||||
node?: ASTNode,
|
||||
targetType?: { kind: string; new (...args: any[]): TargetASTNode }
|
||||
): node is TargetASTNode {
|
||||
return node?.kind === targetType?.kind;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { ASTNodeJSON, ASTNodeJSONOrKind } from '../types';
|
||||
import { ASTMatch } from '../match';
|
||||
import { ASTNode } from '../ast-node';
|
||||
|
||||
export function updateChildNodeHelper(
|
||||
@ -53,9 +54,15 @@ export function getAllChildren(ast: ASTNode): ASTNode[] {
|
||||
return [...ast.children, ...ast.children.map((_child) => getAllChildren(_child)).flat()];
|
||||
}
|
||||
|
||||
/**
|
||||
* isMatchAST is same as ASTMatch.is
|
||||
* @param node
|
||||
* @param targetType
|
||||
* @returns
|
||||
*/
|
||||
export function isMatchAST<TargetASTNode extends ASTNode>(
|
||||
node?: ASTNode,
|
||||
targetType?: { kind: string; new (...args: any[]): TargetASTNode }
|
||||
): node is TargetASTNode {
|
||||
return node?.kind === targetType?.kind;
|
||||
return ASTMatch.is(node, targetType);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user