@@ -6,7 +6,10 @@ import {
66 type BabelNodeExpression ,
77 type BabelNodeFlow ,
88 type BabelNodeMemberExpression ,
9+ type BabelNodeObjectExpression ,
10+ type BabelNodeObjectTypeAnnotation ,
911 type BabelNodeQualifiedName ,
12+ type BabelNodeTSLiteralType ,
1013 type BabelNodeTSTypeAnnotation ,
1114 type BabelNodeTypeAnnotation ,
1215 type BabelTSTypeAnnotation ,
@@ -17,6 +20,7 @@ import {
1720 type FunctionExpression ,
1821 type InterfaceDeclaration ,
1922 type ObjectMethod ,
23+ type ObjectPattern ,
2024 type TSInterfaceDeclaration ,
2125 type TSMethodSignature ,
2226 type TSQualifiedName ,
@@ -46,7 +50,8 @@ import {
4650 isNumberLiteralTypeAnnotation ,
4751 isNumberTypeAnnotation ,
4852 isNumericLiteral ,
49- isObjectExpression ,
53+ isObjectPattern ,
54+ isObjectProperty ,
5055 isObjectTypeAnnotation ,
5156 isObjectTypeIndexer ,
5257 isObjectTypeProperty ,
@@ -100,9 +105,7 @@ import {
100105 isTypeAnnotation ,
101106 isTypeParameterInstantiation ,
102107 isTypeofTypeAnnotation ,
103- isUnaryExpression ,
104- isUnionTypeAnnotation ,
105- isVoidTypeAnnotation ,
108+ isUnaryExpression , isUnionTypeAnnotation , isVoidTypeAnnotation ,
106109} from "@babel/types" ;
107110
108111import type { DataType , Param , Return } from "@webdoc/types" ;
@@ -228,23 +231,52 @@ export function extractParams(
228231 const extraRaw = paramNode . right . extra && paramNode . right . extra . raw ;
229232 const [ defaultValue , dataType ] = extractAssignedValue ( paramNode . right ) ;
230233
231- param = {
232- identifier : paramNode . left . name ,
233- optional : paramNode . optional || false ,
234- default : paramNode . right . raw || extraRaw || defaultValue ,
235- dataType,
236- } ;
237-
238- // This will override the inferred data type.
239- paramTypeAnnotation = paramNode . left . typeAnnotation ;
240- } else if ( isObjectExpression ( paramNode ) ) {
234+ if ( isObjectPattern ( paramNode . left ) ) {
235+ try {
236+ params . push ( ( {
237+ identifier : `__arg${ i } ` ,
238+ optional : false ,
239+ ...( paramNode . left . typeAnnotation && {
240+ dataType : extractType ( paramNode . left . typeAnnotation ) ,
241+ } ) ,
242+ } : $Shape < Param > ) ) ;
243+ params . push ( ...extractParamsFromObjectPatternRecursive (
244+ paramNode . left , paramNode . right ,
245+ paramNode . left . typeAnnotation ? paramNode . left . typeAnnotation . typeAnnotation : null ) ) ;
246+ } catch ( e ) {
247+ ( ( params : any ) ) . flawed = true ;
248+ parserLogger . error ( tag . Indexer , `Failed parse param: ${ e } ` ) ;
249+ }
250+ } else {
251+ param = {
252+ identifier : paramNode . left . name ,
253+ optional : paramNode . optional || false ,
254+ default : paramNode . right . raw || extraRaw || defaultValue ,
255+ dataType ,
256+ } ;
257+ // This will override the inferred data type.
258+ paramTypeAnnotation = paramNode . left . typeAnnotation ;
259+ }
260+ } else if ( isObjectPattern ( paramNode ) ) {
241261 // TODO: Find a way to document {x, y, z} parameters
242262 // e.g. function ({x, y, z}), you would need to give the object pattern an anonymous like
243263 // "", " ", " ", " " or using ‌ because it is truly invisible
244264
245- ( ( params : any ) ) . flawed = true ;
246- parserLogger . error ( tag . Indexer , "Object patterns as parameters can't be documented, at line" ) ;
247- parserLogger . warn ( tag . Indexer , JSON . stringify ( paramNode , null , 2 ) ) ;
265+ try {
266+ params . push ( ( {
267+ identifier : `__arg${ i } ` ,
268+ optional : false ,
269+ ...( paramNode . typeAnnotation && {
270+ dataType : extractType ( paramNode . typeAnnotation ) ,
271+ } ) ,
272+ } : $Shape < Param > ) ) ;
273+ params . push ( ...extractParamsFromObjectPatternRecursive (
274+ paramNode , null ,
275+ paramNode . typeAnnotation ? paramNode . typeAnnotation . typeAnnotation : null ) ) ;
276+ } catch ( e ) {
277+ ( ( params : any ) ) . flawed = true ;
278+ parserLogger . error ( tag . Indexer , `Failed parse param: ${ e } ` ) ;
279+ }
248280 } else {
249281 ( ( params : any ) ) . flawed = true ;
250282 parserLogger . error ( tag . Indexer , "Parameter node couldn't be parsed, " +
@@ -266,6 +298,80 @@ export function extractParams(
266298 return params ;
267299}
268300
301+ function extractParamsFromObjectPatternRecursive (
302+ left : ObjectPattern ,
303+ right ?: ?BabelNodeObjectExpression ,
304+ typeAnnotation ?: BabelNodeObjectTypeAnnotation | BabelNodeTSLiteralType ,
305+ ) : Param [ ] {
306+ const params : Param [ ] = [ ] ;
307+
308+ for ( const prop of left . properties ) {
309+ if ( isObjectProperty ( prop ) ) {
310+ let defaultValue : any ;
311+
312+ if ( isAssignmentPattern ( prop . value ) ) {
313+ defaultValue = prop . value . right ;
314+ } else if ( right ) {
315+ for ( const defaultValueProp of right . properties ) {
316+ if ( defaultValueProp . key && defaultValueProp . key . name === prop . key . name ) {
317+ defaultValue = defaultValueProp . value ;
318+ break ;
319+ }
320+ }
321+ }
322+
323+ let valueTypeAnnotation : any ;
324+
325+ if ( typeAnnotation && isObjectTypeAnnotation ( typeAnnotation ) ) {
326+ for ( const typeProp of typeAnnotation . properties ) {
327+ if ( isIdentifier ( typeProp . key ) && typeProp . key . name === prop . key . name ) {
328+ valueTypeAnnotation = { typeAnnotation : typeProp . value } ;
329+ break ;
330+ }
331+ }
332+ } else if ( typeAnnotation && isTSTypeLiteral ( typeAnnotation ) ) {
333+ for ( const typeProp of typeAnnotation . members ) {
334+ if ( isIdentifier ( typeProp . key ) && typeProp . key . name === prop . key . name ) {
335+ valueTypeAnnotation = typeProp . typeAnnotation ;
336+ break ;
337+ }
338+ }
339+ }
340+
341+ if ( isObjectPattern ( prop . value ) ) {
342+ const embeddedParams = extractParamsFromObjectPatternRecursive (
343+ prop . value ,
344+ defaultValue ,
345+ valueTypeAnnotation ? valueTypeAnnotation . typeAnnotation : null ,
346+ ) ;
347+ const prefix = isIdentifier ( prop . key ) ? prop . key . name : "$error$" ;
348+
349+ for ( const embeddedParam of embeddedParams ) {
350+ embeddedParam . identifier = `.${ prefix } ${ embeddedParam . identifier } ` ;
351+ }
352+
353+ params . push ( ...embeddedParams ) ;
354+ } else if ( isIdentifier ( prop . key ) ) {
355+ const [ defaultValueRaw , impliedDataType ] = defaultValue ?
356+ extractAssignedValue ( defaultValue ) : [ null , null ] ;
357+
358+ // Prefix with dot so it's implied to be not top-level
359+ const param : $Shape < Param > = {
360+ identifier : "." + prop . key . name ,
361+ optional : valueTypeAnnotation && valueTypeAnnotation . optional ,
362+ variadic : false ,
363+ ...( defaultValueRaw && { default : defaultValueRaw } ) ,
364+ dataType : valueTypeAnnotation ? extractType ( valueTypeAnnotation ) : impliedDataType ,
365+ } ;
366+
367+ params.push(param);
368+ }
369+ }
370+ }
371+
372+ return params ;
373+ }
374+
269375// Extract the returns for the method/function
270376export function extractReturns (
271377 node : ClassMethod | ObjectMethod | FunctionDeclaration | FunctionExpression ,
0 commit comments