@@ -254,8 +254,76 @@ export class Parser {
254254 this . shouldIncludeExpression = Boolean ( shouldIncludeExpression ) ;
255255 }
256256
257- private getComponentFromExpression ( exp : ts . Symbol ) {
257+ public getTypeSymbol ( exp : ts . Symbol ) {
258258 const declaration = exp . valueDeclaration || exp . declarations ! [ 0 ] ;
259+ const type = this . checker . getTypeOfSymbolAtLocation ( exp , declaration ) ;
260+ const typeSymbol = type . symbol || type . aliasSymbol ;
261+ return typeSymbol ;
262+ }
263+
264+ public isPlainObjectType ( exp : ts . Symbol ) {
265+ let targetSymbol = exp ;
266+ if ( exp . flags & ts . SymbolFlags . Alias ) {
267+ targetSymbol = this . checker . getAliasedSymbol ( exp ) ;
268+ }
269+ const declaration =
270+ targetSymbol . valueDeclaration || targetSymbol . declarations ! [ 0 ] ;
271+
272+ if ( ts . isClassDeclaration ( declaration ) ) {
273+ return false ;
274+ }
275+
276+ const type = this . checker . getTypeOfSymbolAtLocation (
277+ targetSymbol ,
278+ declaration
279+ ) ;
280+ // Confirm it's an object type
281+ if ( ! ( type . flags & ts . TypeFlags . Object ) ) {
282+ return false ;
283+ }
284+ const objectType = type as ts . ObjectType ;
285+ const isPlain = ! ! (
286+ objectType . objectFlags &
287+ ( ts . ObjectFlags . Anonymous | ts . ObjectFlags . ObjectLiteral )
288+ ) ;
289+ return isPlain ;
290+ }
291+
292+ /**
293+ * Attempts to gather a symbol's exports.
294+ * Some symbol's like `default` exports are aliased, so we need to get the real symbol.
295+ * @param exp symbol
296+ */
297+ public getComponentExports ( exp : ts . Symbol ) {
298+ let targetSymbol = exp ;
299+
300+ if ( targetSymbol . exports ) {
301+ return { symbol : targetSymbol , exports : targetSymbol . exports ! } ;
302+ }
303+
304+ if ( exp . flags & ts . SymbolFlags . Alias ) {
305+ targetSymbol = this . checker . getAliasedSymbol ( exp ) ;
306+ }
307+ if ( targetSymbol . exports ) {
308+ return { symbol : targetSymbol , exports : targetSymbol . exports } ;
309+ }
310+ }
311+
312+ private getComponentFromExpression ( exp : ts . Symbol ) {
313+ let declaration = exp . valueDeclaration || exp . declarations ! [ 0 ] ;
314+ // Lookup component if it's a property assignment
315+ if ( declaration && ts . isPropertyAssignment ( declaration ) ) {
316+ if ( ts . isIdentifier ( declaration . initializer ) ) {
317+ const newSymbol = this . checker . getSymbolAtLocation (
318+ declaration . initializer
319+ ) ;
320+ if ( newSymbol ) {
321+ exp = newSymbol ;
322+ declaration = exp . valueDeclaration || exp . declarations ! [ 0 ] ;
323+ }
324+ }
325+ }
326+
259327 const type = this . checker . getTypeOfSymbolAtLocation ( exp , declaration ) ;
260328 const typeSymbol = type . symbol || type . aliasSymbol ;
261329
@@ -264,7 +332,6 @@ export class Parser {
264332 }
265333
266334 const symbolName = typeSymbol . getName ( ) ;
267-
268335 if (
269336 ( symbolName === 'MemoExoticComponent' ||
270337 symbolName === 'ForwardRefExoticComponent' ) &&
@@ -1230,7 +1297,7 @@ function getTextValueOfFunctionProperty(
12301297 source : ts . SourceFile ,
12311298 propertyName : string
12321299) {
1233- const [ textValue ] = source . statements
1300+ const identifierStatements : [ ts . __String , string ] [ ] = source . statements
12341301 . filter ( statement => ts . isExpressionStatement ( statement ) )
12351302 . filter ( statement => {
12361303 const expr = ( statement as ts . ExpressionStatement )
@@ -1280,11 +1347,25 @@ function getTextValueOfFunctionProperty(
12801347 ) ;
12811348 } )
12821349 . map ( statement => {
1283- return ( ( ( statement as ts . ExpressionStatement )
1284- . expression as ts . BinaryExpression ) . right as ts . Identifier ) . text ;
1350+ const expressionStatement = ( statement as ts . ExpressionStatement )
1351+ . expression as ts . BinaryExpression ;
1352+ const name = ( ( expressionStatement . left as ts . PropertyAccessExpression )
1353+ . expression as ts . Identifier ) . escapedText ;
1354+ const value = ( expressionStatement . right as ts . Identifier ) . text ;
1355+ return [ name , value ] ;
12851356 } ) ;
12861357
1287- return textValue || '' ;
1358+ if ( identifierStatements . length > 0 ) {
1359+ const locatedStatement = identifierStatements . find (
1360+ statement => statement [ 0 ] === exp . escapedName
1361+ ) ;
1362+ if ( locatedStatement ) {
1363+ return locatedStatement [ 1 ] ;
1364+ }
1365+ return identifierStatements [ 0 ] [ 1 ] || '' ;
1366+ }
1367+
1368+ return '' ;
12881369}
12891370
12901371function computeComponentName (
@@ -1450,11 +1531,28 @@ function parseWithProgramProvider(
14501531 return docs ;
14511532 }
14521533
1453- const components = checker . getExportsOfModule ( moduleSymbol ) ;
1534+ const exports = checker . getExportsOfModule ( moduleSymbol ) ;
14541535 const componentDocs : ComponentDoc [ ] = [ ] ;
1536+ const exportsAndMembers : ts . Symbol [ ] = [ ] ;
1537+
1538+ // Examine each export to determine if it's on object which may contain components
1539+ exports . forEach ( exp => {
1540+ // Push symbol for extraction to maintain existing behavior
1541+ exportsAndMembers . push ( exp ) ;
1542+ // Determine if the export symbol is an object
1543+ if ( ! parser . isPlainObjectType ( exp ) ) {
1544+ return ;
1545+ }
1546+ const typeSymbol = parser . getTypeSymbol ( exp ) ;
1547+ if ( typeSymbol ?. members ) {
1548+ typeSymbol . members . forEach ( member => {
1549+ exportsAndMembers . push ( member ) ;
1550+ } ) ;
1551+ }
1552+ } ) ;
14551553
14561554 // First document all components
1457- components . forEach ( exp => {
1555+ exportsAndMembers . forEach ( exp => {
14581556 const doc = parser . getComponentInfo (
14591557 exp ,
14601558 sourceFile ,
@@ -1466,12 +1564,13 @@ function parseWithProgramProvider(
14661564 componentDocs . push ( doc ) ;
14671565 }
14681566
1469- if ( ! exp . exports ) {
1567+ const componentExports = parser . getComponentExports ( exp ) ;
1568+ if ( ! componentExports ) {
14701569 return ;
14711570 }
14721571
14731572 // Then document any static sub-components
1474- exp . exports . forEach ( symbol => {
1573+ componentExports . exports . forEach ( symbol => {
14751574 if ( symbol . flags & ts . SymbolFlags . Prototype ) {
14761575 return ;
14771576 }
@@ -1494,7 +1593,9 @@ function parseWithProgramProvider(
14941593
14951594 if ( doc ) {
14961595 const prefix =
1497- exp . escapedName === 'default' ? '' : `${ exp . escapedName } .` ;
1596+ componentExports . symbol . escapedName === 'default'
1597+ ? ''
1598+ : `${ componentExports . symbol . escapedName } .` ;
14981599
14991600 componentDocs . push ( {
15001601 ...doc ,
0 commit comments