@stacksjs/dtsx 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +25 -25
- package/dist/extract.d.ts +1312 -2
- package/dist/index.js +1 -1
- package/dist/types.d.ts +0 -8
- package/package.json +3 -3
package/dist/extract.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FunctionSignature } from './types';
|
|
1
|
+
import type { FunctionSignature, ImportTrackingState, ProcessedMethod, ProcessingState } from './types';
|
|
2
2
|
|
|
3
3
|
declare function cleanParameterTypes(params: string): string;
|
|
4
4
|
declare function cleanSingleParameter(param: string): string;
|
|
@@ -6,4 +6,1314 @@ export declare function extract(filePath: string): Promise<string>;
|
|
|
6
6
|
export declare function extractDtsTypes(sourceCode: string): string;
|
|
7
7
|
declare function extractFunctionSignature(declaration: string): FunctionSignature;
|
|
8
8
|
declare function extractFunctionName(declaration: string): string;
|
|
9
|
-
declare function
|
|
9
|
+
declare function extractGenerics(rest: string);
|
|
10
|
+
function extractParams(rest: string): { params: string, rest: string } {
|
|
11
|
+
let params = ''
|
|
12
|
+
if (rest.includes('(')) {
|
|
13
|
+
const start = rest.indexOf('(')
|
|
14
|
+
let depth = 1
|
|
15
|
+
let pos = start + 1
|
|
16
|
+
let buffer = ''
|
|
17
|
+
|
|
18
|
+
debugLog('params-extraction-start', `Starting params extraction from pos ${pos}: ${rest}`)
|
|
19
|
+
|
|
20
|
+
for (; pos < rest.length; pos++) {
|
|
21
|
+
const char = rest[pos]
|
|
22
|
+
|
|
23
|
+
if (char === '(')
|
|
24
|
+
depth++
|
|
25
|
+
if (char === ')') {
|
|
26
|
+
depth--
|
|
27
|
+
if (depth === 0) {
|
|
28
|
+
debugLog('params-depth-zero', `Found closing parenthesis at pos ${pos}`)
|
|
29
|
+
break
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
buffer += char
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
params = buffer.trim()
|
|
37
|
+
rest = rest.slice(pos + 1).trim()
|
|
38
|
+
debugLog('signature-params', `Extracted params: ${params}`)
|
|
39
|
+
}
|
|
40
|
+
return { params, rest }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function extractReturnType(rest: string): { returnType: string } {
|
|
44
|
+
let returnType = 'void'
|
|
45
|
+
if (rest.startsWith(':')) {
|
|
46
|
+
debugLog('return-start', `Starting return type extraction with: ${rest}`)
|
|
47
|
+
rest = rest.slice(1).trim()
|
|
48
|
+
|
|
49
|
+
let depth = 0
|
|
50
|
+
let buffer = ''
|
|
51
|
+
let i = 0
|
|
52
|
+
let inString = false
|
|
53
|
+
let stringChar = ''
|
|
54
|
+
let foundEnd = false
|
|
55
|
+
|
|
56
|
+
debugLog('return-extraction', 'Starting character-by-character extraction')
|
|
57
|
+
|
|
58
|
+
while (i < rest.length && !foundEnd) {
|
|
59
|
+
const char = rest[i]
|
|
60
|
+
const prevChar = i > 0 ? rest[i - 1] : ''
|
|
61
|
+
|
|
62
|
+
debugLog('return-char', `Pos ${i}: Char "${char}", Depth ${depth}, InString ${inString}, Buffer length ${buffer.length}`)
|
|
63
|
+
|
|
64
|
+
if ((char === '"' || char === '\'' || char === '`') && prevChar !== '\\') {
|
|
65
|
+
if (!inString) {
|
|
66
|
+
inString = true
|
|
67
|
+
stringChar = char
|
|
68
|
+
debugLog('return-string', `Entering string with ${stringChar}`)
|
|
69
|
+
}
|
|
70
|
+
else if (char === stringChar) {
|
|
71
|
+
inString = false
|
|
72
|
+
debugLog('return-string', 'Exiting string')
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!inString) {
|
|
77
|
+
if (char === '{' || char === '<' || char === '(') {
|
|
78
|
+
depth++
|
|
79
|
+
debugLog('return-depth', `Opening bracket, increasing depth to ${depth}`)
|
|
80
|
+
}
|
|
81
|
+
else if (char === '}' || char === '>' || char === ')') {
|
|
82
|
+
depth--
|
|
83
|
+
debugLog('return-depth', `Closing bracket, decreasing depth to ${depth}`)
|
|
84
|
+
|
|
85
|
+
if (depth === 0 && char === '}') {
|
|
86
|
+
buffer += char
|
|
87
|
+
const nextNonWhitespace = rest.slice(i + 1).trim()[0]
|
|
88
|
+
if (nextNonWhitespace === '{') {
|
|
89
|
+
debugLog('return-end', `Found end of return type at pos ${i}, next char is function body`)
|
|
90
|
+
foundEnd = true
|
|
91
|
+
break
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (depth === 0 && char === ';') {
|
|
97
|
+
debugLog('return-end', 'Found semicolon at depth 0')
|
|
98
|
+
foundEnd = true
|
|
99
|
+
break
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
buffer += char
|
|
104
|
+
debugLog('return-buffer', `Updated buffer: ${buffer}`)
|
|
105
|
+
i++
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
returnType = buffer.trim()
|
|
109
|
+
debugLog('return-final', `Final extracted return type: ${returnType}`)
|
|
110
|
+
}
|
|
111
|
+
return { returnType }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function extractFunctionType(value: string): string | null {
|
|
115
|
+
debugLog('extract-function', `Extracting function type from: ${value}`)
|
|
116
|
+
|
|
117
|
+
const cleanValue = value.trim()
|
|
118
|
+
let pos = 0
|
|
119
|
+
const length = cleanValue.length
|
|
120
|
+
|
|
121
|
+
if (!cleanValue.startsWith('(')) {
|
|
122
|
+
const funcMatch = cleanValue.match(/^function\s*\w*\s*\((.*?)\)/s)
|
|
123
|
+
if (funcMatch) {
|
|
124
|
+
const [, params] = funcMatch
|
|
125
|
+
const cleanParams = cleanParameterTypes(params || '')
|
|
126
|
+
const returnTypeMatch = cleanValue.match(/\):\s*([^{;]+)(?:[{;]|$)/)
|
|
127
|
+
const returnType = returnTypeMatch ? normalizeType(returnTypeMatch[1]) : 'unknown'
|
|
128
|
+
return `(${cleanParams}) => ${returnType}`
|
|
129
|
+
}
|
|
130
|
+
return null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
pos++
|
|
134
|
+
let depth = 1
|
|
135
|
+
const paramsStart = pos
|
|
136
|
+
let inString = false
|
|
137
|
+
let stringChar = ''
|
|
138
|
+
for (; pos < length; pos++) {
|
|
139
|
+
const char = cleanValue[pos]
|
|
140
|
+
const prevChar = pos > 0 ? cleanValue[pos - 1] : ''
|
|
141
|
+
|
|
142
|
+
if (inString) {
|
|
143
|
+
if (char === stringChar && prevChar !== '\\') {
|
|
144
|
+
inString = false
|
|
145
|
+
}
|
|
146
|
+
else if (char === '\\') {
|
|
147
|
+
pos++
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
if (char === '"' || char === '\'' || char === '`') {
|
|
152
|
+
inString = true
|
|
153
|
+
stringChar = char
|
|
154
|
+
}
|
|
155
|
+
else if (char === '(') {
|
|
156
|
+
depth++
|
|
157
|
+
}
|
|
158
|
+
else if (char === ')') {
|
|
159
|
+
depth--
|
|
160
|
+
if (depth === 0) {
|
|
161
|
+
break
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (depth !== 0) {
|
|
167
|
+
debugLog('extract-function', 'Unbalanced parentheses in function parameters')
|
|
168
|
+
return null
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const paramsEnd = pos
|
|
172
|
+
const params = cleanValue.slice(paramsStart, paramsEnd)
|
|
173
|
+
|
|
174
|
+
pos++
|
|
175
|
+
|
|
176
|
+
while (pos < length && /\s/.test(cleanValue[pos])) pos++
|
|
177
|
+
|
|
178
|
+
let returnType = 'unknown'
|
|
179
|
+
if (cleanValue[pos] === ':') {
|
|
180
|
+
pos++
|
|
181
|
+
while (pos < length && /\s/.test(cleanValue[pos])) pos++
|
|
182
|
+
const returnTypeStart = pos
|
|
183
|
+
while (pos < length && !cleanValue.startsWith('=>', pos) && cleanValue[pos] !== '{') {
|
|
184
|
+
pos++
|
|
185
|
+
}
|
|
186
|
+
const returnTypeEnd = pos
|
|
187
|
+
returnType = cleanValue.slice(returnTypeStart, returnTypeEnd).trim()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
while (pos < length && /\s/.test(cleanValue[pos])) pos++
|
|
191
|
+
|
|
192
|
+
if (cleanValue.startsWith('=>', pos)) {
|
|
193
|
+
pos += 2
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
debugLog('extract-function', 'Function expression missing "=>"')
|
|
197
|
+
return null
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const cleanParams = cleanParameterTypes(params || '')
|
|
201
|
+
debugLog('extract-function', `Extracted function type: (${cleanParams}) => ${returnType}`)
|
|
202
|
+
return `(${cleanParams}) => ${returnType}`
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function generateOptimizedImports(state: ImportTrackingState): string[] {
|
|
206
|
+
const imports: string[] = []
|
|
207
|
+
const seenImports = new Set<string>()
|
|
208
|
+
|
|
209
|
+
debugLog('import-gen', `Generating optimized imports. ${state.exportedTypes.size} exported types`)
|
|
210
|
+
|
|
211
|
+
for (const [module, types] of state.typeImports) {
|
|
212
|
+
debugLog('import-type-check', `Checking types from ${module}: ${Array.from(types).join(', ')}`)
|
|
213
|
+
const typeImports = Array.from(types)
|
|
214
|
+
.filter((t) => {
|
|
215
|
+
const isUsed = state.exportedTypes.has(t) || state.usedTypes.has(t)
|
|
216
|
+
debugLog('import-type-filter', `Type ${t}: exported=${state.exportedTypes.has(t)}, used=${state.usedTypes.has(t)}`)
|
|
217
|
+
return isUsed
|
|
218
|
+
})
|
|
219
|
+
.map((t) => {
|
|
220
|
+
const alias = state.valueAliases.get(t)
|
|
221
|
+
return alias ? `${t} as ${alias}` : t
|
|
222
|
+
})
|
|
223
|
+
.sort()
|
|
224
|
+
|
|
225
|
+
if (typeImports.length > 0) {
|
|
226
|
+
const importStatement = `import type { ${typeImports.join(', ')} } from '${module}'`
|
|
227
|
+
if (!seenImports.has(importStatement)) {
|
|
228
|
+
imports.push(importStatement)
|
|
229
|
+
seenImports.add(importStatement)
|
|
230
|
+
debugLog('import-add-type', `Added type import: ${importStatement}`)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
for (const [module, values] of state.valueImports) {
|
|
236
|
+
const moduleAliases = new Map<string, string>()
|
|
237
|
+
const valueImports = Array.from(values)
|
|
238
|
+
.filter((v) => {
|
|
239
|
+
const alias = Array.from(state.valueAliases.entries())
|
|
240
|
+
.find(([_, orig]) => orig === v)?.[0]
|
|
241
|
+
const isUsed = state.exportedValues.has(v)
|
|
242
|
+
|| state.usedValues.has(v)
|
|
243
|
+
|| v === state.defaultExportValue
|
|
244
|
+
|| (alias && (state.exportedValues.has(alias) || alias === state.defaultExportValue))
|
|
245
|
+
|
|
246
|
+
if (isUsed && alias) {
|
|
247
|
+
moduleAliases.set(v, alias)
|
|
248
|
+
}
|
|
249
|
+
return isUsed
|
|
250
|
+
})
|
|
251
|
+
.map((v) => {
|
|
252
|
+
const alias = moduleAliases.get(v)
|
|
253
|
+
return alias ? `${v} as ${alias}` : v
|
|
254
|
+
})
|
|
255
|
+
.sort()
|
|
256
|
+
|
|
257
|
+
if (valueImports.length > 0) {
|
|
258
|
+
const importStatement = `import { ${valueImports.join(', ')} } from '${module}'`
|
|
259
|
+
if (!seenImports.has(importStatement)) {
|
|
260
|
+
imports.push(importStatement)
|
|
261
|
+
seenImports.add(importStatement)
|
|
262
|
+
debugLog('import-add-value', `Added value import: ${importStatement}`)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return imports.sort()
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function extractCompleteObjectContent(value: string): string | null {
|
|
271
|
+
const fullContent = value.trim()
|
|
272
|
+
|
|
273
|
+
if (!fullContent.startsWith('{')) {
|
|
274
|
+
return null
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
let depth = 0
|
|
278
|
+
let inString = false
|
|
279
|
+
let stringChar = ''
|
|
280
|
+
|
|
281
|
+
for (let i = 0; i < fullContent.length; i++) {
|
|
282
|
+
const char = fullContent[i]
|
|
283
|
+
const prevChar = i > 0 ? fullContent[i - 1] : ''
|
|
284
|
+
|
|
285
|
+
if ((char === '"' || char === '\'' || char === '`') && prevChar !== '\\') {
|
|
286
|
+
if (!inString) {
|
|
287
|
+
inString = true
|
|
288
|
+
stringChar = char
|
|
289
|
+
}
|
|
290
|
+
else if (char === stringChar) {
|
|
291
|
+
inString = false
|
|
292
|
+
}
|
|
293
|
+
continue
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (!inString) {
|
|
297
|
+
if (char === '{') {
|
|
298
|
+
depth++
|
|
299
|
+
}
|
|
300
|
+
else if (char === '}') {
|
|
301
|
+
depth--
|
|
302
|
+
if (depth === 0) {
|
|
303
|
+
return fullContent.slice(0, i + 1)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return null
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function formatOutput(state: ProcessingState): string {
|
|
313
|
+
const imports = new Set<string>()
|
|
314
|
+
|
|
315
|
+
state.dtsLines
|
|
316
|
+
.filter(line => line.startsWith('import'))
|
|
317
|
+
.forEach(imp => imports.add(imp.replace(/;+$/, '')))
|
|
318
|
+
|
|
319
|
+
const declarations = state.dtsLines
|
|
320
|
+
.filter(line => !line.startsWith('import'))
|
|
321
|
+
.map((line) => {
|
|
322
|
+
if (line.trim().startsWith('g, ''))
|
|
323
|
+
.filter(line => line.trim() || line === '')
|
|
324
|
+
.join('\n')
|
|
325
|
+
.trim()
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function removeLeadingComments(code: string): string {
|
|
329
|
+
const lines = code.split('\n')
|
|
330
|
+
let index = 0
|
|
331
|
+
while (index < lines.length) {
|
|
332
|
+
const line = lines[index].trim()
|
|
333
|
+
if (line.startsWith('
|
|
334
|
+
index++
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
break
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return lines.slice(index).join('\n')
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function createProcessingState(): ProcessingState {
|
|
344
|
+
return {
|
|
345
|
+
dtsLines: [],
|
|
346
|
+
imports: [],
|
|
347
|
+
usedTypes: new Set(),
|
|
348
|
+
typeSources: new Map(),
|
|
349
|
+
defaultExport: null,
|
|
350
|
+
exportAllStatements: [],
|
|
351
|
+
currentDeclaration: '',
|
|
352
|
+
lastCommentBlock: '',
|
|
353
|
+
bracketCount: 0,
|
|
354
|
+
isMultiLineDeclaration: false,
|
|
355
|
+
moduleImports: new Map(),
|
|
356
|
+
availableTypes: new Map(),
|
|
357
|
+
availableValues: new Map(),
|
|
358
|
+
currentIndentation: '',
|
|
359
|
+
declarationBuffer: null,
|
|
360
|
+
importTracking: createImportTrackingState(),
|
|
361
|
+
defaultExports: new Set(),
|
|
362
|
+
currentScope: 'top',
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function createImportTrackingState(): ImportTrackingState {
|
|
367
|
+
return {
|
|
368
|
+
typeImports: new Map(),
|
|
369
|
+
valueImports: new Map(),
|
|
370
|
+
usedTypes: new Set(),
|
|
371
|
+
usedValues: new Set(),
|
|
372
|
+
exportedTypes: new Set(),
|
|
373
|
+
exportedValues: new Set(),
|
|
374
|
+
valueAliases: new Map(),
|
|
375
|
+
importSources: new Map(),
|
|
376
|
+
typeExportSources: new Map(),
|
|
377
|
+
defaultExportValue: undefined,
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function indentMultilineType(type: string, baseIndent: string, isLast: boolean): string {
|
|
382
|
+
const lines = type.split('\n')
|
|
383
|
+
if (lines.length === 1)
|
|
384
|
+
return `${baseIndent}${type}${isLast ? '' : ' |'}`
|
|
385
|
+
|
|
386
|
+
interface BracketInfo {
|
|
387
|
+
char: string
|
|
388
|
+
indent: string
|
|
389
|
+
isArray: boolean
|
|
390
|
+
depth: number
|
|
391
|
+
isSingleElement?: boolean
|
|
392
|
+
}
|
|
393
|
+
const bracketStack: BracketInfo[] = []
|
|
394
|
+
|
|
395
|
+
let isInSingleElementArray = false
|
|
396
|
+
let arrayElementCount = 0
|
|
397
|
+
lines.forEach((line) => {
|
|
398
|
+
const trimmed = line.trim()
|
|
399
|
+
if (trimmed.startsWith('Array<')) {
|
|
400
|
+
arrayElementCount = 0
|
|
401
|
+
}
|
|
402
|
+
if (trimmed === '}' || trimmed.startsWith('> |') || trimmed === '>') {
|
|
403
|
+
isInSingleElementArray = arrayElementCount === 1
|
|
404
|
+
}
|
|
405
|
+
if (trimmed && !trimmed.startsWith('Array<') && !trimmed.endsWith('>') && !trimmed.startsWith('{') && !trimmed.endsWith('}')) {
|
|
406
|
+
arrayElementCount++
|
|
407
|
+
}
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
const formattedLines = lines.map((line, i) => {
|
|
411
|
+
const trimmed = line.trim()
|
|
412
|
+
if (!trimmed)
|
|
413
|
+
return ''
|
|
414
|
+
|
|
415
|
+
let currentIndent = baseIndent
|
|
416
|
+
if (i > 0) {
|
|
417
|
+
const stackDepth = bracketStack.reduce((depth, info) => depth + info.depth, 0)
|
|
418
|
+
currentIndent = baseIndent + ' '.repeat(stackDepth)
|
|
419
|
+
|
|
420
|
+
if (trimmed.match(/^['"]/)) {
|
|
421
|
+
currentIndent += ' '
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if ((trimmed.startsWith('}') || trimmed.startsWith('>') || trimmed.startsWith('> |')) && bracketStack.length > 0) {
|
|
425
|
+
currentIndent = baseIndent + ' '.repeat(Math.max(0, stackDepth - 1))
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const openBrackets = trimmed.match(/[{<[]/g) || []
|
|
430
|
+
const closeBrackets = trimmed.match(/[}\]>]/g) || []
|
|
431
|
+
|
|
432
|
+
openBrackets.forEach((bracket) => {
|
|
433
|
+
const isArrayBracket = trimmed.startsWith('Array') && bracket === '<'
|
|
434
|
+
bracketStack.push({
|
|
435
|
+
char: bracket,
|
|
436
|
+
indent: currentIndent,
|
|
437
|
+
isArray: isArrayBracket,
|
|
438
|
+
depth: 1,
|
|
439
|
+
isSingleElement: isInSingleElementArray,
|
|
440
|
+
})
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
if (closeBrackets.length > 0) {
|
|
444
|
+
for (let j = 0; j < closeBrackets.length; j++) {
|
|
445
|
+
if (bracketStack.length > 0)
|
|
446
|
+
bracketStack.pop()
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
let needsUnion = false
|
|
451
|
+
if (!isLast && i === lines.length - 1 && !trimmed.endsWith(' |') && !trimmed.endsWith(';')) {
|
|
452
|
+
needsUnion = true
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (trimmed === '}') {
|
|
456
|
+
const lastArray = [...bracketStack].reverse().find(info => info.isArray)
|
|
457
|
+
if (lastArray?.isSingleElement)
|
|
458
|
+
needsUnion = false
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (trimmed.endsWith(' |'))
|
|
462
|
+
needsUnion = false
|
|
463
|
+
|
|
464
|
+
return `${currentIndent}${trimmed}${needsUnion ? ' |' : ''}`
|
|
465
|
+
}).filter(Boolean)
|
|
466
|
+
|
|
467
|
+
return formattedLines.join('\n')
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function inferValueType(value: string): string {
|
|
471
|
+
const normalizedValue = value.split('\n').map(line => line.trim()).join(' ')
|
|
472
|
+
|
|
473
|
+
if (/^['"`].*['"`]$/.test(normalizedValue)) {
|
|
474
|
+
return normalizedValue
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (!Number.isNaN(Number(normalizedValue))) {
|
|
478
|
+
return normalizedValue
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (normalizedValue === 'true' || normalizedValue === 'false') {
|
|
482
|
+
return normalizedValue
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const returnTypeMatch = normalizedValue.match(/\([^)]*\)\s*:\s*([^=>{]+)/)
|
|
486
|
+
if (returnTypeMatch) {
|
|
487
|
+
return returnTypeMatch[1].trim()
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (normalizedValue.includes('=>')) {
|
|
491
|
+
return '(...args: any[]) => unknown'
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return 'unknown'
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function inferArrayType(value: string, state?: ProcessingState, preserveLineBreaks = false): string {
|
|
498
|
+
const content = value.slice(1, -1).trim()
|
|
499
|
+
const isConstAssertion = value.trim().endsWith('as const')
|
|
500
|
+
|
|
501
|
+
if (!content)
|
|
502
|
+
return isConstAssertion ? 'readonly unknown[]' : 'unknown[]'
|
|
503
|
+
|
|
504
|
+
const elements = splitArrayElements(content)
|
|
505
|
+
|
|
506
|
+
if (isConstAssertion || elements.some(el => el.includes('as const'))) {
|
|
507
|
+
const tuples = elements.map((el) => {
|
|
508
|
+
const cleaned = el.trim().replace(/\s*as\s*const\s*$/, '').trim()
|
|
509
|
+
return inferConstArrayType(cleaned, state)
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
if (needsMultilineFormat(tuples)) {
|
|
513
|
+
const formattedContent = tuples.map((type, i) =>
|
|
514
|
+
indentMultilineType(type, ' ', i === tuples.length - 1),
|
|
515
|
+
).join('\n')
|
|
516
|
+
return `readonly [\n${formattedContent}\n ]`
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return `readonly [${tuples.join(', ')}]`
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const elementTypes = elements.map((element) => {
|
|
523
|
+
const trimmed = element.trim()
|
|
524
|
+
if (trimmed.startsWith('[')) {
|
|
525
|
+
return inferArrayType(trimmed, state)
|
|
526
|
+
}
|
|
527
|
+
else if (trimmed.startsWith('{')) {
|
|
528
|
+
return inferComplexObjectType(trimmed, state)
|
|
529
|
+
}
|
|
530
|
+
else if (trimmed.includes('=>') || trimmed.includes('function')) {
|
|
531
|
+
const funcType = extractFunctionType(trimmed)
|
|
532
|
+
return funcType ? `(${funcType})` : '((...args: any[]) => unknown)'
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
return normalizeTypeReference(trimmed)
|
|
536
|
+
}
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
const types = elementTypes.filter(Boolean)
|
|
540
|
+
const needsMultiline = types.some(type =>
|
|
541
|
+
type.includes('\n')
|
|
542
|
+
|| type.includes('{')
|
|
543
|
+
|| type.length > 40
|
|
544
|
+
|| types.join(' | ').length > 60,
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
if (needsMultiline && preserveLineBreaks) {
|
|
548
|
+
const formattedContent = types.map((type, i) =>
|
|
549
|
+
indentMultilineType(type, ' ', i === types.length - 1),
|
|
550
|
+
).join('\n')
|
|
551
|
+
return `Array<\n${formattedContent}\n >`
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return `Array<${types.join(' | ')}>`
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function inferComplexObjectType(value: string, state?: ProcessingState, indentLevel = 0): string {
|
|
558
|
+
const content = extractCompleteObjectContent(value)
|
|
559
|
+
if (!content)
|
|
560
|
+
return 'Record<string, unknown>'
|
|
561
|
+
|
|
562
|
+
const baseIndent = ' '.repeat(indentLevel)
|
|
563
|
+
const propIndent = ' '.repeat(indentLevel + 1)
|
|
564
|
+
const closingIndent = baseIndent
|
|
565
|
+
|
|
566
|
+
const props = processObjectProperties(content, state, indentLevel)
|
|
567
|
+
if (!props.length)
|
|
568
|
+
return '{}'
|
|
569
|
+
|
|
570
|
+
const propertyStrings = props.map(({ key, value }) => {
|
|
571
|
+
return `${propIndent}${key}: ${value}`
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
return `{\n${propertyStrings.join(';\n')}\n${closingIndent}}`
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function inferConstArrayType(value: string, state?: ProcessingState): string {
|
|
578
|
+
|
|
579
|
+
if (/^['"`].*['"`]$/.test(value)) {
|
|
580
|
+
const cleaned = value
|
|
581
|
+
.replace(/\]\s*as\s*cons.*$/, '')
|
|
582
|
+
.replace(/^['"`]|['"`]$/g, '')
|
|
583
|
+
return `'${cleaned}'`
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (value.startsWith('[')) {
|
|
587
|
+
const content = value.slice(1, -1).trim()
|
|
588
|
+
const elements = splitArrayElements(content)
|
|
589
|
+
|
|
590
|
+
const literalTypes = elements.map((element) => {
|
|
591
|
+
let trimmed = element.trim()
|
|
592
|
+
|
|
593
|
+
if (trimmed.includes('] as cons') || trimmed.includes('] as const')) {
|
|
594
|
+
trimmed = trimmed
|
|
595
|
+
.replace(/\]\s*as\s*cons.*$/, '')
|
|
596
|
+
.replace(/\]\s*as\s*const.*$/, '')
|
|
597
|
+
.trim()
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (trimmed.startsWith('[')) {
|
|
601
|
+
return inferConstArrayType(trimmed, state)
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (trimmed.startsWith('{')) {
|
|
605
|
+
const result = inferComplexObjectType(trimmed, state)
|
|
606
|
+
return result.replace(/^\{/, '{ readonly').replace(/;\s+/g, '; readonly ')
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (/^['"`].*['"`]$/.test(trimmed)) {
|
|
610
|
+
const stringContent = trimmed.replace(/^['"`]|['"`]$/g, '')
|
|
611
|
+
return `'${stringContent}'`
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (!Number.isNaN(Number(trimmed))) {
|
|
615
|
+
return trimmed
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (trimmed === 'true' || trimmed === 'false') {
|
|
619
|
+
return trimmed
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const cleanString = trimmed
|
|
623
|
+
.replace(/\]\s*as\s*cons.*$/, '')
|
|
624
|
+
.replace(/\]\s*as\s*const.*$/, '')
|
|
625
|
+
.replace(/^['"`]|['"`]$/g, '')
|
|
626
|
+
.trim()
|
|
627
|
+
|
|
628
|
+
return `'${cleanString}'`
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
return `readonly [${literalTypes.join(', ')}]`
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const cleanString = value
|
|
635
|
+
.replace(/\]\s*as\s*cons.*$/, '')
|
|
636
|
+
.replace(/\]\s*as\s*const.*$/, '')
|
|
637
|
+
.replace(/^['"`]|['"`]$/g, '')
|
|
638
|
+
.trim()
|
|
639
|
+
|
|
640
|
+
return `'${cleanString}'`
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function inferConstType(value: string, state: ProcessingState): string {
|
|
644
|
+
if (value.startsWith('{')) {
|
|
645
|
+
return inferComplexObjectType(value, state)
|
|
646
|
+
}
|
|
647
|
+
if (value.startsWith('[')) {
|
|
648
|
+
return inferArrayType(value, state, true)
|
|
649
|
+
}
|
|
650
|
+
return value
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function inferTypeFromDefaultValue(defaultValue: string): string {
|
|
654
|
+
if (/^['"`].*['"`]$/.test(defaultValue)) {
|
|
655
|
+
return 'string'
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (!Number.isNaN(Number(defaultValue))) {
|
|
659
|
+
return 'number'
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (defaultValue === 'true' || defaultValue === 'false') {
|
|
663
|
+
return 'boolean'
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (defaultValue.startsWith('[')) {
|
|
667
|
+
return 'unknown[]'
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (defaultValue.startsWith('{')) {
|
|
671
|
+
return 'object'
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (defaultValue === 'null')
|
|
675
|
+
return 'null'
|
|
676
|
+
if (defaultValue === 'undefined')
|
|
677
|
+
return 'undefined'
|
|
678
|
+
|
|
679
|
+
return 'unknown'
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
function isDefaultExport(line: string): boolean {
|
|
684
|
+
return line.trim().startsWith('export default')
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function isDeclarationStart(line: string): boolean {
|
|
688
|
+
if (isRegexPattern(line))
|
|
689
|
+
return false
|
|
690
|
+
|
|
691
|
+
const trimmed = line.trim()
|
|
692
|
+
|
|
693
|
+
if (/^(?:export\s+)?(?:async\s+)?function\s*\*?\s*[a-zA-Z_$][\w$]*/.test(trimmed))
|
|
694
|
+
return true
|
|
695
|
+
|
|
696
|
+
return (
|
|
697
|
+
trimmed.startsWith('export ')
|
|
698
|
+
|| trimmed.startsWith('interface ')
|
|
699
|
+
|| trimmed.startsWith('type ')
|
|
700
|
+
|| trimmed.startsWith('const ')
|
|
701
|
+
|| trimmed.startsWith('function ')
|
|
702
|
+
|| trimmed.startsWith('async function ')
|
|
703
|
+
|| trimmed.startsWith('declare ')
|
|
704
|
+
|| trimmed.startsWith('declare module')
|
|
705
|
+
|| /^export\s+(?:interface|type|const|function\*?|async\s+function\*?)/.test(trimmed)
|
|
706
|
+
)
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function isRegexPattern(line: string): boolean {
|
|
710
|
+
return (
|
|
711
|
+
line.includes('\\')
|
|
712
|
+
|| line.includes('[^')
|
|
713
|
+
|| line.includes('(?:')
|
|
714
|
+
|| line.includes('(?=')
|
|
715
|
+
|| line.includes('(?!')
|
|
716
|
+
|| line.includes('\\s*')
|
|
717
|
+
|| line.includes('\\w+')
|
|
718
|
+
|| line.includes('\\d+')
|
|
719
|
+
|| line.includes('(?<')
|
|
720
|
+
|| line.includes('(?!')
|
|
721
|
+
|| line.includes('(?<=')
|
|
722
|
+
|| line.includes('(?<!')
|
|
723
|
+
)
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
export function isFunctionType(type: string): boolean {
|
|
727
|
+
const functionTypeRegex = /^\s*\(.*\)\s*=>\s*(?:\S.*|[\t\v\f \xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF])$/
|
|
728
|
+
return functionTypeRegex.test(type.trim())
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
export function isDeclarationComplete(content: string | string[]): boolean {
|
|
732
|
+
const fullContent = Array.isArray(content) ? content.join('\n') : content
|
|
733
|
+
const trimmedContent = fullContent.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g, '').trim()
|
|
734
|
+
return /;\s*$/.test(trimmedContent) || /\}\s*$/.test(trimmedContent)
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function isVariableInsideFunction(line: string, state: ProcessingState): boolean {
|
|
738
|
+
const trimmed = line.trim()
|
|
739
|
+
return (
|
|
740
|
+
state.currentScope === 'function'
|
|
741
|
+
&& (trimmed.startsWith('const ')
|
|
742
|
+
|| trimmed.startsWith('let ')
|
|
743
|
+
|| trimmed.startsWith('var ')
|
|
744
|
+
|| /^(?:const|let|var)\s+[a-zA-Z_$][\w$]*\s*(?::|=)/.test(trimmed))
|
|
745
|
+
)
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function needsMultilineFormat(types: string[]): boolean {
|
|
749
|
+
return types.some(type =>
|
|
750
|
+
type.includes('\n')
|
|
751
|
+
|| type.includes('{')
|
|
752
|
+
|| type.length > 40
|
|
753
|
+
|| types.join(' | ').length > 60,
|
|
754
|
+
)
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function normalizeTypeReference(value: string): string {
|
|
758
|
+
if (value.includes('=>') || value.match(/\bfunction\b/)) {
|
|
759
|
+
return '((...args: any[]) => unknown)'
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (value.match(/\w+\s*\([^)]*\)/)) {
|
|
763
|
+
return 'unknown'
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if (value.startsWith('new ')) {
|
|
767
|
+
return 'unknown'
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if (value.includes('.')) {
|
|
771
|
+
return 'unknown'
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (/^[a-z_$][\w$]*$/i.test(value)
|
|
775
|
+
&& !['unknown', 'string', 'number', 'boolean', 'null', 'undefined', 'any', 'never', 'void'].includes(value)
|
|
776
|
+
&& !/^['"`]|^\d/.test(value)
|
|
777
|
+
&& value !== 'true'
|
|
778
|
+
&& value !== 'false') {
|
|
779
|
+
return 'unknown'
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return value
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function processBlock(lines: string[], comments: string[], state: ProcessingState): void {
|
|
786
|
+
debugLog('block-processing', 'Starting block processing')
|
|
787
|
+
|
|
788
|
+
const cleanedLines = lines.filter((line) => {
|
|
789
|
+
const trimmed = line.trim()
|
|
790
|
+
return !trimmed.startsWith('.test(cleanDeclaration)) {
|
|
791
|
+
debugLog('block-processing', 'Processing generator function declaration')
|
|
792
|
+
const processed = processGeneratorFunction(cleanDeclaration)
|
|
793
|
+
if (processed) {
|
|
794
|
+
state.dtsLines.push(processed)
|
|
795
|
+
return true
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
if (!/^(?:export\s+)?(?:async\s+)?function\s+[a-zA-Z_$][\w$]*/.test(cleanDeclaration))
|
|
800
|
+
return false
|
|
801
|
+
|
|
802
|
+
debugLog('block-processing', 'Processing function declaration')
|
|
803
|
+
|
|
804
|
+
const declarations = cleanDeclaration
|
|
805
|
+
.split(/[\n;]/)
|
|
806
|
+
.map(d => d.trim())
|
|
807
|
+
.filter(d => d.startsWith('export function') || d.startsWith('function'))
|
|
808
|
+
|
|
809
|
+
if (declarations.length > 1) {
|
|
810
|
+
declarations.forEach((declaration) => {
|
|
811
|
+
if (!declaration.endsWith('{')) {
|
|
812
|
+
const processed = processFunction(declaration, state.usedTypes, declaration.startsWith('export'))
|
|
813
|
+
if (processed)
|
|
814
|
+
state.dtsLines.push(processed)
|
|
815
|
+
}
|
|
816
|
+
})
|
|
817
|
+
return true
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
let signatureEnd = 0
|
|
821
|
+
let parenDepth = 0
|
|
822
|
+
let angleDepth = 0
|
|
823
|
+
|
|
824
|
+
for (let i = 0; i < cleanDeclaration.length; i++) {
|
|
825
|
+
const char = cleanDeclaration[i]
|
|
826
|
+
|
|
827
|
+
if (char === '(')
|
|
828
|
+
parenDepth++
|
|
829
|
+
if (char === ')')
|
|
830
|
+
parenDepth--
|
|
831
|
+
|
|
832
|
+
if (char === '<')
|
|
833
|
+
angleDepth++
|
|
834
|
+
if (char === '>')
|
|
835
|
+
angleDepth--
|
|
836
|
+
|
|
837
|
+
if (char === '{') {
|
|
838
|
+
if (parenDepth === 0 && angleDepth === 0) {
|
|
839
|
+
signatureEnd = i
|
|
840
|
+
break
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
if (signatureEnd === 0)
|
|
846
|
+
signatureEnd = cleanDeclaration.length
|
|
847
|
+
|
|
848
|
+
const signaturePart = cleanDeclaration.slice(0, signatureEnd).trim()
|
|
849
|
+
|
|
850
|
+
debugLog('signature-extraction', `Extracted signature: ${signaturePart}`)
|
|
851
|
+
|
|
852
|
+
const isExported = signaturePart.startsWith('export')
|
|
853
|
+
const processed = processFunction(signaturePart, state.usedTypes, isExported)
|
|
854
|
+
if (processed) {
|
|
855
|
+
debugLog('function-processed', `Generated declaration: ${processed}`)
|
|
856
|
+
state.dtsLines.push(processed)
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
return true
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function processInterfaceBlock(cleanDeclaration: string, declarationText: string, state: ProcessingState): boolean {
|
|
863
|
+
debugLog('interface-processing', `Starting interface processing with declaration: ${cleanDeclaration.slice(0, 100)}...`)
|
|
864
|
+
|
|
865
|
+
if (!cleanDeclaration.startsWith('interface') && !cleanDeclaration.startsWith('export interface')) {
|
|
866
|
+
debugLog('interface-processing', 'Not an interface declaration, skipping')
|
|
867
|
+
return false
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const lines = declarationText.split('\n')
|
|
871
|
+
let bracketDepth = 0
|
|
872
|
+
let angleDepth = 0
|
|
873
|
+
const processedLines: string[] = []
|
|
874
|
+
let isFirstLine = true
|
|
875
|
+
let hasStartedBody = false
|
|
876
|
+
|
|
877
|
+
debugLog('interface-processing', `Processing ${lines.length} lines`)
|
|
878
|
+
|
|
879
|
+
for (let i = 0; i < lines.length; i++) {
|
|
880
|
+
const line = lines[i]
|
|
881
|
+
const trimmedLine = line.trim()
|
|
882
|
+
|
|
883
|
+
const openCurly = (line.match(/\{/g) || []).length
|
|
884
|
+
const closeCurly = (line.match(/\}/g) || []).length
|
|
885
|
+
const openAngle = (line.match(/</g) || []).length
|
|
886
|
+
const closeAngle = (line.match(/>/g) || []).length
|
|
887
|
+
|
|
888
|
+
bracketDepth += openCurly - closeCurly
|
|
889
|
+
angleDepth += openAngle - closeAngle
|
|
890
|
+
|
|
891
|
+
if (trimmedLine.includes('{')) {
|
|
892
|
+
hasStartedBody = true
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
debugLog('interface-depth', `Line ${i + 1}: "${trimmedLine}" `
|
|
896
|
+
+ `Bracket depth: ${bracketDepth}, Angle depth: ${angleDepth}, `
|
|
897
|
+
+ `Has started body: ${hasStartedBody}`)
|
|
898
|
+
|
|
899
|
+
if (isFirstLine) {
|
|
900
|
+
const prefix = trimmedLine.startsWith('export') ? 'export declare' : 'declare'
|
|
901
|
+
processedLines.push(
|
|
902
|
+
line.replace(
|
|
903
|
+
/^(\s*)(?:export\s+)?interface/,
|
|
904
|
+
`$1${prefix} interface`,
|
|
905
|
+
),
|
|
906
|
+
)
|
|
907
|
+
isFirstLine = false
|
|
908
|
+
}
|
|
909
|
+
else {
|
|
910
|
+
processedLines.push(line)
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
const result = processedLines.join('\n')
|
|
915
|
+
const hasCompleteBody = result.includes('{') && result.includes('}')
|
|
916
|
+
const isComplete = (bracketDepth === 0 && (angleDepth === 0 || result.includes('>'))) && hasCompleteBody
|
|
917
|
+
|
|
918
|
+
if (isComplete) {
|
|
919
|
+
debugLog('interface-processing', `Successfully processed interface:\n${result}`)
|
|
920
|
+
state.dtsLines.push(result)
|
|
921
|
+
return true
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
debugLog('interface-processing', `Interface processing incomplete. Bracket depth: ${bracketDepth}, `
|
|
925
|
+
+ `Angle depth: ${angleDepth}, Has started body: ${hasStartedBody}`)
|
|
926
|
+
return false
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function processTypeBlock(cleanDeclaration: string, declarationText: string, state: ProcessingState): boolean {
|
|
930
|
+
if (!cleanDeclaration.startsWith('type') && !cleanDeclaration.startsWith('export type'))
|
|
931
|
+
return false
|
|
932
|
+
|
|
933
|
+
const isExported = cleanDeclaration.startsWith('export')
|
|
934
|
+
state.dtsLines.push(processType(declarationText, isExported))
|
|
935
|
+
return true
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function processDefaultExportBlock(cleanDeclaration: string, state: ProcessingState): boolean {
|
|
939
|
+
if (!isDefaultExport(cleanDeclaration))
|
|
940
|
+
return false
|
|
941
|
+
|
|
942
|
+
const exportedValue = cleanDeclaration.replace(/^export\s+default\s+/, '').replace(/;$/, '')
|
|
943
|
+
state.importTracking.defaultExportValue = exportedValue
|
|
944
|
+
|
|
945
|
+
const defaultExport = cleanDeclaration.endsWith(';')
|
|
946
|
+
? cleanDeclaration
|
|
947
|
+
: `${cleanDeclaration};`
|
|
948
|
+
|
|
949
|
+
state.defaultExports.add(defaultExport)
|
|
950
|
+
return true
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
function processExportAllBlock(cleanDeclaration: string, state: ProcessingState): boolean {
|
|
954
|
+
if (!cleanDeclaration.startsWith('export *'))
|
|
955
|
+
return false
|
|
956
|
+
|
|
957
|
+
state.exportAllStatements.push(cleanDeclaration)
|
|
958
|
+
state.dtsLines.push(cleanDeclaration)
|
|
959
|
+
return true
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function processExportBlock(cleanDeclaration: string, declarationText: string, state: ProcessingState): boolean {
|
|
963
|
+
if (!cleanDeclaration.startsWith('export'))
|
|
964
|
+
return false
|
|
965
|
+
|
|
966
|
+
if (processExportedClass(cleanDeclaration, state))
|
|
967
|
+
return true
|
|
968
|
+
if (processExportedEnum(cleanDeclaration, state))
|
|
969
|
+
return true
|
|
970
|
+
if (processExportedNamespace(cleanDeclaration, state))
|
|
971
|
+
return true
|
|
972
|
+
|
|
973
|
+
if (cleanDeclaration.startsWith('export {')) {
|
|
974
|
+
state.dtsLines.push(declarationText)
|
|
975
|
+
return true
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
debugLog('processing', `Unhandled exported declaration type: ${cleanDeclaration.split('\n')[0]}`)
|
|
979
|
+
return true
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
function processExport(line: string, state: ProcessingState): void {
|
|
983
|
+
debugLog('export-processing', `Processing export: ${line}`)
|
|
984
|
+
|
|
985
|
+
const exportMatch = line.match(/export\s*\{([^}]+)\}(?:\s*from\s*['"]([^'"]+)['"])?/)
|
|
986
|
+
if (!exportMatch) {
|
|
987
|
+
debugLog('export-error', 'Failed to match export pattern')
|
|
988
|
+
return
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
const [, exports, sourceModule] = exportMatch
|
|
992
|
+
debugLog('export-found', `Found exports: ${exports}, source: ${sourceModule || 'local'}`)
|
|
993
|
+
|
|
994
|
+
exports.split(',').forEach((exp) => {
|
|
995
|
+
const [itemName, aliasName] = exp.trim().split(/\s+as\s+/).map(e => e.trim())
|
|
996
|
+
|
|
997
|
+
if (itemName.startsWith('type ')) {
|
|
998
|
+
const typeName = itemName.replace(/^type\s+/, '').trim()
|
|
999
|
+
const exportedName = aliasName || typeName
|
|
1000
|
+
state.importTracking.exportedTypes.add(exportedName)
|
|
1001
|
+
if (sourceModule) {
|
|
1002
|
+
state.importTracking.typeExportSources.set(exportedName, sourceModule)
|
|
1003
|
+
}
|
|
1004
|
+
debugLog('export-type-processed', `Added exported type: ${exportedName}`)
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
const exportedName = aliasName || itemName
|
|
1008
|
+
state.importTracking.exportedValues.add(exportedName)
|
|
1009
|
+
debugLog('export-value-processed', `Added exported value: ${exportedName}`)
|
|
1010
|
+
}
|
|
1011
|
+
})
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
function processExportedClass(cleanDeclaration: string, state: ProcessingState): boolean {
|
|
1015
|
+
if (!cleanDeclaration.startsWith('export class')
|
|
1016
|
+
&& !cleanDeclaration.startsWith('export abstract class')) {
|
|
1017
|
+
return false
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
const processed = `export declare ${cleanDeclaration.replace(/^export\s+/, '')}`
|
|
1021
|
+
state.dtsLines.push(processed)
|
|
1022
|
+
return true
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function processExportedEnum(cleanDeclaration: string, state: ProcessingState): boolean {
|
|
1026
|
+
if (!cleanDeclaration.startsWith('export enum')
|
|
1027
|
+
&& !cleanDeclaration.startsWith('export const enum')) {
|
|
1028
|
+
return false
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
const processed = `export declare ${cleanDeclaration.replace(/^export\s+/, '')}`
|
|
1032
|
+
state.dtsLines.push(processed)
|
|
1033
|
+
return true
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
function processExportedNamespace(cleanDeclaration: string, state: ProcessingState): boolean {
|
|
1037
|
+
if (!cleanDeclaration.startsWith('export namespace'))
|
|
1038
|
+
return false
|
|
1039
|
+
|
|
1040
|
+
const processed = `export declare ${cleanDeclaration.replace(/^export\s+/, '')}`
|
|
1041
|
+
state.dtsLines.push(processed)
|
|
1042
|
+
return true
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
function processModuleBlock(cleanDeclaration: string, declarationText: string, state: ProcessingState): boolean {
|
|
1046
|
+
if (!cleanDeclaration.startsWith('declare module'))
|
|
1047
|
+
return false
|
|
1048
|
+
|
|
1049
|
+
const processed = processModule(declarationText)
|
|
1050
|
+
state.dtsLines.push(processed)
|
|
1051
|
+
return true
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
export function processSpecificDeclaration(declarationWithoutComments: string, fullDeclaration: string, state: ProcessingState): void {
|
|
1055
|
+
|
|
1056
|
+
if (isDefaultExport(declarationWithoutComments)) {
|
|
1057
|
+
|
|
1058
|
+
const defaultExport = declarationWithoutComments.endsWith(';')
|
|
1059
|
+
? declarationWithoutComments
|
|
1060
|
+
: `${declarationWithoutComments};`
|
|
1061
|
+
|
|
1062
|
+
state.defaultExports.add(defaultExport)
|
|
1063
|
+
return
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
if (declarationWithoutComments.startsWith('declare module')) {
|
|
1067
|
+
const processed = processModule(fullDeclaration)
|
|
1068
|
+
state.dtsLines.push(processed)
|
|
1069
|
+
return
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
if (
|
|
1073
|
+
declarationWithoutComments.startsWith('export const')
|
|
1074
|
+
|| declarationWithoutComments.startsWith('const')
|
|
1075
|
+
) {
|
|
1076
|
+
const isExported = declarationWithoutComments.trimStart().startsWith('export')
|
|
1077
|
+
const processed = processVariable(fullDeclaration, isExported, state)
|
|
1078
|
+
state.dtsLines.push(processed)
|
|
1079
|
+
return
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
if (
|
|
1083
|
+
declarationWithoutComments.startsWith('interface')
|
|
1084
|
+
|| declarationWithoutComments.startsWith('export interface')
|
|
1085
|
+
) {
|
|
1086
|
+
const processed = processInterface(
|
|
1087
|
+
fullDeclaration,
|
|
1088
|
+
declarationWithoutComments.startsWith('export'),
|
|
1089
|
+
)
|
|
1090
|
+
state.dtsLines.push(processed)
|
|
1091
|
+
return
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if (
|
|
1095
|
+
declarationWithoutComments.startsWith('type')
|
|
1096
|
+
|| declarationWithoutComments.startsWith('export type')
|
|
1097
|
+
) {
|
|
1098
|
+
const processed = processType(
|
|
1099
|
+
fullDeclaration,
|
|
1100
|
+
declarationWithoutComments.startsWith('export'),
|
|
1101
|
+
)
|
|
1102
|
+
state.dtsLines.push(processed)
|
|
1103
|
+
return
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
if (
|
|
1107
|
+
declarationWithoutComments.startsWith('function')
|
|
1108
|
+
|| declarationWithoutComments.startsWith('export function')
|
|
1109
|
+
|| declarationWithoutComments.startsWith('async function')
|
|
1110
|
+
|| declarationWithoutComments.startsWith('export async function')
|
|
1111
|
+
) {
|
|
1112
|
+
|
|
1113
|
+
const processed = processFunction(
|
|
1114
|
+
fullDeclaration,
|
|
1115
|
+
state.usedTypes,
|
|
1116
|
+
declarationWithoutComments.startsWith('export'),
|
|
1117
|
+
)
|
|
1118
|
+
state.dtsLines.push(processed)
|
|
1119
|
+
return
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
if (declarationWithoutComments.startsWith('export *')) {
|
|
1123
|
+
state.exportAllStatements.push(declarationWithoutComments)
|
|
1124
|
+
state.dtsLines.push(fullDeclaration)
|
|
1125
|
+
return
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
if (declarationWithoutComments.startsWith('export {')) {
|
|
1129
|
+
state.dtsLines.push(fullDeclaration)
|
|
1130
|
+
return
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
if (declarationWithoutComments.startsWith('export type {')) {
|
|
1134
|
+
state.dtsLines.push(fullDeclaration)
|
|
1135
|
+
return
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
if (
|
|
1139
|
+
declarationWithoutComments.startsWith('class')
|
|
1140
|
+
|| declarationWithoutComments.startsWith('export class')
|
|
1141
|
+
|| declarationWithoutComments.startsWith('abstract class')
|
|
1142
|
+
|| declarationWithoutComments.startsWith('export abstract class')
|
|
1143
|
+
) {
|
|
1144
|
+
const isExported = declarationWithoutComments.startsWith('export')
|
|
1145
|
+
const processed = `${isExported ? 'export ' : ''}declare ${declarationWithoutComments.replace(/^export\s+/, '')}`
|
|
1146
|
+
state.dtsLines.push(processed)
|
|
1147
|
+
return
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
if (
|
|
1151
|
+
declarationWithoutComments.startsWith('enum')
|
|
1152
|
+
|| declarationWithoutComments.startsWith('export enum')
|
|
1153
|
+
|| declarationWithoutComments.startsWith('const enum')
|
|
1154
|
+
|| declarationWithoutComments.startsWith('export const enum')
|
|
1155
|
+
) {
|
|
1156
|
+
const isExported = declarationWithoutComments.startsWith('export')
|
|
1157
|
+
const processed = `${isExported ? 'export ' : ''}declare ${declarationWithoutComments.replace(/^export\s+/, '')}`
|
|
1158
|
+
state.dtsLines.push(processed)
|
|
1159
|
+
return
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
if (
|
|
1163
|
+
declarationWithoutComments.startsWith('namespace')
|
|
1164
|
+
|| declarationWithoutComments.startsWith('export namespace')
|
|
1165
|
+
) {
|
|
1166
|
+
const isExported = declarationWithoutComments.startsWith('export')
|
|
1167
|
+
const processed = `${isExported ? 'export ' : ''}declare ${declarationWithoutComments.replace(/^export\s+/, '')}`
|
|
1168
|
+
state.dtsLines.push(processed)
|
|
1169
|
+
return
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
if (
|
|
1173
|
+
declarationWithoutComments.startsWith('let')
|
|
1174
|
+
|| declarationWithoutComments.startsWith('export let')
|
|
1175
|
+
|| declarationWithoutComments.startsWith('var')
|
|
1176
|
+
|| declarationWithoutComments.startsWith('export var')
|
|
1177
|
+
) {
|
|
1178
|
+
const isExported = declarationWithoutComments.startsWith('export')
|
|
1179
|
+
const processed = `${isExported ? 'export ' : ''}declare ${declarationWithoutComments.replace(/^export\s+/, '')}`
|
|
1180
|
+
state.dtsLines.push(processed)
|
|
1181
|
+
return
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
console.warn('Unhandled declaration type:', declarationWithoutComments.split('\n')[0])
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
function processSourceFile(content: string, state: ProcessingState): void {
|
|
1188
|
+
const lines = content.split('\n')
|
|
1189
|
+
let currentBlock: string[] = []
|
|
1190
|
+
let currentComments: string[] = []
|
|
1191
|
+
let bracketDepth = 0
|
|
1192
|
+
let angleDepth = 0
|
|
1193
|
+
let inDeclaration = false
|
|
1194
|
+
state.currentScope = 'top'
|
|
1195
|
+
|
|
1196
|
+
debugLog('source-processing', `Processing source file with ${lines.length} lines`)
|
|
1197
|
+
|
|
1198
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1199
|
+
const line = lines[i]
|
|
1200
|
+
const trimmedLine = line.trim()
|
|
1201
|
+
|
|
1202
|
+
debugLog('source-scan', `First pass - Line ${i + 1}: ${trimmedLine}`)
|
|
1203
|
+
|
|
1204
|
+
if (line.includes('import ')) {
|
|
1205
|
+
processImports(line, state.importTracking)
|
|
1206
|
+
debugLog('import', `Processed import: ${line}`)
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
if (trimmedLine.startsWith('export type {')) {
|
|
1210
|
+
debugLog('type-export', `Found type export: ${trimmedLine}`)
|
|
1211
|
+
processTypeExport(trimmedLine, state)
|
|
1212
|
+
state.dtsLines.push(line)
|
|
1213
|
+
continue
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
if (trimmedLine.startsWith('export {')) {
|
|
1217
|
+
debugLog('mixed-export', `Found mixed export: ${trimmedLine}`)
|
|
1218
|
+
processExport(trimmedLine, state)
|
|
1219
|
+
state.dtsLines.push(line)
|
|
1220
|
+
continue
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1225
|
+
const line = lines[i]
|
|
1226
|
+
const trimmedLine = line.trim()
|
|
1227
|
+
|
|
1228
|
+
if (trimmedLine.startsWith('import ')
|
|
1229
|
+
|| trimmedLine.startsWith('export type {')
|
|
1230
|
+
|| trimmedLine.startsWith('export {')) {
|
|
1231
|
+
continue
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
if (trimmedLine.startsWith('g, '$1')
|
|
1235
|
+
.replace(/\s*,\s*/g, ', ')
|
|
1236
|
+
.trim()
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
function normalizePropertyKey(key: string): string {
|
|
1240
|
+
const cleanKey = key.replace(/^['"`]|['"`]$/g, '')
|
|
1241
|
+
|
|
1242
|
+
if (!/^[a-z_$][\w$]*$/i.test(cleanKey)) {
|
|
1243
|
+
return `'${cleanKey}'`
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
return cleanKey
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
function splitArrayElements(content: string): string[] {
|
|
1250
|
+
const elements: string[] = []
|
|
1251
|
+
let current = ''
|
|
1252
|
+
let depth = 0
|
|
1253
|
+
let inString = false
|
|
1254
|
+
let stringChar = ''
|
|
1255
|
+
|
|
1256
|
+
for (let i = 0; i < content.length; i++) {
|
|
1257
|
+
const char = content[i]
|
|
1258
|
+
const prevChar = i > 0 ? content[i - 1] : ''
|
|
1259
|
+
|
|
1260
|
+
if ((char === '"' || char === '\'' || char === '`') && prevChar !== '\\') {
|
|
1261
|
+
if (!inString) {
|
|
1262
|
+
inString = true
|
|
1263
|
+
stringChar = char
|
|
1264
|
+
}
|
|
1265
|
+
else if (char === stringChar) {
|
|
1266
|
+
inString = false
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
if (!inString) {
|
|
1271
|
+
if (char === '[' || char === '{' || char === '(') {
|
|
1272
|
+
depth++
|
|
1273
|
+
}
|
|
1274
|
+
else if (char === ']' || char === '}' || char === ')') {
|
|
1275
|
+
depth--
|
|
1276
|
+
}
|
|
1277
|
+
else if (char === ',' && depth === 0) {
|
|
1278
|
+
const trimmed = current.trim()
|
|
1279
|
+
if (trimmed) {
|
|
1280
|
+
elements.push(trimmed)
|
|
1281
|
+
}
|
|
1282
|
+
current = ''
|
|
1283
|
+
continue
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
current += char
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
const trimmed = current.trim()
|
|
1291
|
+
if (trimmed) {
|
|
1292
|
+
elements.push(trimmed)
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
return elements
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
function splitFunctionDeclarations(content: string): string[] {
|
|
1299
|
+
const declarations: string[] = []
|
|
1300
|
+
const lines = content.split('\n')
|
|
1301
|
+
let current: string[] = []
|
|
1302
|
+
|
|
1303
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1304
|
+
const line = lines[i].trim()
|
|
1305
|
+
|
|
1306
|
+
if ((line.startsWith('export function') || line.startsWith('function')) && current.length > 0) {
|
|
1307
|
+
declarations.push(current.join('\n'))
|
|
1308
|
+
current = []
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
current.push(lines[i])
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
if (current.length > 0) {
|
|
1315
|
+
declarations.push(current.join('\n'))
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
return declarations
|
|
1319
|
+
}
|