@stacksjs/dtsx 0.6.1 → 0.6.2
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 +5 -5
- package/dist/extract.d.ts +59 -1310
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/extract.d.ts
CHANGED
|
@@ -7,1313 +7,62 @@ export declare function extractDtsTypes(sourceCode: string): string;
|
|
|
7
7
|
declare function extractFunctionSignature(declaration: string): FunctionSignature;
|
|
8
8
|
declare function extractFunctionName(declaration: string): string;
|
|
9
9
|
declare function extractGenerics(rest: string);
|
|
10
|
-
function extractParams(rest: string)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
}
|
|
10
|
+
declare function extractParams(rest: string);
|
|
11
|
+
declare function extractReturnType(rest: string);
|
|
12
|
+
declare function extractFunctionType(value: string): string | null;
|
|
13
|
+
declare function generateOptimizedImports(state: ImportTrackingState): string[];
|
|
14
|
+
declare function extractCompleteObjectContent(value: string): string | null;
|
|
15
|
+
declare function formatOutput(state: ProcessingState): string;
|
|
16
|
+
declare function removeLeadingComments(code: string): string;
|
|
17
|
+
declare function createProcessingState(): ProcessingState;
|
|
18
|
+
declare function createImportTrackingState(): ImportTrackingState;
|
|
19
|
+
declare function indentMultilineType(type: string, baseIndent: string, isLast: boolean): string;
|
|
20
|
+
declare function inferValueType(value: string): string;
|
|
21
|
+
declare function inferArrayType(value: string, state?: ProcessingState, preserveLineBreaks = false): string;
|
|
22
|
+
declare function inferComplexObjectType(value: string, state?: ProcessingState, indentLevel = 0): string;
|
|
23
|
+
declare function inferConstArrayType(value: string, state?: ProcessingState): string;
|
|
24
|
+
declare function inferConstType(value: string, state: ProcessingState): string;
|
|
25
|
+
declare function inferTypeFromDefaultValue(defaultValue: string): string;
|
|
26
|
+
declare function isDefaultExport(line: string): boolean;
|
|
27
|
+
declare function isDeclarationStart(line: string): boolean;
|
|
28
|
+
declare function isRegexPattern(line: string): boolean;
|
|
29
|
+
declare function isBrandedType(declaration: string): boolean;
|
|
30
|
+
export declare function isFunctionType(type: string): boolean;
|
|
31
|
+
export declare function isDeclarationComplete(content: string | string[]): boolean;
|
|
32
|
+
declare function isVariableInsideFunction(line: string, state: ProcessingState): boolean;
|
|
33
|
+
declare function needsMultilineFormat(types: string[]): boolean;
|
|
34
|
+
declare function normalizeTypeReference(value: string): string;
|
|
35
|
+
declare function processBlock(lines: string[], comments: string[], state: ProcessingState): void;
|
|
36
|
+
declare function processVariableBlock(cleanDeclaration: string, lines: string[], state: ProcessingState): boolean;
|
|
37
|
+
declare function processFunctionBlock(cleanDeclaration: string, state: ProcessingState): boolean;
|
|
38
|
+
declare function processInterfaceBlock(cleanDeclaration: string, declarationText: string, state: ProcessingState): boolean;
|
|
39
|
+
declare function processTypeBlock(cleanDeclaration: string, declarationText: string, state: ProcessingState): boolean;
|
|
40
|
+
declare function processDefaultExportBlock(cleanDeclaration: string, state: ProcessingState): boolean;
|
|
41
|
+
declare function processExportAllBlock(cleanDeclaration: string, state: ProcessingState): boolean;
|
|
42
|
+
declare function processExportBlock(cleanDeclaration: string, declarationText: string, state: ProcessingState): boolean;
|
|
43
|
+
declare function processExport(line: string, state: ProcessingState): void;
|
|
44
|
+
declare function processExportedClass(cleanDeclaration: string, state: ProcessingState): boolean;
|
|
45
|
+
declare function processExportedEnum(cleanDeclaration: string, state: ProcessingState): boolean;
|
|
46
|
+
declare function processExportedNamespace(cleanDeclaration: string, state: ProcessingState): boolean;
|
|
47
|
+
declare function processModuleBlock(cleanDeclaration: string, declarationText: string, state: ProcessingState): boolean;
|
|
48
|
+
export declare function processSpecificDeclaration(declarationWithoutComments: string, fullDeclaration: string, state: ProcessingState): void;
|
|
49
|
+
declare function processSourceFile(content: string, state: ProcessingState): void;
|
|
50
|
+
declare function processImports(line: string, state: ImportTrackingState): void;
|
|
51
|
+
declare function processType(declaration: string, isExported = true): string;
|
|
52
|
+
declare function processTypeExport(line: string, state: ProcessingState): void;
|
|
53
|
+
declare function processVariable(declaration: string, isExported: boolean, state: ProcessingState): string;
|
|
54
|
+
declare function processFunction(declaration: string, usedTypes?: Set<string>, isExported = true): string;
|
|
55
|
+
declare function getCleanDeclaration(declaration: string): string;
|
|
56
|
+
declare function processGeneratorFunction(declaration: string): string;
|
|
57
|
+
declare function processInterface(declaration: string, isExported = true): string;
|
|
58
|
+
declare function processModule(declaration: string): string;
|
|
59
|
+
declare function processObjectMethod(declaration: string): ProcessedMethod;
|
|
60
|
+
declare function processObjectProperties(content: string, state?: ProcessingState, indentLevel = 0): Array<{ key: string, value: string }>;
|
|
61
|
+
declare function processPropertyValue(value: string, indentLevel: number, state?: ProcessingState): string;
|
|
62
|
+
declare function trackTypeUsage(content: string, state: ImportTrackingState): void;
|
|
63
|
+
declare function trackValueUsage(content: string, state: ImportTrackingState): void;
|
|
64
|
+
declare function debugLog(category: string, message: string): void;
|
|
65
|
+
declare function normalizeType(type: string): string;
|
|
66
|
+
declare function normalizePropertyKey(key: string): string;
|
|
67
|
+
declare function splitArrayElements(content: string): string[];
|
|
68
|
+
declare function splitFunctionDeclarations(content: string): string[];
|