|
1 | 1 | import type { Result } from 'neverthrow' |
2 | 2 | import type { DtsGenerationConfig, DtsGenerationOption } from './types' |
3 | | -import { readFile, rm, mkdir } from 'node:fs/promises' |
| 3 | +import { rm, mkdir } from 'node:fs/promises' |
4 | 4 | import { join, relative, dirname } from 'node:path' |
5 | 5 | import { err, ok } from 'neverthrow' |
6 | 6 | import { config } from './config' |
7 | 7 | import { writeToFile, getAllTypeScriptFiles, checkIsolatedDeclarations } from './utils' |
| 8 | +import { extractTypeFromSource, extractConfigTypeFromSource, extractIndexTypeFromSource } from './extract' |
8 | 9 |
|
9 | 10 | export async function generateDeclarationsFromFiles(options: DtsGenerationConfig = config): Promise<void> { |
10 | 11 | // Check for isolatedModules setting |
@@ -65,113 +66,6 @@ export async function generate(options?: DtsGenerationOption): Promise<void> { |
65 | 66 | await generateDeclarationsFromFiles({ ...config, ...options }) |
66 | 67 | } |
67 | 68 |
|
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 | | - |
175 | 69 | function formatDeclarations(declarations: string, isConfigFile: boolean): string { |
176 | 70 | if (isConfigFile) { |
177 | 71 | return declarations |
|
0 commit comments