Skip to content

Commit 8d7e1cc

Browse files
committed
chore: wip
chore: wip chore: wip
1 parent 7833930 commit 8d7e1cc

2 files changed

Lines changed: 176 additions & 105 deletions

File tree

fixtures/output/example-0001.d.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import type { BunPlugin } from 'bun'
22
import type { DtsGenerationConfig, DtsGenerationOption } from '@stacksjs/dtsx'
3-
import { existsSync } from 'node:fs'
4-
import { generate, deepMerge } from '@stacksjs/dtsx'
5-
import { resolve } from 'node:path'
6-
import process from 'node:process'
3+
import { generate } from '@stacksjs/dtsx'
4+
75

86
/**
97
* Example of const declaration

src/extract.ts

Lines changed: 174 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -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> {
234255
export 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+as\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(/\basync\s+/, '')
465-
return processFunctionDeclaration(processed, state.usedTypes, true)
466-
}
467-
468-
if (trimmed.startsWith('function') || trimmed.startsWith('async function')) {
469-
const processed = trimmed.replace(/\basync\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(/import\s+type\s*\{([^}]+)\}\s*from\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+as\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(/import\s*\{([^}]+)\}\s*from\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+as\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 = /(?:extends|implements|:|<)\s*([A-Z][a-zA-Z0-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+
/export\s+declare\s+\{\s*([^}\s]+)(?:\s*,\s*[^}\s]+)*\s*\}/g,
1551+
// Declared exports
1552+
/export\s+declare\s+(?:const|function|class)\s+([a-zA-Z_$][\w$]*)/g,
1553+
// Direct exports
1554+
/export\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(/(?:export|declare)\s+(?:const|function|class)\s+([a-zA-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

Comments
 (0)