mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-12 11:20:11 +00:00
307 lines
7.5 KiB
TypeScript
307 lines
7.5 KiB
TypeScript
import { omit, pick, isNil, uniq } from 'lodash';
|
|
import { safeEval, isEvaluable } from '../utils';
|
|
import { debug } from '../core';
|
|
|
|
const log = debug.extend('parse:transform');
|
|
|
|
export function transformType(itemType: any) {
|
|
if (typeof itemType === 'string') return itemType;
|
|
const {
|
|
name,
|
|
elements,
|
|
value = elements,
|
|
computed,
|
|
required,
|
|
type,
|
|
raw,
|
|
params,
|
|
returns,
|
|
} = itemType;
|
|
if (computed !== undefined && value) {
|
|
return safeEval(value);
|
|
}
|
|
const result: any = {
|
|
type: name,
|
|
};
|
|
if (required) {
|
|
result.isRequired = required;
|
|
}
|
|
switch (name) {
|
|
case 'number':
|
|
case 'string':
|
|
case 'bool':
|
|
case 'any':
|
|
case 'symbol':
|
|
case 'object':
|
|
case 'null':
|
|
case 'array':
|
|
case 'element':
|
|
case 'node':
|
|
case 'void':
|
|
break;
|
|
case 'func':
|
|
if (params) {
|
|
result.params = params.map((x) => {
|
|
const res: any = {
|
|
name: x.name,
|
|
propType: transformType(x.type || x.propType),
|
|
};
|
|
if (x.description) {
|
|
res.description = x.description;
|
|
}
|
|
return res;
|
|
});
|
|
}
|
|
if (returns) {
|
|
result.returns = {
|
|
propType: transformType(returns.type || returns.propType),
|
|
};
|
|
}
|
|
if (raw) {
|
|
result.raw = raw;
|
|
}
|
|
break;
|
|
case 'literal': {
|
|
result.type = 'oneOf';
|
|
try {
|
|
const literalValue = safeEval(value);
|
|
result.value = [literalValue];
|
|
} catch (e) {
|
|
result.value = [raw];
|
|
}
|
|
break;
|
|
}
|
|
case 'enum':
|
|
case 'oneOf':
|
|
result.type = 'oneOf';
|
|
result.value = value.map(transformType);
|
|
break;
|
|
case 'tuple':
|
|
result.type = 'tuple';
|
|
result.value = value.map(transformType);
|
|
break;
|
|
case 'union': {
|
|
if (itemType.raw) {
|
|
if (itemType.raw.match(/ReactNode$/)) {
|
|
result.type = 'node';
|
|
break;
|
|
} else if (itemType.raw.match(/Element$/)) {
|
|
result.type = 'element';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// eslint-disable-next-line no-fallthrough
|
|
case 'oneOfType':
|
|
result.type = 'oneOfType';
|
|
result.value = value.map(transformType);
|
|
break;
|
|
case 'boolean':
|
|
result.type = 'bool';
|
|
break;
|
|
case 'Function':
|
|
result.type = 'func';
|
|
break;
|
|
case 'unknown':
|
|
result.type = 'any';
|
|
break;
|
|
case 'Array':
|
|
case 'arrayOf': {
|
|
result.type = 'arrayOf';
|
|
let _itemType = transformType(value[0]);
|
|
if (typeof _itemType === 'object') {
|
|
_itemType = omit(_itemType, ['isRequired']);
|
|
}
|
|
|
|
result.value = _itemType;
|
|
break;
|
|
}
|
|
case 'signature': {
|
|
if (typeof type === 'string') {
|
|
result.type = type;
|
|
break;
|
|
}
|
|
result.type = 'shape';
|
|
const properties = type?.signature?.properties || itemType?.signature?.properties || [];
|
|
if (properties.length === 0) {
|
|
if (raw?.includes('=>')) {
|
|
result.type = 'func';
|
|
result.raw = raw;
|
|
} else {
|
|
result.type = 'object';
|
|
}
|
|
} else if (properties.length === 1 && typeof properties[0].key === 'object') {
|
|
const v = transformType(properties[0].value);
|
|
if (v === 'any') {
|
|
result.type = 'object';
|
|
} else if (typeof v === 'string') {
|
|
result.value = v;
|
|
result.type = 'objectOf';
|
|
} else if (typeof v?.type === 'string') {
|
|
result.value = v.type;
|
|
result.type = 'objectOf';
|
|
} else {
|
|
result.type = 'object';
|
|
}
|
|
} else if (properties.length === 1 && properties[0].key === '__call') {
|
|
result.type = 'func';
|
|
} else {
|
|
result.value = properties
|
|
.filter((item: any) => typeof item.key !== 'object')
|
|
.map((prop: any) => {
|
|
const { key } = prop;
|
|
const typeItem = {
|
|
...omit(prop.value, 'name'),
|
|
type: prop.value.type || {},
|
|
};
|
|
typeItem.type = {
|
|
...typeItem.type,
|
|
...pick(prop.value, ['name', 'value']),
|
|
};
|
|
return transformItem(key, typeItem);
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
case 'objectOf':
|
|
case 'instanceOf':
|
|
result.value = transformType(value);
|
|
break;
|
|
case 'exact':
|
|
case 'shape':
|
|
result.value = Object.keys(value).map((n) => {
|
|
const { name: _name, ...others } = value[n];
|
|
return transformItem(n, {
|
|
...others,
|
|
type: {
|
|
name: _name,
|
|
},
|
|
});
|
|
});
|
|
break;
|
|
case (name.match(/ReactNode$/) || {}).input:
|
|
result.type = 'node';
|
|
break;
|
|
case (name.match(/JSX\.Element$/) || {}).input:
|
|
result.type = 'element';
|
|
break;
|
|
default:
|
|
result.type = 'object';
|
|
break;
|
|
}
|
|
if (Object.keys(result).length === 1) {
|
|
return result.type;
|
|
}
|
|
if (result?.type === 'oneOfType') {
|
|
return combineOneOfValues(result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function combineOneOfValues(propType) {
|
|
if (propType.type !== 'oneOfType') {
|
|
return propType;
|
|
}
|
|
const newValue = [];
|
|
let oneOfItem = null;
|
|
let firstBooleanIndex = -1;
|
|
propType.value.forEach((item) => {
|
|
if (item?.type === 'oneOf') {
|
|
if (!oneOfItem) {
|
|
oneOfItem = {
|
|
type: 'oneOf',
|
|
value: [],
|
|
};
|
|
}
|
|
if (item.value.includes(true) || item.value.includes(false)) {
|
|
if (firstBooleanIndex !== -1) {
|
|
oneOfItem.value.splice(firstBooleanIndex, 1);
|
|
newValue.push('bool');
|
|
} else {
|
|
firstBooleanIndex = oneOfItem.value.length;
|
|
oneOfItem.value = oneOfItem.value.concat(item.value);
|
|
}
|
|
} else {
|
|
oneOfItem.value = oneOfItem.value.concat(item.value);
|
|
}
|
|
} else {
|
|
newValue.push(item);
|
|
}
|
|
});
|
|
let result = propType;
|
|
const oneOfItemLength = oneOfItem?.value?.length;
|
|
if (oneOfItemLength) {
|
|
newValue.push(oneOfItem);
|
|
}
|
|
if (firstBooleanIndex !== -1 || oneOfItemLength) {
|
|
result = {
|
|
...propType,
|
|
value: newValue,
|
|
};
|
|
}
|
|
if (result.value.length === 1 && result.value[0]?.type === 'oneOf') {
|
|
result = {
|
|
...result,
|
|
type: 'oneOf',
|
|
value: result.value[0].value,
|
|
};
|
|
}
|
|
result.value = uniq(result.value);
|
|
return result;
|
|
}
|
|
|
|
export function transformItem(name: string, item: any) {
|
|
const {
|
|
description,
|
|
flowType,
|
|
tsType,
|
|
type = tsType || flowType,
|
|
optional,
|
|
required = optional,
|
|
defaultValue,
|
|
...others
|
|
} = item;
|
|
const result: any = {
|
|
name,
|
|
};
|
|
|
|
if (type) {
|
|
result.propType = transformType({
|
|
...type,
|
|
...omit(others, ['name']),
|
|
required: !!required,
|
|
});
|
|
}
|
|
if (description) {
|
|
if (description.includes('\n')) {
|
|
result.description = description.split('\n')[0];
|
|
} else {
|
|
result.description = description;
|
|
}
|
|
}
|
|
if (!isNil(defaultValue) && typeof defaultValue === 'object' && isEvaluable(defaultValue)) {
|
|
if (defaultValue === null) {
|
|
result.defaultValue = defaultValue;
|
|
} else if ('computed' in defaultValue) {
|
|
// parsed data from react-docgen
|
|
try {
|
|
if (isEvaluable(defaultValue.value)) {
|
|
result.defaultValue = safeEval(defaultValue.value);
|
|
} else {
|
|
result.defaultValue = defaultValue.value;
|
|
}
|
|
} catch (e) {
|
|
log(e);
|
|
}
|
|
} else {
|
|
// parsed data from react-docgen-typescript
|
|
result.defaultValue = defaultValue.value;
|
|
}
|
|
}
|
|
if (result.propType === undefined) {
|
|
delete result.propType;
|
|
}
|
|
|
|
return result;
|
|
}
|