@getmikk/core 2.0.14 → 2.0.15
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/README.md +4 -4
- package/package.json +2 -1
- package/src/analysis/type-flow.ts +1 -1
- package/src/cache/incremental-cache.ts +86 -80
- package/src/contract/contract-reader.ts +1 -0
- package/src/contract/lock-compiler.ts +95 -13
- package/src/contract/schema.ts +2 -0
- package/src/error-handler.ts +2 -1
- package/src/graph/cluster-detector.ts +2 -4
- package/src/graph/dead-code-detector.ts +303 -117
- package/src/graph/graph-builder.ts +21 -161
- package/src/graph/impact-analyzer.ts +1 -0
- package/src/graph/index.ts +2 -0
- package/src/graph/rich-function-index.ts +1080 -0
- package/src/graph/symbol-table.ts +252 -0
- package/src/hash/hash-store.ts +1 -0
- package/src/index.ts +2 -0
- package/src/parser/base-extractor.ts +19 -0
- package/src/parser/boundary-checker.ts +31 -12
- package/src/parser/error-recovery.ts +5 -4
- package/src/parser/function-body-extractor.ts +248 -0
- package/src/parser/go/go-extractor.ts +249 -676
- package/src/parser/index.ts +132 -318
- package/src/parser/language-registry.ts +57 -0
- package/src/parser/oxc-parser.ts +166 -28
- package/src/parser/oxc-resolver.ts +179 -11
- package/src/parser/parser-constants.ts +1 -0
- package/src/parser/rust/rust-extractor.ts +109 -0
- package/src/parser/tree-sitter/parser.ts +369 -62
- package/src/parser/tree-sitter/queries.ts +106 -10
- package/src/parser/types.ts +20 -1
- package/src/search/bm25.ts +21 -8
- package/src/search/direct-search.ts +472 -0
- package/src/search/embedding-provider.ts +249 -0
- package/src/search/index.ts +12 -0
- package/src/search/semantic-search.ts +435 -0
- package/src/utils/artifact-transaction.ts +1 -0
- package/src/utils/atomic-write.ts +1 -0
- package/src/utils/errors.ts +89 -4
- package/src/utils/fs.ts +104 -50
- package/src/utils/json.ts +1 -0
- package/src/utils/language-registry.ts +84 -6
- package/src/utils/path.ts +26 -0
- package/tests/dead-code.test.ts +3 -2
- package/tests/direct-search.test.ts +435 -0
- package/tests/error-recovery.test.ts +143 -0
- package/tests/fixtures/simple-api/src/index.ts +1 -1
- package/tests/go-parser.test.ts +19 -335
- package/tests/js-parser.test.ts +18 -1089
- package/tests/language-registry-all.test.ts +276 -0
- package/tests/language-registry.test.ts +6 -4
- package/tests/parse-diagnostics.test.ts +9 -96
- package/tests/parser.test.ts +42 -771
- package/tests/polyglot-parser.test.ts +117 -0
- package/tests/rich-function-index.test.ts +703 -0
- package/tests/tree-sitter-parser.test.ts +108 -80
- package/tests/ts-parser.test.ts +8 -8
- package/tests/verification.test.ts +175 -0
- package/src/parser/base-parser.ts +0 -16
- package/src/parser/go/go-parser.ts +0 -43
- package/src/parser/javascript/js-extractor.ts +0 -278
- package/src/parser/javascript/js-parser.ts +0 -101
- package/src/parser/typescript/ts-extractor.ts +0 -447
- package/src/parser/typescript/ts-parser.ts +0 -36
|
@@ -1,21 +1,36 @@
|
|
|
1
|
-
import { describe, expect, test, beforeEach } from 'bun:test'
|
|
1
|
+
import { describe, expect, test, beforeEach, beforeAll } from 'bun:test'
|
|
2
2
|
import { TreeSitterParser } from '../src/parser/tree-sitter/parser.js'
|
|
3
3
|
import { TreeSitterResolver } from '../src/parser/tree-sitter/resolver.js'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
let parser: TreeSitterParser
|
|
6
|
+
let treeSitterAvailable = false
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
parser = new TreeSitterParser()
|
|
10
|
+
treeSitterAvailable = await parser.isRuntimeAvailable()
|
|
11
|
+
})
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const createTests = () => {
|
|
14
|
+
describe('TreeSitterParser - Comprehensive Language Testing', () => {
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
parser = new TreeSitterParser()
|
|
17
|
+
if (!treeSitterAvailable) {
|
|
18
|
+
treeSitterAvailable = await parser.isRuntimeAvailable()
|
|
19
|
+
}
|
|
20
|
+
})
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
// ==========================================
|
|
23
|
+
// PYTHON TESTS - Full Coverage
|
|
24
|
+
// ==========================================
|
|
25
|
+
|
|
26
|
+
describe('Python - Function & Class Extraction', () => {
|
|
27
|
+
test('parses basic Python functions', async () => {
|
|
28
|
+
if (!treeSitterAvailable) {
|
|
29
|
+
console.warn('[tree-sitter] Skipping test - runtime not available')
|
|
30
|
+
expect(true).toBe(true)
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
const content = `
|
|
19
34
|
def hello_world():
|
|
20
35
|
"""Simple hello function"""
|
|
21
36
|
print("Hello, World!")
|
|
@@ -26,20 +41,25 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
26
41
|
async def async_function():
|
|
27
42
|
await some_async_call()
|
|
28
43
|
`
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
const result = await parser.extract('test.py', content)
|
|
45
|
+
|
|
46
|
+
expect(result.functions.length).toBe(3)
|
|
47
|
+
expect(result.functions.some(f => f.name === 'hello_world')).toBe(true)
|
|
48
|
+
expect(result.functions.some(f => f.name === 'add_numbers')).toBe(true)
|
|
49
|
+
expect(result.functions.some(f => f.name === 'async_function')).toBe(true)
|
|
50
|
+
|
|
51
|
+
const addFn = result.functions.find(f => f.name === 'add_numbers')
|
|
52
|
+
expect(addFn?.returnType).toBe('int')
|
|
53
|
+
expect(addFn?.isAsync).toBe(false)
|
|
54
|
+
expect(addFn?.params.length).toBe(2)
|
|
55
|
+
})
|
|
41
56
|
|
|
42
57
|
test('parses Python classes with methods', async () => {
|
|
58
|
+
if (!treeSitterAvailable) {
|
|
59
|
+
console.warn('[tree-sitter] Skipping test - runtime not available')
|
|
60
|
+
expect(true).toBe(true)
|
|
61
|
+
return
|
|
62
|
+
}
|
|
43
63
|
const content = `
|
|
44
64
|
class User:
|
|
45
65
|
def __init__(self, name: str):
|
|
@@ -52,7 +72,7 @@ class User:
|
|
|
52
72
|
def full_name(self) -> str:
|
|
53
73
|
return f"{self.name}"
|
|
54
74
|
`
|
|
55
|
-
const result = await parser.
|
|
75
|
+
const result = await parser.extract('user.py', content)
|
|
56
76
|
|
|
57
77
|
expect(result.classes.length).toBe(1)
|
|
58
78
|
expect(result.classes[0].name).toBe('User')
|
|
@@ -60,6 +80,11 @@ class User:
|
|
|
60
80
|
})
|
|
61
81
|
|
|
62
82
|
test('detects Python export visibility', async () => {
|
|
83
|
+
if (!treeSitterAvailable) {
|
|
84
|
+
console.warn('[tree-sitter] Skipping test - runtime not available')
|
|
85
|
+
expect(true).toBe(true)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
63
88
|
const content = `
|
|
64
89
|
def public_function():
|
|
65
90
|
pass
|
|
@@ -73,7 +98,7 @@ class PublicClass:
|
|
|
73
98
|
class _PrivateClass:
|
|
74
99
|
pass
|
|
75
100
|
`
|
|
76
|
-
const result = await parser.
|
|
101
|
+
const result = await parser.extract('exports.py', content)
|
|
77
102
|
|
|
78
103
|
const publicFn = result.functions.find(f => f.name === 'public_function')
|
|
79
104
|
const privateFn = result.functions.find(f => f.name === '_private_function')
|
|
@@ -96,7 +121,7 @@ def generic_function(items):
|
|
|
96
121
|
# type: (list[dict]) -> dict
|
|
97
122
|
return {}
|
|
98
123
|
`
|
|
99
|
-
const result = await parser.
|
|
124
|
+
const result = await parser.extract('types.py', content)
|
|
100
125
|
|
|
101
126
|
const fn = result.functions.find(f => f.name === 'typed_function')
|
|
102
127
|
expect(fn).toBeDefined()
|
|
@@ -112,7 +137,7 @@ import json
|
|
|
112
137
|
from pathlib import Path
|
|
113
138
|
from typing import List, Dict, Optional
|
|
114
139
|
`
|
|
115
|
-
const result = await parser.
|
|
140
|
+
const result = await parser.extract('imports.py', content)
|
|
116
141
|
|
|
117
142
|
expect(result.imports.length).toBe(5)
|
|
118
143
|
expect(result.imports.some(i => i.source === 'os')).toBe(true)
|
|
@@ -128,7 +153,7 @@ from .. import parent
|
|
|
128
153
|
from ..sibling import something
|
|
129
154
|
from .utils import helper
|
|
130
155
|
`
|
|
131
|
-
const result = await parser.
|
|
156
|
+
const result = await parser.extract('relative.py', content)
|
|
132
157
|
|
|
133
158
|
expect(result.imports.length).toBe(4)
|
|
134
159
|
expect(result.imports.some(i => i.source.startsWith('.'))).toBe(true)
|
|
@@ -170,7 +195,7 @@ public class UserService implements IUserService {
|
|
|
170
195
|
private void deleteUser() {}
|
|
171
196
|
}
|
|
172
197
|
`
|
|
173
|
-
const result = await parser.
|
|
198
|
+
const result = await parser.extract('UserService.java', content)
|
|
174
199
|
|
|
175
200
|
expect(result.classes.length).toBe(1)
|
|
176
201
|
expect(result.classes[0].name).toBe('UserService')
|
|
@@ -188,7 +213,7 @@ public class Test {
|
|
|
188
213
|
void packagePrivate() {}
|
|
189
214
|
}
|
|
190
215
|
`
|
|
191
|
-
const result = await parser.
|
|
216
|
+
const result = await parser.extract('Test.java', content)
|
|
192
217
|
|
|
193
218
|
const pub = result.functions.find(f => f.name === 'publicMethod')
|
|
194
219
|
const priv = result.functions.find(f => f.name === 'privateMethod')
|
|
@@ -208,7 +233,7 @@ public class GenericRepository<T> {
|
|
|
208
233
|
public Map<String, T> findById(String id) { return null; }
|
|
209
234
|
}
|
|
210
235
|
`
|
|
211
|
-
const result = await parser.
|
|
236
|
+
const result = await parser.extract('GenericRepository.java', content)
|
|
212
237
|
|
|
213
238
|
expect(result.classes.length).toBe(1)
|
|
214
239
|
expect(result.classes[0].name).toBe('GenericRepository')
|
|
@@ -235,7 +260,7 @@ async fn async_operation() -> Vec<u8> {
|
|
|
235
260
|
vec![1, 2, 3]
|
|
236
261
|
}
|
|
237
262
|
`
|
|
238
|
-
const result = await parser.
|
|
263
|
+
const result = await parser.extract('lib.rs', content)
|
|
239
264
|
|
|
240
265
|
expect(result.functions.length).toBe(3)
|
|
241
266
|
expect(result.functions.some(f => f.name === 'public_function')).toBe(true)
|
|
@@ -256,7 +281,7 @@ struct PrivateStruct {
|
|
|
256
281
|
field: String,
|
|
257
282
|
}
|
|
258
283
|
`
|
|
259
|
-
const result = await parser.
|
|
284
|
+
const result = await parser.extract('mod.rs', content)
|
|
260
285
|
|
|
261
286
|
const pubFn = result.functions.find(f => f.name === 'public_fn')
|
|
262
287
|
const privFn = result.functions.find(f => f.name === 'private_fn')
|
|
@@ -283,7 +308,7 @@ impl UserRepository for InMemoryRepo {
|
|
|
283
308
|
}
|
|
284
309
|
}
|
|
285
310
|
`
|
|
286
|
-
const result = await parser.
|
|
311
|
+
const result = await parser.extract('repository.rs', content)
|
|
287
312
|
|
|
288
313
|
expect(result.path).toBe('repository.rs')
|
|
289
314
|
expect(result.language).toBe('rust')
|
|
@@ -315,7 +340,7 @@ void process_data(const char* data) {
|
|
|
315
340
|
printf("%s\\n", data);
|
|
316
341
|
}
|
|
317
342
|
`
|
|
318
|
-
const result = await parser.
|
|
343
|
+
const result = await parser.extract('test.c', content)
|
|
319
344
|
|
|
320
345
|
expect(result.functions.length).toBe(2)
|
|
321
346
|
expect(result.functions.some(f => f.name === 'add')).toBe(true)
|
|
@@ -340,7 +365,7 @@ enum Color {
|
|
|
340
365
|
BLUE = 2
|
|
341
366
|
};
|
|
342
367
|
`
|
|
343
|
-
const result = await parser.
|
|
368
|
+
const result = await parser.extract('types.c', content)
|
|
344
369
|
|
|
345
370
|
expect(result.classes.length).toBe(3) // struct + union + enum
|
|
346
371
|
expect(result.classes.some(c => c.name === 'Point')).toBe(true)
|
|
@@ -359,7 +384,7 @@ public:
|
|
|
359
384
|
int add(int a, int b) { return a + b; }
|
|
360
385
|
};
|
|
361
386
|
`
|
|
362
|
-
const result = await parser.
|
|
387
|
+
const result = await parser.extract('calc.cpp', content)
|
|
363
388
|
|
|
364
389
|
expect(result.classes.length).toBe(1)
|
|
365
390
|
expect(result.classes[0].name).toBe('Calculator')
|
|
@@ -382,7 +407,7 @@ INLINE T max_value(const T& a, const T& b) {
|
|
|
382
407
|
|
|
383
408
|
#endif
|
|
384
409
|
`
|
|
385
|
-
const result = await parser.
|
|
410
|
+
const result = await parser.extract('vector_utils.hpp', content)
|
|
386
411
|
|
|
387
412
|
expect(result.path).toBe('vector_utils.hpp')
|
|
388
413
|
expect(result.language).toBe('cpp')
|
|
@@ -420,7 +445,7 @@ function globalFunction(): void {
|
|
|
420
445
|
echo "Hello";
|
|
421
446
|
}
|
|
422
447
|
`
|
|
423
|
-
const result = await parser.
|
|
448
|
+
const result = await parser.extract('UserService.php', content)
|
|
424
449
|
|
|
425
450
|
expect(result.classes.length).toBeGreaterThanOrEqual(1)
|
|
426
451
|
expect(result.classes.some(c => c.name === 'UserService')).toBe(true)
|
|
@@ -436,7 +461,7 @@ class VisibilityTest {
|
|
|
436
461
|
protected function protectedMethod() {}
|
|
437
462
|
}
|
|
438
463
|
`
|
|
439
|
-
const result = await parser.
|
|
464
|
+
const result = await parser.extract('vis.php', content)
|
|
440
465
|
|
|
441
466
|
const pub = result.functions.find(f => f.name === 'publicMethod')
|
|
442
467
|
const priv = result.functions.find(f => f.name === 'privateMethod')
|
|
@@ -464,7 +489,7 @@ class UserController extends Controller {
|
|
|
464
489
|
}
|
|
465
490
|
}
|
|
466
491
|
`
|
|
467
|
-
const result = await parser.
|
|
492
|
+
const result = await parser.extract('UserController.php', content)
|
|
468
493
|
|
|
469
494
|
expect(result.path).toBe('UserController.php')
|
|
470
495
|
expect(result.language).toBe('php')
|
|
@@ -504,7 +529,7 @@ namespace App.Services {
|
|
|
504
529
|
}
|
|
505
530
|
}
|
|
506
531
|
`
|
|
507
|
-
const result = await parser.
|
|
532
|
+
const result = await parser.extract('UserService.cs', content)
|
|
508
533
|
|
|
509
534
|
// C# tree-sitter may not load properly in all environments
|
|
510
535
|
// Check for valid parse OR graceful fallback
|
|
@@ -536,7 +561,7 @@ namespace App.Api.Controllers {
|
|
|
536
561
|
}
|
|
537
562
|
}
|
|
538
563
|
`
|
|
539
|
-
const result = await parser.
|
|
564
|
+
const result = await parser.extract('UsersController.cs', content)
|
|
540
565
|
|
|
541
566
|
expect(result.path).toBe('UsersController.cs')
|
|
542
567
|
expect(result.language).toBe('csharp')
|
|
@@ -564,7 +589,7 @@ fun String.slugify(): String {
|
|
|
564
589
|
`
|
|
565
590
|
let result
|
|
566
591
|
try {
|
|
567
|
-
result = await parser.
|
|
592
|
+
result = await parser.extract('UserService.kt', content)
|
|
568
593
|
} catch {
|
|
569
594
|
result = { path: 'UserService.kt', language: 'kotlin', functions: [], classes: [] }
|
|
570
595
|
}
|
|
@@ -593,7 +618,7 @@ struct InMemoryUserRepository: UserRepository {
|
|
|
593
618
|
}
|
|
594
619
|
}
|
|
595
620
|
`
|
|
596
|
-
const result = await parser.
|
|
621
|
+
const result = await parser.extract('Sources/App/UserRepository.swift', content)
|
|
597
622
|
|
|
598
623
|
expect(result.path).toBe('Sources/App/UserRepository.swift')
|
|
599
624
|
expect(result.language).toBe('swift')
|
|
@@ -635,8 +660,8 @@ end
|
|
|
635
660
|
`
|
|
636
661
|
let result
|
|
637
662
|
try {
|
|
638
|
-
result = await parser.
|
|
639
|
-
} catch (
|
|
663
|
+
result = await parser.extract('auth.rb', content)
|
|
664
|
+
} catch (_e) {
|
|
640
665
|
// Ruby WASM may not load - test passes if we at least have filename
|
|
641
666
|
result = { path: 'auth.rb', functions: [], classes: [] }
|
|
642
667
|
}
|
|
@@ -657,7 +682,7 @@ end
|
|
|
657
682
|
`
|
|
658
683
|
let result
|
|
659
684
|
try {
|
|
660
|
-
result = await parser.
|
|
685
|
+
result = await parser.extract('user.rb', content)
|
|
661
686
|
} catch {
|
|
662
687
|
result = { path: 'user.rb', language: 'ruby', functions: [], classes: [] }
|
|
663
688
|
}
|
|
@@ -675,7 +700,7 @@ end
|
|
|
675
700
|
|
|
676
701
|
describe('Edge Cases & Error Handling', () => {
|
|
677
702
|
test('handles completely empty files', async () => {
|
|
678
|
-
const result = await parser.
|
|
703
|
+
const result = await parser.extract('empty.py', '')
|
|
679
704
|
|
|
680
705
|
expect(result.functions.length).toBe(0)
|
|
681
706
|
expect(result.classes.length).toBe(0)
|
|
@@ -684,7 +709,7 @@ end
|
|
|
684
709
|
})
|
|
685
710
|
|
|
686
711
|
test('handles files with only whitespace', async () => {
|
|
687
|
-
const result = await parser.
|
|
712
|
+
const result = await parser.extract('whitespace.py', ' \n\n \n')
|
|
688
713
|
|
|
689
714
|
expect(result.functions.length).toBe(0)
|
|
690
715
|
expect(result.classes.length).toBe(0)
|
|
@@ -699,7 +724,7 @@ end
|
|
|
699
724
|
* Multi-line comment
|
|
700
725
|
*/
|
|
701
726
|
`
|
|
702
|
-
const result = await parser.
|
|
727
|
+
const result = await parser.extract('comments.py', content)
|
|
703
728
|
|
|
704
729
|
expect(result.functions.length).toBe(0)
|
|
705
730
|
expect(result.classes.length).toBe(0)
|
|
@@ -717,7 +742,7 @@ class GoodClass:
|
|
|
717
742
|
pass
|
|
718
743
|
`
|
|
719
744
|
// Should not throw
|
|
720
|
-
const result = await parser.
|
|
745
|
+
const result = await parser.extract('malformed.py', badContent)
|
|
721
746
|
|
|
722
747
|
expect(result.path).toBe('malformed.py')
|
|
723
748
|
expect(result.language).toBe('python')
|
|
@@ -725,7 +750,7 @@ class GoodClass:
|
|
|
725
750
|
|
|
726
751
|
test('handles very long lines', async () => {
|
|
727
752
|
const longLine = 'x = "' + 'a'.repeat(10000) + '"'
|
|
728
|
-
const result = await parser.
|
|
753
|
+
const result = await parser.extract('long.py', longLine)
|
|
729
754
|
|
|
730
755
|
expect(result.path).toBe('long.py')
|
|
731
756
|
})
|
|
@@ -738,7 +763,7 @@ def unicode_函数():
|
|
|
738
763
|
class 用户:
|
|
739
764
|
pass
|
|
740
765
|
`
|
|
741
|
-
const result = await parser.
|
|
766
|
+
const result = await parser.extract('unicode.py', content)
|
|
742
767
|
|
|
743
768
|
expect(result.functions.length).toBeGreaterThanOrEqual(1)
|
|
744
769
|
})
|
|
@@ -752,7 +777,7 @@ class Outer:
|
|
|
752
777
|
def outer_method(self):
|
|
753
778
|
pass
|
|
754
779
|
`
|
|
755
|
-
const result = await parser.
|
|
780
|
+
const result = await parser.extract('nested.py', content)
|
|
756
781
|
|
|
757
782
|
expect(result.classes.length).toBe(2) // Outer + Inner
|
|
758
783
|
})
|
|
@@ -766,7 +791,7 @@ higher_order(lambda: print("callback"))
|
|
|
766
791
|
|
|
767
792
|
arr = list(map(lambda x: x * 2, [1, 2, 3]))
|
|
768
793
|
`
|
|
769
|
-
const result = await parser.
|
|
794
|
+
const result = await parser.extract('callbacks.py', content)
|
|
770
795
|
|
|
771
796
|
expect(result.functions.length).toBe(2) // higher_order + lambda (might be anonymous)
|
|
772
797
|
})
|
|
@@ -783,7 +808,7 @@ class DecoratedClass:
|
|
|
783
808
|
def prop(self):
|
|
784
809
|
return 1
|
|
785
810
|
`
|
|
786
|
-
const result = await parser.
|
|
811
|
+
const result = await parser.extract('decorators.py', content)
|
|
787
812
|
|
|
788
813
|
expect(result.functions.some(f => f.name === 'decorated_function')).toBe(true)
|
|
789
814
|
expect(result.classes.length).toBe(1)
|
|
@@ -804,7 +829,7 @@ def error_handling():
|
|
|
804
829
|
def raise_exception():
|
|
805
830
|
raise ValueError("error")
|
|
806
831
|
`
|
|
807
|
-
const result = await parser.
|
|
832
|
+
const result = await parser.extract('errors.py', content)
|
|
808
833
|
|
|
809
834
|
expect(result.functions.length).toBe(2)
|
|
810
835
|
expect(result.functions.some(f => f.name === 'error_handling')).toBe(true)
|
|
@@ -821,7 +846,7 @@ async def main():
|
|
|
821
846
|
data = await fetch_data("https://api.example.com")
|
|
822
847
|
return data
|
|
823
848
|
`
|
|
824
|
-
const result = await parser.
|
|
849
|
+
const result = await parser.extract('async.py', content)
|
|
825
850
|
|
|
826
851
|
const fetchFn = result.functions.find(f => f.name === 'fetch_data')
|
|
827
852
|
const mainFn = result.functions.find(f => f.name === 'main')
|
|
@@ -843,7 +868,7 @@ def range_like(start, end):
|
|
|
843
868
|
yield current
|
|
844
869
|
current += 1
|
|
845
870
|
`
|
|
846
|
-
const result = await parser.
|
|
871
|
+
const result = await parser.extract('generators.py', content)
|
|
847
872
|
|
|
848
873
|
expect(result.functions.length).toBe(2)
|
|
849
874
|
expect(result.functions.some(f => f.name === 'gen')).toBe(true)
|
|
@@ -862,13 +887,13 @@ class DatabaseConnection:
|
|
|
862
887
|
def __exit__(self, *args):
|
|
863
888
|
pass
|
|
864
889
|
`
|
|
865
|
-
const result = await parser.
|
|
890
|
+
const result = await parser.extract('context.py', content)
|
|
866
891
|
|
|
867
892
|
expect(result.classes.length).toBe(1)
|
|
868
893
|
})
|
|
869
894
|
|
|
870
895
|
test('handles files with no extension', async () => {
|
|
871
|
-
const result = await parser.
|
|
896
|
+
const result = await parser.extract('Makefile', 'all:\n\techo "hello"')
|
|
872
897
|
|
|
873
898
|
expect(result.language).toBe('unknown')
|
|
874
899
|
})
|
|
@@ -885,7 +910,7 @@ def maybe_return(x: Optional[int]) -> Union[int, str]:
|
|
|
885
910
|
def union_type(x: int | str) -> int | str:
|
|
886
911
|
return x
|
|
887
912
|
`
|
|
888
|
-
const result = await parser.
|
|
913
|
+
const result = await parser.extract('union.py', content)
|
|
889
914
|
|
|
890
915
|
// Should parse without error
|
|
891
916
|
expect(result.functions.length).toBe(2)
|
|
@@ -898,7 +923,7 @@ def union_type(x: int | str) -> int | str:
|
|
|
898
923
|
content += ' '.repeat(i - 1) + ' pass\n'
|
|
899
924
|
}
|
|
900
925
|
|
|
901
|
-
const result = await parser.
|
|
926
|
+
const result = await parser.extract('deep.py', content)
|
|
902
927
|
|
|
903
928
|
expect(result.functions.length).toBeGreaterThan(0)
|
|
904
929
|
})
|
|
@@ -911,7 +936,7 @@ triple = """triple quotes"""
|
|
|
911
936
|
fstring = f"formatted {variable}"
|
|
912
937
|
raw = r"raw \\string"
|
|
913
938
|
`
|
|
914
|
-
const result = await parser.
|
|
939
|
+
const result = await parser.extract('strings.py', content)
|
|
915
940
|
|
|
916
941
|
expect(result.path).toBe('strings.py')
|
|
917
942
|
})
|
|
@@ -988,42 +1013,42 @@ raw = r"raw \\string"
|
|
|
988
1013
|
|
|
989
1014
|
describe('Language Detection', () => {
|
|
990
1015
|
test('detects Python correctly', async () => {
|
|
991
|
-
const result = await parser.
|
|
1016
|
+
const result = await parser.extract('test.py', 'def foo(): pass')
|
|
992
1017
|
expect(result.language).toBe('python')
|
|
993
1018
|
})
|
|
994
1019
|
|
|
995
1020
|
test('detects Java correctly', async () => {
|
|
996
|
-
const result = await parser.
|
|
1021
|
+
const result = await parser.extract('Test.java', 'public class Test {}')
|
|
997
1022
|
expect(result.language).toBe('java')
|
|
998
1023
|
})
|
|
999
1024
|
|
|
1000
1025
|
test('detects Rust correctly', async () => {
|
|
1001
|
-
const result = await parser.
|
|
1026
|
+
const result = await parser.extract('lib.rs', 'fn main() {}')
|
|
1002
1027
|
expect(result.language).toBe('rust')
|
|
1003
1028
|
})
|
|
1004
1029
|
|
|
1005
1030
|
test('detects C correctly', async () => {
|
|
1006
|
-
const result = await parser.
|
|
1031
|
+
const result = await parser.extract('test.c', 'int main() { return 0; }')
|
|
1007
1032
|
expect(result.language).toBe('c')
|
|
1008
1033
|
})
|
|
1009
1034
|
|
|
1010
1035
|
test('detects C++ correctly', async () => {
|
|
1011
|
-
const result = await parser.
|
|
1036
|
+
const result = await parser.extract('test.cpp', 'int main() { return 0; }')
|
|
1012
1037
|
expect(result.language).toBe('cpp')
|
|
1013
1038
|
})
|
|
1014
1039
|
|
|
1015
1040
|
test('detects PHP correctly', async () => {
|
|
1016
|
-
const result = await parser.
|
|
1041
|
+
const result = await parser.extract('test.php', '<?php echo "hello"; ?>')
|
|
1017
1042
|
expect(result.language).toBe('php')
|
|
1018
1043
|
})
|
|
1019
1044
|
|
|
1020
1045
|
test('detects C# correctly', async () => {
|
|
1021
|
-
const result = await parser.
|
|
1046
|
+
const result = await parser.extract('Test.cs', 'public class Test {}')
|
|
1022
1047
|
expect(result.language).toBe('csharp')
|
|
1023
1048
|
})
|
|
1024
1049
|
|
|
1025
1050
|
test('returns unknown for unsupported extensions', async () => {
|
|
1026
|
-
const result = await parser.
|
|
1051
|
+
const result = await parser.extract('test.xyz', 'some content')
|
|
1027
1052
|
expect(result.language).toBe('unknown')
|
|
1028
1053
|
})
|
|
1029
1054
|
})
|
|
@@ -1039,7 +1064,7 @@ raw = r"raw \\string"
|
|
|
1039
1064
|
content += `def function_${i}():\n pass\n\n`
|
|
1040
1065
|
}
|
|
1041
1066
|
|
|
1042
|
-
const result = await parser.
|
|
1067
|
+
const result = await parser.extract('many_funcs.py', content)
|
|
1043
1068
|
|
|
1044
1069
|
expect(result.functions.length).toBe(100)
|
|
1045
1070
|
})
|
|
@@ -1050,7 +1075,7 @@ raw = r"raw \\string"
|
|
|
1050
1075
|
content += `class Class_${i}:\n pass\n\n`
|
|
1051
1076
|
}
|
|
1052
1077
|
|
|
1053
|
-
const result = await parser.
|
|
1078
|
+
const result = await parser.extract('many_classes.py', content)
|
|
1054
1079
|
|
|
1055
1080
|
expect(result.classes.length).toBe(50)
|
|
1056
1081
|
})
|
|
@@ -1059,16 +1084,19 @@ raw = r"raw \\string"
|
|
|
1059
1084
|
let content = 'import '
|
|
1060
1085
|
content += Array.from({ length: 50 }, (_, i) => `module_${i}`).join(', ')
|
|
1061
1086
|
|
|
1062
|
-
const result = await parser.
|
|
1087
|
+
const result = await parser.extract('many_imports.py', content)
|
|
1063
1088
|
|
|
1064
1089
|
expect(result.imports.length).toBeGreaterThan(0)
|
|
1065
1090
|
})
|
|
1066
1091
|
|
|
1067
1092
|
test('produces identical hash for identical content', async () => {
|
|
1068
1093
|
const content = 'def stable():\n return 42\n'
|
|
1069
|
-
const a = await parser.
|
|
1070
|
-
const b = await parser.
|
|
1094
|
+
const a = await parser.extract('stable.py', content)
|
|
1095
|
+
const b = await parser.extract('stable.py', content)
|
|
1071
1096
|
expect(a.hash).toBe(b.hash)
|
|
1072
1097
|
})
|
|
1073
1098
|
})
|
|
1074
|
-
})
|
|
1099
|
+
})
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
createTests()
|
package/tests/ts-parser.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, beforeAll, afterAll } from 'bun:test'
|
|
2
2
|
import * as path from 'node:path'
|
|
3
3
|
import * as fs from 'node:fs/promises'
|
|
4
|
-
import {
|
|
4
|
+
import { TypescriptExtractor } from '../src/parser/oxc-parser'
|
|
5
5
|
|
|
6
6
|
describe('ts-parser config resolution', () => {
|
|
7
7
|
const FIXTURE_DIR = path.join(process.cwd(), '.test-fixture-tsconfig')
|
|
@@ -37,7 +37,7 @@ describe('ts-parser config resolution', () => {
|
|
|
37
37
|
})
|
|
38
38
|
|
|
39
39
|
it('resolves compiler paths from tsconfig', async () => {
|
|
40
|
-
const parser = new
|
|
40
|
+
const parser = new TypescriptExtractor()
|
|
41
41
|
|
|
42
42
|
const srcDir = path.join(FIXTURE_DIR, 'src', 'app')
|
|
43
43
|
await fs.mkdir(srcDir, { recursive: true })
|
|
@@ -56,7 +56,7 @@ describe('ts-parser config resolution', () => {
|
|
|
56
56
|
`)
|
|
57
57
|
|
|
58
58
|
// Parse and resolve imports
|
|
59
|
-
const parsed = await parser.
|
|
59
|
+
const parsed = await parser.extract(filePath, await fs.readFile(filePath, 'utf-8'))
|
|
60
60
|
const resolved = (await parser.resolveImports([parsed], FIXTURE_DIR))[0]
|
|
61
61
|
|
|
62
62
|
const impApp = resolved.imports.find(i => i.source === '@app/local')
|
|
@@ -71,10 +71,10 @@ describe('ts-parser config resolution', () => {
|
|
|
71
71
|
})
|
|
72
72
|
|
|
73
73
|
describe('TypeScriptParser Edge Cases & Fault Tolerance', () => {
|
|
74
|
-
const parser = new
|
|
74
|
+
const parser = new TypescriptExtractor()
|
|
75
75
|
|
|
76
76
|
it('handles completely empty files', async () => {
|
|
77
|
-
const result = await parser.
|
|
77
|
+
const result = await parser.extract('src/empty.ts', '')
|
|
78
78
|
expect(result.functions).toHaveLength(0)
|
|
79
79
|
expect(result.classes).toHaveLength(0)
|
|
80
80
|
expect(result.language).toBe('typescript')
|
|
@@ -94,7 +94,7 @@ describe('TypeScriptParser Edge Cases & Fault Tolerance', () => {
|
|
|
94
94
|
@Injectable()
|
|
95
95
|
export class HalfClass implements {
|
|
96
96
|
`
|
|
97
|
-
const result = await parser.
|
|
97
|
+
const result = await parser.extract('src/broken.ts', malformedCode)
|
|
98
98
|
expect(result.functions.length).toBeGreaterThanOrEqual(0)
|
|
99
99
|
expect(result.classes.length).toBeGreaterThanOrEqual(0)
|
|
100
100
|
expect(Array.isArray(result.imports)).toBe(true)
|
|
@@ -109,14 +109,14 @@ describe('TypeScriptParser Edge Cases & Fault Tolerance', () => {
|
|
|
109
109
|
*/
|
|
110
110
|
// End of file
|
|
111
111
|
`
|
|
112
|
-
const result = await parser.
|
|
112
|
+
const result = await parser.extract('src/docs.ts', commentsCode)
|
|
113
113
|
expect(result.functions).toHaveLength(0)
|
|
114
114
|
expect(result.hash).toBeDefined()
|
|
115
115
|
})
|
|
116
116
|
|
|
117
117
|
it('parses Windows line endings consistently', async () => {
|
|
118
118
|
const winCode = 'export function ping() {\r\n return 1\r\n}\r\n'
|
|
119
|
-
const result = await parser.
|
|
119
|
+
const result = await parser.extract('src/win.ts', winCode)
|
|
120
120
|
expect(result.functions.some(f => f.name === 'ping')).toBe(true)
|
|
121
121
|
})
|
|
122
122
|
})
|