11import { GetConfig } from '../types'
2+ import { getFullTypeChecker } from '../utils'
23
3- const reactTypesPath = 'node_modules/@types/react/index.d.ts '
4+ const reactTypesPath = 'node_modules/@types/react/'
45
56const symbolCache = new Map < string , boolean > ( )
67
@@ -53,24 +54,6 @@ export default (entries: ts.CompletionEntry[], node: ts.Node, position: number,
5354 timings [ `${ name } Count` ] ??= 0
5455 timings [ `${ name } Count` ] ++
5556 }
56- const getIsJsxComponentSignature = ( signature : ts . Signature ) => {
57- let returnType : ts . Type | undefined = signature . getReturnType ( )
58- if ( ! returnType ) return
59- // todo setting to allow any!
60- if ( returnType . flags & ts . TypeFlags . Any ) return false
61- returnType = getPossiblyJsxType ( returnType )
62- if ( ! returnType ) return false
63- startMark ( )
64- // todo(perf) this seems to be taking a lot of time (mui test 180ms)
65- const typeString = typeChecker . typeToString ( returnType )
66- addMark ( 'stringType' )
67- // todo-low resolve indentifier instead
68- // or compare node name from decl (invest perf)
69- if ( [ 'Element' , 'ReactElement' ] . every ( s => ! typeString . startsWith ( s ) ) ) return
70- const declFile = returnType . getSymbol ( ) ?. declarations ?. [ 0 ] ?. getSourceFile ( ) . fileName
71- if ( ! declFile ?. endsWith ( reactTypesPath ) ) return
72- return true
73- }
7457 const getIsEntryReactComponent = ( entry : ts . CompletionEntry ) => {
7558 // todo add more checks from ref https://github.com/microsoft/TypeScript/blob/e4816ed44cf9bcfe7cebb997b1f44cdb5564dac4/src/compiler/checker.ts#L30030
7659 // todo support classes
@@ -112,7 +95,7 @@ export default (entries: ts.CompletionEntry[], node: ts.Node, position: number,
11295 // startMark()
11396 const signatures = typeChecker . getSignaturesOfType ( entryType , ts . SignatureKind . Call )
11497 // addMark('signatures')
115- const result = signatures . length > 0 && signatures . every ( signature => getIsJsxComponentSignature ( signature ) )
98+ const result = isJsxElement ( typeChecker , signatures , entryType )
11699 if ( shouldBeCached ) symbolCache . set ( symbolSerialized , result )
117100 return result
118101 }
@@ -162,16 +145,30 @@ const isJsxOpeningElem = (position: number, node: ts.Node) => {
162145 return false
163146}
164147
165- const getReactElementType = ( program : ts . Program ) => {
166- const reactDeclSource = program . getSourceFiles ( ) . find ( name => name . fileName . endsWith ( reactTypesPath ) )
167- const namespace = reactDeclSource && ts . forEachChild ( reactDeclSource , s => ts . isModuleDeclaration ( s ) && s . name . text === 'React' && s )
168- if ( ! namespace || ! namespace . body ) return
169- return ts . forEachChild ( namespace . body , node => {
170- if ( ts . isInterfaceDeclaration ( node ) && node . name . text === 'ReactElement' ) {
171- return node
172- }
173- return undefined
174- } )
148+ const isJsxElement = ( typeChecker : ts . TypeChecker , signatures : readonly ts . Signature [ ] , type : ts . Type ) => {
149+ if ( signatures . length > 0 && signatures . every ( signature => getIsJsxComponentSignature ( typeChecker , signature ) ) ) return true
150+ // allow pattern: const Component = condition ? 'div' : 'a'
151+ if ( type . isUnion ( ) && type . types . every ( type => type . isStringLiteral ( ) ) ) return true
152+ return false
153+ }
154+
155+ const getIsJsxComponentSignature = ( typeChecker : ts . TypeChecker , signature : ts . Signature ) => {
156+ let returnType : ts . Type | undefined = signature . getReturnType ( )
157+ if ( ! returnType ) return
158+ // todo setting to allow any
159+ if ( returnType . flags & ts . TypeFlags . Any ) return false
160+ returnType = getPossiblyJsxType ( returnType )
161+ if ( ! returnType ) return false
162+ // startMark()
163+ // todo(perf) this seems to be taking a lot of time (mui test 180ms)
164+ const typeString = typeChecker . typeToString ( returnType )
165+ // addMark('stringType')
166+ // todo-low resolve indentifier instead
167+ // or compare node name from decl (invest perf)
168+ if ( [ 'Element' , 'ReactElement' ] . every ( s => ! typeString . startsWith ( s ) ) ) return
169+ const declFile = returnType . getSymbol ( ) ?. declarations ?. [ 0 ] ?. getSourceFile ( ) . fileName
170+ if ( ! declFile ?. includes ( reactTypesPath ) ) return
171+ return true
175172}
176173
177174const getPossiblyJsxType = ( type : ts . Type ) => {
@@ -188,3 +185,37 @@ const getPossiblyJsxType = (type: ts.Type) => {
188185 }
189186 return type . flags & ts . TypeFlags . Object ? type : undefined
190187}
188+
189+ const getGlobalJsxElementType = ( program : ts . Program ) => {
190+ const checker = getFullTypeChecker ( program . getTypeChecker ( ) )
191+ const globalJsxNamespace = checker . resolveName ( 'JSX' , undefined , ts . SymbolFlags . Namespace , false )
192+ if ( ! globalJsxNamespace ) return
193+ const exportsSymbols = checker . getExportsOfModule ( globalJsxNamespace )
194+ const symbolTable = tsFull . createSymbolTable ( exportsSymbols )
195+ const elementSymbol = getSymbol ( checker , symbolTable , 'Element' , ts . SymbolFlags . Type )
196+ if ( ! elementSymbol ) return
197+ return checker . getDeclaredTypeOfSymbol ( elementSymbol )
198+ }
199+
200+ function getSymbol (
201+ checker : import ( 'typescript-full' ) . TypeChecker ,
202+ symbols : import ( 'typescript-full' ) . SymbolTable ,
203+ name : string ,
204+ meaning : ts . SymbolFlags ,
205+ ) : import ( 'typescript-full' ) . Symbol | undefined {
206+ if ( meaning ) {
207+ const symbol = checker . getMergedSymbol ( symbols . get ( name as ts . __String ) ! )
208+ if ( symbol ) {
209+ if ( symbol . flags & meaning ) {
210+ return symbol
211+ }
212+ if ( symbol . flags & ts . SymbolFlags . Alias ) {
213+ const target = checker . getAliasedSymbol ( symbol )
214+ if ( checker . isUnknownSymbol ( target ) || target . flags & meaning ) {
215+ return symbol
216+ }
217+ }
218+ }
219+ }
220+ return undefined
221+ }
0 commit comments