agentmap 0.2.0 → 0.3.0
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 +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/extract/definitions.d.ts.map +1 -1
- package/dist/extract/definitions.js +80 -14
- package/dist/extract/definitions.js.map +1 -1
- package/dist/extract/definitions.test.d.ts +2 -0
- package/dist/extract/definitions.test.d.ts.map +1 -0
- package/dist/extract/definitions.test.js +1414 -0
- package/dist/extract/definitions.test.js.map +1 -0
- package/dist/extract/git-status.d.ts +51 -0
- package/dist/extract/git-status.d.ts.map +1 -0
- package/dist/extract/git-status.js +289 -0
- package/dist/extract/git-status.js.map +1 -0
- package/dist/extract/git-status.test.d.ts +2 -0
- package/dist/extract/git-status.test.d.ts.map +1 -0
- package/dist/extract/git-status.test.js +456 -0
- package/dist/extract/git-status.test.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/map/builder.d.ts.map +1 -1
- package/dist/map/builder.js +41 -3
- package/dist/map/builder.js.map +1 -1
- package/dist/map/yaml.d.ts.map +1 -1
- package/dist/map/yaml.js +7 -2
- package/dist/map/yaml.js.map +1 -1
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +32 -4
- package/dist/scanner.js.map +1 -1
- package/dist/types.d.ts +43 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -9
- package/README +0 -158
- package/dist/opencode/plugin.d.ts +0 -3
- package/dist/opencode/plugin.d.ts.map +0 -1
- package/dist/opencode/plugin.js +0 -18
- package/dist/opencode/plugin.js.map +0 -1
|
@@ -0,0 +1,1414 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, test } from 'bun:test';
|
|
2
|
+
import { parseCode, initParser } from '../parser/index.js';
|
|
3
|
+
import { extractDefinitions } from './definitions.js';
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Setup
|
|
6
|
+
// ============================================================================
|
|
7
|
+
beforeAll(async () => {
|
|
8
|
+
await initParser();
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* Helper to extract definitions from code string
|
|
12
|
+
*/
|
|
13
|
+
async function getDefinitions(code, language) {
|
|
14
|
+
const tree = await parseCode(code, language);
|
|
15
|
+
return extractDefinitions(tree.rootNode, language);
|
|
16
|
+
}
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// TypeScript Tests
|
|
19
|
+
// ============================================================================
|
|
20
|
+
describe('TypeScript', () => {
|
|
21
|
+
test('large function (>7 lines) is included', async () => {
|
|
22
|
+
const code = `function processData(input: string): string {
|
|
23
|
+
const trimmed = input.trim()
|
|
24
|
+
const upper = trimmed.toUpperCase()
|
|
25
|
+
const parts = upper.split(',')
|
|
26
|
+
const filtered = parts.filter(Boolean)
|
|
27
|
+
const joined = filtered.join('-')
|
|
28
|
+
const result = joined + '!'
|
|
29
|
+
return result
|
|
30
|
+
}`;
|
|
31
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
32
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
33
|
+
[
|
|
34
|
+
{
|
|
35
|
+
"endLine": 9,
|
|
36
|
+
"exported": false,
|
|
37
|
+
"line": 1,
|
|
38
|
+
"name": "processData",
|
|
39
|
+
"type": "function",
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
`);
|
|
43
|
+
});
|
|
44
|
+
test('small function (<=7 lines) is excluded', async () => {
|
|
45
|
+
const code = `function add(a: number, b: number): number {
|
|
46
|
+
return a + b
|
|
47
|
+
}`;
|
|
48
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
49
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
50
|
+
});
|
|
51
|
+
test('large class is included', async () => {
|
|
52
|
+
const code = `class Calculator {
|
|
53
|
+
private value: number = 0
|
|
54
|
+
|
|
55
|
+
add(n: number): this {
|
|
56
|
+
this.value += n
|
|
57
|
+
return this
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
subtract(n: number): this {
|
|
61
|
+
this.value -= n
|
|
62
|
+
return this
|
|
63
|
+
}
|
|
64
|
+
}`;
|
|
65
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
66
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
67
|
+
[
|
|
68
|
+
{
|
|
69
|
+
"endLine": 13,
|
|
70
|
+
"exported": false,
|
|
71
|
+
"line": 1,
|
|
72
|
+
"name": "Calculator",
|
|
73
|
+
"type": "class",
|
|
74
|
+
},
|
|
75
|
+
]
|
|
76
|
+
`);
|
|
77
|
+
});
|
|
78
|
+
test('small class is excluded', async () => {
|
|
79
|
+
const code = `class Point {
|
|
80
|
+
x: number
|
|
81
|
+
y: number
|
|
82
|
+
}`;
|
|
83
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
84
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
85
|
+
});
|
|
86
|
+
test('large arrow function is included', async () => {
|
|
87
|
+
const code = `const processItems = (items: string[]) => {
|
|
88
|
+
const result: string[] = []
|
|
89
|
+
for (const item of items) {
|
|
90
|
+
const processed = item.trim()
|
|
91
|
+
const upper = processed.toUpperCase()
|
|
92
|
+
result.push(upper)
|
|
93
|
+
}
|
|
94
|
+
return result
|
|
95
|
+
}`;
|
|
96
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
97
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
98
|
+
[
|
|
99
|
+
{
|
|
100
|
+
"endLine": 9,
|
|
101
|
+
"exported": false,
|
|
102
|
+
"line": 1,
|
|
103
|
+
"name": "processItems",
|
|
104
|
+
"type": "function",
|
|
105
|
+
},
|
|
106
|
+
]
|
|
107
|
+
`);
|
|
108
|
+
});
|
|
109
|
+
test('small arrow function is excluded', async () => {
|
|
110
|
+
const code = `const double = (n: number) => n * 2`;
|
|
111
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
112
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
113
|
+
});
|
|
114
|
+
test('exported const is included', async () => {
|
|
115
|
+
const code = `export const CONFIG = {
|
|
116
|
+
apiUrl: 'https://api.example.com',
|
|
117
|
+
timeout: 5000,
|
|
118
|
+
}`;
|
|
119
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
120
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
121
|
+
[
|
|
122
|
+
{
|
|
123
|
+
"endLine": 4,
|
|
124
|
+
"exported": true,
|
|
125
|
+
"line": 1,
|
|
126
|
+
"name": "CONFIG",
|
|
127
|
+
"type": "const",
|
|
128
|
+
},
|
|
129
|
+
]
|
|
130
|
+
`);
|
|
131
|
+
});
|
|
132
|
+
test('non-exported const is excluded', async () => {
|
|
133
|
+
const code = `const CONFIG = { timeout: 5000 }`;
|
|
134
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
135
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
136
|
+
});
|
|
137
|
+
test('interface is included (any size)', async () => {
|
|
138
|
+
const code = `interface User {
|
|
139
|
+
name: string
|
|
140
|
+
age: number
|
|
141
|
+
}`;
|
|
142
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
143
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
144
|
+
[
|
|
145
|
+
{
|
|
146
|
+
"endLine": 4,
|
|
147
|
+
"exported": false,
|
|
148
|
+
"line": 1,
|
|
149
|
+
"name": "User",
|
|
150
|
+
"type": "interface",
|
|
151
|
+
},
|
|
152
|
+
]
|
|
153
|
+
`);
|
|
154
|
+
});
|
|
155
|
+
test('type alias is included (any size)', async () => {
|
|
156
|
+
const code = `type Status = 'pending' | 'active' | 'done'`;
|
|
157
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
158
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
159
|
+
[
|
|
160
|
+
{
|
|
161
|
+
"endLine": 1,
|
|
162
|
+
"exported": false,
|
|
163
|
+
"line": 1,
|
|
164
|
+
"name": "Status",
|
|
165
|
+
"type": "type",
|
|
166
|
+
},
|
|
167
|
+
]
|
|
168
|
+
`);
|
|
169
|
+
});
|
|
170
|
+
test('enum is included (any size)', async () => {
|
|
171
|
+
const code = `enum Color {
|
|
172
|
+
Red,
|
|
173
|
+
Green,
|
|
174
|
+
Blue,
|
|
175
|
+
}`;
|
|
176
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
177
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
178
|
+
[
|
|
179
|
+
{
|
|
180
|
+
"endLine": 5,
|
|
181
|
+
"exported": false,
|
|
182
|
+
"line": 1,
|
|
183
|
+
"name": "Color",
|
|
184
|
+
"type": "enum",
|
|
185
|
+
},
|
|
186
|
+
]
|
|
187
|
+
`);
|
|
188
|
+
});
|
|
189
|
+
test('exported function', async () => {
|
|
190
|
+
const code = `export function fetchData(url: string): Promise<Response> {
|
|
191
|
+
const headers = new Headers()
|
|
192
|
+
headers.set('Content-Type', 'application/json')
|
|
193
|
+
const options = { method: 'GET', headers }
|
|
194
|
+
const response = fetch(url, options)
|
|
195
|
+
return response
|
|
196
|
+
}`;
|
|
197
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
198
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
199
|
+
[
|
|
200
|
+
{
|
|
201
|
+
"endLine": 7,
|
|
202
|
+
"exported": true,
|
|
203
|
+
"line": 1,
|
|
204
|
+
"name": "fetchData",
|
|
205
|
+
"type": "function",
|
|
206
|
+
},
|
|
207
|
+
]
|
|
208
|
+
`);
|
|
209
|
+
});
|
|
210
|
+
test('exported large function', async () => {
|
|
211
|
+
const code = `export function fetchData(url: string): Promise<Response> {
|
|
212
|
+
const headers = new Headers()
|
|
213
|
+
headers.set('Content-Type', 'application/json')
|
|
214
|
+
headers.set('Accept', 'application/json')
|
|
215
|
+
const options = { method: 'GET', headers }
|
|
216
|
+
const response = fetch(url, options)
|
|
217
|
+
return response
|
|
218
|
+
}`;
|
|
219
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
220
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
221
|
+
[
|
|
222
|
+
{
|
|
223
|
+
"endLine": 8,
|
|
224
|
+
"exported": true,
|
|
225
|
+
"line": 1,
|
|
226
|
+
"name": "fetchData",
|
|
227
|
+
"type": "function",
|
|
228
|
+
},
|
|
229
|
+
]
|
|
230
|
+
`);
|
|
231
|
+
});
|
|
232
|
+
test('async function', async () => {
|
|
233
|
+
const code = `async function loadData(id: string): Promise<Data> {
|
|
234
|
+
const url = buildUrl(id)
|
|
235
|
+
const response = await fetch(url)
|
|
236
|
+
const json = await response.json()
|
|
237
|
+
const validated = validate(json)
|
|
238
|
+
const transformed = transform(validated)
|
|
239
|
+
return transformed
|
|
240
|
+
}`;
|
|
241
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
242
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
243
|
+
[
|
|
244
|
+
{
|
|
245
|
+
"endLine": 8,
|
|
246
|
+
"exported": false,
|
|
247
|
+
"line": 1,
|
|
248
|
+
"name": "loadData",
|
|
249
|
+
"type": "function",
|
|
250
|
+
},
|
|
251
|
+
]
|
|
252
|
+
`);
|
|
253
|
+
});
|
|
254
|
+
test('generator function', async () => {
|
|
255
|
+
const code = `function* generateSequence(start: number, end: number) {
|
|
256
|
+
let current = start
|
|
257
|
+
while (current <= end) {
|
|
258
|
+
const value = current
|
|
259
|
+
current++
|
|
260
|
+
yield value
|
|
261
|
+
console.log('yielded', value)
|
|
262
|
+
}
|
|
263
|
+
}`;
|
|
264
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
265
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
266
|
+
});
|
|
267
|
+
test('abstract class', async () => {
|
|
268
|
+
const code = `abstract class BaseProcessor {
|
|
269
|
+
abstract process(input: string): string
|
|
270
|
+
|
|
271
|
+
protected validate(input: string): boolean {
|
|
272
|
+
return input.length > 0
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
protected transform(input: string): string {
|
|
276
|
+
return input.trim()
|
|
277
|
+
}
|
|
278
|
+
}`;
|
|
279
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
280
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
281
|
+
[
|
|
282
|
+
{
|
|
283
|
+
"endLine": 11,
|
|
284
|
+
"exported": false,
|
|
285
|
+
"line": 1,
|
|
286
|
+
"name": "BaseProcessor",
|
|
287
|
+
"type": "class",
|
|
288
|
+
},
|
|
289
|
+
]
|
|
290
|
+
`);
|
|
291
|
+
});
|
|
292
|
+
test('generic function', async () => {
|
|
293
|
+
const code = `function identity<T>(value: T): T {
|
|
294
|
+
const result = value
|
|
295
|
+
console.log('identity called')
|
|
296
|
+
console.log('value:', result)
|
|
297
|
+
console.log('type:', typeof result)
|
|
298
|
+
console.log('returning...')
|
|
299
|
+
return result
|
|
300
|
+
}`;
|
|
301
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
302
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
303
|
+
[
|
|
304
|
+
{
|
|
305
|
+
"endLine": 8,
|
|
306
|
+
"exported": false,
|
|
307
|
+
"line": 1,
|
|
308
|
+
"name": "identity",
|
|
309
|
+
"type": "function",
|
|
310
|
+
},
|
|
311
|
+
]
|
|
312
|
+
`);
|
|
313
|
+
});
|
|
314
|
+
test('generic class', async () => {
|
|
315
|
+
const code = `class Container<T> {
|
|
316
|
+
private items: T[] = []
|
|
317
|
+
|
|
318
|
+
add(item: T): void {
|
|
319
|
+
this.items.push(item)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
get(index: number): T {
|
|
323
|
+
return this.items[index]
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
size(): number {
|
|
327
|
+
return this.items.length
|
|
328
|
+
}
|
|
329
|
+
}`;
|
|
330
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
331
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
332
|
+
[
|
|
333
|
+
{
|
|
334
|
+
"endLine": 15,
|
|
335
|
+
"exported": false,
|
|
336
|
+
"line": 1,
|
|
337
|
+
"name": "Container",
|
|
338
|
+
"type": "class",
|
|
339
|
+
},
|
|
340
|
+
]
|
|
341
|
+
`);
|
|
342
|
+
});
|
|
343
|
+
test('multiple exports in one line', async () => {
|
|
344
|
+
const code = `export const a = 1, b = 2, c = 3`;
|
|
345
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
346
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
347
|
+
[
|
|
348
|
+
{
|
|
349
|
+
"endLine": 1,
|
|
350
|
+
"exported": true,
|
|
351
|
+
"line": 1,
|
|
352
|
+
"name": "a",
|
|
353
|
+
"type": "const",
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"endLine": 1,
|
|
357
|
+
"exported": true,
|
|
358
|
+
"line": 1,
|
|
359
|
+
"name": "b",
|
|
360
|
+
"type": "const",
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
"endLine": 1,
|
|
364
|
+
"exported": true,
|
|
365
|
+
"line": 1,
|
|
366
|
+
"name": "c",
|
|
367
|
+
"type": "const",
|
|
368
|
+
},
|
|
369
|
+
]
|
|
370
|
+
`);
|
|
371
|
+
});
|
|
372
|
+
test('export default function', async () => {
|
|
373
|
+
const code = `export default function handler(req: Request): Response {
|
|
374
|
+
const body = req.body
|
|
375
|
+
const parsed = JSON.parse(body)
|
|
376
|
+
const validated = validate(parsed)
|
|
377
|
+
const processed = processData(validated)
|
|
378
|
+
const formatted = format(processed)
|
|
379
|
+
const response = JSON.stringify(formatted)
|
|
380
|
+
return new Response(response)
|
|
381
|
+
}`;
|
|
382
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
383
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
384
|
+
[
|
|
385
|
+
{
|
|
386
|
+
"endLine": 9,
|
|
387
|
+
"exported": true,
|
|
388
|
+
"line": 1,
|
|
389
|
+
"name": "handler",
|
|
390
|
+
"type": "function",
|
|
391
|
+
},
|
|
392
|
+
]
|
|
393
|
+
`);
|
|
394
|
+
});
|
|
395
|
+
test('class with decorators', async () => {
|
|
396
|
+
const code = `@Injectable()
|
|
397
|
+
@Singleton()
|
|
398
|
+
class UserService {
|
|
399
|
+
constructor(private db: Database) {}
|
|
400
|
+
|
|
401
|
+
async findUser(id: string): Promise<User> {
|
|
402
|
+
return this.db.users.find(id)
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async createUser(data: UserData): Promise<User> {
|
|
406
|
+
return this.db.users.create(data)
|
|
407
|
+
}
|
|
408
|
+
}`;
|
|
409
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
410
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
411
|
+
[
|
|
412
|
+
{
|
|
413
|
+
"endLine": 13,
|
|
414
|
+
"exported": false,
|
|
415
|
+
"line": 1,
|
|
416
|
+
"name": "UserService",
|
|
417
|
+
"type": "class",
|
|
418
|
+
},
|
|
419
|
+
]
|
|
420
|
+
`);
|
|
421
|
+
});
|
|
422
|
+
test('mixed definitions - filters correctly', async () => {
|
|
423
|
+
const code = `// Small function - excluded
|
|
424
|
+
function util(x: number) {
|
|
425
|
+
return x * 2
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Large function - included
|
|
429
|
+
export function processData(input: string): string {
|
|
430
|
+
const step1 = input.trim()
|
|
431
|
+
const step2 = step1.toUpperCase()
|
|
432
|
+
const step3 = step2.split('')
|
|
433
|
+
const step4 = step3.reverse()
|
|
434
|
+
const step5 = step4.join('')
|
|
435
|
+
return step5
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Interface - included
|
|
439
|
+
interface Config {
|
|
440
|
+
name: string
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Small class - excluded
|
|
444
|
+
class Tiny {
|
|
445
|
+
x = 1
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Exported const - included
|
|
449
|
+
export const VERSION = '1.0.0'
|
|
450
|
+
|
|
451
|
+
// Type alias - included
|
|
452
|
+
type ID = string | number`;
|
|
453
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
454
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
455
|
+
[
|
|
456
|
+
{
|
|
457
|
+
"endLine": 14,
|
|
458
|
+
"exported": true,
|
|
459
|
+
"line": 7,
|
|
460
|
+
"name": "processData",
|
|
461
|
+
"type": "function",
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
"endLine": 19,
|
|
465
|
+
"exported": false,
|
|
466
|
+
"line": 17,
|
|
467
|
+
"name": "Config",
|
|
468
|
+
"type": "interface",
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
"endLine": 27,
|
|
472
|
+
"exported": true,
|
|
473
|
+
"line": 27,
|
|
474
|
+
"name": "VERSION",
|
|
475
|
+
"type": "const",
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
"endLine": 30,
|
|
479
|
+
"exported": false,
|
|
480
|
+
"line": 30,
|
|
481
|
+
"name": "ID",
|
|
482
|
+
"type": "type",
|
|
483
|
+
},
|
|
484
|
+
]
|
|
485
|
+
`);
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
// ============================================================================
|
|
489
|
+
// JavaScript Tests
|
|
490
|
+
// ============================================================================
|
|
491
|
+
describe('JavaScript', () => {
|
|
492
|
+
test('large function', async () => {
|
|
493
|
+
const code = `function buildResponse(data) {
|
|
494
|
+
const headers = { 'Content-Type': 'application/json' }
|
|
495
|
+
const body = JSON.stringify(data)
|
|
496
|
+
const status = 200
|
|
497
|
+
const statusText = 'OK'
|
|
498
|
+
const response = { headers, body, status, statusText }
|
|
499
|
+
return response
|
|
500
|
+
}`;
|
|
501
|
+
const defs = await getDefinitions(code, 'javascript');
|
|
502
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
503
|
+
[
|
|
504
|
+
{
|
|
505
|
+
"endLine": 8,
|
|
506
|
+
"exported": false,
|
|
507
|
+
"line": 1,
|
|
508
|
+
"name": "buildResponse",
|
|
509
|
+
"type": "function",
|
|
510
|
+
},
|
|
511
|
+
]
|
|
512
|
+
`);
|
|
513
|
+
});
|
|
514
|
+
test('small function', async () => {
|
|
515
|
+
const code = `function add(a, b) {
|
|
516
|
+
return a + b
|
|
517
|
+
}`;
|
|
518
|
+
const defs = await getDefinitions(code, 'javascript');
|
|
519
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
520
|
+
});
|
|
521
|
+
test('large class', async () => {
|
|
522
|
+
const code = `class EventEmitter {
|
|
523
|
+
constructor() {
|
|
524
|
+
this.events = {}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
on(event, callback) {
|
|
528
|
+
if (!this.events[event]) {
|
|
529
|
+
this.events[event] = []
|
|
530
|
+
}
|
|
531
|
+
this.events[event].push(callback)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
emit(event, data) {
|
|
535
|
+
const callbacks = this.events[event]
|
|
536
|
+
if (callbacks) {
|
|
537
|
+
callbacks.forEach(cb => cb(data))
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}`;
|
|
541
|
+
const defs = await getDefinitions(code, 'javascript');
|
|
542
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
543
|
+
[
|
|
544
|
+
{
|
|
545
|
+
"endLine": 19,
|
|
546
|
+
"exported": false,
|
|
547
|
+
"line": 1,
|
|
548
|
+
"name": "EventEmitter",
|
|
549
|
+
"type": "class",
|
|
550
|
+
},
|
|
551
|
+
]
|
|
552
|
+
`);
|
|
553
|
+
});
|
|
554
|
+
test('IIFE is not extracted', async () => {
|
|
555
|
+
const code = `(function() {
|
|
556
|
+
console.log('init')
|
|
557
|
+
const x = 1
|
|
558
|
+
const y = 2
|
|
559
|
+
const z = 3
|
|
560
|
+
console.log(x, y, z)
|
|
561
|
+
return { x, y, z }
|
|
562
|
+
})()`;
|
|
563
|
+
const defs = await getDefinitions(code, 'javascript');
|
|
564
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
565
|
+
});
|
|
566
|
+
test('function expression assigned to var', async () => {
|
|
567
|
+
const code = `var processItems = function(items) {
|
|
568
|
+
const result = []
|
|
569
|
+
for (const item of items) {
|
|
570
|
+
const processed = item.trim()
|
|
571
|
+
const upper = processed.toUpperCase()
|
|
572
|
+
result.push(upper)
|
|
573
|
+
}
|
|
574
|
+
return result
|
|
575
|
+
}`;
|
|
576
|
+
const defs = await getDefinitions(code, 'javascript');
|
|
577
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
// ============================================================================
|
|
581
|
+
// Python Tests
|
|
582
|
+
// ============================================================================
|
|
583
|
+
describe('Python', () => {
|
|
584
|
+
test('large function', async () => {
|
|
585
|
+
const code = `def process_data(items):
|
|
586
|
+
result = []
|
|
587
|
+
for item in items:
|
|
588
|
+
cleaned = item.strip()
|
|
589
|
+
upper = cleaned.upper()
|
|
590
|
+
parts = upper.split(',')
|
|
591
|
+
result.extend(parts)
|
|
592
|
+
return result`;
|
|
593
|
+
const defs = await getDefinitions(code, 'python');
|
|
594
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
595
|
+
[
|
|
596
|
+
{
|
|
597
|
+
"endLine": 8,
|
|
598
|
+
"exported": false,
|
|
599
|
+
"line": 1,
|
|
600
|
+
"name": "process_data",
|
|
601
|
+
"type": "function",
|
|
602
|
+
},
|
|
603
|
+
]
|
|
604
|
+
`);
|
|
605
|
+
});
|
|
606
|
+
test('small function', async () => {
|
|
607
|
+
const code = `def add(a, b):
|
|
608
|
+
return a + b`;
|
|
609
|
+
const defs = await getDefinitions(code, 'python');
|
|
610
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
611
|
+
});
|
|
612
|
+
test('large class', async () => {
|
|
613
|
+
const code = `class DataProcessor:
|
|
614
|
+
def __init__(self, data):
|
|
615
|
+
self.data = data
|
|
616
|
+
self.processed = False
|
|
617
|
+
|
|
618
|
+
def process(self):
|
|
619
|
+
self.data = [x.strip() for x in self.data]
|
|
620
|
+
self.processed = True
|
|
621
|
+
return self
|
|
622
|
+
|
|
623
|
+
def get_result(self):
|
|
624
|
+
if not self.processed:
|
|
625
|
+
raise ValueError("Not processed")
|
|
626
|
+
return self.data`;
|
|
627
|
+
const defs = await getDefinitions(code, 'python');
|
|
628
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
629
|
+
[
|
|
630
|
+
{
|
|
631
|
+
"endLine": 14,
|
|
632
|
+
"exported": false,
|
|
633
|
+
"line": 1,
|
|
634
|
+
"name": "DataProcessor",
|
|
635
|
+
"type": "class",
|
|
636
|
+
},
|
|
637
|
+
]
|
|
638
|
+
`);
|
|
639
|
+
});
|
|
640
|
+
test('small class', async () => {
|
|
641
|
+
const code = `class Point:
|
|
642
|
+
x: int
|
|
643
|
+
y: int`;
|
|
644
|
+
const defs = await getDefinitions(code, 'python');
|
|
645
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
646
|
+
});
|
|
647
|
+
test('async function', async () => {
|
|
648
|
+
const code = `async def fetch_data(url):
|
|
649
|
+
async with aiohttp.ClientSession() as session:
|
|
650
|
+
async with session.get(url) as response:
|
|
651
|
+
data = await response.json()
|
|
652
|
+
validated = validate(data)
|
|
653
|
+
processed = process(validated)
|
|
654
|
+
return processed`;
|
|
655
|
+
const defs = await getDefinitions(code, 'python');
|
|
656
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
657
|
+
[
|
|
658
|
+
{
|
|
659
|
+
"endLine": 7,
|
|
660
|
+
"exported": false,
|
|
661
|
+
"line": 1,
|
|
662
|
+
"name": "fetch_data",
|
|
663
|
+
"type": "function",
|
|
664
|
+
},
|
|
665
|
+
]
|
|
666
|
+
`);
|
|
667
|
+
});
|
|
668
|
+
test('decorated function', async () => {
|
|
669
|
+
const code = `@app.route('/api/users')
|
|
670
|
+
@require_auth
|
|
671
|
+
def get_users():
|
|
672
|
+
users = db.query(User).all()
|
|
673
|
+
serialized = [u.to_dict() for u in users]
|
|
674
|
+
filtered = filter_active(serialized)
|
|
675
|
+
sorted_users = sort_by_name(filtered)
|
|
676
|
+
return jsonify(sorted_users)`;
|
|
677
|
+
const defs = await getDefinitions(code, 'python');
|
|
678
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
679
|
+
});
|
|
680
|
+
test('class with inheritance', async () => {
|
|
681
|
+
const code = `class AdminUser(User, PermissionMixin):
|
|
682
|
+
def __init__(self, name, permissions):
|
|
683
|
+
super().__init__(name)
|
|
684
|
+
self.permissions = permissions
|
|
685
|
+
|
|
686
|
+
def has_permission(self, perm):
|
|
687
|
+
return perm in self.permissions
|
|
688
|
+
|
|
689
|
+
def grant(self, perm):
|
|
690
|
+
self.permissions.add(perm)`;
|
|
691
|
+
const defs = await getDefinitions(code, 'python');
|
|
692
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
693
|
+
[
|
|
694
|
+
{
|
|
695
|
+
"endLine": 10,
|
|
696
|
+
"exported": false,
|
|
697
|
+
"line": 1,
|
|
698
|
+
"name": "AdminUser",
|
|
699
|
+
"type": "class",
|
|
700
|
+
},
|
|
701
|
+
]
|
|
702
|
+
`);
|
|
703
|
+
});
|
|
704
|
+
test('lambda is not extracted', async () => {
|
|
705
|
+
const code = `double = lambda x: x * 2
|
|
706
|
+
triple = lambda x: x * 3`;
|
|
707
|
+
const defs = await getDefinitions(code, 'python');
|
|
708
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
// ============================================================================
|
|
712
|
+
// Rust Tests
|
|
713
|
+
// ============================================================================
|
|
714
|
+
describe('Rust', () => {
|
|
715
|
+
test('large function', async () => {
|
|
716
|
+
const code = `fn process_items(items: Vec<String>) -> Vec<String> {
|
|
717
|
+
let mut result = Vec::new();
|
|
718
|
+
for item in items {
|
|
719
|
+
let trimmed = item.trim();
|
|
720
|
+
let upper = trimmed.to_uppercase();
|
|
721
|
+
let parts: Vec<&str> = upper.split(',').collect();
|
|
722
|
+
result.extend(parts.iter().map(|s| s.to_string()));
|
|
723
|
+
}
|
|
724
|
+
result
|
|
725
|
+
}`;
|
|
726
|
+
const defs = await getDefinitions(code, 'rust');
|
|
727
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
728
|
+
[
|
|
729
|
+
{
|
|
730
|
+
"endLine": 10,
|
|
731
|
+
"exported": false,
|
|
732
|
+
"line": 1,
|
|
733
|
+
"name": "process_items",
|
|
734
|
+
"type": "function",
|
|
735
|
+
},
|
|
736
|
+
]
|
|
737
|
+
`);
|
|
738
|
+
});
|
|
739
|
+
test('small function', async () => {
|
|
740
|
+
const code = `fn add(a: i32, b: i32) -> i32 {
|
|
741
|
+
a + b
|
|
742
|
+
}`;
|
|
743
|
+
const defs = await getDefinitions(code, 'rust');
|
|
744
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
745
|
+
});
|
|
746
|
+
test('large struct', async () => {
|
|
747
|
+
const code = `struct Config {
|
|
748
|
+
api_url: String,
|
|
749
|
+
timeout: u64,
|
|
750
|
+
retries: u32,
|
|
751
|
+
headers: HashMap<String, String>,
|
|
752
|
+
auth_token: Option<String>,
|
|
753
|
+
debug_mode: bool,
|
|
754
|
+
log_level: String,
|
|
755
|
+
max_connections: usize,
|
|
756
|
+
}`;
|
|
757
|
+
const defs = await getDefinitions(code, 'rust');
|
|
758
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
759
|
+
[
|
|
760
|
+
{
|
|
761
|
+
"endLine": 10,
|
|
762
|
+
"exported": false,
|
|
763
|
+
"line": 1,
|
|
764
|
+
"name": "Config",
|
|
765
|
+
"type": "class",
|
|
766
|
+
},
|
|
767
|
+
]
|
|
768
|
+
`);
|
|
769
|
+
});
|
|
770
|
+
test('small struct', async () => {
|
|
771
|
+
const code = `struct Point {
|
|
772
|
+
x: i32,
|
|
773
|
+
y: i32,
|
|
774
|
+
}`;
|
|
775
|
+
const defs = await getDefinitions(code, 'rust');
|
|
776
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
777
|
+
});
|
|
778
|
+
test('large impl block', async () => {
|
|
779
|
+
const code = `impl Calculator {
|
|
780
|
+
fn new() -> Self {
|
|
781
|
+
Calculator { value: 0 }
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
fn add(&mut self, n: i32) -> &mut Self {
|
|
785
|
+
self.value += n;
|
|
786
|
+
self
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
fn result(&self) -> i32 {
|
|
790
|
+
self.value
|
|
791
|
+
}
|
|
792
|
+
}`;
|
|
793
|
+
const defs = await getDefinitions(code, 'rust');
|
|
794
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
795
|
+
[
|
|
796
|
+
{
|
|
797
|
+
"endLine": 14,
|
|
798
|
+
"exported": false,
|
|
799
|
+
"line": 1,
|
|
800
|
+
"name": "Calculator",
|
|
801
|
+
"type": "class",
|
|
802
|
+
},
|
|
803
|
+
]
|
|
804
|
+
`);
|
|
805
|
+
});
|
|
806
|
+
test('large trait', async () => {
|
|
807
|
+
const code = `trait Processor {
|
|
808
|
+
fn process(&self, input: &str) -> String;
|
|
809
|
+
fn validate(&self, input: &str) -> bool;
|
|
810
|
+
fn transform(&self, input: &str) -> Vec<String>;
|
|
811
|
+
fn cleanup(&self);
|
|
812
|
+
fn reset(&mut self);
|
|
813
|
+
fn get_status(&self) -> Status;
|
|
814
|
+
fn set_config(&mut self, config: Config);
|
|
815
|
+
fn run(&self) -> Result<(), Error>;
|
|
816
|
+
}`;
|
|
817
|
+
const defs = await getDefinitions(code, 'rust');
|
|
818
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
819
|
+
[
|
|
820
|
+
{
|
|
821
|
+
"endLine": 10,
|
|
822
|
+
"exported": false,
|
|
823
|
+
"line": 1,
|
|
824
|
+
"name": "Processor",
|
|
825
|
+
"type": "class",
|
|
826
|
+
},
|
|
827
|
+
]
|
|
828
|
+
`);
|
|
829
|
+
});
|
|
830
|
+
test('enum', async () => {
|
|
831
|
+
const code = `enum Status {
|
|
832
|
+
Pending,
|
|
833
|
+
Active,
|
|
834
|
+
Done,
|
|
835
|
+
}`;
|
|
836
|
+
const defs = await getDefinitions(code, 'rust');
|
|
837
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
838
|
+
[
|
|
839
|
+
{
|
|
840
|
+
"endLine": 5,
|
|
841
|
+
"exported": false,
|
|
842
|
+
"line": 1,
|
|
843
|
+
"name": "Status",
|
|
844
|
+
"type": "enum",
|
|
845
|
+
},
|
|
846
|
+
]
|
|
847
|
+
`);
|
|
848
|
+
});
|
|
849
|
+
test('type alias', async () => {
|
|
850
|
+
const code = `type Result<T> = std::result::Result<T, Error>;`;
|
|
851
|
+
const defs = await getDefinitions(code, 'rust');
|
|
852
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
853
|
+
[
|
|
854
|
+
{
|
|
855
|
+
"endLine": 1,
|
|
856
|
+
"exported": false,
|
|
857
|
+
"line": 1,
|
|
858
|
+
"name": "Result",
|
|
859
|
+
"type": "type",
|
|
860
|
+
},
|
|
861
|
+
]
|
|
862
|
+
`);
|
|
863
|
+
});
|
|
864
|
+
test('const item', async () => {
|
|
865
|
+
const code = `const MAX_SIZE: usize = 1024;`;
|
|
866
|
+
const defs = await getDefinitions(code, 'rust');
|
|
867
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
868
|
+
[
|
|
869
|
+
{
|
|
870
|
+
"endLine": 1,
|
|
871
|
+
"exported": false,
|
|
872
|
+
"line": 1,
|
|
873
|
+
"name": "MAX_SIZE",
|
|
874
|
+
"type": "const",
|
|
875
|
+
},
|
|
876
|
+
]
|
|
877
|
+
`);
|
|
878
|
+
});
|
|
879
|
+
test('static item', async () => {
|
|
880
|
+
const code = `static COUNTER: AtomicUsize = AtomicUsize::new(0);`;
|
|
881
|
+
const defs = await getDefinitions(code, 'rust');
|
|
882
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
883
|
+
[
|
|
884
|
+
{
|
|
885
|
+
"endLine": 1,
|
|
886
|
+
"exported": false,
|
|
887
|
+
"line": 1,
|
|
888
|
+
"name": "COUNTER",
|
|
889
|
+
"type": "const",
|
|
890
|
+
},
|
|
891
|
+
]
|
|
892
|
+
`);
|
|
893
|
+
});
|
|
894
|
+
test('pub function', async () => {
|
|
895
|
+
const code = `pub fn public_handler(request: Request) -> Response {
|
|
896
|
+
let body = request.body();
|
|
897
|
+
let parsed = parse_body(body);
|
|
898
|
+
let validated = validate(parsed);
|
|
899
|
+
let processed = process(validated);
|
|
900
|
+
let serialized = serialize(processed);
|
|
901
|
+
Response::new(serialized)
|
|
902
|
+
}`;
|
|
903
|
+
const defs = await getDefinitions(code, 'rust');
|
|
904
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
905
|
+
[
|
|
906
|
+
{
|
|
907
|
+
"endLine": 8,
|
|
908
|
+
"exported": false,
|
|
909
|
+
"line": 1,
|
|
910
|
+
"name": "public_handler",
|
|
911
|
+
"type": "function",
|
|
912
|
+
},
|
|
913
|
+
]
|
|
914
|
+
`);
|
|
915
|
+
});
|
|
916
|
+
test('async function', async () => {
|
|
917
|
+
const code = `async fn fetch_data(url: &str) -> Result<Data, Error> {
|
|
918
|
+
let client = reqwest::Client::new();
|
|
919
|
+
let response = client.get(url).send().await?;
|
|
920
|
+
let body = response.text().await?;
|
|
921
|
+
let parsed = serde_json::from_str(&body)?;
|
|
922
|
+
let validated = validate(parsed)?;
|
|
923
|
+
Ok(validated)
|
|
924
|
+
}`;
|
|
925
|
+
const defs = await getDefinitions(code, 'rust');
|
|
926
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
927
|
+
[
|
|
928
|
+
{
|
|
929
|
+
"endLine": 8,
|
|
930
|
+
"exported": false,
|
|
931
|
+
"line": 1,
|
|
932
|
+
"name": "fetch_data",
|
|
933
|
+
"type": "function",
|
|
934
|
+
},
|
|
935
|
+
]
|
|
936
|
+
`);
|
|
937
|
+
});
|
|
938
|
+
test('macro definition is not extracted', async () => {
|
|
939
|
+
const code = `macro_rules! create_function {
|
|
940
|
+
($name:ident) => {
|
|
941
|
+
fn $name() {
|
|
942
|
+
println!("Called {}", stringify!($name));
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
}`;
|
|
946
|
+
const defs = await getDefinitions(code, 'rust');
|
|
947
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
948
|
+
});
|
|
949
|
+
});
|
|
950
|
+
// ============================================================================
|
|
951
|
+
// Go Tests
|
|
952
|
+
// ============================================================================
|
|
953
|
+
describe('Go', () => {
|
|
954
|
+
test('large function', async () => {
|
|
955
|
+
const code = `func processItems(items []string) []string {
|
|
956
|
+
result := make([]string, 0)
|
|
957
|
+
for _, item := range items {
|
|
958
|
+
trimmed := strings.TrimSpace(item)
|
|
959
|
+
upper := strings.ToUpper(trimmed)
|
|
960
|
+
parts := strings.Split(upper, ",")
|
|
961
|
+
result = append(result, parts...)
|
|
962
|
+
}
|
|
963
|
+
return result
|
|
964
|
+
}`;
|
|
965
|
+
const defs = await getDefinitions(code, 'go');
|
|
966
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
967
|
+
[
|
|
968
|
+
{
|
|
969
|
+
"endLine": 10,
|
|
970
|
+
"exported": false,
|
|
971
|
+
"line": 1,
|
|
972
|
+
"name": "processItems",
|
|
973
|
+
"type": "function",
|
|
974
|
+
},
|
|
975
|
+
]
|
|
976
|
+
`);
|
|
977
|
+
});
|
|
978
|
+
test('small function', async () => {
|
|
979
|
+
const code = `func add(a, b int) int {
|
|
980
|
+
return a + b
|
|
981
|
+
}`;
|
|
982
|
+
const defs = await getDefinitions(code, 'go');
|
|
983
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
984
|
+
});
|
|
985
|
+
test('large method', async () => {
|
|
986
|
+
const code = `func (c *Calculator) Process(input string) string {
|
|
987
|
+
step1 := strings.TrimSpace(input)
|
|
988
|
+
step2 := strings.ToUpper(step1)
|
|
989
|
+
step3 := strings.Split(step2, ",")
|
|
990
|
+
step4 := strings.Join(step3, "-")
|
|
991
|
+
step5 := step4 + "!"
|
|
992
|
+
return step5
|
|
993
|
+
}`;
|
|
994
|
+
const defs = await getDefinitions(code, 'go');
|
|
995
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
996
|
+
[
|
|
997
|
+
{
|
|
998
|
+
"endLine": 8,
|
|
999
|
+
"exported": false,
|
|
1000
|
+
"line": 1,
|
|
1001
|
+
"name": "Process",
|
|
1002
|
+
"type": "function",
|
|
1003
|
+
},
|
|
1004
|
+
]
|
|
1005
|
+
`);
|
|
1006
|
+
});
|
|
1007
|
+
test('type struct', async () => {
|
|
1008
|
+
const code = `type Config struct {
|
|
1009
|
+
APIUrl string
|
|
1010
|
+
Timeout int
|
|
1011
|
+
}`;
|
|
1012
|
+
const defs = await getDefinitions(code, 'go');
|
|
1013
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1014
|
+
[
|
|
1015
|
+
{
|
|
1016
|
+
"endLine": 4,
|
|
1017
|
+
"exported": false,
|
|
1018
|
+
"line": 1,
|
|
1019
|
+
"name": "Config",
|
|
1020
|
+
"type": "type",
|
|
1021
|
+
},
|
|
1022
|
+
]
|
|
1023
|
+
`);
|
|
1024
|
+
});
|
|
1025
|
+
test('type interface', async () => {
|
|
1026
|
+
const code = `type Reader interface {
|
|
1027
|
+
Read(p []byte) (n int, err error)
|
|
1028
|
+
}`;
|
|
1029
|
+
const defs = await getDefinitions(code, 'go');
|
|
1030
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1031
|
+
[
|
|
1032
|
+
{
|
|
1033
|
+
"endLine": 3,
|
|
1034
|
+
"exported": false,
|
|
1035
|
+
"line": 1,
|
|
1036
|
+
"name": "Reader",
|
|
1037
|
+
"type": "type",
|
|
1038
|
+
},
|
|
1039
|
+
]
|
|
1040
|
+
`);
|
|
1041
|
+
});
|
|
1042
|
+
test('const declaration', async () => {
|
|
1043
|
+
const code = `const MaxRetries = 3`;
|
|
1044
|
+
const defs = await getDefinitions(code, 'go');
|
|
1045
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1046
|
+
[
|
|
1047
|
+
{
|
|
1048
|
+
"endLine": 1,
|
|
1049
|
+
"exported": false,
|
|
1050
|
+
"line": 1,
|
|
1051
|
+
"name": "MaxRetries",
|
|
1052
|
+
"type": "const",
|
|
1053
|
+
},
|
|
1054
|
+
]
|
|
1055
|
+
`);
|
|
1056
|
+
});
|
|
1057
|
+
test('var declaration', async () => {
|
|
1058
|
+
const code = `var DefaultTimeout = time.Second * 30`;
|
|
1059
|
+
const defs = await getDefinitions(code, 'go');
|
|
1060
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1061
|
+
[
|
|
1062
|
+
{
|
|
1063
|
+
"endLine": 1,
|
|
1064
|
+
"exported": false,
|
|
1065
|
+
"line": 1,
|
|
1066
|
+
"name": "DefaultTimeout",
|
|
1067
|
+
"type": "const",
|
|
1068
|
+
},
|
|
1069
|
+
]
|
|
1070
|
+
`);
|
|
1071
|
+
});
|
|
1072
|
+
test('exported function (capitalized)', async () => {
|
|
1073
|
+
const code = `func ProcessData(input string) (string, error) {
|
|
1074
|
+
if input == "" {
|
|
1075
|
+
return "", errors.New("empty input")
|
|
1076
|
+
}
|
|
1077
|
+
trimmed := strings.TrimSpace(input)
|
|
1078
|
+
upper := strings.ToUpper(trimmed)
|
|
1079
|
+
validated := validate(upper)
|
|
1080
|
+
return validated, nil
|
|
1081
|
+
}`;
|
|
1082
|
+
const defs = await getDefinitions(code, 'go');
|
|
1083
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1084
|
+
[
|
|
1085
|
+
{
|
|
1086
|
+
"endLine": 9,
|
|
1087
|
+
"exported": false,
|
|
1088
|
+
"line": 1,
|
|
1089
|
+
"name": "ProcessData",
|
|
1090
|
+
"type": "function",
|
|
1091
|
+
},
|
|
1092
|
+
]
|
|
1093
|
+
`);
|
|
1094
|
+
});
|
|
1095
|
+
test('init function is extracted if large', async () => {
|
|
1096
|
+
const code = `func init() {
|
|
1097
|
+
config = loadConfig()
|
|
1098
|
+
db = connectDB()
|
|
1099
|
+
cache = initCache()
|
|
1100
|
+
logger = setupLogger()
|
|
1101
|
+
metrics = initMetrics()
|
|
1102
|
+
validator = newValidator()
|
|
1103
|
+
handlers = registerHandlers()
|
|
1104
|
+
}`;
|
|
1105
|
+
const defs = await getDefinitions(code, 'go');
|
|
1106
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1107
|
+
[
|
|
1108
|
+
{
|
|
1109
|
+
"endLine": 9,
|
|
1110
|
+
"exported": false,
|
|
1111
|
+
"line": 1,
|
|
1112
|
+
"name": "init",
|
|
1113
|
+
"type": "function",
|
|
1114
|
+
},
|
|
1115
|
+
]
|
|
1116
|
+
`);
|
|
1117
|
+
});
|
|
1118
|
+
test('type alias', async () => {
|
|
1119
|
+
const code = `type UserID = int64`;
|
|
1120
|
+
const defs = await getDefinitions(code, 'go');
|
|
1121
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
1122
|
+
});
|
|
1123
|
+
});
|
|
1124
|
+
// ============================================================================
|
|
1125
|
+
// Edge Cases
|
|
1126
|
+
// ============================================================================
|
|
1127
|
+
describe('edge cases', () => {
|
|
1128
|
+
test('empty file', async () => {
|
|
1129
|
+
const defs = await getDefinitions('', 'typescript');
|
|
1130
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
1131
|
+
});
|
|
1132
|
+
test('only comments', async () => {
|
|
1133
|
+
const code = `// This is a comment
|
|
1134
|
+
// Another comment
|
|
1135
|
+
/* Block comment */`;
|
|
1136
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1137
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
1138
|
+
});
|
|
1139
|
+
test('only imports', async () => {
|
|
1140
|
+
const code = `import { foo } from 'bar'
|
|
1141
|
+
import * as baz from 'qux'`;
|
|
1142
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1143
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
1144
|
+
});
|
|
1145
|
+
test('nested functions - only top level', async () => {
|
|
1146
|
+
const code = `function outer(x: number): number {
|
|
1147
|
+
function inner(y: number): number {
|
|
1148
|
+
return y * 2
|
|
1149
|
+
}
|
|
1150
|
+
const result = inner(x)
|
|
1151
|
+
const doubled = result * 2
|
|
1152
|
+
const tripled = result * 3
|
|
1153
|
+
return doubled + tripled
|
|
1154
|
+
}`;
|
|
1155
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1156
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1157
|
+
[
|
|
1158
|
+
{
|
|
1159
|
+
"endLine": 9,
|
|
1160
|
+
"exported": false,
|
|
1161
|
+
"line": 1,
|
|
1162
|
+
"name": "outer",
|
|
1163
|
+
"type": "function",
|
|
1164
|
+
},
|
|
1165
|
+
]
|
|
1166
|
+
`);
|
|
1167
|
+
});
|
|
1168
|
+
test('boundary: exactly 8 lines included', async () => {
|
|
1169
|
+
const code = `function eightLines(): void {
|
|
1170
|
+
const a = 1
|
|
1171
|
+
const b = 2
|
|
1172
|
+
const c = 3
|
|
1173
|
+
const d = 4
|
|
1174
|
+
const e = 5
|
|
1175
|
+
console.log(a, b, c, d, e)
|
|
1176
|
+
}`;
|
|
1177
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1178
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1179
|
+
[
|
|
1180
|
+
{
|
|
1181
|
+
"endLine": 8,
|
|
1182
|
+
"exported": false,
|
|
1183
|
+
"line": 1,
|
|
1184
|
+
"name": "eightLines",
|
|
1185
|
+
"type": "function",
|
|
1186
|
+
},
|
|
1187
|
+
]
|
|
1188
|
+
`);
|
|
1189
|
+
});
|
|
1190
|
+
test('boundary: exactly 7 lines excluded', async () => {
|
|
1191
|
+
const code = `function sevenLines(): void {
|
|
1192
|
+
const a = 1
|
|
1193
|
+
const b = 2
|
|
1194
|
+
const c = 3
|
|
1195
|
+
const d = 4
|
|
1196
|
+
console.log(a, b, c, d)
|
|
1197
|
+
}`;
|
|
1198
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1199
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1200
|
+
[
|
|
1201
|
+
{
|
|
1202
|
+
"endLine": 7,
|
|
1203
|
+
"exported": false,
|
|
1204
|
+
"line": 1,
|
|
1205
|
+
"name": "sevenLines",
|
|
1206
|
+
"type": "function",
|
|
1207
|
+
},
|
|
1208
|
+
]
|
|
1209
|
+
`);
|
|
1210
|
+
});
|
|
1211
|
+
test('function with multiline signature', async () => {
|
|
1212
|
+
const code = `function complexSignature(
|
|
1213
|
+
param1: string,
|
|
1214
|
+
param2: number,
|
|
1215
|
+
param3: boolean,
|
|
1216
|
+
param4: object
|
|
1217
|
+
): Result {
|
|
1218
|
+
const combined = combine(param1, param2)
|
|
1219
|
+
const processed = process(combined, param3)
|
|
1220
|
+
return finalize(processed, param4)
|
|
1221
|
+
}`;
|
|
1222
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1223
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1224
|
+
[
|
|
1225
|
+
{
|
|
1226
|
+
"endLine": 10,
|
|
1227
|
+
"exported": false,
|
|
1228
|
+
"line": 1,
|
|
1229
|
+
"name": "complexSignature",
|
|
1230
|
+
"type": "function",
|
|
1231
|
+
},
|
|
1232
|
+
]
|
|
1233
|
+
`);
|
|
1234
|
+
});
|
|
1235
|
+
test('class with static members', async () => {
|
|
1236
|
+
const code = `class Singleton {
|
|
1237
|
+
private static instance: Singleton
|
|
1238
|
+
|
|
1239
|
+
private constructor() {}
|
|
1240
|
+
|
|
1241
|
+
static getInstance(): Singleton {
|
|
1242
|
+
if (!Singleton.instance) {
|
|
1243
|
+
Singleton.instance = new Singleton()
|
|
1244
|
+
}
|
|
1245
|
+
return Singleton.instance
|
|
1246
|
+
}
|
|
1247
|
+
}`;
|
|
1248
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1249
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1250
|
+
[
|
|
1251
|
+
{
|
|
1252
|
+
"endLine": 12,
|
|
1253
|
+
"exported": false,
|
|
1254
|
+
"line": 1,
|
|
1255
|
+
"name": "Singleton",
|
|
1256
|
+
"type": "class",
|
|
1257
|
+
},
|
|
1258
|
+
]
|
|
1259
|
+
`);
|
|
1260
|
+
});
|
|
1261
|
+
test('re-export statement', async () => {
|
|
1262
|
+
const code = `export { foo, bar } from './module'
|
|
1263
|
+
export * from './other'`;
|
|
1264
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1265
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
1266
|
+
});
|
|
1267
|
+
test('interface with generics', async () => {
|
|
1268
|
+
const code = `interface Repository<T, ID> {
|
|
1269
|
+
find(id: ID): Promise<T | null>
|
|
1270
|
+
findAll(): Promise<T[]>
|
|
1271
|
+
save(entity: T): Promise<T>
|
|
1272
|
+
delete(id: ID): Promise<void>
|
|
1273
|
+
}`;
|
|
1274
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1275
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1276
|
+
[
|
|
1277
|
+
{
|
|
1278
|
+
"endLine": 6,
|
|
1279
|
+
"exported": false,
|
|
1280
|
+
"line": 1,
|
|
1281
|
+
"name": "Repository",
|
|
1282
|
+
"type": "interface",
|
|
1283
|
+
},
|
|
1284
|
+
]
|
|
1285
|
+
`);
|
|
1286
|
+
});
|
|
1287
|
+
test('mapped type', async () => {
|
|
1288
|
+
const code = `type Readonly<T> = {
|
|
1289
|
+
readonly [P in keyof T]: T[P]
|
|
1290
|
+
}`;
|
|
1291
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1292
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1293
|
+
[
|
|
1294
|
+
{
|
|
1295
|
+
"endLine": 3,
|
|
1296
|
+
"exported": false,
|
|
1297
|
+
"line": 1,
|
|
1298
|
+
"name": "Readonly",
|
|
1299
|
+
"type": "type",
|
|
1300
|
+
},
|
|
1301
|
+
]
|
|
1302
|
+
`);
|
|
1303
|
+
});
|
|
1304
|
+
test('conditional type', async () => {
|
|
1305
|
+
const code = `type NonNullable<T> = T extends null | undefined ? never : T`;
|
|
1306
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1307
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1308
|
+
[
|
|
1309
|
+
{
|
|
1310
|
+
"endLine": 1,
|
|
1311
|
+
"exported": false,
|
|
1312
|
+
"line": 1,
|
|
1313
|
+
"name": "NonNullable",
|
|
1314
|
+
"type": "type",
|
|
1315
|
+
},
|
|
1316
|
+
]
|
|
1317
|
+
`);
|
|
1318
|
+
});
|
|
1319
|
+
test('overloaded function signatures', async () => {
|
|
1320
|
+
const code = `function process(input: string): string
|
|
1321
|
+
function process(input: number): number
|
|
1322
|
+
function process(input: string | number): string | number {
|
|
1323
|
+
if (typeof input === 'string') {
|
|
1324
|
+
return input.toUpperCase()
|
|
1325
|
+
}
|
|
1326
|
+
return input * 2
|
|
1327
|
+
}`;
|
|
1328
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1329
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1330
|
+
[
|
|
1331
|
+
{
|
|
1332
|
+
"endLine": 8,
|
|
1333
|
+
"exported": false,
|
|
1334
|
+
"line": 3,
|
|
1335
|
+
"name": "process",
|
|
1336
|
+
"type": "function",
|
|
1337
|
+
},
|
|
1338
|
+
]
|
|
1339
|
+
`);
|
|
1340
|
+
});
|
|
1341
|
+
test('namespace', async () => {
|
|
1342
|
+
const code = `namespace Utils {
|
|
1343
|
+
export function helper(x: number): number {
|
|
1344
|
+
return x * 2
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
export const VERSION = '1.0.0'
|
|
1348
|
+
}`;
|
|
1349
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1350
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
1351
|
+
});
|
|
1352
|
+
test('declare statements', async () => {
|
|
1353
|
+
const code = `declare function external(x: string): void
|
|
1354
|
+
declare class ExternalClass {
|
|
1355
|
+
method(): void
|
|
1356
|
+
}
|
|
1357
|
+
declare const CONFIG: { url: string }`;
|
|
1358
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1359
|
+
expect(defs).toMatchInlineSnapshot(`[]`);
|
|
1360
|
+
});
|
|
1361
|
+
test('unicode identifiers', async () => {
|
|
1362
|
+
const code = `function 计算(输入: number): number {
|
|
1363
|
+
const 结果 = 输入 * 2
|
|
1364
|
+
const 验证 = validate(结果)
|
|
1365
|
+
const 处理 = process(验证)
|
|
1366
|
+
const 格式化 = format(处理)
|
|
1367
|
+
const 输出 = finalize(格式化)
|
|
1368
|
+
return 输出
|
|
1369
|
+
}`;
|
|
1370
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1371
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1372
|
+
[
|
|
1373
|
+
{
|
|
1374
|
+
"endLine": 8,
|
|
1375
|
+
"exported": false,
|
|
1376
|
+
"line": 1,
|
|
1377
|
+
"name": "计算",
|
|
1378
|
+
"type": "function",
|
|
1379
|
+
},
|
|
1380
|
+
]
|
|
1381
|
+
`);
|
|
1382
|
+
});
|
|
1383
|
+
test('private class fields', async () => {
|
|
1384
|
+
const code = `class SecureStorage {
|
|
1385
|
+
#data: Map<string, string> = new Map()
|
|
1386
|
+
#key: string
|
|
1387
|
+
|
|
1388
|
+
constructor(key: string) {
|
|
1389
|
+
this.#key = key
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
#encrypt(value: string): string {
|
|
1393
|
+
return btoa(value + this.#key)
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
set(key: string, value: string): void {
|
|
1397
|
+
this.#data.set(key, this.#encrypt(value))
|
|
1398
|
+
}
|
|
1399
|
+
}`;
|
|
1400
|
+
const defs = await getDefinitions(code, 'typescript');
|
|
1401
|
+
expect(defs).toMatchInlineSnapshot(`
|
|
1402
|
+
[
|
|
1403
|
+
{
|
|
1404
|
+
"endLine": 16,
|
|
1405
|
+
"exported": false,
|
|
1406
|
+
"line": 1,
|
|
1407
|
+
"name": "SecureStorage",
|
|
1408
|
+
"type": "class",
|
|
1409
|
+
},
|
|
1410
|
+
]
|
|
1411
|
+
`);
|
|
1412
|
+
});
|
|
1413
|
+
});
|
|
1414
|
+
//# sourceMappingURL=definitions.test.js.map
|