Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docgen: Find parameters by index rather than name and handle array destructuring #29945

Merged
merged 6 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/docgen/lib/get-jsdoc-from-token.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,15 @@ module.exports = ( token ) => {
spacing: 'preserve',
} )[ 0 ];
if ( jsdoc ) {
let paramCount = 0;
jsdoc.tags = jsdoc.tags.map( ( tag ) => {
const isUnqualifiedParam =
tag.tag === 'param' && ! tag.name.includes( '.' );
noahtallen marked this conversation as resolved.
Show resolved Hide resolved
const index = isUnqualifiedParam ? paramCount++ : paramCount;
noahtallen marked this conversation as resolved.
Show resolved Hide resolved

return {
...tag,
type: getTypeAnnotation( tag, token ),
type: getTypeAnnotation( tag, token, index ),
description:
tag.description === '\n'
? tag.description.trim()
Expand Down
50 changes: 35 additions & 15 deletions packages/docgen/lib/get-type-annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ function getFunctionToken( token ) {
function getFunctionNameForError( declarationToken ) {
let namedFunctionToken = declarationToken;
if ( babelTypes.isExportNamedDeclaration( declarationToken ) ) {
namedFunctionToken = declarationToken;
namedFunctionToken = declarationToken.declaration;
}

if ( babelTypes.isVariableDeclaration( namedFunctionToken ) ) {
Expand All @@ -404,20 +404,14 @@ function getFunctionNameForError( declarationToken ) {
/**
* @param {CommentTag} tag The documented parameter.
* @param {ASTNode} declarationToken The function the parameter is documented on.
* @param {number} paramIndex The parameter index.
* @return {null | string} The parameter's type annotation.
*/
function getParamTypeAnnotation( tag, declarationToken ) {
function getParamTypeAnnotation( tag, declarationToken, paramIndex ) {
const functionToken = getFunctionToken( declarationToken );

// otherwise find the corresponding parameter token for the documented parameter
/** @type {babelTypes.Identifier} */
const paramToken = functionToken.params.reduce( ( found, pToken ) => {
if ( found ) return found;
const tokenName = babelTypes.isRestElement( pToken )
? pToken.argument.name
: pToken.name;
return tokenName === tag.name ? pToken : found;
}, null );
const paramToken = functionToken.params[ paramIndex ];

// This shouldn't happen due to our ESLint enforcing correctly documented parameter names but just in case
// we'll give a descriptive error so that it's easy to diagnose the issue.
Expand All @@ -430,9 +424,34 @@ function getParamTypeAnnotation( tag, declarationToken ) {
}

try {
/** @type {babelTypes.TSTypeAnnotation} */
const typeAnnotation = paramToken.typeAnnotation.typeAnnotation;
return getTypeAnnotation( typeAnnotation );
const paramType = paramToken.typeAnnotation.typeAnnotation;
if (
babelTypes.isIdentifier( paramToken ) ||
babelTypes.isRestElement( paramToken ) ||
( babelTypes.isArrayPattern( paramToken ) &&
! tag.name.includes( '.' ) )
) {
return getTypeAnnotation( paramType );
} else if ( babelTypes.isArrayPattern( paramToken ) ) {
// qualified name i.e., an element of the array being destructured
const position = parseInt(
tag.name.split( '.' ).slice( -1 )[ 0 ],
0
);
if ( babelTypes.isTSArrayType( paramType ) ) {
if ( babelTypes.isTSTypeReference( paramType.elementType ) ) {
// just get the element type for the array
return paramType.elementType.typeName.name;
}
return getTypeAnnotation(
paramType.elementType.typeAnnotation
);
} else if ( babelTypes.isTSTupleType( paramType ) ) {
return getTypeAnnotation( paramType.elementTypes[ position ] );
}
// anything else, `Alias[ position ]`
return `( ${ getTypeAnnotation( paramType ) } )[ ${ position } ]`;
}
} catch ( e ) {
throw new Error(
`Could not find type for parameter '${
Expand Down Expand Up @@ -464,17 +483,18 @@ module.exports =
/**
* @param {CommentTag} tag A comment tag.
* @param {ASTNode} token A function token.
* @param {number | null} index The index of the parameter or `null` if not a param tag.
* @return {null | string} The type annotation for the given tag or null if the tag has no type annotation.
*/
function ( tag, token ) {
function ( tag, token, index ) {
// If the file is using JSDoc type annotations, use the JSDoc.
if ( tag.type ) {
return tag.type;
}

switch ( tag.tag ) {
case 'param': {
return getParamTypeAnnotation( tag, token );
return getParamTypeAnnotation( tag, token, index );
}
case 'return': {
return getReturnTypeAnnotation( token );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function fn( [ first, second ]: ( T & S ) | V ): S {
noahtallen marked this conversation as resolved.
Show resolved Hide resolved
return second;
}
Loading