@@ -82,6 +82,7 @@ export type ExtractionSnapshot = {
8282 readonly definitions : DefinitionNode [ ] ;
8383 readonly unresolvedNames : Map < ts . EntityName , NameNode > ;
8484 readonly nameDefinitions : Map < ts . DeclarationStatement , NameDefinition > ;
85+ readonly implicitNameDefinitions : Map < NameDefinition , ts . TypeReferenceNode > ;
8586 readonly typesWithTypename : Set < string > ;
8687 readonly interfaceDeclarations : Array < ts . InterfaceDeclaration > ;
8788} ;
@@ -113,6 +114,8 @@ class Extractor {
113114 // Snapshot data
114115 unresolvedNames : Map < ts . EntityName , NameNode > = new Map ( ) ;
115116 nameDefinitions : Map < ts . DeclarationStatement , NameDefinition > = new Map ( ) ;
117+ implicitNameDefinitions : Map < NameDefinition , ts . TypeReferenceNode > =
118+ new Map ( ) ;
116119 typesWithTypename : Set < string > = new Set ( ) ;
117120 interfaceDeclarations : Array < ts . InterfaceDeclaration > = [ ] ;
118121
@@ -132,6 +135,7 @@ class Extractor {
132135 name : NameNode ,
133136 kind : NameDefinition [ "kind" ] ,
134137 ) : void {
138+ // @ts -ignore FIXME
135139 this . nameDefinitions . set ( node , { name, kind } ) ;
136140 }
137141
@@ -218,8 +222,12 @@ class Extractor {
218222 if ( ! ts . isDeclarationStatement ( node ) ) {
219223 this . report ( tag , E . contextTagOnNonDeclaration ( ) ) ;
220224 } else {
221- const name = this . gql . name ( tag , "CONTEXT_DUMMY_NAME" ) ;
222- this . recordTypeName ( node , name , "CONTEXT" ) ;
225+ if ( ts . isFunctionDeclaration ( node ) ) {
226+ this . recordDerivedContext ( node , tag ) ;
227+ } else {
228+ const name = this . gql . name ( tag , "CONTEXT_DUMMY_NAME" ) ;
229+ this . recordTypeName ( node , name , "CONTEXT" ) ;
230+ }
223231 }
224232 break ;
225233 }
@@ -293,11 +301,41 @@ class Extractor {
293301 definitions : this . definitions ,
294302 unresolvedNames : this . unresolvedNames ,
295303 nameDefinitions : this . nameDefinitions ,
304+ implicitNameDefinitions : this . implicitNameDefinitions ,
296305 typesWithTypename : this . typesWithTypename ,
297306 interfaceDeclarations : this . interfaceDeclarations ,
298307 } ) ;
299308 }
300309
310+ recordDerivedContext ( node : ts . FunctionDeclaration , tag : ts . JSDocTag ) {
311+ const returnType = node . type ;
312+ if ( returnType == null ) {
313+ throw new Error ( "Function declaration must have a return type" ) ;
314+ }
315+ if ( ! ts . isTypeReferenceNode ( returnType ) ) {
316+ throw new Error ( "Function declaration must return an explicit type" ) ;
317+ }
318+
319+ const funcName = this . namedFunctionExportName ( node ) ;
320+
321+ if ( ! ts . isSourceFile ( node . parent ) ) {
322+ return this . report ( node , E . functionFieldNotTopLevel ( ) ) ;
323+ }
324+
325+ const tsModulePath = relativePath ( node . getSourceFile ( ) . fileName ) ;
326+
327+ const name = this . gql . name ( tag , "CONTEXT_DUMMY_NAME" ) ;
328+ this . implicitNameDefinitions . set (
329+ {
330+ kind : "DERIVED_CONTEXT" ,
331+ name,
332+ path : tsModulePath ,
333+ exportName : funcName ?. text ?? null ,
334+ } ,
335+ returnType ,
336+ ) ;
337+ }
338+
301339 extractType ( node : ts . Node , tag : ts . JSDocTag ) {
302340 if ( ts . isClassDeclaration ( node ) ) {
303341 this . typeClassDeclaration ( node , tag ) ;
0 commit comments