mirror of
https://gitee.com/ByteDance/flowgram.ai.git
synced 2025-07-07 17:43:29 +08:00
chore(variable): move json schema logic from variable core to materials (#256)
This commit is contained in:
parent
f2abdd8cca
commit
11dda65150
@ -1,3 +1,4 @@
|
||||
import { JsonSchemaUtils } from '@flowgram.ai/form-materials';
|
||||
import {
|
||||
definePluginCreator,
|
||||
FlowNodeVariableData,
|
||||
@ -37,7 +38,7 @@ export const createSyncVariablePlugin: PluginCreator<SyncVariablePluginOptions>
|
||||
|
||||
// Create an Type AST from the output data's JSON schema
|
||||
// NOTICE: You can create a new function to generate an AST based on YOUR CUSTOM DSL
|
||||
const typeAST = ASTFactory.createTypeASTFromSchema(value);
|
||||
const typeAST = JsonSchemaUtils.schemaToAST(value);
|
||||
|
||||
if (typeAST) {
|
||||
// Use the node's title or its ID as the title for the variable
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { VarJSONSchema } from '@flowgram.ai/fixed-layout-editor';
|
||||
import type { IJsonSchema, IBasicJsonSchema } from '@flowgram.ai/form-materials';
|
||||
|
||||
export type BasicType = VarJSONSchema.BasicType;
|
||||
export type JsonSchema = VarJSONSchema.ISchema;
|
||||
export type BasicType = IBasicJsonSchema;
|
||||
export type JsonSchema = IJsonSchema;
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
FreeLayoutPluginContext,
|
||||
ASTFactory,
|
||||
} from '@flowgram.ai/free-layout-editor';
|
||||
import { JsonSchemaUtils } from '@flowgram.ai/form-materials';
|
||||
|
||||
export interface SyncVariablePluginOptions {}
|
||||
|
||||
@ -37,7 +38,7 @@ export const createSyncVariablePlugin: PluginCreator<SyncVariablePluginOptions>
|
||||
|
||||
// Create an Type AST from the output data's JSON schema
|
||||
// NOTICE: You can create a new function to generate an AST based on YOUR CUSTOM DSL
|
||||
const typeAST = ASTFactory.createTypeASTFromSchema(value);
|
||||
const typeAST = JsonSchemaUtils.schemaToAST(value);
|
||||
|
||||
if (typeAST) {
|
||||
// Use the node's title or its ID as the title for the variable
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { VarJSONSchema } from '@flowgram.ai/free-layout-editor';
|
||||
import type { IJsonSchema, IBasicJsonSchema } from '@flowgram.ai/form-materials';
|
||||
|
||||
export type BasicType = VarJSONSchema.BasicType;
|
||||
export type JsonSchema = VarJSONSchema.ISchema;
|
||||
export type BasicType = IBasicJsonSchema;
|
||||
export type JsonSchema = IJsonSchema;
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import { PrivateScopeProvider, VarJSONSchema } from '@flowgram.ai/editor';
|
||||
import { PrivateScopeProvider } from '@flowgram.ai/editor';
|
||||
|
||||
import { VariableSelector, VariableSelectorProps } from '../variable-selector';
|
||||
import { IJsonSchema } from '../../typings';
|
||||
|
||||
const batchVariableSchema: VarJSONSchema.ISchema = {
|
||||
const batchVariableSchema: IJsonSchema = {
|
||||
type: 'array',
|
||||
extra: { weak: true },
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { VarJSONSchema } from '@flowgram.ai/editor';
|
||||
import { IJsonSchema } from '../../typings';
|
||||
|
||||
export interface Strategy<Value = any> {
|
||||
hit: (schema: VarJSONSchema.ISchema) => boolean;
|
||||
hit: (schema: IJsonSchema) => boolean;
|
||||
Renderer: React.FC<RendererProps<Value>>;
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ export interface RendererProps<Value = any> {
|
||||
}
|
||||
|
||||
export interface PropsType extends RendererProps {
|
||||
schema: VarJSONSchema.ISchema;
|
||||
schema: IJsonSchema;
|
||||
strategies?: Strategy[];
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import { VarJSONSchema } from '@flowgram.ai/editor';
|
||||
import { IconButton } from '@douyinfe/semi-ui';
|
||||
import { IconSetting } from '@douyinfe/semi-icons';
|
||||
|
||||
@ -9,6 +8,7 @@ import { ConstantInput } from '../constant-input';
|
||||
import { IFlowConstantRefValue } from '../../typings/flow-value';
|
||||
import { UIContainer, UIMain, UITrigger } from './styles';
|
||||
import { VariableSelector } from '../variable-selector';
|
||||
import { IJsonSchema } from '../../typings';
|
||||
|
||||
interface PropsType {
|
||||
value?: IFlowConstantRefValue;
|
||||
@ -16,7 +16,7 @@ interface PropsType {
|
||||
readonly?: boolean;
|
||||
hasError?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
schema?: VarJSONSchema.ISchema;
|
||||
schema?: IJsonSchema;
|
||||
constantProps?: {
|
||||
strategies?: Strategy[];
|
||||
[key: string]: any;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "json-schema-editor",
|
||||
"depMaterials": ["type-selector"],
|
||||
"depMaterials": ["type-selector", "typings/json-schema"],
|
||||
"depPackages": ["@douyinfe/semi-ui", "@douyinfe/semi-icons", "styled-components"]
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { IJsonSchema } from '../../typings';
|
||||
import { PropertyValueType } from './types';
|
||||
import { JsonSchema } from '../type-selector';
|
||||
|
||||
let _id = 0;
|
||||
function genId() {
|
||||
@ -100,7 +100,7 @@ export function usePropertiesEdit(
|
||||
const next = updater(_list);
|
||||
|
||||
// onChange to parent
|
||||
const nextProperties: Record<string, JsonSchema> = {};
|
||||
const nextProperties: Record<string, IJsonSchema> = {};
|
||||
const nextRequired: string[] = [];
|
||||
|
||||
for (const _property of next) {
|
||||
|
||||
@ -10,8 +10,8 @@ import {
|
||||
IconMinus,
|
||||
} from '@douyinfe/semi-icons';
|
||||
|
||||
import { JsonSchema } from '../type-selector/types';
|
||||
import { TypeSelector } from '../type-selector';
|
||||
import { IJsonSchema } from '../../typings';
|
||||
import { ConfigType, PropertyValueType } from './types';
|
||||
import {
|
||||
IconAddChildren,
|
||||
@ -33,8 +33,8 @@ import { UIRow } from './styles';
|
||||
import { usePropertiesEdit } from './hooks';
|
||||
|
||||
export function JsonSchemaEditor(props: {
|
||||
value?: JsonSchema;
|
||||
onChange?: (value: JsonSchema) => void;
|
||||
value?: IJsonSchema;
|
||||
onChange?: (value: IJsonSchema) => void;
|
||||
config?: ConfigType;
|
||||
}) {
|
||||
const { value = { type: 'object' }, config = {}, onChange: onChangeProps } = props;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { JsonSchema } from '../type-selector/types';
|
||||
import { IJsonSchema } from '../../typings';
|
||||
|
||||
export interface PropertyValueType extends JsonSchema {
|
||||
export interface PropertyValueType extends IJsonSchema {
|
||||
name?: string;
|
||||
key?: number;
|
||||
isPropertyRequired?: boolean;
|
||||
@ -8,7 +8,7 @@ export interface PropertyValueType extends JsonSchema {
|
||||
|
||||
export type PropertiesValueType = Pick<PropertyValueType, 'properties' | 'required'>;
|
||||
|
||||
export type JsonSchemaProperties = JsonSchema['properties'];
|
||||
export type JsonSchemaProperties = IJsonSchema['properties'];
|
||||
|
||||
export interface ConfigType {
|
||||
placeholder?: string;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "type-selector",
|
||||
"depMaterials": [],
|
||||
"depMaterials": ["typings/json-schema"],
|
||||
"depPackages": ["@douyinfe/semi-ui", "@douyinfe/semi-icons"]
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import React from 'react';
|
||||
import { CascaderData } from '@douyinfe/semi-ui/lib/es/cascader';
|
||||
import Icon from '@douyinfe/semi-icons';
|
||||
|
||||
import { JsonSchema } from './types';
|
||||
import { IJsonSchema } from '../../typings';
|
||||
|
||||
export const VariableTypeIcons: { [key: string]: React.ReactNode } = {
|
||||
custom: (
|
||||
@ -271,7 +271,7 @@ export const ArrayIcons: { [key: string]: React.ReactNode } = {
|
||||
),
|
||||
};
|
||||
|
||||
export const getSchemaIcon = (value?: Partial<JsonSchema>) => {
|
||||
export const getSchemaIcon = (value?: Partial<IJsonSchema>) => {
|
||||
if (value?.type === 'array') {
|
||||
return ArrayIcons[value.items?.type || 'object'];
|
||||
}
|
||||
|
||||
@ -2,17 +2,17 @@ import React, { useMemo } from 'react';
|
||||
|
||||
import { Button, Cascader } from '@douyinfe/semi-ui';
|
||||
|
||||
import { JsonSchema } from './types';
|
||||
import { IJsonSchema } from '../../typings';
|
||||
import { ArrayIcons, VariableTypeIcons, getSchemaIcon, options } from './constants';
|
||||
|
||||
interface PropTypes {
|
||||
value?: Partial<JsonSchema>;
|
||||
onChange: (value?: Partial<JsonSchema>) => void;
|
||||
value?: Partial<IJsonSchema>;
|
||||
onChange: (value?: Partial<IJsonSchema>) => void;
|
||||
disabled?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export const getTypeSelectValue = (value?: Partial<JsonSchema>): string[] | undefined => {
|
||||
export const getTypeSelectValue = (value?: Partial<IJsonSchema>): string[] | undefined => {
|
||||
if (value?.type === 'array' && value?.items) {
|
||||
return [value.type, ...(getTypeSelectValue(value.items) || [])];
|
||||
}
|
||||
@ -20,7 +20,7 @@ export const getTypeSelectValue = (value?: Partial<JsonSchema>): string[] | unde
|
||||
return value?.type ? [value.type] : undefined;
|
||||
};
|
||||
|
||||
export const parseTypeSelectValue = (value?: string[]): Partial<JsonSchema> | undefined => {
|
||||
export const parseTypeSelectValue = (value?: string[]): Partial<IJsonSchema> | undefined => {
|
||||
const [type, ...subTypes] = value || [];
|
||||
|
||||
if (type === 'array') {
|
||||
@ -54,4 +54,4 @@ export function TypeSelector(props: PropTypes) {
|
||||
);
|
||||
}
|
||||
|
||||
export { JsonSchema, VariableTypeIcons, ArrayIcons, getSchemaIcon };
|
||||
export { VariableTypeIcons, ArrayIcons, getSchemaIcon };
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
import { VarJSONSchema } from '@flowgram.ai/editor';
|
||||
|
||||
export type BasicType = VarJSONSchema.BasicType;
|
||||
|
||||
export type JsonSchema = VarJSONSchema.ISchema;
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "variable-selector",
|
||||
"depMaterials": ["type-selector"],
|
||||
"depMaterials": ["type-selector", "utils/json-schema", "typings/json-schema"],
|
||||
"depPackages": ["@douyinfe/semi-ui", "styled-components"]
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { VarJSONSchema } from '@flowgram.ai/editor';
|
||||
import { TriggerRenderProps } from '@douyinfe/semi-ui/lib/es/treeSelect';
|
||||
import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
|
||||
import { IconChevronDownStroked, IconIssueStroked } from '@douyinfe/semi-icons';
|
||||
|
||||
import { IJsonSchema } from '../../typings/json-schema';
|
||||
import { useVariableTree } from './use-variable-tree';
|
||||
import { UIRootTitle, UITag, UITreeSelect } from './styles';
|
||||
|
||||
@ -15,8 +15,8 @@ interface PropTypes {
|
||||
notFoundContent?: string;
|
||||
};
|
||||
onChange: (value?: string[]) => void;
|
||||
includeSchema?: VarJSONSchema.ISchema | VarJSONSchema.ISchema[];
|
||||
excludeSchema?: VarJSONSchema.ISchema | VarJSONSchema.ISchema[];
|
||||
includeSchema?: IJsonSchema | IJsonSchema[];
|
||||
excludeSchema?: IJsonSchema | IJsonSchema[];
|
||||
readonly?: boolean;
|
||||
hasError?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { useScopeAvailable, ASTMatch, BaseVariableField, VarJSONSchema } from '@flowgram.ai/editor';
|
||||
import { useScopeAvailable, ASTMatch, BaseVariableField } from '@flowgram.ai/editor';
|
||||
import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
|
||||
import { Icon } from '@douyinfe/semi-ui';
|
||||
|
||||
import { ArrayIcons, VariableTypeIcons } from '../type-selector/constants';
|
||||
import { JsonSchemaUtils } from '../../utils/json-schema';
|
||||
import { IJsonSchema } from '../../typings/json-schema';
|
||||
|
||||
type VariableField = BaseVariableField<{ icon?: string | JSX.Element; title?: string }>;
|
||||
|
||||
export function useVariableTree(params: {
|
||||
includeSchema?: VarJSONSchema.ISchema | VarJSONSchema.ISchema[];
|
||||
excludeSchema?: VarJSONSchema.ISchema | VarJSONSchema.ISchema[];
|
||||
includeSchema?: IJsonSchema | IJsonSchema[];
|
||||
excludeSchema?: IJsonSchema | IJsonSchema[];
|
||||
}): TreeNodeData[] {
|
||||
const { includeSchema, excludeSchema } = params;
|
||||
|
||||
@ -68,8 +70,12 @@ export function useVariableTree(params: {
|
||||
const keyPath = [...parentFields.map((_field) => _field.key), variable.key];
|
||||
const key = keyPath.join('.');
|
||||
|
||||
const isSchemaInclude = includeSchema ? type.isEqualWithJSONSchema(includeSchema) : true;
|
||||
const isSchemaExclude = excludeSchema ? type.isEqualWithJSONSchema(excludeSchema) : false;
|
||||
const isSchemaInclude = includeSchema
|
||||
? JsonSchemaUtils.isASTMatchSchema(type, includeSchema)
|
||||
: true;
|
||||
const isSchemaExclude = excludeSchema
|
||||
? JsonSchemaUtils.isASTMatchSchema(type, excludeSchema)
|
||||
: false;
|
||||
const isSchemaMatch = isSchemaInclude && !isSchemaExclude;
|
||||
|
||||
// If not match, and no children, return null
|
||||
|
||||
@ -1 +1,2 @@
|
||||
export * from './flow-value';
|
||||
export * from './json-schema';
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "json-schema",
|
||||
"depMaterials": [],
|
||||
"depPackages": []
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
export type JsonSchemaBasicType =
|
||||
| 'boolean'
|
||||
| 'string'
|
||||
| 'integer'
|
||||
| 'number'
|
||||
| 'object'
|
||||
| 'array'
|
||||
| 'map';
|
||||
|
||||
export interface IJsonSchema<T = string> {
|
||||
type?: T;
|
||||
default?: any;
|
||||
title?: string;
|
||||
description?: string;
|
||||
enum?: (string | number)[];
|
||||
properties?: Record<string, IJsonSchema<T>>;
|
||||
additionalProperties?: IJsonSchema<T>;
|
||||
items?: IJsonSchema<T>;
|
||||
required?: string[];
|
||||
$ref?: string;
|
||||
extra?: {
|
||||
index?: number;
|
||||
// Used in BaseType.isEqualWithJSONSchema, the type comparison will be weak
|
||||
weak?: boolean;
|
||||
// Set the render component
|
||||
formComponent?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
export type IBasicJsonSchema = IJsonSchema<JsonSchemaBasicType>;
|
||||
@ -1 +1,2 @@
|
||||
export * from './format-legacy-refs';
|
||||
export * from './json-schema';
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "json-schema",
|
||||
"depMaterials": ["typings/json-schema"],
|
||||
"depPackages": ["lodash"]
|
||||
}
|
||||
154
packages/materials/form-materials/src/utils/json-schema/index.ts
Normal file
154
packages/materials/form-materials/src/utils/json-schema/index.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import { get } from 'lodash';
|
||||
import { ASTFactory, ASTKind, ASTMatch, ASTNode, ASTNodeJSON, BaseType } from '@flowgram.ai/editor';
|
||||
|
||||
import { IJsonSchema } from '../../typings/json-schema';
|
||||
|
||||
export namespace JsonSchemaUtils {
|
||||
/**
|
||||
* Converts a JSON schema to an Abstract Syntax Tree (AST) representation.
|
||||
* This function recursively processes the JSON schema and creates corresponding AST nodes.
|
||||
*
|
||||
* For more information on JSON Schema, refer to the official documentation:
|
||||
* https://json-schema.org/
|
||||
*
|
||||
* @param jsonSchema - The JSON schema to convert.
|
||||
* @returns An AST node representing the JSON schema, or undefined if the schema type is not recognized.
|
||||
*/
|
||||
export function schemaToAST(jsonSchema: IJsonSchema): ASTNodeJSON | undefined {
|
||||
const { type, extra } = jsonSchema || {};
|
||||
const { weak = false } = extra || {};
|
||||
|
||||
if (!type) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'object':
|
||||
if (weak) {
|
||||
return { kind: ASTKind.Object, weak: true };
|
||||
}
|
||||
return ASTFactory.createObject({
|
||||
properties: Object.entries(jsonSchema.properties || {})
|
||||
/**
|
||||
* Sorts the properties of a JSON schema based on the 'extra.index' field.
|
||||
* If the 'extra.index' field is not present, the property will be treated as having an index of 0.
|
||||
*/
|
||||
.sort((a, b) => (get(a?.[1], 'extra.index') || 0) - (get(b?.[1], 'extra.index') || 0))
|
||||
.map(([key, _property]) => ({
|
||||
key,
|
||||
type: schemaToAST(_property),
|
||||
meta: { description: _property.description },
|
||||
})),
|
||||
});
|
||||
case 'array':
|
||||
if (weak) {
|
||||
return { kind: ASTKind.Array, weak: true };
|
||||
}
|
||||
return ASTFactory.createArray({
|
||||
items: schemaToAST(jsonSchema.items!),
|
||||
});
|
||||
case 'map':
|
||||
if (weak) {
|
||||
return { kind: ASTKind.Map, weak: true };
|
||||
}
|
||||
return ASTFactory.createMap({
|
||||
valueType: schemaToAST(jsonSchema.additionalProperties!),
|
||||
});
|
||||
case 'string':
|
||||
return ASTFactory.createString();
|
||||
case 'number':
|
||||
return ASTFactory.createNumber();
|
||||
case 'boolean':
|
||||
return ASTFactory.createBoolean();
|
||||
case 'integer':
|
||||
return ASTFactory.createInteger();
|
||||
|
||||
default:
|
||||
// If the type is not recognized, return CustomType
|
||||
return ASTFactory.createCustomType({ typeName: type });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert AST To JSON Schema
|
||||
* @param typeAST
|
||||
* @returns
|
||||
*/
|
||||
export function astToSchema(typeAST: ASTNode): IJsonSchema | undefined {
|
||||
if (ASTMatch.isString(typeAST)) {
|
||||
return {
|
||||
type: 'string',
|
||||
};
|
||||
}
|
||||
|
||||
if (ASTMatch.isBoolean(typeAST)) {
|
||||
return {
|
||||
type: 'boolean',
|
||||
};
|
||||
}
|
||||
|
||||
if (ASTMatch.isNumber(typeAST)) {
|
||||
return {
|
||||
type: 'number',
|
||||
};
|
||||
}
|
||||
|
||||
if (ASTMatch.isInteger(typeAST)) {
|
||||
return {
|
||||
type: 'integer',
|
||||
};
|
||||
}
|
||||
|
||||
if (ASTMatch.isObject(typeAST)) {
|
||||
return {
|
||||
type: 'object',
|
||||
properties: Object.fromEntries(
|
||||
Object.entries(typeAST.properties).map(([key, value]) => [key, astToSchema(value)!])
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (ASTMatch.isArray(typeAST)) {
|
||||
return {
|
||||
type: 'array',
|
||||
items: astToSchema(typeAST.items),
|
||||
};
|
||||
}
|
||||
|
||||
if (ASTMatch.isMap(typeAST)) {
|
||||
return {
|
||||
type: 'map',
|
||||
items: astToSchema(typeAST.valueType),
|
||||
};
|
||||
}
|
||||
|
||||
if (ASTMatch.isCustomType(typeAST)) {
|
||||
return {
|
||||
type: typeAST.typeName,
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the AST type is match the JSON Schema
|
||||
* @param typeAST
|
||||
* @param schema
|
||||
* @returns
|
||||
*/
|
||||
export function isASTMatchSchema(
|
||||
typeAST: BaseType,
|
||||
schema: IJsonSchema | IJsonSchema[]
|
||||
): boolean {
|
||||
if (Array.isArray(schema)) {
|
||||
return typeAST.isTypeEqual(
|
||||
ASTFactory.createUnion({
|
||||
types: schema.map((_schema) => schemaToAST(_schema)!).filter(Boolean),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return typeAST.isTypeEqual(schemaToAST(schema));
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,5 @@
|
||||
import { get } from 'lodash';
|
||||
|
||||
import { ASTKind, ASTNodeJSON } from './types';
|
||||
import { MapJSON } from './type/map';
|
||||
import { VarJSONSchema } from './type/json-schema';
|
||||
import { ArrayJSON } from './type/array';
|
||||
import { CustomTypeJSON, ObjectJSON, UnionJSON } from './type';
|
||||
import {
|
||||
@ -77,74 +74,6 @@ export namespace ASTFactory {
|
||||
...json,
|
||||
});
|
||||
|
||||
/**
|
||||
* Converts a JSON schema to an Abstract Syntax Tree (AST) representation.
|
||||
* This function recursively processes the JSON schema and creates corresponding AST nodes.
|
||||
*
|
||||
* For more information on JSON Schema, refer to the official documentation:
|
||||
* https://json-schema.org/
|
||||
*
|
||||
*
|
||||
* @param jsonSchema - The JSON schema to convert.
|
||||
* @returns An AST node representing the JSON schema, or undefined if the schema type is not recognized.
|
||||
*/
|
||||
export function createTypeASTFromSchema(
|
||||
jsonSchema: VarJSONSchema.ISchema
|
||||
): ASTNodeJSON | undefined {
|
||||
const { type, extra } = jsonSchema || {};
|
||||
const { weak = false } = extra || {};
|
||||
|
||||
if (!type) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'object':
|
||||
if (weak) {
|
||||
return { kind: ASTKind.Object, weak: true };
|
||||
}
|
||||
return ASTFactory.createObject({
|
||||
properties: Object.entries(jsonSchema.properties || {})
|
||||
/**
|
||||
* Sorts the properties of a JSON schema based on the 'extra.index' field.
|
||||
* If the 'extra.index' field is not present, the property will be treated as having an index of 0.
|
||||
*/
|
||||
.sort((a, b) => (get(a?.[1], 'extra.index') || 0) - (get(b?.[1], 'extra.index') || 0))
|
||||
.map(([key, _property]) => ({
|
||||
key,
|
||||
type: createTypeASTFromSchema(_property),
|
||||
meta: { description: _property.description },
|
||||
})),
|
||||
});
|
||||
case 'array':
|
||||
if (weak) {
|
||||
return { kind: ASTKind.Array, weak: true };
|
||||
}
|
||||
return ASTFactory.createArray({
|
||||
items: createTypeASTFromSchema(jsonSchema.items!),
|
||||
});
|
||||
case 'map':
|
||||
if (weak) {
|
||||
return { kind: ASTKind.Map, weak: true };
|
||||
}
|
||||
return ASTFactory.createMap({
|
||||
valueType: createTypeASTFromSchema(jsonSchema.additionalProperties!),
|
||||
});
|
||||
case 'string':
|
||||
return ASTFactory.createString();
|
||||
case 'number':
|
||||
return ASTFactory.createNumber();
|
||||
case 'boolean':
|
||||
return ASTFactory.createBoolean();
|
||||
case 'integer':
|
||||
return ASTFactory.createInteger();
|
||||
|
||||
default:
|
||||
// If the type is not recognized, return CustomType
|
||||
return ASTFactory.createCustomType({ typeName: type });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 AST Class 创建
|
||||
*/
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
import { parseTypeJsonOrKind } from '../utils/helpers';
|
||||
import { VarJSONSchema } from './json-schema';
|
||||
import { ASTKind, ASTNodeJSON, ASTNodeJSONOrKind } from '../types';
|
||||
import { ASTNodeFlags } from '../flags';
|
||||
import { BaseVariableField } from '../declaration';
|
||||
import { ASTNode } from '../ast-node';
|
||||
import { UnionJSON } from './union';
|
||||
import { ASTFactory } from '../factory';
|
||||
|
||||
export abstract class BaseType<JSON extends ASTNodeJSON = any, InjectOpts = any> extends ASTNode<
|
||||
JSON,
|
||||
@ -47,31 +45,4 @@ export abstract class BaseType<JSON extends ASTNodeJSON = any, InjectOpts = any>
|
||||
kind: this.kind,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Standard JSON Schema for current base type
|
||||
* @returns
|
||||
*/
|
||||
toJSONSchema(): VarJSONSchema.ISchema {
|
||||
return {
|
||||
type: this.kind.toLowerCase() as VarJSONSchema.BasicType,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the type is equal with json schema
|
||||
*/
|
||||
isEqualWithJSONSchema(schema: VarJSONSchema.ISchema | VarJSONSchema.ISchema[]): boolean {
|
||||
if (Array.isArray(schema)) {
|
||||
return this.isTypeEqual(
|
||||
ASTFactory.createUnion({
|
||||
types: schema
|
||||
.map((_schema) => ASTFactory.createTypeASTFromSchema(_schema)!)
|
||||
.filter(Boolean),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return this.isTypeEqual(ASTFactory.createTypeASTFromSchema(schema));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { VarJSONSchema } from './json-schema';
|
||||
import { ASTKind } from '../types';
|
||||
import { BaseType } from './base-type';
|
||||
|
||||
@ -8,10 +7,4 @@ export class BooleanType extends BaseType {
|
||||
fromJSON(): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
toJSONSchema(): VarJSONSchema.ISchema {
|
||||
return {
|
||||
type: 'boolean',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { parseTypeJsonOrKind } from '../utils/helpers';
|
||||
import { VarJSONSchema } from './json-schema';
|
||||
import { ASTKind, ASTNodeJSONOrKind } from '../types';
|
||||
import { type UnionJSON } from './union';
|
||||
import { BaseType } from './base-type';
|
||||
@ -36,10 +35,4 @@ export class CustomType extends BaseType<CustomTypeJSON> {
|
||||
|
||||
return targetTypeJSON?.kind === this.kind && targetTypeJSON?.typeName === this.typeName;
|
||||
}
|
||||
|
||||
toJSONSchema(): VarJSONSchema.ISchema {
|
||||
return {
|
||||
type: this._typeName,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,4 +12,3 @@ export {
|
||||
export { BaseType } from './base-type';
|
||||
export { type UnionJSON } from './union';
|
||||
export { CustomType, type CustomTypeJSON } from './custom-type';
|
||||
export { VarJSONSchema } from './json-schema';
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { VarJSONSchema } from './json-schema';
|
||||
import { ASTKind } from '../types';
|
||||
import { ASTNodeFlags } from '../flags';
|
||||
import { BaseType } from './base-type';
|
||||
@ -11,10 +10,4 @@ export class IntegerType extends BaseType {
|
||||
fromJSON(): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
toJSONSchema(): VarJSONSchema.ISchema {
|
||||
return {
|
||||
type: 'integer',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
export namespace VarJSONSchema {
|
||||
export type BasicType = 'boolean' | 'string' | 'integer' | 'number' | 'object' | 'array' | 'map';
|
||||
|
||||
export interface ISchema<T = string> {
|
||||
type?: T;
|
||||
default?: any;
|
||||
title?: string;
|
||||
description?: string;
|
||||
enum?: (string | number)[];
|
||||
properties?: Record<string, ISchema<T>>;
|
||||
additionalProperties?: ISchema<T>;
|
||||
items?: ISchema<T>;
|
||||
required?: string[];
|
||||
$ref?: string;
|
||||
extra?: {
|
||||
index?: number;
|
||||
// Used in BaseType.isEqualWithJSONSchema, the type comparison will be weak
|
||||
weak?: boolean;
|
||||
// Set the render component
|
||||
formComponent?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
export type IBasicSchema = ISchema<BasicType>;
|
||||
}
|
||||
@ -1,5 +1,4 @@
|
||||
import { parseTypeJsonOrKind } from '../utils/helpers';
|
||||
import { VarJSONSchema } from './json-schema';
|
||||
import { ASTKind, ASTNodeJSON, ASTNodeJSONOrKind } from '../types';
|
||||
import { BaseType } from './base-type';
|
||||
|
||||
@ -75,11 +74,4 @@ export class MapType extends BaseType<MapJSON> {
|
||||
valueType: this.valueType?.toJSON(),
|
||||
};
|
||||
}
|
||||
|
||||
toJSONSchema(): VarJSONSchema.ISchema {
|
||||
return {
|
||||
type: 'map',
|
||||
additionalProperties: this.valueType?.toJSONSchema(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { VarJSONSchema } from './json-schema';
|
||||
import { ASTKind } from '../types';
|
||||
import { BaseType } from './base-type';
|
||||
|
||||
@ -8,10 +7,4 @@ export class NumberType extends BaseType {
|
||||
fromJSON(): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
toJSONSchema(): VarJSONSchema.ISchema {
|
||||
return {
|
||||
type: 'number',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { xor } from 'lodash';
|
||||
|
||||
import { parseTypeJsonOrKind } from '../utils/helpers';
|
||||
import { VarJSONSchema } from './json-schema';
|
||||
import { ASTNodeJSON, ASTKind, ASTNodeJSONOrKind, type GlobalEventActionType } from '../types';
|
||||
import { ASTNodeFlags } from '../flags';
|
||||
import { Property, type PropertyJSON } from '../declaration/property';
|
||||
@ -149,14 +148,4 @@ export class ObjectType extends BaseType<ObjectJSON> {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
toJSONSchema(): VarJSONSchema.ISchema {
|
||||
return {
|
||||
type: 'object',
|
||||
properties: this.properties.reduce((acc, _property) => {
|
||||
acc[_property.key] = _property.type?.toJSONSchema();
|
||||
return acc;
|
||||
}, {} as Record<string, VarJSONSchema.ISchema>),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { VarJSONSchema } from './json-schema';
|
||||
import { ASTKind } from '../types';
|
||||
import { ASTNodeFlags } from '../flags';
|
||||
import { BaseType } from './base-type';
|
||||
@ -11,10 +10,4 @@ export class StringType extends BaseType {
|
||||
fromJSON(): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
toJSONSchema(): VarJSONSchema.ISchema {
|
||||
return {
|
||||
type: 'string',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user