mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-13 09:41:57 +00:00
Merge branch feat/material-parser-jsdoc into release/1.0.0
Title: 支持解析propType上方的jsdoc 1. 解析func类型propType上方的jsdoc,获取params和returns 2. 解析ts interface 的返回值,获取returns(params已在上个迭代完成) 3. 在func类型中添加params、returns和raw字段,分别记录其入参、出参和函数签名 Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/3932322
This commit is contained in:
commit
fc99b3d9ca
@ -31,6 +31,7 @@ export default async (args: IParseArgs) => {
|
||||
try {
|
||||
return parseJS(moduleFileAbsolutePath);
|
||||
} catch (e) {
|
||||
log(e);
|
||||
await install(args);
|
||||
const info = parseDynamic(mainFileAbsolutePath);
|
||||
if (!info || !info.length) {
|
||||
|
||||
@ -3,6 +3,7 @@ import defaultPropsHandler from './defaultPropsHandler';
|
||||
import flowTypeHandler from './flowTypeHandler';
|
||||
import componentMethodsHandler from './componentMethodsHandler';
|
||||
import preProcessHandler from './preProcessHandler';
|
||||
import propTypeJsDocHandler from './propTypeJsDocHandler';
|
||||
|
||||
const { handlers } = require('react-docgen');
|
||||
|
||||
@ -13,6 +14,7 @@ const defaultHandlers = [
|
||||
childContextTypeHandler,
|
||||
handlers.propTypeCompositionHandler,
|
||||
handlers.propDocBlockHandler,
|
||||
propTypeJsDocHandler,
|
||||
flowTypeHandler,
|
||||
defaultPropsHandler,
|
||||
handlers.componentDocblockHandler,
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
/* 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('@') && propDescriptor?.type?.name === 'func') {
|
||||
const jsDoc = parseJsDoc(description);
|
||||
propDescriptor.description = jsDoc.description;
|
||||
if (jsDoc.params) {
|
||||
set(propDescriptor, ['type', 'params'], jsDoc.params);
|
||||
}
|
||||
if (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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract info from the propType jsdoc blocks. Must be run after
|
||||
* propDocBlockHandler.
|
||||
*/
|
||||
export default function propTypeJsDocHandler(documentation, path) {
|
||||
let propTypesPath = getMemberValuePath(path, 'propTypes');
|
||||
if (!propTypesPath) {
|
||||
return;
|
||||
}
|
||||
propTypesPath = resolveToValue(propTypesPath);
|
||||
if (!propTypesPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
resolveDocumentation(documentation);
|
||||
}
|
||||
@ -6,7 +6,17 @@ 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 } = itemType;
|
||||
const {
|
||||
name,
|
||||
elements,
|
||||
value = elements,
|
||||
computed,
|
||||
required,
|
||||
type,
|
||||
raw,
|
||||
params,
|
||||
returns,
|
||||
} = itemType;
|
||||
if (computed !== undefined && value) {
|
||||
return safeEval(value);
|
||||
}
|
||||
@ -27,13 +37,27 @@ export function transformType(itemType: any) {
|
||||
case 'array':
|
||||
case 'element':
|
||||
case 'node':
|
||||
case 'void':
|
||||
break;
|
||||
case 'func':
|
||||
if (value) {
|
||||
result.value = value.map(x => ({
|
||||
...x,
|
||||
propType: transformType(x.propType),
|
||||
}));
|
||||
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;
|
||||
@ -225,7 +249,8 @@ export function transformItem(name: string, item: any) {
|
||||
flowType,
|
||||
tsType,
|
||||
type = tsType || flowType,
|
||||
required,
|
||||
optional,
|
||||
required = optional,
|
||||
defaultValue,
|
||||
...others
|
||||
} = item;
|
||||
|
||||
@ -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({
|
||||
@ -321,13 +319,19 @@ function getDocgenTypeHelper(
|
||||
} else if (type?.symbol?.valueDeclaration?.parameters?.length) {
|
||||
return makeResult({
|
||||
name: 'func',
|
||||
value: getFunctionParams(
|
||||
params: getFunctionParams(
|
||||
// @ts-ignore
|
||||
type?.symbol?.valueDeclaration?.parameters,
|
||||
checker,
|
||||
parentIds,
|
||||
type,
|
||||
),
|
||||
returns: getFunctionReturns(
|
||||
checker.typeToTypeNode(type, type?.symbol?.valueDeclaration),
|
||||
checker,
|
||||
parentIds,
|
||||
type,
|
||||
),
|
||||
});
|
||||
} else if (
|
||||
// @ts-ignore
|
||||
@ -335,7 +339,7 @@ function getDocgenTypeHelper(
|
||||
) {
|
||||
return makeResult({
|
||||
name: 'func',
|
||||
value: getFunctionParams(
|
||||
params: getFunctionParams(
|
||||
// @ts-ignore
|
||||
type?.members?.get('__call')?.declarations[0]?.symbol?.declarations[0]?.parameters,
|
||||
checker,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -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',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Binary file not shown.
@ -1,40 +1,48 @@
|
||||
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
||||
import _createClass from "@babel/runtime/helpers/createClass";
|
||||
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
||||
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
||||
import _inherits from "@babel/runtime/helpers/inherits";
|
||||
import _classCallCheck from '@babel/runtime/helpers/classCallCheck';
|
||||
import _createClass from '@babel/runtime/helpers/createClass';
|
||||
import _possibleConstructorReturn from '@babel/runtime/helpers/possibleConstructorReturn';
|
||||
import _getPrototypeOf from '@babel/runtime/helpers/getPrototypeOf';
|
||||
import _inherits from '@babel/runtime/helpers/inherits';
|
||||
|
||||
/* eslint-disable react/no-unused-prop-types */
|
||||
|
||||
/* eslint-disable react/require-default-props */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import "./main.css";
|
||||
import './main.css';
|
||||
|
||||
const Demo =
|
||||
/* #__PURE__ */
|
||||
function (_React$Component) {
|
||||
_inherits(Demo, _React$Component);
|
||||
/* #__PURE__ */
|
||||
(function(_React$Component) {
|
||||
_inherits(Demo, _React$Component);
|
||||
|
||||
function Demo() {
|
||||
_classCallCheck(this, Demo);
|
||||
function Demo() {
|
||||
_classCallCheck(this, Demo);
|
||||
|
||||
return _possibleConstructorReturn(this, _getPrototypeOf(Demo).apply(this, arguments));
|
||||
}
|
||||
return _possibleConstructorReturn(this, _getPrototypeOf(Demo).apply(this, arguments));
|
||||
}
|
||||
|
||||
_createClass(Demo, [{
|
||||
key: "render",
|
||||
value: function render() {
|
||||
return React.createElement("div", null, " Test ");
|
||||
},
|
||||
}]);
|
||||
_createClass(Demo, [
|
||||
{
|
||||
key: 'render',
|
||||
value: function render() {
|
||||
return React.createElement('div', null, ' Test ');
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
return Demo;
|
||||
}(React.Component);
|
||||
return Demo;
|
||||
})(React.Component);
|
||||
|
||||
Demo.propTypes = {
|
||||
optionalArray: PropTypes.array,
|
||||
optionalBool: PropTypes.bool,
|
||||
/**
|
||||
* desc
|
||||
* @param {string} title - The title of the book.
|
||||
* @param {string} author - The author of the book.
|
||||
* @returns {any}
|
||||
*/
|
||||
optionalFunc: PropTypes.func,
|
||||
optionalNumber: PropTypes.number,
|
||||
optionalObject: PropTypes.object,
|
||||
@ -54,7 +62,11 @@ Demo.propTypes = {
|
||||
// it as an enum.
|
||||
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
|
||||
// An object that could be one of many types
|
||||
optionalUnion: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.instanceOf(Demo)]),
|
||||
optionalUnion: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
PropTypes.instanceOf(Demo),
|
||||
]),
|
||||
// An array of a certain type
|
||||
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
|
||||
// An object with property values of a certain type
|
||||
@ -82,4 +94,4 @@ Demo.propTypes = {
|
||||
Demo.defaultProps = {
|
||||
optionalNumber: 123,
|
||||
};
|
||||
export default Demo;
|
||||
export default Demo;
|
||||
|
||||
@ -14,6 +14,12 @@ class Demo extends React.Component {
|
||||
Demo.propTypes = {
|
||||
optionalArray: PropTypes.array,
|
||||
optionalBool: PropTypes.bool,
|
||||
/**
|
||||
* desc
|
||||
* @param {string} title - The title of the book.
|
||||
* @param {string} author - The author of the book.
|
||||
* @returns {any}
|
||||
*/
|
||||
optionalFunc: PropTypes.func,
|
||||
optionalNumber: PropTypes.number,
|
||||
optionalObject: PropTypes.object,
|
||||
|
||||
@ -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',
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user