@@ -200,14 +200,14 @@ function extractBalancedSymbols(text: string, openSymbol: string, closeSymbol: s
200
200
*/
201
201
function extractFunctionSignature ( declaration : string ) : FunctionSignature {
202
202
// Remove comments and clean up the declaration
203
- const cleanDeclaration = removeLeadingComments ( declaration ) . trim ( )
203
+ const cleanDeclaration = removeLeadingComments ( declaration ) . trim ( ) . replace ( / ^ e x p o r t \s + / , '' ) // Remove leading export if present
204
204
205
- // Regex to match function declarations, including complex generics and export
206
- const functionPattern = / ^ \s * ( e x p o r t \s + ) ? (?: d e c l a r e \s + ) ? (?: a s y n c \s + ) ? f u n c t i o n \s * ( \* ) ? \s * ( [ ^ \s ( < ] + ) /
205
+ // Enhanced regex to capture async and generator functions
206
+ const functionPattern = / ^ (?: a s y n c \s + ) ? f u n c t i o n \s * ( \* ) ? \s * ( [ a - z A - Z _ $ ] [ \w $ ] * ) (?: < ( [ ^ > ] * ) > ) ? \s * \( ( . * ? ) \) (?: \s * : \s * ( [ ^ { ; ] + ) ) ? / s
207
207
const functionMatch = cleanDeclaration . match ( functionPattern )
208
208
209
209
if ( ! functionMatch ) {
210
- console . error ( 'Function name could not be extracted from declaration:' , declaration )
210
+ console . error ( 'Function name could not be extracted from declaration:' , cleanDeclaration )
211
211
return {
212
212
name : '' ,
213
213
params : '' ,
@@ -216,46 +216,19 @@ function extractFunctionSignature(declaration: string): FunctionSignature {
216
216
}
217
217
}
218
218
219
- const name = functionMatch [ 3 ]
220
- let rest = cleanDeclaration . slice ( cleanDeclaration . indexOf ( name ) + name . length ) . trim ( )
219
+ const [ , isGenerator , name , generics = '' , params = '' , returnType = 'void' ] = functionMatch
221
220
222
- // Extract generics
223
- let generics = ''
224
- if ( rest . startsWith ( '<' ) ) {
225
- const genericsResult = extractBalancedSymbols ( rest , '<' , '>' )
226
- if ( genericsResult ) {
227
- generics = genericsResult . content // This includes the angle brackets
228
- rest = genericsResult . rest . trim ( )
229
- }
230
- }
231
-
232
- // Extract parameters
233
- let params = ''
234
- if ( rest . startsWith ( '(' ) ) {
235
- const paramsResult = extractBalancedSymbols ( rest , '(' , ')' )
236
- if ( paramsResult ) {
237
- params = paramsResult . content . slice ( 1 , - 1 ) . trim ( )
238
- rest = paramsResult . rest . trim ( )
239
- }
240
- }
241
-
242
- // Extract return type
243
- let returnType = 'void'
244
- if ( rest . startsWith ( ':' ) ) {
245
- rest = rest . slice ( 1 ) . trim ( )
246
- const returnTypeResult = extractReturnType ( rest )
247
- debugLog ( undefined , 'return-type' , `Extracted return type: ${ returnTypeResult ?. returnType } ` )
248
- if ( returnTypeResult ) {
249
- returnType = returnTypeResult . returnType . trim ( )
250
- rest = returnTypeResult . rest . trim ( )
251
- }
221
+ // Handle async generator functions
222
+ let finalReturnType = returnType . trim ( )
223
+ if ( isGenerator ) {
224
+ finalReturnType = `AsyncGenerator<${ finalReturnType } , void, unknown>`
252
225
}
253
226
254
227
return {
255
228
name,
256
229
params : cleanParameterTypes ( params ) ,
257
- returnType : normalizeType ( returnType ) ,
258
- generics,
230
+ returnType : normalizeType ( finalReturnType ) ,
231
+ generics : generics ? `< ${ generics } >` : '' ,
259
232
}
260
233
}
261
234
@@ -700,7 +673,7 @@ function inferArrayType(value: string, state?: ProcessingState, indentLevel = 0)
700
673
return `readonly [${ tuples . join ( ', ' ) } ]`
701
674
}
702
675
703
- const elementTypes = elements . map ( ( element , index ) => {
676
+ const elementTypes = elements . map ( ( element ) => {
704
677
const trimmed = element . trim ( )
705
678
// debugLog(state, 'element-processing', `Processing element ${index}: "${trimmed}"`)
706
679
@@ -907,6 +880,24 @@ export function isDefaultExport(line: string): boolean {
907
880
}
908
881
909
882
function isDeclarationStart ( line : string ) : boolean {
883
+ // Skip regex patterns
884
+ if ( isRegexPattern ( line ) )
885
+ return false
886
+
887
+ const validIdentifierRegex = / ^ [ a - z _ $ ] [ \w $ ] * $ / i
888
+
889
+ // Handle function declarations
890
+ if ( line . startsWith ( 'function' ) || line . startsWith ( 'async function' ) ) {
891
+ const nameMatch = line . match ( / f u n c t i o n \s + ( [ ^ ( < \s ] + ) / )
892
+ return nameMatch ? validIdentifierRegex . test ( nameMatch [ 1 ] ) : false
893
+ }
894
+
895
+ if ( line . startsWith ( 'export function' ) || line . startsWith ( 'export async function' ) ) {
896
+ const nameMatch = line . match ( / f u n c t i o n \s + ( [ ^ ( < \s ] + ) / )
897
+ return nameMatch ? validIdentifierRegex . test ( nameMatch [ 1 ] ) : false
898
+ }
899
+
900
+ // Handle other declarations
910
901
return (
911
902
line . startsWith ( 'export ' )
912
903
|| line . startsWith ( 'interface ' )
@@ -922,6 +913,23 @@ function isDeclarationStart(line: string): boolean {
922
913
)
923
914
}
924
915
916
+ function isRegexPattern ( line : string ) : boolean {
917
+ return (
918
+ line . includes ( '\\' )
919
+ || line . includes ( '[^' )
920
+ || line . includes ( '(?:' )
921
+ || line . includes ( '(?=' )
922
+ || line . includes ( '(?!' )
923
+ || line . includes ( '\\s*' )
924
+ || line . includes ( '\\w+' )
925
+ || line . includes ( '\\d+' )
926
+ || line . includes ( '(?<' )
927
+ || line . includes ( '(?!' )
928
+ || line . includes ( '(?<=' )
929
+ || line . includes ( '(?<!' )
930
+ )
931
+ }
932
+
925
933
/**
926
934
* Check if a given type string represents a function type
927
935
*/
@@ -940,6 +948,18 @@ export function isDeclarationComplete(content: string | string[]): boolean {
940
948
return / ; \s * $ / . test ( trimmedContent ) || / \} \s * $ / . test ( trimmedContent )
941
949
}
942
950
951
+ function isVariableInsideFunction ( line : string , state : ProcessingState ) : boolean {
952
+ const trimmed = line . trim ( )
953
+ return (
954
+ state . currentScope === 'function'
955
+ && ( trimmed . startsWith ( 'const ' )
956
+ || trimmed . startsWith ( 'let ' )
957
+ || trimmed . startsWith ( 'var ' )
958
+ // Handle multiline variable declarations
959
+ || / ^ (?: c o n s t | l e t | v a r ) \s + [ a - z A - Z _ $ ] [ \w $ ] * \s * (?: : | = ) / . test ( trimmed ) )
960
+ )
961
+ }
962
+
943
963
function needsMultilineFormat ( types : string [ ] ) : boolean {
944
964
return types . some ( type =>
945
965
type . includes ( '\n' )
@@ -986,20 +1006,24 @@ function processBlock(lines: string[], comments: string[], state: ProcessingStat
986
1006
const declarationText = lines . join ( '\n' )
987
1007
const cleanDeclaration = removeLeadingComments ( declarationText ) . trim ( )
988
1008
989
- // Keep track of declaration for debugging
990
- state . debug . currentProcessing = cleanDeclaration
991
1009
debugLog ( state , 'block-processing' , `Full block content:\n${ cleanDeclaration } ` )
992
1010
993
1011
if ( ! cleanDeclaration ) {
994
1012
debugLog ( state , 'block-processing' , 'Empty declaration block' )
995
1013
return
996
1014
}
997
1015
998
- // Try each processor in order
999
- if ( processVariableBlock ( cleanDeclaration , lines , state ) )
1016
+ // Early check for variables inside functions
1017
+ if ( isVariableInsideFunction ( cleanDeclaration , state ) ) {
1018
+ debugLog ( state , 'block-processing' , 'Skipping variable declaration inside function' )
1000
1019
return
1020
+ }
1021
+
1022
+ // Try each processor in order
1001
1023
if ( processFunctionBlock ( cleanDeclaration , state ) )
1002
1024
return
1025
+ if ( processVariableBlock ( cleanDeclaration , lines , state ) )
1026
+ return
1003
1027
if ( processInterfaceBlock ( cleanDeclaration , declarationText , state ) )
1004
1028
return
1005
1029
if ( processTypeBlock ( cleanDeclaration , declarationText , state ) )
@@ -1013,7 +1037,6 @@ function processBlock(lines: string[], comments: string[], state: ProcessingStat
1013
1037
if ( processModuleBlock ( cleanDeclaration , declarationText , state ) )
1014
1038
return
1015
1039
1016
- // Log any unhandled declarations
1017
1040
debugLog ( state , 'processing' , `Unhandled declaration type: ${ cleanDeclaration . split ( '\n' ) [ 0 ] } ` )
1018
1041
}
1019
1042
@@ -1022,7 +1045,14 @@ function processVariableBlock(cleanDeclaration: string, lines: string[], state:
1022
1045
if ( ! variableMatch )
1023
1046
return false
1024
1047
1048
+ // Double-check we're not inside a function
1049
+ if ( isVariableInsideFunction ( cleanDeclaration , state ) ) {
1050
+ debugLog ( state , 'variable-processing' , 'Skipping variable inside function' )
1051
+ return true // Return true because we handled it (by skipping)
1052
+ }
1053
+
1025
1054
const isExported = cleanDeclaration . startsWith ( 'export' )
1055
+
1026
1056
// Only process variables at the top level
1027
1057
if ( state . currentScope === 'top' ) {
1028
1058
const fullDeclaration = lines . join ( '\n' )
@@ -1035,26 +1065,33 @@ function processVariableBlock(cleanDeclaration: string, lines: string[], state:
1035
1065
}
1036
1066
1037
1067
function processFunctionBlock ( cleanDeclaration : string , state : ProcessingState ) : boolean {
1038
- if ( ! / ^ ( e x p o r t \s + ) ? ( a s y n c \s + ) ? f u n c t i o n / . test ( cleanDeclaration ) )
1068
+ // Check for function declarations including async and generator functions
1069
+ if ( ! / ^ (?: e x p o r t \s + ) ? (?: a s y n c \s + ) ? f u n c t i o n \s * ( \* ) ? \s * [ a - z A - Z _ $ ] [ \w $ ] * / . test ( cleanDeclaration ) )
1039
1070
return false
1040
1071
1041
1072
debugLog ( state , 'block-processing' , 'Processing function declaration' )
1042
1073
const isExported = cleanDeclaration . startsWith ( 'export' )
1043
1074
1044
- // Split block into separate function declarations if multiple exist
1045
- const declarations = cleanDeclaration . split ( / e x p o r t f u n c t i o n | f u n c t i o n / )
1075
+ // Split function declarations - handle export separately
1076
+ const declarations = cleanDeclaration
1077
+ . replace ( / ^ e x p o r t \s + / , '' ) // Remove leading export once
1078
+ . split ( / \n e x p o r t \s + f u n c t i o n | \n f u n c t i o n / )
1046
1079
. filter ( Boolean )
1047
- . map ( d => ( isExported ? `export function${ d } ` : `function${ d } ` ) )
1080
+ . map ( d => d . trim ( ) )
1081
+ . filter ( d => d . startsWith ( 'function' ) || d . startsWith ( 'async function' ) )
1048
1082
1049
1083
for ( const declaration of declarations ) {
1050
1084
// Process only the function signature for overloads and declarations
1051
1085
const signature = declaration . split ( '{' ) [ 0 ] . trim ( )
1052
1086
if ( signature ) {
1053
1087
const processed = processFunction ( signature , state . usedTypes , isExported )
1054
- if ( processed )
1088
+ if ( processed ) {
1089
+ debugLog ( state , 'function-processing' , `Processed function: ${ processed } ` )
1055
1090
state . dtsLines . push ( processed )
1091
+ }
1056
1092
}
1057
1093
}
1094
+
1058
1095
return true
1059
1096
}
1060
1097
@@ -1322,7 +1359,6 @@ function processSourceFile(content: string, state: ProcessingState): void {
1322
1359
let bracketDepth = 0
1323
1360
let parenDepth = 0
1324
1361
let inDeclaration = false
1325
- // Ensure currentScope is initialized
1326
1362
state . currentScope = 'top'
1327
1363
1328
1364
for ( let i = 0 ; i < lines . length ; i ++ ) {
@@ -1353,9 +1389,8 @@ function processSourceFile(content: string, state: ProcessingState): void {
1353
1389
bracketDepth += ( line . match ( / \{ / g) || [ ] ) . length
1354
1390
bracketDepth -= ( line . match ( / \} / g) || [ ] ) . length
1355
1391
1356
- // Check if we're entering a function scope
1392
+ // Update scope
1357
1393
if ( / ^ ( e x p o r t \s + ) ? ( a s y n c \s + ) ? f u n c t i o n / . test ( trimmedLine ) ) {
1358
- debugLog ( state , 'function-scope' , `Entering function scope: ${ trimmedLine } ` )
1359
1394
state . currentScope = 'function'
1360
1395
}
1361
1396
@@ -1372,28 +1407,26 @@ function processSourceFile(content: string, state: ProcessingState): void {
1372
1407
bracketDepth += ( line . match ( / \{ / g) || [ ] ) . length
1373
1408
bracketDepth -= ( line . match ( / \} / g) || [ ] ) . length
1374
1409
1375
- // Check if the declaration is complete
1376
- const isComplete = (
1377
- parenDepth === 0
1378
- && bracketDepth === 0
1379
- && (
1410
+ // Check if declaration is complete
1411
+ if ( parenDepth === 0 && bracketDepth === 0 && ! trimmedLine . endsWith ( ',' ) ) {
1412
+ const isComplete = (
1380
1413
trimmedLine . endsWith ( ';' )
1381
1414
|| trimmedLine . endsWith ( '}' )
1382
1415
|| ( ! trimmedLine . endsWith ( '{' ) && ! trimmedLine . endsWith ( ',' ) )
1383
1416
)
1384
- )
1385
-
1386
- if ( isComplete ) {
1387
- processBlock ( currentBlock , currentComments , state )
1388
- currentBlock = [ ]
1389
- currentComments = [ ]
1390
- inDeclaration = false
1391
- bracketDepth = 0
1392
- parenDepth = 0
1393
1417
1394
- // Reset scope after function ends
1395
- if ( state . currentScope === 'function' ) {
1396
- state . currentScope = 'top' // Reset currentScope here
1418
+ if ( isComplete ) {
1419
+ processBlock ( currentBlock , currentComments , state )
1420
+ currentBlock = [ ]
1421
+ currentComments = [ ]
1422
+ inDeclaration = false
1423
+ bracketDepth = 0
1424
+ parenDepth = 0
1425
+
1426
+ // Reset scope after function ends
1427
+ if ( state . currentScope === 'function' ) {
1428
+ state . currentScope = 'top'
1429
+ }
1397
1430
}
1398
1431
}
1399
1432
}
@@ -1515,49 +1548,45 @@ function processVariable(declaration: string, isExported: boolean, state: Proces
1515
1548
* Process function declarations with overloads
1516
1549
*/
1517
1550
function processFunction ( declaration : string , usedTypes ?: Set < string > , isExported = true ) : string {
1518
- const cleanDeclaration = removeLeadingComments ( declaration ) . trim ( )
1519
- const { name, params, returnType, generics } = extractFunctionSignature ( cleanDeclaration )
1520
- debugLog ( undefined , 'process-function' , `Processing function declaration: ${ name } (${ params } ): ${ returnType } ` )
1551
+ // Clean up declaration first
1552
+ const cleanDeclaration = removeLeadingComments ( declaration ) . trim ( ) . replace ( / ^ e x p o r t \s + / , '' ) // Remove leading export if present
1553
+
1554
+ const signature = extractFunctionSignature ( cleanDeclaration )
1521
1555
1522
- if ( ! name ) {
1523
- console . error ( 'Function name could not be extracted from declaration:' , declaration )
1556
+ if ( ! signature . name ) {
1557
+ debugLog ( undefined , 'function-processing' , `Failed to process function: ${ cleanDeclaration } ` )
1524
1558
return ''
1525
1559
}
1526
1560
1561
+ // Handle async functions
1562
+ const isAsync = cleanDeclaration . includes ( 'async function' )
1563
+ let returnType = signature . returnType
1564
+ if ( isAsync && ! returnType . startsWith ( 'Promise<' ) && ! returnType . startsWith ( 'AsyncGenerator<' ) ) {
1565
+ returnType = `Promise<${ returnType } >`
1566
+ }
1567
+
1527
1568
if ( usedTypes ) {
1528
- trackUsedTypes ( `${ generics } ${ params } ${ returnType } ` , usedTypes )
1569
+ trackUsedTypes ( `${ signature . generics } ${ signature . params } ${ returnType } ` , usedTypes )
1529
1570
}
1530
1571
1531
- // Build the declaration string without function body
1532
1572
const parts = [
1533
1573
isExported ? 'export' : '' ,
1534
1574
'declare' ,
1535
1575
'function' ,
1536
- name ,
1537
- generics ,
1538
- `(${ params } )` ,
1576
+ signature . name ,
1577
+ signature . generics ,
1578
+ `(${ signature . params } )` ,
1579
+ ':' ,
1580
+ returnType ,
1539
1581
]
1540
1582
1541
- if ( returnType && returnType !== 'void' ) {
1542
- parts . push ( ':' , returnType )
1543
- }
1544
-
1545
- parts . push ( ';' )
1546
-
1547
- const ps = parts
1583
+ return `${ parts
1548
1584
. filter ( Boolean )
1549
1585
. join ( ' ' )
1550
- // Remove all spaces between name and parenthesis
1551
1586
. replace ( / ( \w + ) \s + \( / g, '$1(' )
1552
- // Ensure no space before colon, one space after
1553
1587
. replace ( / \s * : \s * / g, ': ' )
1554
- // Clean up spaces around semicolon
1555
- . replace ( / \s * ; / g, ';' )
1556
- . trim ( )
1557
-
1558
- debugLog ( undefined , 'process-function' , `Processed function declaration: ${ ps } ` )
1559
-
1560
- return ps
1588
+ . replace ( / \s + ; / g, ';' )
1589
+ . trim ( ) } ;`
1561
1590
}
1562
1591
1563
1592
/**
0 commit comments