diff --git a/fixtures/input/type.ts b/fixtures/input/type.ts index 6ee27b9..decdce1 100644 --- a/fixtures/input/type.ts +++ b/fixtures/input/type.ts @@ -65,3 +65,19 @@ export type DynamicRecord = { ? Record : never } + +export type RecordMerge = IsEmptyType extends true + ? T + : [T, U] extends [any[], any[]] + ? U + : [T, U] extends [object, object] + ? { + [K in keyof T | keyof U]: K extends keyof T + ? K extends keyof U + ? RecordMerge + : T[K] + : K extends keyof U + ? U[K] + : never + } + : U diff --git a/fixtures/output/0003.d.ts b/fixtures/output/0003.d.ts index f26111c..c0c872e 100644 --- a/fixtures/output/0003.d.ts +++ b/fixtures/output/0003.d.ts @@ -9,4 +9,4 @@ declare interface AutoImportsPlugin { export declare function autoImports(options: Partial): AutoImportsPlugin; export declare type AutoImportsOptions = UnimportOptions -export default autoImports; \ No newline at end of file +export default autoImports \ No newline at end of file diff --git a/fixtures/output/0004.d.ts b/fixtures/output/0004.d.ts index e7173ef..832f2d2 100644 --- a/fixtures/output/0004.d.ts +++ b/fixtures/output/0004.d.ts @@ -9,8 +9,10 @@ export declare interface DtsGenerationConfig { verbose: boolean } export declare type DtsGenerationOption = Partial -export declare type DtsGenerationOptions = DtsGenerationOption | DtsGenerationOption[] -export declare interface RegexPatterns { + +export type DtsGenerationOptions = DtsGenerationOption | DtsGenerationOption[] + +export interface RegexPatterns { readonly typeImport: RegExp readonly regularImport: RegExp readonly bracketOpen: RegExp @@ -37,7 +39,8 @@ export declare interface RegexPatterns { readonly moduleDeclaration: RegExp readonly moduleAugmentation: RegExp } -export declare interface ImportTrackingState { + +export interface ImportTrackingState { typeImports: Map> valueImports: Map> usedTypes: Set @@ -49,7 +52,8 @@ export declare interface ImportTrackingState { typeExportSources: Map defaultExportValue?: string } -export declare interface ProcessingState { + +export interface ProcessingState { dtsLines: string[] imports: string[] usedTypes: Set @@ -74,33 +78,38 @@ export declare interface ProcessingState { defaultExports: Set currentScope: 'top' | 'function' } -export declare interface MethodSignature { + +export interface MethodSignature { name: string async: boolean generics: string params: string returnType: string } -export declare interface PropertyInfo { + +export interface PropertyInfo { key: string value: string type: string nested?: PropertyInfo[] method?: MethodSignature } -export declare interface ImportInfo { + +export interface ImportInfo { kind: 'type' | 'value' | 'mixed' usedTypes: Set usedValues: Set source: string } -export declare interface FunctionSignature { + +export interface FunctionSignature { name: string params: string returnType: string generics: string } -export declare interface ProcessedMethod { + +export interface ProcessedMethod { name: string signature: string } \ No newline at end of file diff --git a/fixtures/output/type.d.ts b/fixtures/output/type.d.ts index 60ce05e..6478eae 100644 --- a/fixtures/output/type.d.ts +++ b/fixtures/output/type.d.ts @@ -9,17 +9,21 @@ export type ComplexUnionIntersection = & { metadata: Record } -export declare type ReadonlyDeep = { + + +export type ReadonlyDeep = { readonly [P in keyof T]: T[P] extends object ? ReadonlyDeep : T[P] } -export declare type ConditionalResponse = T extends Array + +export type ConditionalResponse = T extends Array ? ApiResponse : T extends object ? ApiResponse : ApiResponse -export declare type EventType = 'click' | 'focus' | 'blur' + +export type EventType = 'click' | 'focus' | 'blur' export type ElementType = 'button' | 'input' | 'form' -export declare type EventHandler = `on${Capitalize}${Capitalize}` +export type EventHandler = `on${Capitalize}${Capitalize}` export type RecursiveObject = { id: string @@ -27,25 +31,46 @@ export type RecursiveObject = { parent?: RecursiveObject metadata: Record } -export declare type UserId = string & { readonly __brand: unique symbol } + +export type UserId = string & { readonly __brand: unique symbol } export type ProductId = number & { readonly __brand: unique symbol } -export declare type DeepPartial = T extends object ? { + +export type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial } : T -export declare type DeepRequired = T extends object ? { + +export type DeepRequired = T extends object ? { [P in keyof T]-?: DeepRequired } : T -export declare type PolymorphicComponent

= { + +export type PolymorphicComponent

= { ( props: { as?: C } & Omit, keyof P> & P ): React.ReactElement | null } -export declare type DynamicRecord = { + +export type DynamicRecord = { [P in K]: P extends number ? Array : P extends string ? Record : never -} \ No newline at end of file +} + +export type RecordMerge = IsEmptyType extends true + ? T + : [T, U] extends [any[], any[]] + ? U + : [T, U] extends [object, object] + ? { + [K in keyof T | keyof U]: K extends keyof T + ? K extends keyof U + ? RecordMerge + : T[K] + : K extends keyof U + ? U[K] + : never + } + : U \ No newline at end of file diff --git a/src/extract.ts b/src/extract.ts index 93dd7bc..e384864 100644 --- a/src/extract.ts +++ b/src/extract.ts @@ -1142,7 +1142,6 @@ function isDeclarationStart(line: string): boolean { || trimmed.startsWith('function ') || trimmed.startsWith('async function ') || trimmed.startsWith('declare ') - || trimmed.startsWith('declare module') || /^export\s+(?:interface|type|const|function\*?|async\s+function\*?)/.test(trimmed) ) } @@ -1925,22 +1924,52 @@ function processSourceFile(content: string, state: ProcessingState): void { bracketDepth += openCurly - closeCurly angleDepth += openAngle - closeAngle - // Check for end of declaration - const isComplete = bracketDepth === 0 && angleDepth === 0 && trimmedLine.endsWith('}') - - // Look ahead for continuation - const nextLine = i < lines.length - 1 ? lines[i + 1]?.trim() : '' - const shouldContinue = bracketDepth > 0 || angleDepth > 0 - || (nextLine && !nextLine.startsWith('export') && !nextLine.startsWith('interface')) - - if (!shouldContinue || isComplete) { - debugLog('declaration-complete', `Declaration complete at line ${i + 1}`) - processBlock(currentBlock, currentComments, state) - currentBlock = [] - currentComments = [] - inDeclaration = false - bracketDepth = 0 - angleDepth = 0 + // Special handling for type declarations + const isTypeDeclaration = currentBlock[0].trim().startsWith('type') + || currentBlock[0].trim().startsWith('export type') + + if (isTypeDeclaration) { + // For type declarations, we need to track the complete expression + const nextLine = i < lines.length - 1 ? lines[i + 1]?.trim() : '' + const shouldContinue = bracketDepth > 0 || angleDepth > 0 + || !trimmedLine.endsWith(';') // No semicolon yet + || trimmedLine.endsWith('?') // Conditional type continues + || trimmedLine.endsWith(':') // Property type continues + || (nextLine && ( + nextLine.startsWith('?') + || nextLine.startsWith(':') + || nextLine.startsWith('|') + || nextLine.startsWith('&') + || nextLine.startsWith('extends') + || nextLine.startsWith('=>') + )) + + if (!shouldContinue) { + debugLog('declaration-complete', `Type declaration complete at line ${i + 1}`) + processBlock(currentBlock, currentComments, state) + currentBlock = [] + currentComments = [] + inDeclaration = false + bracketDepth = 0 + angleDepth = 0 + } + } + else { + // Original handling for non-type declarations + const isComplete = bracketDepth === 0 && angleDepth === 0 && trimmedLine.endsWith('}') + const nextLine = i < lines.length - 1 ? lines[i + 1]?.trim() : '' + const shouldContinue = bracketDepth > 0 || angleDepth > 0 + || (nextLine && !nextLine.startsWith('export') && !nextLine.startsWith('interface')) + + if (!shouldContinue || isComplete) { + debugLog('declaration-complete', `Declaration complete at line ${i + 1}`) + processBlock(currentBlock, currentComments, state) + currentBlock = [] + currentComments = [] + inDeclaration = false + bracketDepth = 0 + angleDepth = 0 + } } } }