import { CallExpression , ExportDeclaration , ExportDefaultDeclaration , ExportDefaultExpression , ExportNamedDeclaration , ModuleDeclaration , TsType , } from '@swc/core' ; import Visitor from '@swc/core/Visitor' ; function isComponentLikeName ( name ) { return typeof name === 'string' && name [ 0 ] >= 'A' && name [ 0 ] <= 'Z' ; } function neverCheck ( foo ) { throw new Error ( `expected never got ${ foo }` ) ; } export default class ExportNameCollector extends Visitor { exportedNames = new Set < string > ( ) ; isAbsolutelyNotReactRefreshBoundary = false ; visitTsType ( n ) { return n ; } addName ( name ) { if ( isComponentLikeName ( name ) ) { this . exportedNames . add ( name ) ; } else { this . isAbsolutelyNotReactRefreshBoundary = true ; } } handleExportDefaultExpression ( node ) { const { expression : exp } = node ; if ( exp . type === 'Identifier' ) { this . addName ( exp . value ) ; } else if ( exp . type === 'CallExpression' ) { const isMemoFn = exp . callee . type === 'Identifier' && exp . callee . value === 'memo' ; const componentName = exp . arguments [ 0 ] && exp . arguments [ 0 ] . expression . type === 'Identifier' ? exp . arguments [ 0 ] . expression . value : null ; if ( isMemoFn && componentName ) { this . addName ( componentName ) ; } else { this . isAbsolutelyNotReactRefreshBoundary = true ; } } else { this . isAbsolutelyNotReactRefreshBoundary = true ; } } handleExportDeclaration ( node ) { const { declaration } = node ; const { type } = declaration ; if ( type === 'TsInterfaceDeclaration' || type === 'TsModuleDeclaration' || type === 'TsTypeAliasDeclaration' ) { } else if ( type === 'TsEnumDeclaration' || type === 'ClassDeclaration' ) { this . isAbsolutelyNotReactRefreshBoundary = true ; } else if ( type === 'FunctionDeclaration' ) { this . addName ( declaration . identifier . value ) ; } else if ( type === 'VariableDeclaration' ) { declaration . declarations . flatMap ( ( declarator ) => declarator . id . type === 'Identifier' ? [ declarator . id . value ] : [ ] ) . forEach ( ( name ) => this . addName ( name ) ) ; } else { neverCheck ( type ) ; } } handleExportDefaultDeclaration ( moduleDecl ) { const { decl } = moduleDecl ; if ( decl . type === 'FunctionExpression' ) { this . addName ( decl . identifier . value ) ; } else if ( decl . type === 'ClassExpression' ) { this . isAbsolutelyNotReactRefreshBoundary = true ; } } handleExportNamedDeclaration ( moduleDecl ) { for ( const s of moduleDecl . specifiers ) { let exportName ; if ( s . type === 'ExportNamespaceSpecifier' ) { this . isAbsolutelyNotReactRefreshBoundary = true ; break ; } else if ( s . type == 'ExportDefaultSpecifier' ) { exportName = s . exported . value ; } else { exportName = s . exported ?. value ?? s . orig . value ; if ( exportName === 'default' ) { exportName = s . orig . value ; } } this . addName ( exportName ) ; } } visitModuleDeclaration ( moduleDecl ) { if ( this . isAbsolutelyNotReactRefreshBoundary ) { return moduleDecl ; } if ( moduleDecl . type === 'ImportDeclaration' || moduleDecl . type === 'TsExportAssignment' || moduleDecl . type === 'TsImportEqualsDeclaration' || moduleDecl . type === 'TsNamespaceExportDeclaration' ) { } else if ( moduleDecl . type === 'ExportAllDeclaration' ) { this . isAbsolutelyNotReactRefreshBoundary = true ; } else if ( moduleDecl . type === 'ExportDefaultExpression' ) { this . handleExportDefaultExpression ( moduleDecl ) ; } else if ( moduleDecl . type === 'ExportDeclaration' ) { this . handleExportDeclaration ( moduleDecl ) ; } else if ( moduleDecl . type === 'ExportDefaultDeclaration' ) { this . handleExportDefaultDeclaration ( moduleDecl ) ; } else if ( moduleDecl . type === 'ExportNamedDeclaration' ) { this . handleExportNamedDeclaration ( moduleDecl ) ; } return moduleDecl ; } }