Skip to content

Commit db1a261

Browse files
committed
chore: wip
chore: wip
1 parent d28d9dd commit db1a261

File tree

2 files changed

+105
-26
lines changed

2 files changed

+105
-26
lines changed

fixtures/output/function.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ export declare function processData(data: unknown): unknown;
1313
export declare function complexAsyncGenerator(): any;
1414
export declare function isUser(value: unknown): value is User;
1515
export declare function extractFunctionSignature(declaration: string): FunctionSignature;
16-
export declare function createApi<T extends Record<string, (...args: any[]) => any>(endpoints: T): { [K in keyof T]: ReturnType<T[K]> extends Promise<infer R> ? R : ReturnType<T[K]> } { return {} as any };
16+
export declare function createApi<T extends Record<string, (...args: any[]) => any>>(endpoints: T): { [K in keyof T]: ReturnType<T[K]> extends Promise<infer R> ? R : ReturnType<T[K]> } { return {} as any };

src/extract.ts

Lines changed: 104 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -163,16 +163,16 @@ function extractBalancedSymbols(text: string, openSymbol: string, closeSymbol: s
163163

164164
// Track depth when not in string
165165
if (!inString) {
166-
if (char === openSymbol || char === '{' || char === '<')
166+
if (char === openSymbol || char === '{' || char === '<' || char === '(')
167167
depth++
168-
if (char === closeSymbol || char === '}' || char === '>')
168+
if (char === closeSymbol || char === '}' || char === '>' || char === ')')
169169
depth--
170170
}
171171

172172
content.push(char)
173173
pos = i
174174

175-
// Found matching closing symbol
175+
// Found matching closing symbol at correct depth
176176
if (depth === 0 && content.length > 0 && char === closeSymbol) {
177177
return {
178178
content: content.join(''),
@@ -181,6 +181,18 @@ function extractBalancedSymbols(text: string, openSymbol: string, closeSymbol: s
181181
}
182182
}
183183

184+
// If we reach here without finding a match, return the best effort match
185+
if (content.length > 0) {
186+
// Add closing symbol if missing
187+
if (content[content.length - 1] !== closeSymbol) {
188+
content.push(closeSymbol)
189+
}
190+
return {
191+
content: content.join(''),
192+
rest: text.slice(pos + 1),
193+
}
194+
}
195+
184196
return null
185197
}
186198

@@ -264,7 +276,7 @@ function extractFunctionSignature(declaration: string): FunctionSignature {
264276
let rest = cleanDeclaration.slice(cleanDeclaration.indexOf(name) + name.length).trim()
265277
debugLog(undefined, 'signature-content', `Content after name: ${rest}`)
266278

267-
// Extract generics
279+
// Extract generics with improved depth tracking
268280
const { generics, rest: restAfterGenerics } = extractGenerics(rest)
269281
rest = restAfterGenerics.trim()
270282
debugLog(undefined, 'signature-after-generics', `Remaining content: ${rest}`)
@@ -300,26 +312,75 @@ function extractFunctionName(declaration: string): string {
300312
function extractGenerics(rest: string): { generics: string, rest: string } {
301313
let generics = ''
302314
if (rest.startsWith('<')) {
303-
let depth = 1
304-
let pos = 1
305-
let buffer = '<'
315+
let depth = 1 // Start at 1 since we're starting with an opening bracket
316+
let pos = 0
317+
let buffer = '<' // Start buffer with opening bracket
318+
let inString = false
319+
let stringChar = ''
306320

307-
for (; pos < rest.length && depth > 0; pos++) {
308-
const char = rest[pos]
309-
if (char === '<')
310-
depth++
311-
if (char === '>')
312-
depth--
313-
buffer += char
321+
debugLog(undefined, 'generics-input', `Starting generic extraction with: ${rest}`)
322+
323+
// Start from position 1 since we already handled the first '<'
324+
for (let i = 1; i < rest.length; i++) {
325+
const char = rest[i]
326+
const nextChar = i < rest.length - 1 ? rest[i + 1] : ''
327+
const prevChar = i > 0 ? rest[i - 1] : ''
328+
329+
debugLog(undefined, 'generics-char', `Processing char: ${char}, next char: ${nextChar}, depth: ${depth}, pos: ${i}`)
330+
331+
// Handle string boundaries
332+
if ((char === '"' || char === '\'' || char === '`') && prevChar !== '\\') {
333+
if (!inString) {
334+
inString = true
335+
stringChar = char
336+
debugLog(undefined, 'generics-string', `Entering string with ${stringChar}`)
337+
}
338+
else if (char === stringChar) {
339+
inString = false
340+
debugLog(undefined, 'generics-string', 'Exiting string')
341+
}
342+
}
343+
344+
// Track depth when not in string
345+
if (!inString) {
346+
if (char === '<') {
347+
depth++
348+
debugLog(undefined, 'generics-depth', `Increasing depth to ${depth} at pos ${i}`)
349+
}
350+
else if (char === '>') {
351+
depth--
352+
debugLog(undefined, 'generics-depth', `Decreasing depth to ${depth} at pos ${i}`)
353+
354+
// If we hit zero depth and the next char is also '>', include both
355+
if (depth === 0 && nextChar === '>') {
356+
buffer += '>>' // Add both closing brackets
357+
pos = i + 1 // Skip the next '>' since we've included it
358+
debugLog(undefined, 'generics-complete', `Found double closing bracket at pos ${i}, final buffer: ${buffer}`)
359+
break
360+
}
361+
else if (depth === 0) {
362+
buffer += '>'
363+
pos = i
364+
debugLog(undefined, 'generics-complete', `Found single closing bracket at pos ${i}, final buffer: ${buffer}`)
365+
break
366+
}
367+
}
368+
}
369+
370+
if (depth > 0) { // Only add to buffer if we're still inside generic parameters
371+
buffer += char
372+
debugLog(undefined, 'generics-buffer', `Current buffer: ${buffer}`)
373+
}
314374
}
315375

316-
if (depth === 0) {
376+
if (buffer) {
317377
generics = buffer
318-
rest = rest.slice(pos).trim()
319-
debugLog(undefined, 'signature-generics', `Extracted generics: ${generics}`)
378+
rest = rest.slice(pos + 1)
379+
debugLog(undefined, 'generics-success', `Successfully extracted generics: ${generics}`)
380+
debugLog(undefined, 'generics-rest', `Remaining text: ${rest}`)
320381
}
321382
else {
322-
debugLog(undefined, 'signature-generics', `Unclosed generics in: ${rest}`)
383+
debugLog(undefined, 'generics-fail', `Failed to extract generics from: ${rest}`)
323384
}
324385
}
325386
return { generics, rest }
@@ -373,17 +434,35 @@ function extractReturnType(rest: string, declaration: string): { returnType: str
373434
let depth = 0
374435
let buffer = ''
375436
let i = 0
437+
let inString = false
438+
let stringChar = ''
439+
376440
while (i < rest.length) {
377441
const char = rest[i]
442+
const prevChar = i > 0 ? rest[i - 1] : ''
378443

379-
if (char === '{' || char === '<' || char === '(')
380-
depth++
381-
else if (char === '}' || char === '>' || char === ')')
382-
depth--
444+
// Handle string literals
445+
if ((char === '"' || char === '\'' || char === '`') && prevChar !== '\\') {
446+
if (!inString) {
447+
inString = true
448+
stringChar = char
449+
}
450+
else if (char === stringChar) {
451+
inString = false
452+
}
453+
}
383454

384-
// Stop at function body start
385-
if (depth === 0 && char === '{') {
386-
break
455+
// Track depth when not in string
456+
if (!inString) {
457+
if (char === '{' || char === '<' || char === '(')
458+
depth++
459+
else if (char === '}' || char === '>' || char === ')')
460+
depth--
461+
462+
// Stop at function body start or when we hit a semicolon outside any depth
463+
if ((depth === 0 && char === '{') || (depth === 0 && char === ';')) {
464+
break
465+
}
387466
}
388467

389468
buffer += char

0 commit comments

Comments
 (0)