Skip to content

Commit

Permalink
feat(typings): create correct typings from jsdoc
Browse files Browse the repository at this point in the history
  • Loading branch information
Heymdall committed Jun 14, 2018
1 parent c9d0a19 commit 6136e90
Showing 1 changed file with 51 additions and 20 deletions.
71 changes: 51 additions & 20 deletions typings/stringify-component-definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,32 @@ const parseJsDoc = require('react-docgen/dist/utils/parseJsDoc').default;

function stringifyType(type, componentName, propName, description, typeRefs) {
const typeName = `${componentName}${upperCamelCase(propName)}FieldType`;

if (typeof type === 'string' || !type) {
type = { name: type };
}

switch (type.name) {
case 'string':
case 'String':
return 'string';
case 'number':
case 'Number':
return 'number';
case 'bool':
case 'boolean':
case 'Boolean':
return 'boolean';
case 'array':
case 'Array':
return 'ReadonlyArray<any>';
case 'node':
return 'ReactNode';
case 'union':
typeRefs.push(`export type ${typeName} = ${stringifyUnion(type, componentName, propName, typeRefs)};`);
return typeName;
case 'func':
return stringifyFunc(type, componentName, propName, description);
return stringifyFunc(type, componentName, propName, description, typeRefs);
case 'enum':
typeRefs.push(`export type ${typeName} = ${stringifyEnum(type)};`);
return typeName;
Expand All @@ -40,6 +50,11 @@ function stringifyType(type, componentName, propName, description, typeRefs) {
return 'object';
case 'any':
return 'any';
case 'Event':
case 'HTMLElement':
case 'Date':
case 'File':
return type.name;
default:
return 'any' +
`/* Не нашёлся встроенный тип для типа ${JSON.stringify(type)}
Expand Down Expand Up @@ -67,31 +82,41 @@ function stringifyDescription(description, docblock) {
*/\n`;
}

function stringifyFunc(type, componentName, propName, description) {
function stringifyFunc(type, componentName, propName, description, typeRefs) {
if (!description) {
return 'Function';
}
try {
if (!description) {
return 'Function';
}
const parsedDoc = parseJsDoc(description);
if (!parsedDoc || (parsedDoc.params.length === 0 && parsedDoc.returns === null)) {
return 'Function';
}

const paramsTypes = parsedDoc.params
.map(p => `${p.name}?: any /*${p.type ? p.type.name : 'any'}*/`);

const returnType = parsedDoc.returns
? parsedDoc.returns.type.name
: 'void';

return `(${paramsTypes.join(', ')}) => any /*${returnType}*/`;
return stringifyFunctionDefinition(parsedDoc, componentName, propName, description, typeRefs, true);
} catch (e) {
// eslint-disable-next-line no-console
console.warn(`Unable to parse doc block for ${componentName}.${propName}`);
return 'Function';
}
}

function stringifyFunctionDefinition(type, componentName, propName, description, typeRefs, useArrowNotation = false) {
if (!type || (type.params.length === 0 && type.returns === null)) {
return useArrowNotation ? 'Function' : '(...args: any[]): any';
}

const paramsTypes = type.params
.map((p) => {
const type = p.type
? stringifyType(p.type, componentName, `${propName}${upperCamelCase(p.name)}Param`, null, typeRefs)
: 'any';
return `${p.name}?: ${type}`;
});

const returnType = type.returns
? stringifyType(type.returns.type || type.returns, componentName, `${propName}Return`, null, typeRefs)
: 'void';

return `(${paramsTypes.join(', ')}) ${useArrowNotation ? '=>' : ':'} ${returnType}`;
}

function stringifyField(fieldName, type, componentName, propName, typeRefs) {
const typeDescription = stringifyType(
type, componentName, `${propName}${upperCamelCase(fieldName)}`, type.description, typeRefs
Expand Down Expand Up @@ -121,9 +146,11 @@ function stringifyObjectOf(type, componentName, propName, typeRefs) {
}`;
}

function stringifyMethod({ name, docblock, params, description }) { // eslint-disable-line object-curly-newline
return stringifyDescription(description, docblock) + // eslint-disable-line prefer-template
`${name}(${params.map(({ name }) => `${name}?: any`).join(',')}): any;`;
function stringifyClassMethod(type, componentName, typeRefs) {
const typeDef = stringifyFunctionDefinition(type, componentName, type.name, null, typeRefs, false);
const description = stringifyDescription(type.description, type.docblock);

return `${description}${type.name}${typeDef};`;
}

function stringifyComponentDefinition(info) {
Expand All @@ -145,6 +172,10 @@ function stringifyComponentDefinition(info) {
`
/* eslint-enable indent, object-curly-newline */
);

const methodsDefs = info.methods
.map(type => stringifyClassMethod(type, info.displayName, typeRefs));

return (
`
import { Component, ReactNode } from 'react';
Expand All @@ -159,7 +190,7 @@ function stringifyComponentDefinition(info) {
${stringifyDescription(info.description, info.docblock)}
export default class ${info.displayName} extends Component<${propsInterfaceName}> {
static propTypes: ${propTypesTypeName};
${info.methods.map(stringifyMethod).join('')}
${methodsDefs.join('\n')}
}
`
);
Expand Down

0 comments on commit 6136e90

Please sign in to comment.