Skip to content

Commit 32188de

Browse files
committed
chore: wip
1 parent ebba5f7 commit 32188de

File tree

2 files changed

+91
-56
lines changed

2 files changed

+91
-56
lines changed

fixtures/output/example-0001.d.ts

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import type { BunPlugin } from 'bun'
22
import type { DtsGenerationConfig, DtsGenerationOption } from '@stacksjs/dtsx'
33
import type { BunPlugin } from 'bun';
44
/**
5-
* Example of const declaration
6-
*/
7-
export declare const conf: conf;
5+
* Example of const declaration
6+
*/
7+
export declare const conf: { [key: string]: string };
88
export declare const someObject: {
99
someString: 'Stacks';
1010
someNumber: 1000;
@@ -13,11 +13,11 @@ export declare const someObject: {
1313
someFunction: (...args: any[]) => void;
1414
anotherOne: (...args: any[]) => string;
1515
someArray: Array<1 | 2 | 3>;
16-
someNestedArray: Array<1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10>;
17-
someNestedArray2: Array<1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 'dummy value'>;
18-
someNestedArray3: Array<1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 'dummy value', [11, 12, 13]'>;
19-
someOtherNestedArray: Array<'some text', 2, console.log, () => console.log('hello world'), helloWorld'>;
20-
someComplexArray: Array<Object>;
16+
someNestedArray: Array<Array<1 | 2 | 3> | Array<4 | 5 | 6 | 7 | 8 | 9 | 10>>;
17+
someNestedArray2: Array<Array<1 | 2 | 3> | Array<4 | 5 | 6 | 7 | 8 | 9 | 10> | 'dummy value'>;
18+
someNestedArray3: Array<Array<1 | 2 | 3> | Array<4 | 5 | 6 | 7 | 8 | 9 | 10> | 'dummy value', [11, 12, 13]'>;
19+
someOtherNestedArray: Array<Array<'some text', 2, console.log, () => console.log('hello world'), helloWorld'>>;
20+
someComplexArray: Array<Array<Object>>;
2121
someObject: {
2222
key: 'value';
2323
};
@@ -35,49 +35,49 @@ export declare const someObject: {
3535
someInlineCall3: (...args: any[]) => void;
3636
};
3737
/**
38-
* Example of interface declaration
39-
* with another comment in an extra line
40-
*/
38+
* Example of interface declaration
39+
* with another comment in an extra line
40+
*/
4141
export declare interface User {
4242
id: number;
4343
name: string;
4444
email: string;
4545
}
4646
/**
47-
* Example of type declaration
48-
*
49-
* with multiple lines of comments, including an empty line
50-
*/
47+
* Example of type declaration
48+
*
49+
* with multiple lines of comments, including an empty line
50+
*/
5151
export declare interface ResponseData {
5252
success: boolean;
5353
data: User[];
5454
}
5555
/**
56-
* Example of function declaration
57-
*
58-
*
59-
* with multiple empty lines, including an empty lines
60-
*/
56+
* Example of function declaration
57+
*
58+
*
59+
* with multiple empty lines, including an empty lines
60+
*/
6161
export declare function fetchUsers(): Promise<ResponseData>;
6262
export declare interface ApiResponse<T> {
6363
status: number;
6464
message: string;
6565
data: T;
6666
}
6767
/**
68-
* Example of another const declaration
69-
*
70-
* with multiple empty lines, including being poorly formatted
71-
*/
72-
declare const settings: settings;
68+
* Example of another const declaration
69+
*
70+
* with multiple empty lines, including being poorly formatted
71+
*/
72+
declare const settings: { [key: string]: any };
7373
export declare interface Product {
7474
id: number;
7575
name: string;
7676
price: number;
7777
}
7878
/**
79-
* Example of function declaration
80-
*/
79+
* Example of function declaration
80+
*/
8181
export declare function getProduct(id: number): Promise<ApiResponse<Product>>;
8282
export declare interface AuthResponse {
8383
token: string;
@@ -95,7 +95,7 @@ declare interface Options<T> {
9595
defaultConfig: T;
9696
}
9797
export declare async function loadConfig<T extends Record<string, unknown>>(): void;
98-
declare const dtsConfig: dtsConfig;
98+
declare const dtsConfig: DtsGenerationConfig;
9999
export { generate, dtsConfig }
100100
export declare type { DtsGenerationOption }
101101
export { config } from './config'
@@ -104,4 +104,5 @@ export * from './generate'
104104
export * from './types'
105105
export * from './utils'
106106

107-
export default export default dts;;
107+
export default
108+
export default dts;

src/extract.ts

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ interface TypeAnnotation {
6161
}
6262

6363
function getTypeAnnotation(declaration: string): TypeAnnotation {
64+
// eslint-disable-next-line regexp/no-super-linear-backtracking
6465
const match = declaration.match(/:\s*(\{[^=]+\}|\[[^\]]+\]|[^=]+?)\s*=/)
6566
return {
6667
raw: match?.[1]?.trim() ?? null,
@@ -376,46 +377,79 @@ function inferArrayType(value: string): string {
376377
if (elements.length === 0)
377378
return 'never[]'
378379

379-
const elementTypes = elements.map(element => inferElementType(element.trim()))
380-
381-
// Check if we have nested arrays
382-
if (elements.some(el => el.trim().startsWith('['))) {
383-
// Preserve array nesting structure
384-
return `Array<${elementTypes.join(' | ').replace(/'+$/, '')}>`
385-
}
386-
387-
const uniqueTypes = [...new Set(elementTypes)]
388-
return `Array<${uniqueTypes.join(' | ')}>`
380+
// Use processNestedArray to handle the array structure
381+
const processedType = processNestedArray(elements)
382+
return `Array<${processedType}>`
389383
}
390384

391385
/**
392386
* Infer element type from a single array element
393387
*/
394388
export function inferElementType(element: string): string {
395-
if (element.trim().startsWith('[')) {
396-
const nested = inferArrayType(element)
397-
return nested
398-
}
389+
const trimmed = element.trim()
399390

400-
if (element.trim().startsWith('{'))
401-
return formatObjectType(parseObjectLiteral(element))
391+
// Handle string literals
392+
if (trimmed.startsWith('\'') || trimmed.startsWith('"')) {
393+
const cleanValue = trimmed.slice(1, -1).replace(/'+$/, '')
394+
return `'${cleanValue}'`
395+
}
402396

403-
if (element.trim().startsWith('\'') || element.trim().startsWith('"'))
404-
return `'${element.slice(1, -1).replace(/'+$/, '')}'`
397+
// Handle numbers
398+
if (!Number.isNaN(Number(trimmed))) {
399+
return trimmed
400+
}
405401

406-
if (!Number.isNaN(Number(element.trim())))
407-
return element.trim()
402+
// Handle objects
403+
if (trimmed.startsWith('{')) {
404+
return formatObjectType(parseObjectLiteral(trimmed))
405+
}
408406

409-
if (element.trim() === 'console.log')
407+
// Handle known function references
408+
if (trimmed === 'console.log' || trimmed.endsWith('.log')) {
410409
return '(...args: any[]) => void'
410+
}
411411

412-
if (element.includes('=>'))
412+
// Handle arrow functions
413+
if (trimmed.includes('=>')) {
413414
return '(...args: any[]) => void'
415+
}
416+
417+
// Handle function calls
418+
if (trimmed.includes('(') && trimmed.includes(')')) {
419+
return 'unknown'
420+
}
414421

415-
if (element.includes('.'))
422+
// Handle references to global objects
423+
if (trimmed.includes('.')) {
416424
return 'unknown'
425+
}
426+
427+
// Default case
428+
return 'unknown'
429+
}
430+
431+
/**
432+
* Process nested array structures
433+
*/
434+
export function processNestedArray(elements: string[]): string {
435+
const processedTypes = elements.map((element) => {
436+
const trimmed = element.trim()
437+
438+
// Handle nested arrays
439+
if (trimmed.startsWith('[')) {
440+
const nestedContent = extractNestedContent(trimmed, '[', ']')
441+
if (nestedContent) {
442+
const nestedElements = splitArrayElements(nestedContent)
443+
return `Array<${processNestedArray(nestedElements)}>`
444+
}
445+
return 'never'
446+
}
447+
448+
return inferElementType(trimmed)
449+
})
417450

418-
return 'any'
451+
const uniqueTypes = [...new Set(processedTypes.filter(type => type !== 'never'))]
452+
return uniqueTypes.join(' | ')
419453
}
420454

421455
/**
@@ -491,7 +525,7 @@ export function splitArrayElements(content: string): string[] {
491525
if (current.trim())
492526
elements.push(current.trim())
493527

494-
return elements
528+
return elements.filter(Boolean)
495529
}
496530

497531
/**
@@ -654,10 +688,10 @@ export function isCommentLine(line: string): boolean {
654688

655689
function processCommentLine(line: string, state: ProcessingState): void {
656690
const indentedLine = line.startsWith('*')
657-
? ` ${line}` // Add indentation for content lines
691+
? ` ${line}` // Add indentation for content lines
658692
: line.startsWith('/**') || line.startsWith('*/')
659693
? line // Keep delimiters at original indentation
660-
: ` ${line}` // Add indentation for other lines
694+
: ` ${line}` // Add indentation for other lines
661695

662696
if (line.startsWith('/**'))
663697
state.lastCommentBlock = ''

0 commit comments

Comments
 (0)