Skip to content

Commit 864627e

Browse files
committed
chore: wip
chore: wip chore: wip chore: wip chore: wip chore: wip
1 parent a7e6e7f commit 864627e

2 files changed

Lines changed: 128 additions & 83 deletions

File tree

fixtures/output/example-0001.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ declare interface Options<T> {
6666
cwd?: string
6767
defaultConfig: T
6868
}
69-
export declare function loadConfig<T extends Record<string, unknown>(): void;
69+
export declare function loadConfig<T extends Record<string, unknown>>({ name, cwd, defaultConfig }: Options<T>): Promise<T>;
7070
declare const dtsConfig: DtsGenerationConfig;
7171
export { generate, dtsConfig }
7272
export type { DtsGenerationOption }

src/extract.ts

Lines changed: 127 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ export interface FunctionSignature {
179179
name: string
180180
params: string
181181
returnType: string
182-
isAsync: boolean
182+
// isAsync: boolean
183183
generics: string
184184
}
185185

@@ -286,6 +286,72 @@ export function extractDtsTypes(sourceCode: string): string {
286286
return formatOutput(state)
287287
}
288288

289+
/**
290+
* Extracts a substring that contains balanced opening and closing symbols, handling nested structures.
291+
* @param text - The text to extract from.
292+
* @param openSymbol - The opening symbol (e.g., '<', '(', '{').
293+
* @param closeSymbol - The closing symbol (e.g., '>', ')', '}').
294+
* @returns An object containing the content and the rest of the string.
295+
*/
296+
function extractBalancedSymbols(text: string, openSymbol: string, closeSymbol: string): { content: string, rest: string } | null {
297+
if (!text.startsWith(openSymbol)) {
298+
return null
299+
}
300+
301+
const stack: string[] = []
302+
let inString = false
303+
let stringChar = ''
304+
for (let i = 0; i < text.length; i++) {
305+
const char = text[i]
306+
const prevChar = text[i - 1]
307+
308+
// Handle string literals
309+
if ((char === '"' || char === '\'' || char === '`') && prevChar !== '\\') {
310+
if (!inString) {
311+
inString = true
312+
stringChar = char
313+
}
314+
else if (char === stringChar) {
315+
inString = false
316+
}
317+
}
318+
319+
if (!inString) {
320+
if (char === openSymbol) {
321+
stack.push(char)
322+
}
323+
else if (char === closeSymbol) {
324+
if (stack.length === 0) {
325+
// Unbalanced closing symbol
326+
return null
327+
}
328+
stack.pop()
329+
if (stack.length === 0) {
330+
// All symbols balanced, return substring
331+
return {
332+
content: text.slice(0, i + 1),
333+
rest: text.slice(i + 1).trim(),
334+
}
335+
}
336+
}
337+
}
338+
}
339+
340+
return null // Unbalanced symbols
341+
}
342+
343+
function extractFunctionName(declaration: string): { name: string, rest: string } {
344+
const match = declaration.match(/^([a-z_$][\w$]*)/i)
345+
if (match) {
346+
const name = match[1]
347+
const rest = declaration.slice(match[0].length).trim()
348+
return { name, rest }
349+
}
350+
else {
351+
return { name: '', rest: declaration }
352+
}
353+
}
354+
289355
/**
290356
* Main line processing function
291357
* Handles different types of content and maintains state
@@ -1079,6 +1145,8 @@ function isDeclarationStart(line: string): boolean {
10791145
|| line.startsWith('declare ')
10801146
// Handle possible export combinations
10811147
|| /^export\s+(interface|type|const|function|async\s+function)/.test(line)
1148+
// Handle 'export async function'
1149+
|| line.startsWith('export async function')
10821150
)
10831151
}
10841152

@@ -1531,80 +1599,89 @@ function netBraceCount(line: string): number {
15311599
}
15321600

15331601
/**
1534-
* Extract complete function signature
1602+
* Extract complete function signature using regex
15351603
*/
1536-
export function extractFunctionSignature(declaration: string): FunctionSignature {
1604+
function extractFunctionSignature(declaration: string): FunctionSignature {
15371605
// Remove comments from the declaration
15381606
const cleanDeclaration = removeLeadingComments(declaration).trim()
15391607

1540-
// Check if the function is async
1541-
const isAsync = /^async\s+/.test(cleanDeclaration)
1542-
1543-
// Remove 'export' and 'async' keywords
1544-
let declarationWithoutKeywords = cleanDeclaration
1545-
.replace(/^export\s+/, '')
1546-
.replace(/^async\s+/, '')
1547-
.trim()
1548-
1549-
// Remove 'function' keyword
1550-
declarationWithoutKeywords = declarationWithoutKeywords.replace(/^function\s+/, '').trim()
1551-
1552-
// Extract the function name
1553-
const nameMatch = declarationWithoutKeywords.match(/^([a-z_$][\w$]*)/i)
1554-
const name = nameMatch ? nameMatch[1] : ''
1608+
// Remove leading 'export', 'async', 'function' keywords
1609+
const withoutKeywords = cleanDeclaration.replace(/^\s*(export\s+)?(async\s+)?function\s+/, '').trim()
15551610

1611+
// Extract function name
1612+
const { name, rest: afterName } = extractFunctionName(withoutKeywords)
15561613
if (!name) {
15571614
console.error('Function name could not be extracted from declaration:', declaration)
15581615
return {
15591616
name: '',
15601617
params: '',
15611618
returnType: 'void',
1562-
isAsync: false,
15631619
generics: '',
15641620
}
15651621
}
15661622

1567-
// Remove the function name from the declaration
1568-
let afterName = declarationWithoutKeywords.slice(name.length).trim()
1623+
let rest = afterName
15691624

1570-
// Extract generics if present
1625+
// Extract generics
15711626
let generics = ''
1572-
const genericsMatch = afterName.match(/^<[^>]+>/)
1573-
if (genericsMatch) {
1574-
generics = genericsMatch[0]
1575-
afterName = afterName.slice(generics.length).trim()
1627+
if (rest.startsWith('<')) {
1628+
const genericsResult = extractBalancedSymbols(rest, '<', '>')
1629+
if (genericsResult) {
1630+
generics = genericsResult.content
1631+
rest = genericsResult.rest.trim()
1632+
}
1633+
else {
1634+
console.error('Generics could not be extracted from declaration:', declaration)
1635+
}
15761636
}
15771637

15781638
// Extract parameters
1579-
const paramsMatch = afterName.match(/^\(([^)]*)\)/)
15801639
let params = ''
1581-
if (paramsMatch) {
1582-
params = paramsMatch[1].trim()
1583-
afterName = afterName.slice(paramsMatch[0].length).trim()
1640+
if (rest.startsWith('(')) {
1641+
const paramsResult = extractBalancedSymbols(rest, '(', ')')
1642+
if (paramsResult) {
1643+
params = paramsResult.content.slice(1, -1).trim() // Remove the surrounding parentheses
1644+
rest = paramsResult.rest.trim()
1645+
}
1646+
else {
1647+
console.error('Parameters could not be extracted from declaration:', declaration)
1648+
return {
1649+
name,
1650+
params: '',
1651+
returnType: 'void',
1652+
generics,
1653+
}
1654+
}
1655+
}
1656+
else {
1657+
console.error('Parameters could not be extracted from declaration:', declaration)
1658+
return {
1659+
name,
1660+
params: '',
1661+
returnType: 'void',
1662+
generics,
1663+
}
15841664
}
1585-
1586-
params = cleanParameters(params)
15871665

15881666
// Extract return type
1589-
const returnTypeMatch = afterName.match(/^:\s*([^;{]+)/)
1590-
let returnType = returnTypeMatch ? returnTypeMatch[1].trim() : 'void'
1591-
1592-
returnType = normalizeType(returnType)
1667+
let returnType = 'void'
1668+
if (rest.startsWith(':')) {
1669+
const match = rest.match(/^:\s*([^{]+)/)
1670+
if (match) {
1671+
returnType = match[1].trim()
1672+
}
1673+
}
15931674

15941675
return {
15951676
name,
15961677
params,
1597-
returnType,
1598-
isAsync,
1678+
returnType: normalizeType(returnType),
15991679
generics,
16001680
}
16011681
}
16021682

16031683
/**
16041684
* Process function declarations with overloads
1605-
* @param declaration - Function declaration to process
1606-
* @param usedTypes - Set of used types to track
1607-
* @param isExported - Whether the function is exported
16081685
*/
16091686
export function processFunctionDeclaration(
16101687
declaration: string,
@@ -1614,16 +1691,18 @@ export function processFunctionDeclaration(
16141691
// Remove comments from the declaration for parsing
16151692
const cleanDeclaration = removeLeadingComments(declaration).trim()
16161693

1617-
// Strip out the function body by removing everything after the parameter list and return type
1618-
const functionSignature = cleanDeclaration.replace(/\{[\s\S]*$/, '').trim()
1619-
16201694
const {
16211695
name,
16221696
params,
16231697
returnType,
1624-
isAsync,
16251698
generics,
1626-
} = extractFunctionSignature(functionSignature)
1699+
} = extractFunctionSignature(cleanDeclaration)
1700+
1701+
// Add logs to verify extracted components
1702+
console.log('Function Name:', name)
1703+
console.log('Generics:', generics)
1704+
console.log('Parameters:', params)
1705+
console.log('Return Type:', returnType)
16271706

16281707
// Track used types if provided
16291708
if (usedTypes) {
@@ -1645,6 +1724,7 @@ export function processFunctionDeclaration(
16451724
return parts
16461725
.filter(Boolean)
16471726
.join(' ')
1727+
// Include ':' in the character classes to handle spacing around colons
16481728
.replace(/\s+([<>(),;:])/g, '$1')
16491729
.replace(/([<>(),;:])\s+/g, '$1 ')
16501730
.replace(/\s{2,}/g, ' ')
@@ -1662,42 +1742,7 @@ function cleanDeclaration(text: string): string {
16621742
* Clean and normalize parameters
16631743
*/
16641744
export function cleanParameters(params: string): string {
1665-
if (!params.trim())
1666-
return ''
1667-
1668-
return params
1669-
.replace(REGEX.destructuredParams, (_, props, type) => {
1670-
const typeName = normalizeType(type.trim())
1671-
return `options: ${typeName}`
1672-
})
1673-
.replace(/\s*([,:])\s*/g, '$1 ')
1674-
.replace(/,(\S)/g, ', $1')
1675-
.replace(/\s*\?\s*:/g, '?: ')
1676-
.replace(/\s*([<[\]>])\s*/g, '$1')
1677-
.replace(/\s{2,}/g, ' ')
1678-
.trim()
1679-
}
1680-
1681-
function cleanImports(imports: string[]): string[] {
1682-
const seen = new Set<string>()
1683-
return imports
1684-
.filter((imp) => {
1685-
const normalized = imp.trim()
1686-
if (seen.has(normalized))
1687-
return false
1688-
seen.add(normalized)
1689-
return true
1690-
})
1691-
.sort((a, b) => {
1692-
// Sort type imports before regular imports
1693-
const aIsType = a.includes('import type')
1694-
const bIsType = b.includes('import type')
1695-
if (aIsType && !bIsType)
1696-
return -1
1697-
if (!aIsType && bIsType)
1698-
return 1
1699-
return a.localeCompare(b)
1700-
})
1745+
return params.trim()
17011746
}
17021747

17031748
/**

0 commit comments

Comments
 (0)