feat: support params & returns of func propType

This commit is contained in:
gengyang 2020-10-19 20:28:09 +08:00
parent 0b80be68ae
commit 0e46e49c42
12 changed files with 22521 additions and 11480 deletions

View File

@ -1,18 +1,46 @@
/* eslint-disable no-param-reassign */
import { set, get } from 'lodash';
import { debug } from '../../../core';
const log = debug.extend('parse:js');
const parseJsDoc = require('react-docgen/dist/utils/parseJsDoc').default;
const { getMemberValuePath, resolveToValue } = require('react-docgen').utils;
function getType(type = 'void') {
const typeOfType = typeof type;
if (typeOfType === 'string') {
return typeOfType;
} else if (typeOfType === 'object') {
return get(type, 'name', 'void');
}
return 'void';
}
function generateRaw(params = [], returns = { type: 'void' }): string {
const raw = `(${params.filter(x => !!x).map(x => `${x.name}: ${getType(x.type)}`).join(', ')}) => ${returns ? getType(returns.type) : 'void'}`;
return raw;
}
function resolveDocumentation(documentation) {
documentation._props.forEach(propDescriptor => {
const { description } = propDescriptor;
if (description.includes('@')) {
if (description.includes('@') && propDescriptor?.type?.name === 'func') {
const jsDoc = parseJsDoc(description);
propDescriptor.description = jsDoc.description;
if (jsDoc.params) {
propDescriptor.params = jsDoc.params;
set(propDescriptor, ['type', 'params'], jsDoc.params);
}
if (jsDoc.returns) {
propDescriptor.returns = jsDoc.returns;
set(propDescriptor, ['type', 'returns'], jsDoc.returns);
}
try {
const raw = generateRaw(jsDoc.params, jsDoc.returns);
if (raw) {
set(propDescriptor, ['type', 'raw'], raw);
}
} catch (e) {
log(e);
}
}
});

View File

@ -37,19 +37,25 @@ export function transformType(itemType: any) {
case 'array':
case 'element':
case 'node':
case 'void':
break;
case 'func':
if (params) {
result.params = params.map(x => ({
...x,
propType: transformType(x.propType),
}));
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 = returns.map(x => ({
...x,
propType: transformType(x.propType),
}));
result.returns = {
propType: transformType(returns.type || returns.propType),
};
}
if (raw) {
result.raw = raw;
@ -243,7 +249,8 @@ export function transformItem(name: string, item: any) {
flowType,
tsType,
type = tsType || flowType,
required,
optional,
required = optional,
defaultValue,
...others
} = item;

View File

@ -1,5 +1,5 @@
import { Parser, ComponentDoc } from 'react-docgen-typescript';
import ts, { SymbolFlags, TypeFlags } from 'typescript';
import ts, { SymbolFlags, TypeFlags, SyntaxKind } from 'typescript';
import { isEmpty, isEqual } from 'lodash';
import { debug } from '../../core';
import { Json } from '../../types';
@ -43,20 +43,12 @@ function getFunctionParams(parameters: any[] = [], checker, parentIds, type) {
});
}
/**
* Indicates that a symbol is an alias that does not merge with a local declaration.
* OR Is a JSContainer which may merge an alias with a local declaration
*/
// function isNonLocalAlias(
// symbol: ts.Symbol | undefined,
// excludes = SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace,
// ): symbol is ts.Symbol {
// if (!symbol) return false;
// return (
// (symbol.flags & (SymbolFlags.Alias | excludes)) === SymbolFlags.Alias ||
// !!(symbol.flags & SymbolFlags.Alias && symbol.flags & SymbolFlags.Assignment)
// );
// }
function getFunctionReturns(node: any, checker, parentIds, type) {
const propType = getDocgenTypeHelper(checker, node.type, false, getNextParentIds(parentIds, type));
return {
propType,
};
}
const blacklistNames = [
'prototype',
@ -138,10 +130,10 @@ function getDocgenTypeHelper(
function getShapeFromArray(symbolArr: ts.Symbol[], _type: ts.Type) {
const shape: Array<{
key:
| {
name: string;
}
| string;
| {
name: string;
}
| string;
value: any;
}> = symbolArr.map(prop => {
const propType = checker.getTypeOfSymbolAtLocation(
@ -230,6 +222,14 @@ function getDocgenTypeHelper(
}
}
// @ts-ignore
if (type?.kind === SyntaxKind.VoidExpression) {
return makeResult({
name: 'void',
raw: 'void',
});
}
const pattern = /^__global\.(.+)$/;
// @ts-ignore
if (parentIds.includes(type?.symbol?.id)) {
@ -281,9 +281,7 @@ function getDocgenTypeHelper(
return makeResult({
name: 'union',
// @ts-ignore
value: type.types.map(t =>
getDocgenTypeHelper(checker, t, true, getNextParentIds(parentIds, type)),
),
value: type.types.map(t => getDocgenTypeHelper(checker, t, true, getNextParentIds(parentIds, type))),
});
} else if (isComplexType(type)) {
return makeResult({
@ -328,6 +326,12 @@ function getDocgenTypeHelper(
parentIds,
type,
),
returns: getFunctionReturns(
checker.typeToTypeNode(type, type?.symbol?.valueDeclaration),
checker,
parentIds,
type,
),
});
} else if (
// @ts-ignore

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -327,8 +327,27 @@ Generated by [AVA](https://avajs.dev).
propType: 'bool',
},
{
description: 'desc',
name: 'optionalFunc',
propType: 'func',
propType: {
params: [
{
description: 'The title of the book.',
name: 'title',
propType: 'string',
},
{
description: 'The author of the book.',
name: 'author',
propType: 'string',
},
],
raw: '(title: string, author: string) => any',
returns: {
propType: 'any',
},
type: 'func',
},
},
{
defaultValue: 123,
@ -547,6 +566,30 @@ Generated by [AVA](https://avajs.dev).
version: '1.0.0',
},
props: [
{
name: 'error',
propType: {
isRequired: true,
params: [
{
name: 'a',
propType: 'string',
},
],
raw: '(a: string) => number',
returns: {
propType: 'number',
},
type: 'func',
},
},
{
name: 'void',
propType: {
isRequired: true,
type: 'void',
},
},
{
name: 'object',
propType: {
@ -640,9 +683,7 @@ Generated by [AVA](https://avajs.dev).
{
name: 'fun',
propType: {
raw: '(a: string[]) => void',
type: 'func',
value: [
params: [
{
name: 'a',
propType: {
@ -651,6 +692,11 @@ Generated by [AVA](https://avajs.dev).
},
},
],
raw: '(a: string[]) => void',
returns: {
propType: 'number',
},
type: 'func',
},
},
],
@ -667,14 +713,17 @@ Generated by [AVA](https://avajs.dev).
name: 'func',
propType: {
isRequired: true,
raw: '{ (arg: string): number; (a: string): Element; }',
type: 'func',
value: [
params: [
{
name: 'arg',
propType: 'string',
},
],
raw: '(arg: string) => number',
returns: {
propType: 'number',
},
type: 'func',
},
},
{
@ -693,9 +742,7 @@ Generated by [AVA](https://avajs.dev).
{
name: 'a',
propType: {
raw: '(arg: string, num: number) => void',
type: 'func',
value: [
params: [
{
name: 'arg',
propType: 'string',
@ -705,6 +752,11 @@ Generated by [AVA](https://avajs.dev).
propType: 'number',
},
],
raw: '(arg: string, num: number) => void',
returns: {
propType: 'number',
},
type: 'func',
},
},
],
@ -778,14 +830,17 @@ Generated by [AVA](https://avajs.dev).
name: 'refFunc',
propType: {
isRequired: true,
raw: '(p: Props) => void',
type: 'func',
value: [
params: [
{
name: 'p',
propType: 'object',
},
],
raw: '(p: Props) => void',
returns: {
propType: 'number',
},
type: 'func',
},
},
{
@ -796,14 +851,14 @@ Generated by [AVA](https://avajs.dev).
value: [
'element',
{
raw: 'Func',
type: 'func',
value: [
params: [
{
name: 'a',
propType: 'string',
},
],
raw: 'Func',
type: 'func',
},
],
},
@ -908,14 +963,14 @@ Generated by [AVA](https://avajs.dev).
name: 'func2',
propType: {
isRequired: true,
raw: 'Func',
type: 'func',
value: [
params: [
{
name: 'a',
propType: 'string',
},
],
raw: 'Func',
type: 'func',
},
},
{

View File

@ -39,7 +39,8 @@ Demo.propTypes = {
optionalBool: PropTypes.bool,
/**
* desc
* @param {{ok:String}} userName
* @param {string} title - The title of the book.
* @param {string} author - The author of the book.
* @returns {any}
*/
optionalFunc: PropTypes.func,

View File

@ -16,7 +16,8 @@ Demo.propTypes = {
optionalBool: PropTypes.bool,
/**
* desc
* @param {{ok:String}} userName
* @param {string} title - The title of the book.
* @param {string} author - The author of the book.
* @returns {any}
*/
optionalFunc: PropTypes.func,

View File

@ -24,6 +24,8 @@ type Union =
};
interface Props {
error(a: string): number;
void: void;
object: Object;
trigger?: Array<'click' | 'hover' | 'contextMenu'>;
str?: string;
@ -75,7 +77,6 @@ interface Props {
elementType?: React.ElementType;
union: Union;
// eslint-disable-next-line @typescript-eslint/adjacent-overload-signatures
func(a: string): JSX.Element;
func2: Func;
html: HTMLBaseElement;
loading?: boolean | { delay?: number };
@ -93,8 +94,8 @@ App.defaultProps = {
a: '1',
b: '2',
},
func(a) {
return a;
func(a: string) {
return 123;
},
str: 'str',
};