Skip to content

Commit 78f51b3

Browse files
committed
chore: wip
1 parent caedf34 commit 78f51b3

3 files changed

Lines changed: 113 additions & 108 deletions

File tree

src/extract.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { readFile } from 'node:fs/promises'
2+
3+
async function extractTypeFromSource(filePath: string): Promise<string> {
4+
const fileContent = await readFile(filePath, 'utf-8')
5+
let declarations = ''
6+
let usedTypes = new Set<string>()
7+
let importMap = new Map<string, Set<string>>()
8+
9+
// Capture all imported types
10+
const importRegex = /import\s+type\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g
11+
let importMatch
12+
while ((importMatch = importRegex.exec(fileContent)) !== null) {
13+
const types = importMatch[1].split(',').map(t => t.trim())
14+
const from = importMatch[2]
15+
if (!importMap.has(from)) importMap.set(from, new Set())
16+
types.forEach(type => importMap.get(from)!.add(type))
17+
}
18+
19+
// Function to add used types
20+
const addUsedType = (type: string) => {
21+
const cleanType = type.replace(/[\[\]?]/g, '').trim() // Remove brackets and question marks
22+
if (/^[A-Z]/.test(cleanType)) { // Only add if it starts with a capital letter (likely a type)
23+
usedTypes.add(cleanType)
24+
}
25+
}
26+
27+
// Handle exported functions with comments
28+
const exportedFunctionRegex = /(\/\*\*[\s\S]*?\*\/\s*)?(export\s+(async\s+)?function\s+(\w+)\s*\(([^)]*)\)\s*:\s*([^{]+))/g
29+
let match
30+
while ((match = exportedFunctionRegex.exec(fileContent)) !== null) {
31+
const [, comment, , isAsync, name, params, returnType] = match
32+
const cleanParams = params.replace(/\s*=\s*[^,)]+/g, '')
33+
const declaration = `${comment || ''}export declare ${isAsync || ''}function ${name}(${cleanParams}): ${returnType.trim()}`
34+
declarations += `${declaration}\n\n`
35+
36+
// Check for types used in parameters
37+
const paramTypes = params.match(/:\s*([^,)]+)/g) || []
38+
paramTypes.forEach(type => addUsedType(type.slice(1).trim()))
39+
40+
// Check for return type
41+
addUsedType(returnType.trim())
42+
}
43+
44+
// Handle other exports (interface, type, const)
45+
const otherExportRegex = /(\/\*\*[\s\S]*?\*\/\s*)?(export\s+((?:interface|type|const)\s+\w+(?:\s*=\s*[^;]+|\s*\{[^}]*\})));?/gs
46+
while ((match = otherExportRegex.exec(fileContent)) !== null) {
47+
const [, comment, exportStatement, declaration] = match
48+
declarations += `${comment || ''}${exportStatement}\n\n`
49+
50+
// Check for types used in the declaration
51+
const typeRegex = /\b([A-Z]\w+)\b/g
52+
let typeMatch
53+
while ((typeMatch = typeRegex.exec(declaration)) !== null) {
54+
addUsedType(typeMatch[1])
55+
}
56+
}
57+
58+
// Generate import statements for used types
59+
let importDeclarations = ''
60+
importMap.forEach((types, path) => {
61+
const usedTypesFromPath = [...types].filter(type => usedTypes.has(type))
62+
if (usedTypesFromPath.length > 0) {
63+
importDeclarations += `import type { ${usedTypesFromPath.join(', ')} } from '${path}'\n`
64+
}
65+
})
66+
67+
if (importDeclarations) {
68+
declarations = importDeclarations + '\n' + declarations
69+
}
70+
71+
return declarations.trim() + '\n'
72+
}
73+
74+
export async function extractConfigTypeFromSource(filePath: string): Promise<string> {
75+
const fileContent = await readFile(filePath, 'utf-8')
76+
let declarations = ''
77+
78+
// Handle type imports
79+
const importRegex = /import\s+type\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g
80+
let importMatch
81+
while ((importMatch = importRegex.exec(fileContent)) !== null) {
82+
const types = importMatch[1].split(',').map(t => t.trim())
83+
const from = importMatch[2]
84+
declarations += `import type { ${types.join(', ')} } from '${from}'\n\n` // Add two newlines here
85+
}
86+
87+
// Handle exports
88+
const exportRegex = /export\s+const\s+(\w+)\s*:\s*([^=]+)\s*=/g
89+
let exportMatch
90+
while ((exportMatch = exportRegex.exec(fileContent)) !== null) {
91+
const [, name, type] = exportMatch
92+
declarations += `export declare const ${name}: ${type.trim()}\n`
93+
}
94+
95+
return declarations.trim() + '\n'
96+
}
97+
98+
export async function extractIndexTypeFromSource(filePath: string): Promise<string> {
99+
const fileContent = await readFile(filePath, 'utf-8')
100+
let declarations = ''
101+
102+
// Handle re-exports
103+
const reExportRegex = /export\s*(?:\*|\{[^}]*\})\s*from\s*['"]([^'"]+)['"]/g
104+
let match
105+
while ((match = reExportRegex.exec(fileContent)) !== null) {
106+
declarations += `${match[0]}\n`
107+
}
108+
109+
return declarations.trim() + '\n'
110+
}

src/generate.ts

Lines changed: 2 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import type { Result } from 'neverthrow'
22
import type { DtsGenerationConfig, DtsGenerationOption } from './types'
3-
import { readFile, rm, mkdir } from 'node:fs/promises'
3+
import { rm, mkdir } from 'node:fs/promises'
44
import { join, relative, dirname } from 'node:path'
55
import { err, ok } from 'neverthrow'
66
import { config } from './config'
77
import { writeToFile, getAllTypeScriptFiles, checkIsolatedDeclarations } from './utils'
8+
import { extractTypeFromSource, extractConfigTypeFromSource, extractIndexTypeFromSource } from './extract'
89

910
export async function generateDeclarationsFromFiles(options: DtsGenerationConfig = config): Promise<void> {
1011
// Check for isolatedModules setting
@@ -65,113 +66,6 @@ export async function generate(options?: DtsGenerationOption): Promise<void> {
6566
await generateDeclarationsFromFiles({ ...config, ...options })
6667
}
6768

68-
async function extractTypeFromSource(filePath: string): Promise<string> {
69-
const fileContent = await readFile(filePath, 'utf-8')
70-
let declarations = ''
71-
let imports = new Set<string>()
72-
73-
// Handle imported types
74-
const importRegex = /import\s+type\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g
75-
let importMatch
76-
while ((importMatch = importRegex.exec(fileContent)) !== null) {
77-
const types = importMatch[1].split(',').map(t => t.trim())
78-
const from = importMatch[2]
79-
types.forEach(type => imports.add(`${type}:${from}`))
80-
}
81-
82-
// Handle exported functions with comments
83-
const exportedFunctionRegex = /(\/\*\*[\s\S]*?\*\/\s*)?(export\s+(async\s+)?function\s+(\w+)\s*\(([^)]*)\)\s*:\s*([^{]+))/g
84-
let match
85-
while ((match = exportedFunctionRegex.exec(fileContent)) !== null) {
86-
const [, comment, , isAsync, name, params, returnType] = match
87-
const cleanParams = params.replace(/\s*=\s*[^,)]+/g, '')
88-
const declaration = `${comment || ''}export declare ${isAsync || ''}function ${name}(${cleanParams}): ${returnType.trim()}`
89-
declarations += `${declaration}\n\n`
90-
91-
// Check for types used in the declaration and add them to imports
92-
const usedTypes = [...params.matchAll(/(\w+):\s*([A-Z]\w+)/g), ...returnType.matchAll(/\b([A-Z]\w+)\b/g)]
93-
usedTypes.forEach(([, , type]) => {
94-
if (type) imports.add(type)
95-
})
96-
}
97-
98-
// Handle other exports (interface, type, const)
99-
const otherExportRegex = /(\/\*\*[\s\S]*?\*\/\s*)?(export\s+((?:interface|type|const)\s+\w+(?:\s*=\s*[^;]+|\s*\{[^}]*\})));?/gs
100-
while ((match = otherExportRegex.exec(fileContent)) !== null) {
101-
const [, comment, exportStatement, declaration] = match
102-
if (declaration.startsWith('interface') || declaration.startsWith('type')) {
103-
declarations += `${comment || ''}${exportStatement}\n\n`
104-
} else if (declaration.startsWith('const')) {
105-
const [, name, type] = declaration.match(/const\s+(\w+):\s*([^=]+)/) || []
106-
if (name && type) {
107-
declarations += `${comment || ''}export declare const ${name}: ${type.trim()}\n\n`
108-
}
109-
}
110-
111-
// Check for types used in the declaration and add them to imports
112-
const usedTypes = declaration.match(/\b([A-Z]\w+)\b/g) || []
113-
usedTypes.forEach(type => imports.add(type))
114-
}
115-
116-
// Generate import statements for used types
117-
let importDeclarations = ''
118-
const importMap = new Map()
119-
imports.forEach(typeWithPath => {
120-
const [type, path] = typeWithPath.split(':')
121-
if (path) {
122-
if (!importMap.has(path)) importMap.set(path, new Set())
123-
importMap.get(path).add(type)
124-
}
125-
})
126-
importMap.forEach((types, path) => {
127-
importDeclarations += `import type { ${Array.from(types).join(', ')} } from '${path}'\n`
128-
})
129-
130-
if (importDeclarations) {
131-
declarations = importDeclarations + '\n' + declarations
132-
}
133-
134-
return declarations.trim() + '\n'
135-
}
136-
137-
async function extractConfigTypeFromSource(filePath: string): Promise<string> {
138-
const fileContent = await readFile(filePath, 'utf-8')
139-
let declarations = ''
140-
141-
// Handle type imports
142-
const importRegex = /import\s+type\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g
143-
let importMatch
144-
while ((importMatch = importRegex.exec(fileContent)) !== null) {
145-
const types = importMatch[1].split(',').map(t => t.trim())
146-
const from = importMatch[2]
147-
declarations += `import type { ${types.join(', ')} } from '${from}'\n\n` // Add two newlines here
148-
}
149-
150-
// Handle exports
151-
const exportRegex = /export\s+const\s+(\w+)\s*:\s*([^=]+)\s*=/g
152-
let exportMatch
153-
while ((exportMatch = exportRegex.exec(fileContent)) !== null) {
154-
const [, name, type] = exportMatch
155-
declarations += `export declare const ${name}: ${type.trim()}\n`
156-
}
157-
158-
return declarations.trim() + '\n'
159-
}
160-
161-
async function extractIndexTypeFromSource(filePath: string): Promise<string> {
162-
const fileContent = await readFile(filePath, 'utf-8')
163-
let declarations = ''
164-
165-
// Handle re-exports
166-
const reExportRegex = /export\s*(?:\*|\{[^}]*\})\s*from\s*['"]([^'"]+)['"]/g
167-
let match
168-
while ((match = reExportRegex.exec(fileContent)) !== null) {
169-
declarations += `${match[0]}\n`
170-
}
171-
172-
return declarations.trim() + '\n'
173-
}
174-
17569
function formatDeclarations(declarations: string, isConfigFile: boolean): string {
17670
if (isConfigFile) {
17771
return declarations

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { config } from './config'
22
export * from './generate'
33
export * from './types'
4+
export * from './utils'

0 commit comments

Comments
 (0)