@@ -86,6 +86,7 @@ export type ExtractionSnapshot = {
8686 readonly definitions : DefinitionNode [ ] ;
8787 readonly unresolvedNames : Map < ts . EntityName , NameNode > ;
8888 readonly nameDefinitions : Map < ts . DeclarationStatement , NameDefinition > ;
89+ readonly implicitNameDefinitions : Map < NameDefinition , ts . TypeReferenceNode > ;
8990 readonly typesWithTypename : Set < string > ;
9091 readonly interfaceDeclarations : Array < ts . InterfaceDeclaration > ;
9192} ;
@@ -117,6 +118,8 @@ class Extractor {
117118 // Snapshot data
118119 unresolvedNames : Map < ts . EntityName , NameNode > = new Map ( ) ;
119120 nameDefinitions : Map < ts . DeclarationStatement , NameDefinition > = new Map ( ) ;
121+ implicitNameDefinitions : Map < NameDefinition , ts . TypeReferenceNode > =
122+ new Map ( ) ;
120123 typesWithTypename : Set < string > = new Set ( ) ;
121124 interfaceDeclarations : Array < ts . InterfaceDeclaration > = [ ] ;
122125
@@ -136,6 +139,7 @@ class Extractor {
136139 name : NameNode ,
137140 kind : NameDefinition [ "kind" ] ,
138141 ) : void {
142+ // @ts -ignore FIXME
139143 this . nameDefinitions . set ( node , { name, kind } ) ;
140144 }
141145
@@ -188,8 +192,12 @@ class Extractor {
188192 if ( ! ts . isDeclarationStatement ( node ) ) {
189193 this . report ( tag , E . contextTagOnNonDeclaration ( ) ) ;
190194 } else {
191- const name = this . gql . name ( tag , "CONTEXT_DUMMY_NAME" ) ;
192- this . recordTypeName ( node , name , "CONTEXT" ) ;
195+ if ( ts . isFunctionDeclaration ( node ) ) {
196+ this . recordDerivedContext ( node , tag ) ;
197+ } else {
198+ const name = this . gql . name ( tag , "CONTEXT_DUMMY_NAME" ) ;
199+ this . recordTypeName ( node , name , "CONTEXT" ) ;
200+ }
193201 }
194202 break ;
195203 }
@@ -270,6 +278,7 @@ class Extractor {
270278 definitions : this . definitions ,
271279 unresolvedNames : this . unresolvedNames ,
272280 nameDefinitions : this . nameDefinitions ,
281+ implicitNameDefinitions : this . implicitNameDefinitions ,
273282 typesWithTypename : this . typesWithTypename ,
274283 interfaceDeclarations : this . interfaceDeclarations ,
275284 } ) ;
@@ -329,6 +338,38 @@ class Extractor {
329338 }
330339 }
331340 }
341+ recordDerivedContext ( node : ts . FunctionDeclaration , tag : ts . JSDocTag ) {
342+ const returnType = node . type ;
343+ if ( returnType == null ) {
344+ throw new Error ( "Function declaration must have a return type" ) ;
345+ }
346+ if ( ! ts . isTypeReferenceNode ( returnType ) ) {
347+ throw new Error ( "Function declaration must return an explicit type" ) ;
348+ }
349+
350+ const funcName = this . namedFunctionExportName ( node ) ;
351+
352+ if ( ! ts . isSourceFile ( node . parent ) ) {
353+ return this . report ( node , E . functionFieldNotTopLevel ( ) ) ;
354+ }
355+
356+ const tsModulePath = relativePath ( node . getSourceFile ( ) . fileName ) ;
357+
358+ const paramResults = this . resolverParams ( node . parameters ) ;
359+ if ( paramResults == null ) return null ;
360+
361+ const name = this . gql . name ( tag , "CONTEXT_DUMMY_NAME" ) ;
362+ this . implicitNameDefinitions . set (
363+ {
364+ kind : "DERIVED_CONTEXT" ,
365+ name,
366+ path : tsModulePath ,
367+ exportName : funcName ?. text ?? null ,
368+ args : paramResults . resolverParams ,
369+ } ,
370+ returnType ,
371+ ) ;
372+ }
332373
333374 extractType ( node : ts . Node , tag : ts . JSDocTag ) {
334375 if ( ts . isClassDeclaration ( node ) ) {
0 commit comments