grommet/react-desc

View on GitHub
src/descToTypescript.js

Summary

Maintainability
B
6 hrs
Test Coverage
A
98%

const arrayFormat = array =>
  array.map(propType => propTypeFormat(propType));

const shapeFormat = (shape) => {
  const props = Object.keys(shape).map((key) => {
    const value = shape[key];
    let valueFormat;
    if (value.type &&
      ((value.type === 'arrayOf' || value.type === 'oneOfType' ||
        value.type === 'oneOf') && Array.isArray(value.args))) {
      valueFormat = `${propTypeFormat(value)}`;
    } else if (value.type === 'shape') {
      valueFormat = `${propTypeFormat(value)}`;
    } else {
      valueFormat = propTypeFormat(value);
    }
    return `${key}${value.reactDesc && value.reactDesc.required ? '' : '?'}: ${valueFormat}`;
  });
  return `{${props.join(',')}}`;
};

const propTypeFormat = (propType, joinWith) => {
  let result;
  if (Array.isArray(propType)) {
    result = arrayFormat(propType).join(joinWith);
  } else if (typeof propType !== 'function' && propType.type) {
    switch (propType.type) {
      case 'array':
        result = 'any[]';
        break;
      case 'arrayOf':
        if (propType.args.type === 'oneOfType') {
          result = `(${propTypeFormat(propType.args, ' | ')})[]`;
        } else {
          result = `${propTypeFormat(propType.args, '\n')}[]`;
        }
        break;
      case 'bool':
        result = 'boolean';
        break;
      case 'func':
        result = '((...args: any[]) => any)';
        break;
      case 'node':
        result = 'React.ReactNode';
        break;
      case 'element':
        result = 'JSX.Element';
        break;
      case 'instanceOf':
        result = 'any';
        break;
      case 'symbol':
        result = 'any';
        break;
      case 'objectOf':
        result = `{ [key: string]: ${propTypeFormat(propType.args)} }`;
        break;
      case 'oneOf':
        result = propType.args.map(a => `"${a}"`).join(' | ');
        break;
      case 'oneOfType':
        result = `${propTypeFormat(propType.args, ' | ')}`;
        break;
      case 'shape':
        result = `${shapeFormat(propType.args)}`;
        break;
      default:
        result = `${propType.type}`;
        break;
    }
  } else {
    result = 'any';
  }
  return result;
};

const propTypeAsTypescript = (propType, propName) => {
  const documentation = {
    ...propType.reactDesc,
    name: propName,
  };

  documentation.format = propTypeFormat(propType);

  return documentation;
};

export default function descToTypescript(component, reactDesc = {}) {
  if (!component) {
    throw new Error('react-desc: component is required');
  }

  const documentation = {
    name: component.displayName || component.name,
    ...reactDesc,
  };
  if (reactDesc) {
    delete documentation.propTypes;

    if (reactDesc.propTypes) {
      const propTypes = [];
      Object.keys(reactDesc.propTypes).forEach((propName) => {
        const propType = reactDesc.propTypes[propName];
        propTypes.push(
          propTypeAsTypescript(
            propType, propName,
          ),
        );
      });
      if (propTypes.length > 0) {
        documentation.properties = propTypes;
      }
    }
  }
  return documentation;
}