import PropTypes from 'prop-types'; import { isValidElement } from 'react'; import { isElement } from '@ali/lowcode-utils'; import { PropConfig } from '@ali/lowcode-types'; export const primitiveTypes = [ 'string', 'number', 'array', 'bool', 'func', 'object', 'node', 'element', 'symbol', 'any', ]; function makeRequired(propType: any, lowcodeType: string | object) { function lowcodeCheckTypeIsRequired(...rest: any[]) { return propType.isRequired(...rest); } if (typeof lowcodeType === 'string') { lowcodeType = { type: lowcodeType, }; } lowcodeCheckTypeIsRequired.lowcodeType = { ...lowcodeType, isRequired: true, }; return lowcodeCheckTypeIsRequired; } function define(propType: any = PropTypes.any, lowcodeType: string | object = {}) { if (!propType._inner && propType.name !== 'lowcodeCheckType') { propType.lowcodeType = lowcodeType; } function lowcodeCheckType(...rest: any[]) { return propType(...rest); } lowcodeCheckType.lowcodeType = lowcodeType; lowcodeCheckType.isRequired = makeRequired(propType, lowcodeType); return lowcodeCheckType; } const LowcodeTypes: any = { ...PropTypes, define, }; (window as any).PropTypes = LowcodeTypes; (window as any).React.PropTypes = LowcodeTypes; // override primitive type chechers primitiveTypes.forEach(type => { const propType = (PropTypes as any)[type]; if (!propType) { return; } propType._inner = true; LowcodeTypes[type] = define(propType, type); }); // You can ensure that your prop is limited to specific values by treating // it as an enum. LowcodeTypes.oneOf = (list: any[]) => { return define(PropTypes.oneOf(list), { type: 'oneOf', value: list, }); }; // An array of a certain type LowcodeTypes.arrayOf = (type: any) => { return define(PropTypes.arrayOf(type), { type: 'arrayOf', value: type.lowcodeType || 'any', }); }; // An object with property values of a certain type LowcodeTypes.objectOf = (type: any) => { return define(PropTypes.objectOf(type), { type: 'objectOf', value: type.lowcodeType || 'any', }); }; // An object that could be one of many types LowcodeTypes.oneOfType = (types: any[]) => { const itemTypes = types.map(type => type.lowcodeType || 'any'); return define(PropTypes.oneOfType(types), { type: 'oneOfType', value: itemTypes, }); }; // An object with warnings on extra properties LowcodeTypes.exact = (typesMap: any) => { const configs = Object.keys(typesMap).map(key => { return { name: key, propType: typesMap[key].lowcodeType || 'any', }; }); return define(PropTypes.exact(typesMap), { type: 'exact', value: configs, }); }; // An object taking on a particular shape LowcodeTypes.shape = (typesMap: any) => { const configs = Object.keys(typesMap).map(key => { return { name: key, propType: typesMap[key].lowcodeType || 'any', }; }); return define(PropTypes.shape(typesMap), { type: 'shape', value: configs, }); }; const BasicTypes = ['string', 'number', 'object']; export function parseProps(component: any): PropConfig[] { if (!component) { return []; } const propTypes = component.propTypes || ({} as any); const defaultProps = component.defaultProps || ({} as any); const result: any = {}; if (!propTypes) return []; Object.keys(propTypes).forEach(key => { const propTypeItem = propTypes[key]; const defaultValue = defaultProps[key]; const lowcodeType = propTypeItem.lowcodeType; if (lowcodeType) { result[key] = { name: key, propType: lowcodeType, }; if (defaultValue != null) { result[key].defaultValue = defaultValue; } return; } let i = primitiveTypes.length; while (i-- > 0) { const k = primitiveTypes[i]; if ((LowcodeTypes as any)[k] === propTypeItem) { result[key] = { name: key, propType: k, }; if (defaultValue != null) { result[key].defaultValue = defaultValue; } return; } } result[key] = { name: key, propType: 'any', }; if (defaultValue != null) { result[key].defaultValue = defaultValue; } }); Object.keys(defaultProps).forEach(key => { if (result[key]) return; const defaultValue = defaultProps[key]; let type: string = typeof defaultValue; if (type === 'boolean') { type = 'bool'; } else if (type === 'function') { type = 'func'; } else if (type === 'object' && Array.isArray(defaultValue)) { type = 'array'; } else if (defaultValue && isValidElement(defaultValue)) { type = 'node'; } else if (defaultValue && isElement(defaultValue)) { type = 'element'; } else if (!BasicTypes.includes(type)) { type = 'any'; } result[key] = { name: key, propType: type || 'any', defaultValue, }; }); return Object.keys(result).map(key => result[key]); } export function parseMetadata(component: any): any { return { props: parseProps(component), ...component.componentMetadata, }; }