@@ -241,60 +241,135 @@ const metadata = {
241
241
function hasCorrectType (
242
242
node : tsModule . FunctionDeclaration | tsModule . VariableDeclaration
243
243
) : boolean {
244
- if ( ! node . type ) {
245
- return false
246
- }
247
-
248
244
const ts = getTs ( )
249
245
const typeChecker = getTypeChecker ( )
250
246
if ( ! typeChecker ) {
251
247
return false
252
248
}
253
249
254
- const type = typeChecker . getTypeFromTypeNode ( node . type )
255
- if ( ! type ) {
256
- return false
257
- }
258
-
259
- // Get the type name, handling aliases
260
- const symbol = type . getSymbol ( )
261
- if ( ! symbol ) {
262
- return false
263
- }
264
-
265
250
// For generateMetadata, check if it's Promise<Metadata> for async or Metadata for sync
266
251
if ( ts . isFunctionDeclaration ( node ) ) {
267
- const isAsync =
268
- node . modifiers ?. some ( ( m ) => m . kind === ts . SyntaxKind . AsyncKeyword ) ??
269
- false
270
-
271
- if ( isAsync ) {
272
- // For async functions, we need Promise<Metadata>
273
- const typeSymbol = type . getSymbol ( )
274
- if ( ! typeSymbol || typeSymbol . getName ( ) !== 'Promise' ) {
275
- return false
276
- }
252
+ return checkFunctionReturnType ( node , typeChecker )
253
+ } else {
254
+ // For variable declarations (const/let/var)
255
+ const name = node . name . getText ( )
277
256
278
- // Get the type argument of Promise<T>
279
- if ( ! ts . isTypeReferenceNode ( node . type ) ) {
257
+ if ( name === 'generateMetadata' ) {
258
+ // For generateMetadata as a variable, it must be a function expression or arrow function
259
+ if (
260
+ ! node . initializer ||
261
+ ( ! ts . isFunctionExpression ( node . initializer ) &&
262
+ ! ts . isArrowFunction ( node . initializer ) )
263
+ ) {
280
264
return false
281
265
}
282
- const typeArgs = typeChecker . getTypeArguments (
283
- type as tsModule . TypeReference
284
- )
285
- if ( ! typeArgs || typeArgs . length !== 1 ) {
286
- return false
266
+
267
+ // Check the return type of the function expression/arrow function
268
+ if ( node . initializer . type ) {
269
+ // If it has an explicit return type annotation
270
+ return checkFunctionReturnType ( node . initializer , typeChecker )
271
+ } else {
272
+ // If no explicit return type, infer it from the function
273
+ const signature = typeChecker . getSignatureFromDeclaration (
274
+ node . initializer
275
+ )
276
+ if ( ! signature ) return false
277
+
278
+ const returnType = typeChecker . getReturnTypeOfSignature ( signature )
279
+ if ( ! returnType ) return false
280
+
281
+ const isAsync =
282
+ node . initializer . modifiers ?. some (
283
+ ( m ) => m . kind === ts . SyntaxKind . AsyncKeyword
284
+ ) ?? false
285
+
286
+ if ( isAsync ) {
287
+ // For async functions, check if it's Promise<Metadata>
288
+ const typeSymbol = returnType . getSymbol ( )
289
+ if ( ! typeSymbol || typeSymbol . getName ( ) !== 'Promise' ) return false
290
+
291
+ // Check if it's a reference type (like Promise<T>)
292
+ if (
293
+ ! ( returnType . flags & ts . TypeFlags . Object ) ||
294
+ ! ( 'typeArguments' in returnType )
295
+ ) {
296
+ return false
297
+ }
298
+
299
+ const typeArgs = (
300
+ returnType as { typeArguments : readonly tsModule . Type [ ] }
301
+ ) . typeArguments
302
+ if ( ! typeArgs || typeArgs . length !== 1 ) return false
303
+
304
+ const promiseType = typeArgs [ 0 ]
305
+ const promiseTypeSymbol = promiseType . getSymbol ( )
306
+ return promiseTypeSymbol ?. getName ( ) === 'Metadata'
307
+ } else {
308
+ // For sync functions, check if it returns Metadata
309
+ const returnTypeSymbol = returnType . getSymbol ( )
310
+ return returnTypeSymbol ?. getName ( ) === 'Metadata'
311
+ }
287
312
}
288
- const promiseType = typeArgs [ 0 ]
289
- const promiseTypeSymbol = promiseType . getSymbol ( )
290
- return promiseTypeSymbol ?. getName ( ) === 'Metadata'
291
313
} else {
292
- // For sync functions, we need Metadata directly
293
- return symbol . getName ( ) === 'Metadata'
314
+ // For metadata export, we just need Metadata type
315
+ if ( ! node . type ) return false
316
+ const type = typeChecker . getTypeFromTypeNode ( node . type )
317
+ if ( ! type ) return false
318
+ const symbol = type . getSymbol ( )
319
+ return symbol ?. getName ( ) === 'Metadata'
320
+ }
321
+ }
322
+ }
323
+
324
+ function checkFunctionReturnType (
325
+ node :
326
+ | tsModule . FunctionDeclaration
327
+ | tsModule . FunctionExpression
328
+ | tsModule . ArrowFunction ,
329
+ typeChecker : tsModule . TypeChecker
330
+ ) : boolean {
331
+ const ts = getTs ( )
332
+
333
+ if ( ! node . type ) return false
334
+
335
+ const returnType = typeChecker . getTypeFromTypeNode ( node . type )
336
+ if ( ! returnType ) return false
337
+
338
+ const isAsync =
339
+ node . modifiers ?. some ( ( m ) => m . kind === ts . SyntaxKind . AsyncKeyword ) ?? false
340
+
341
+ if ( isAsync ) {
342
+ // For async functions, we need Promise<Metadata>
343
+ const typeSymbol = returnType . getSymbol ( )
344
+ if ( ! typeSymbol || typeSymbol . getName ( ) !== 'Promise' ) {
345
+ return false
346
+ }
347
+
348
+ // Get the type argument of Promise<T>
349
+ if ( ! ts . isTypeReferenceNode ( node . type ) ) {
350
+ return false
351
+ }
352
+
353
+ // Check if it's a reference type (like Promise<T>)
354
+ if (
355
+ ! ( returnType . flags & ts . TypeFlags . Object ) ||
356
+ ! ( 'typeArguments' in returnType )
357
+ ) {
358
+ return false
359
+ }
360
+
361
+ const typeArgs = ( returnType as { typeArguments : readonly tsModule . Type [ ] } )
362
+ . typeArguments
363
+ if ( ! typeArgs || typeArgs . length !== 1 ) {
364
+ return false
294
365
}
366
+ const promiseType = typeArgs [ 0 ]
367
+ const promiseTypeSymbol = promiseType . getSymbol ( )
368
+ return promiseTypeSymbol ?. getName ( ) === 'Metadata'
295
369
} else {
296
- // For metadata export, we just need Metadata
297
- return symbol . getName ( ) === 'Metadata'
370
+ // For sync functions, we need Metadata directly
371
+ const symbol = returnType . getSymbol ( )
372
+ return symbol ?. getName ( ) === 'Metadata'
298
373
}
299
374
}
300
375
0 commit comments