@@ -50,6 +50,13 @@ export interface RegexPatterns {
5050 readonly functionOverload : RegExp
5151}
5252
53+ interface ImportTrackingState {
54+ typeImports : Map < string , Set < string > > // module -> Set of type names
55+ valueImports : Map < string , Set < string > > // module -> Set of value names
56+ usedTypes : Set < string > // All used type names
57+ usedValues : Set < string > // All used value names
58+ }
59+
5360/**
5461 * Regular expression patterns used throughout the module
5562 * @remarks These patterns are optimized for performance and reliability
@@ -139,6 +146,7 @@ interface ProcessingState {
139146 lines : string [ ]
140147 comments : string [ ]
141148 } | null
149+ importTracking : ImportTrackingState
142150}
143151
144152/**
@@ -209,6 +217,19 @@ export function createProcessingState(): ProcessingState {
209217 availableValues : new Map ( ) ,
210218 currentIndentation : '' ,
211219 declarationBuffer : null ,
220+ importTracking : createImportTrackingState ( ) ,
221+ }
222+ }
223+
224+ /**
225+ * Creates initial import tracking state
226+ */
227+ function createImportTrackingState ( ) : ImportTrackingState {
228+ return {
229+ typeImports : new Map ( ) ,
230+ valueImports : new Map ( ) ,
231+ usedTypes : new Set ( ) ,
232+ usedValues : new Set ( ) ,
212233 }
213234}
214235
@@ -234,9 +255,34 @@ export async function extract(filePath: string): Promise<string> {
234255export function extractDtsTypes ( sourceCode : string ) : string {
235256 const state = createProcessingState ( )
236257
237- // Process the entire source file
258+ // Process imports first
259+ sourceCode . split ( '\n' ) . forEach ( ( line ) => {
260+ if ( line . includes ( 'import ' ) ) {
261+ processImports ( line , state . importTracking )
262+ }
263+ } )
264+
265+ // Process declarations
238266 processSourceFile ( sourceCode , state )
239267
268+ // Final pass to track what actually made it to the output
269+ state . dtsLines . forEach ( ( line ) => {
270+ if ( line . trim ( ) && ! line . startsWith ( 'import' ) ) {
271+ trackTypeUsage ( line , state . importTracking )
272+ trackValueUsage ( line , state . importTracking , state . dtsLines )
273+ }
274+ } )
275+
276+ // Generate optimized imports based on actual output
277+ const optimizedImports = generateOptimizedImports ( state . importTracking , state . dtsLines )
278+
279+ // Replace existing imports with optimized ones
280+ state . dtsLines = [
281+ ...optimizedImports ,
282+ '' , // Add blank line after imports
283+ ...state . dtsLines . filter ( line => ! line . trim ( ) . startsWith ( 'import' ) ) ,
284+ ]
285+
240286 return formatOutput ( state )
241287}
242288
@@ -371,119 +417,77 @@ export function generateImports(state: ProcessingState): string[] {
371417}
372418
373419/**
374- * Process imports while preserving their original sources
420+ * Generate optimized imports based on usage
375421 */
376- export function processImports ( imports : string [ ] , usedTypes : Set < string > ) : string [ ] {
377- const importMap = new Map < string , Set < string > > ( )
378- const reExportedTypes = new Set < string > ( )
379-
380- for ( const line of imports ) {
381- const typeImportMatch = line . match ( REGEX . typeImport )
382- const regularImportMatch = line . match ( REGEX . regularImport )
383- const match = typeImportMatch || regularImportMatch
384-
385- if ( match ) {
386- const types = match [ 1 ]
387- . split ( ',' )
388- . map ( ( t ) => {
389- const [ type , alias ] = t . trim ( ) . split ( / \s + a s \s + / )
390- return alias || type . trim ( )
391- } )
392- const module = match [ 2 ]
422+ export function generateOptimizedImports ( state : ImportTrackingState , dtsLines : string [ ] ) : string [ ] {
423+ const imports : string [ ] = [ ]
393424
394- if ( ! importMap . has ( module ) )
395- importMap . set ( module , new Set ( ) )
425+ // Generate type imports
426+ for ( const [ module , types ] of state . typeImports ) {
427+ const usedTypes = Array . from ( types )
428+ . filter ( t => state . usedTypes . has ( t ) )
429+ . sort ( )
396430
397- types . forEach ( ( type ) => {
398- importMap . get ( module ) ! . add ( type )
399- if ( usedTypes . has ( type ) )
400- reExportedTypes . add ( type )
401- } )
431+ if ( usedTypes . length > 0 ) {
432+ imports . push ( `import type { ${ usedTypes . join ( ', ' ) } } from '${ module } '` )
402433 }
403434 }
404435
405- return Array . from ( importMap . entries ( ) )
406- . map ( ( [ module , types ] ) => {
407- const relevantTypes = Array . from ( types ) . filter ( type =>
408- usedTypes . has ( type ) || reExportedTypes . has ( type ) )
409-
410- if ( relevantTypes . length === 0 )
411- return ''
436+ // Generate value imports
437+ for ( const [ module , values ] of state . valueImports ) {
438+ const usedValues = Array . from ( values )
439+ . filter ( v => state . usedValues . has ( v ) )
440+ // Only include values that appear in actual declarations
441+ . filter ( v => dtsLines . some ( line =>
442+ line . includes ( `declare ${ v } ` )
443+ || line . includes ( `export declare ${ v } ` )
444+ || line . includes ( `export { ${ v } ` )
445+ || line . includes ( `, ${ v } ` )
446+ || line . includes ( `${ v } }` ) ,
447+ ) )
448+ . sort ( )
449+
450+ if ( usedValues . length > 0 ) {
451+ imports . push ( `import { ${ usedValues . join ( ', ' ) } } from '${ module } '` )
452+ }
453+ }
412454
413- return `import type { ${ relevantTypes . sort ( ) . join ( ', ' ) } } from '${ module } ';`
414- } )
415- . filter ( Boolean )
416- . sort ( )
455+ return imports . sort ( )
417456}
418457
419458/**
420- * Process declarations
459+ * Process imports and track their usage
421460 */
422- export function processDeclaration ( declaration : string , state : ProcessingState ) : string {
423- console . log ( 'Processing declaration:' , { declaration, type : 'START' } )
424-
425- const trimmed = declaration . trim ( )
426-
427- // Handle different types of declarations
428- if ( trimmed . startsWith ( 'export type' ) || trimmed . startsWith ( 'type' ) ) {
429- console . log ( 'Handling type declaration' )
430- return processTypeDeclaration ( trimmed , trimmed . startsWith ( 'export' ) )
431- }
432-
433- if ( trimmed . startsWith ( 'export interface' ) || trimmed . startsWith ( 'interface' ) ) {
434- console . log ( 'Handling interface declaration' )
435- return processInterfaceDeclaration ( trimmed , trimmed . startsWith ( 'export' ) )
436- }
437-
438- if ( trimmed . startsWith ( 'export const' ) ) {
439- console . log ( 'Handling exported const declaration' )
440- return processConstDeclaration ( trimmed )
441- }
442-
443- if ( trimmed . startsWith ( 'export interface' ) ) {
444- return processInterfaceDeclaration ( trimmed )
445- }
446-
447- if ( trimmed . startsWith ( 'interface' ) ) {
448- return processInterfaceDeclaration ( trimmed , false )
449- }
450-
451- if ( trimmed . startsWith ( 'export type {' ) ) {
452- return trimmed
453- }
454-
455- if ( trimmed . startsWith ( 'export type' ) ) {
456- return processTypeDeclaration ( trimmed )
457- }
458-
459- if ( trimmed . startsWith ( 'type' ) ) {
460- return processTypeDeclaration ( trimmed , false )
461- }
462-
463- if ( trimmed . startsWith ( 'export function' ) || trimmed . startsWith ( 'export async function' ) ) {
464- const processed = trimmed . replace ( / \b a s y n c \s + / , '' )
465- return processFunctionDeclaration ( processed , state . usedTypes , true )
466- }
467-
468- if ( trimmed . startsWith ( 'function' ) || trimmed . startsWith ( 'async function' ) ) {
469- const processed = trimmed . replace ( / \b a s y n c \s + / , '' )
470- return processFunctionDeclaration ( processed , state . usedTypes , false )
471- }
472-
473- if ( trimmed . startsWith ( 'export default' ) ) {
474- return `${ trimmed } ;`
461+ export function processImports ( line : string , state : ImportTrackingState ) : void {
462+ // Handle type imports
463+ const typeImportMatch = line . match ( / i m p o r t \s + t y p e \s * \{ ( [ ^ } ] + ) \} \s * f r o m \s * [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / )
464+ if ( typeImportMatch ) {
465+ const [ , names , module ] = typeImportMatch
466+ if ( ! state . typeImports . has ( module ) ) {
467+ state . typeImports . set ( module , new Set ( ) )
468+ }
469+ names . split ( ',' ) . forEach ( ( name ) => {
470+ const cleanName = name . trim ( ) . split ( / \s + a s \s + / ) . shift ( ) ! // Use shift() to get original name before 'as'
471+ state . typeImports . get ( module ) ! . add ( cleanName )
472+ } )
473+ return
475474 }
476475
477- if ( trimmed . startsWith ( 'export' ) ) {
478- return trimmed
476+ // Handle value imports
477+ const valueImportMatch = line . match ( / i m p o r t \s * \{ ( [ ^ } ] + ) \} \s * f r o m \s * [ ' " ] ( [ ^ ' " ] + ) [ ' " ] / )
478+ if ( valueImportMatch ) {
479+ const [ , names , module ] = valueImportMatch
480+ if ( ! state . valueImports . has ( module ) ) {
481+ state . valueImports . set ( module , new Set ( ) )
482+ }
483+ names . split ( ',' ) . forEach ( ( name ) => {
484+ const cleanName = name . trim ( ) . split ( / \s + a s \s + / ) . shift ( ) ! // Use shift() to get original name before 'as'
485+ state . valueImports . get ( module ) ! . add ( cleanName )
486+ } )
479487 }
480-
481- console . log ( 'Processing declaration:' , { declaration, type : 'END' } )
482-
483- return `declare ${ trimmed } `
484488}
485489
486- function processDeclarationBuffer (
490+ export function processDeclarationBuffer (
487491 buffer : NonNullable < ProcessingState [ 'declarationBuffer' ] > ,
488492 state : ProcessingState ,
489493 isExported : boolean ,
@@ -516,16 +520,16 @@ function processDeclarationBuffer(
516520 }
517521}
518522
519- function processDeclarationBlock ( lines : string [ ] , comments : string [ ] , state : ProcessingState ) : void {
523+ export function processDeclarationBlock ( lines : string [ ] , comments : string [ ] , state : ProcessingState ) : void {
520524 const declaration = lines . join ( '\n' )
521525 const trimmed = declaration . trim ( )
522526
523527 if ( ! trimmed || trimmed . startsWith ( '//' ) )
524528 return
525529
526530 // Keep original indentation
527- const indentMatch = lines [ 0 ] . match ( / ^ ( \s * ) / )
528- const baseIndent = indentMatch ? indentMatch [ 1 ] : ''
531+ // const indentMatch = lines[0].match(/^(\s*)/)
532+ // const baseIndent = indentMatch ? indentMatch[1] : ''
529533
530534 if ( comments . length > 0 ) {
531535 state . dtsLines . push ( ...comments )
@@ -1523,6 +1527,59 @@ export function trackUsedTypes(content: string, usedTypes: Set<string>): void {
15231527 }
15241528}
15251529
1530+ /**
1531+ * Track type usage in declarations
1532+ */
1533+ function trackTypeUsage ( content : string , state : ImportTrackingState ) : void {
1534+ // Only look for capitalized type references that are actually used in declarations
1535+ const typePattern = / (?: e x t e n d s | i m p l e m e n t s | : | < ) \s * ( [ A - Z ] [ a - z A - Z 0 - 9 ] * (?: < [ ^ > ] + > ) ? ) / g
1536+ let match
1537+ while ( ( match = typePattern . exec ( content ) ) !== null ) {
1538+ const typeName = match [ 1 ] . split ( '<' ) [ 0 ] // Handle generic types
1539+ state . usedTypes . add ( typeName )
1540+ }
1541+ }
1542+
1543+ /**
1544+ * Track value usage in declarations
1545+ */
1546+ function trackValueUsage ( content : string , state : ImportTrackingState , dtsLines ?: string [ ] ) : void {
1547+ // Track values in declarations
1548+ const patterns = [
1549+ // Export statements in declarations
1550+ / e x p o r t \s + d e c l a r e \s + \{ \s * ( [ ^ } \s ] + ) (?: \s * , \s * [ ^ } \s ] + ) * \s * \} / g,
1551+ // Declared exports
1552+ / e x p o r t \s + d e c l a r e \s + (?: c o n s t | f u n c t i o n | c l a s s ) \s + ( [ a - z A - Z _ $ ] [ \w $ ] * ) / g,
1553+ // Direct exports
1554+ / e x p o r t \s + \{ \s * ( [ ^ } \s ] + ) (?: \s * , \s * [ ^ } \s ] + ) * \s * \} / g,
1555+ ]
1556+
1557+ for ( const pattern of patterns ) {
1558+ let match
1559+ while ( ( match = pattern . exec ( content ) ) !== null ) {
1560+ const values = match [ 1 ] . split ( ',' ) . map ( v => v . trim ( ) )
1561+ for ( const value of values ) {
1562+ if ( ! [ 'type' , 'interface' , 'declare' , 'extends' , 'implements' , 'function' , 'const' , 'let' , 'var' ] . includes ( value ) ) {
1563+ state . usedValues . add ( value )
1564+ }
1565+ }
1566+ }
1567+ }
1568+
1569+ // Track values in the final output lines if provided
1570+ if ( dtsLines ) {
1571+ dtsLines . forEach ( ( line ) => {
1572+ if ( line . includes ( 'declare' ) || line . includes ( 'export' ) ) {
1573+ // Look for exported values
1574+ const exportMatch = line . match ( / (?: e x p o r t | d e c l a r e ) \s + (?: c o n s t | f u n c t i o n | c l a s s ) \s + ( [ a - z A - Z _ $ ] [ \w $ ] * ) / )
1575+ if ( exportMatch ) {
1576+ state . usedValues . add ( exportMatch [ 1 ] )
1577+ }
1578+ }
1579+ } )
1580+ }
1581+ }
1582+
15261583/**
15271584 * Process simple value types
15281585 */
@@ -1633,6 +1690,12 @@ function processDeclarationLine(line: string, state: ProcessingState): void {
16331690 return
16341691 }
16351692
1693+ // Track type and value usage in declarations
1694+ if ( line . includes ( 'declare' ) || line . includes ( 'export' ) ) {
1695+ trackTypeUsage ( line , state . importTracking )
1696+ trackValueUsage ( line , state . importTracking , state . dtsLines )
1697+ }
1698+
16361699 // Process declaration start
16371700 if ( isDeclarationStart ( trimmedLine ) ) {
16381701 // Clean up any existing declaration first
@@ -1729,3 +1792,13 @@ function needsExport(line: string): boolean {
17291792 || trimmed . startsWith ( 'export interface ' )
17301793 )
17311794}
1795+
1796+ function shouldTrackDeclaration ( line : string ) : boolean {
1797+ return line . includes ( ':' )
1798+ || line . includes ( 'extends' )
1799+ || line . includes ( 'implements' )
1800+ || line . includes ( '=' )
1801+ || line . includes ( '(' )
1802+ || line . includes ( ')' )
1803+ || line . includes ( 'export' ) // Added export tracking
1804+ }
0 commit comments