@getmikk/core 2.0.10 → 2.0.12

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.
@@ -2,6 +2,7 @@ import { describe, test, expect } from 'bun:test'
2
2
  import { JavaScriptExtractor } from '../src/parser/javascript/js-extractor'
3
3
  import { JavaScriptParser } from '../src/parser/javascript/js-parser'
4
4
  import { JavaScriptResolver } from '../src/parser/javascript/js-resolver'
5
+ import { TypeScriptExtractor } from '../src/parser/typescript/ts-extractor'
5
6
  import { getParser } from '../src/parser/index'
6
7
 
7
8
  // ─── Sample JS source files ───────────────────────────────────────────────────
@@ -649,3 +650,446 @@ describe('getParser — JS extensions', () => {
649
650
  expect(() => getParser('src/app.xyz')).toThrow()
650
651
  })
651
652
  })
653
+
654
+ // ==========================================
655
+ // ADDITIONAL COMPREHENSIVE TESTS
656
+ // ==========================================
657
+
658
+ describe('JavaScript - Additional Edge Cases', () => {
659
+
660
+ describe('Dynamic Imports', () => {
661
+ test('handles dynamic import()', () => {
662
+ const src = `
663
+ const module = await import('./dynamic')
664
+ const mod = await import('lodash')
665
+ `
666
+ const ext = new JavaScriptExtractor('src/app.js', src)
667
+ const imports = ext.extractImports()
668
+ expect(imports.length).toBeGreaterThanOrEqual(0)
669
+ })
670
+ })
671
+
672
+ describe('Class Syntax', () => {
673
+ test('extracts ES6 classes', () => {
674
+ const src = `
675
+ class User {
676
+ constructor(name) {
677
+ this.name = name
678
+ }
679
+
680
+ getName() {
681
+ return this.name
682
+ }
683
+
684
+ static create(data) {
685
+ return new User(data.name)
686
+ }
687
+ }
688
+
689
+ class Admin extends User {
690
+ constructor(name, role) {
691
+ super(name)
692
+ this.role = role
693
+ }
694
+ }
695
+ `
696
+ const ext = new JavaScriptExtractor('src/user.js', src)
697
+ const classes = ext.extractClasses()
698
+ expect(classes.length).toBe(2)
699
+ expect(classes[0].name).toBe('User')
700
+ expect(classes[1].name).toBe('Admin')
701
+ })
702
+
703
+ test('extracts class methods', () => {
704
+ const src = `
705
+ class Calculator {
706
+ add(a, b) { return a + b }
707
+ subtract(a, b) { return a - b }
708
+ }
709
+ `
710
+ const ext = new JavaScriptExtractor('src/calc.js', src)
711
+ const classes = ext.extractClasses()
712
+ expect(classes[0].methods.length).toBe(2)
713
+ })
714
+ })
715
+
716
+ describe('Object Patterns', () => {
717
+ test('handles object literal functions', () => {
718
+ const src = `
719
+ function formatDate() { return 'date' }
720
+ function parseJSON() { return 'json' }
721
+ `
722
+ const ext = new JavaScriptExtractor('src/utils.js', src)
723
+ const fns = ext.extractFunctions()
724
+ expect(fns.length).toBe(2)
725
+ })
726
+
727
+ test('handles computed property names', () => {
728
+ const src = `
729
+ const obj = {
730
+ [key]: value,
731
+ ['computed' + 'Key']() {}
732
+ }
733
+ `
734
+ const ext = new JavaScriptExtractor('src/obj.js', src)
735
+ expect(() => ext.extractFunctions()).not.toThrow()
736
+ })
737
+ })
738
+
739
+ describe('Arrow Functions', () => {
740
+ test('handles various arrow function patterns', () => {
741
+ const src = `
742
+ const add = (a, b) => a + b
743
+ const greet = name => \`Hello \${name}\`
744
+ const promise = () => new Promise(resolve => resolve())
745
+ const multi = (a, b) => {
746
+ return a + b
747
+ }
748
+ `
749
+ const ext = new JavaScriptExtractor('src/arrow.js', src)
750
+ const fns = ext.extractFunctions()
751
+ expect(fns.length).toBe(4)
752
+ })
753
+ })
754
+
755
+ describe('Callback Patterns', () => {
756
+ test('handles callback functions', () => {
757
+ const src = `
758
+ items.forEach(item => console.log(item))
759
+ const filtered = items.filter(x => x > 0)
760
+ const mapped = items.map(x => x * 2)
761
+ const found = items.find(x => x.id === id)
762
+ `
763
+ const ext = new JavaScriptExtractor('src/callbacks.js', src)
764
+ const fns = ext.extractFunctions()
765
+ expect(fns.length).toBe(0) // callbacks aren't function declarations
766
+ })
767
+ })
768
+
769
+ describe('Error Handling in Functions', () => {
770
+ test('detects try-catch blocks', () => {
771
+ const src = `
772
+ function safeParse(json) {
773
+ try {
774
+ return JSON.parse(json)
775
+ } catch (e) {
776
+ console.error(e)
777
+ return null
778
+ }
779
+ }
780
+ `
781
+ const ext = new JavaScriptExtractor('src/safe.js', src)
782
+ const fns = ext.extractFunctions()
783
+ expect(fns[0].errorHandling.length).toBeGreaterThan(0)
784
+ })
785
+
786
+ test('detects throw statements', () => {
787
+ const src = `
788
+ function validate(value) {
789
+ if (!value) {
790
+ throw new Error('value required')
791
+ }
792
+ return true
793
+ }
794
+ `
795
+ const ext = new JavaScriptExtractor('src/validate.js', src)
796
+ const fns = ext.extractFunctions()
797
+ expect(fns[0].errorHandling.some(e => e.type === 'throw')).toBe(true)
798
+ })
799
+ })
800
+
801
+ describe('Complex Return Statements', () => {
802
+ test('handles early returns', () => {
803
+ const src = `
804
+ function findUser(id) {
805
+ if (!id) return null
806
+ const user = db.find(id)
807
+ if (!user) return null
808
+ return user
809
+ }
810
+ `
811
+ const ext = new JavaScriptExtractor('src/find.js', src)
812
+ const fns = ext.extractFunctions()
813
+ expect(fns[0].edgeCasesHandled.length).toBeGreaterThan(0)
814
+ })
815
+
816
+ test('handles conditional returns', () => {
817
+ const src = `
818
+ function getStatus(isActive) {
819
+ return isActive ? 'active' : 'inactive'
820
+ }
821
+ `
822
+ const ext = new JavaScriptExtractor('src/status.js', src)
823
+ const fns = ext.extractFunctions()
824
+ expect(fns.length).toBe(1)
825
+ })
826
+ })
827
+
828
+ describe('Async/Await', () => {
829
+ test('handles async arrow functions', () => {
830
+ const src = `
831
+ const fetchData = async (url) => {
832
+ const res = await fetch(url)
833
+ return res.json()
834
+ }
835
+ `
836
+ const ext = new JavaScriptExtractor('src/async.js', src)
837
+ const fns = ext.extractFunctions()
838
+ expect(fns[0].isAsync).toBe(true)
839
+ })
840
+
841
+ test('handles await without async wrapper', () => {
842
+ const src = `
843
+ async function main() {
844
+ await doSomething()
845
+ }
846
+ `
847
+ const ext = new JavaScriptExtractor('src/main.js', src)
848
+ const fns = ext.extractFunctions()
849
+ expect(fns[0].isAsync).toBe(true)
850
+ })
851
+
852
+ test('handles Promise.all', () => {
853
+ const src = `
854
+ async function loadAll(urls) {
855
+ return Promise.all(urls.map(fetch))
856
+ }
857
+ `
858
+ const ext = new JavaScriptExtractor('src/load.js', src)
859
+ const fns = ext.extractFunctions()
860
+ expect(fns[0].isAsync).toBe(true)
861
+ })
862
+ })
863
+
864
+ describe('Generator Functions', () => {
865
+ test('handles generator functions', () => {
866
+ const src = `
867
+ function* numberGenerator() {
868
+ yield 1
869
+ yield 2
870
+ yield 3
871
+ }
872
+ `
873
+ const ext = new JavaScriptExtractor('src/gen.js', src)
874
+ const fns = ext.extractFunctions()
875
+ expect(fns.length).toBe(1)
876
+ })
877
+ })
878
+
879
+ describe('Destructuring', () => {
880
+ test('handles destructured parameters', () => {
881
+ const src = `
882
+ function process({ name, age }, [first, ...rest]) {
883
+ return name + age + first
884
+ }
885
+ `
886
+ const ext = new JavaScriptExtractor('src/dest.js', src)
887
+ const fns = ext.extractFunctions()
888
+ expect(fns[0].params.length).toBe(2)
889
+ })
890
+ })
891
+
892
+ describe('Rest/Spread', () => {
893
+ test('handles rest parameters', () => {
894
+ const src = `
895
+ function sum(...numbers) {
896
+ return numbers.reduce((a, b) => a + b, 0)
897
+ }
898
+ `
899
+ const ext = new JavaScriptExtractor('src/rest.js', src)
900
+ const fns = ext.extractFunctions()
901
+ expect(fns[0].params[0].name).toBe('numbers')
902
+ })
903
+
904
+ test('handles spread in function calls', () => {
905
+ const src = `
906
+ function apply(...args) {
907
+ return fn(...args)
908
+ }
909
+ `
910
+ const ext = new JavaScriptExtractor('src/spread.js', src)
911
+ const fns = ext.extractFunctions()
912
+ expect(fns.length).toBe(1)
913
+ })
914
+ })
915
+
916
+ describe('Template Literals', () => {
917
+ test('handles template literals', () => {
918
+ const src = `
919
+ function greet(name) {
920
+ return \`Hello, \${name}!\`
921
+ }
922
+ `
923
+ const ext = new JavaScriptExtractor('src/tmpl.js', src)
924
+ const fns = ext.extractFunctions()
925
+ expect(fns.length).toBe(1)
926
+ })
927
+ })
928
+
929
+ describe('Regex Patterns', () => {
930
+ test('handles regex in code', () => {
931
+ const src = `
932
+ function validateEmail(email) {
933
+ const re = /^[a-zA-Z0-9@]+.[a-zA-Z0-9@]+$/
934
+ return re.test(email)
935
+ }
936
+ `
937
+ const ext = new JavaScriptExtractor('src/regex.js', src)
938
+ const fns = ext.extractFunctions()
939
+ expect(fns.length).toBe(1)
940
+ })
941
+ })
942
+
943
+ describe('Module Patterns', () => {
944
+ test('handles IIFE', () => {
945
+ const src = `
946
+ (function() {
947
+ const privateVar = 'secret'
948
+ window.init = function() {}
949
+ })()
950
+
951
+ (async () => {
952
+ await load()
953
+ })()
954
+ `
955
+ const ext = new JavaScriptExtractor('src/iife.js', src)
956
+ expect(() => ext.extractFunctions()).not.toThrow()
957
+ })
958
+
959
+ test('handles UMD pattern', () => {
960
+ const src = `
961
+ (function(root, factory) {
962
+ if (typeof module === 'object') {
963
+ module.exports = factory()
964
+ } else {
965
+ root.MyLib = factory()
966
+ }
967
+ }(this, function() {
968
+ return { version: '1.0' }
969
+ }))
970
+ `
971
+ const ext = new JavaScriptExtractor('src/umd.js', src)
972
+ expect(() => ext.extractFunctions()).not.toThrow()
973
+ })
974
+ })
975
+
976
+ describe('Complex Types', () => {
977
+ test('handles JSDoc comments', () => {
978
+ const src = `
979
+ /**
980
+ * Adds two numbers
981
+ * @param {number} a - First number
982
+ * @param {number} b - Second number
983
+ * @returns {number} Sum
984
+ */
985
+ function add(a, b) {
986
+ return a + b
987
+ }
988
+ `
989
+ const ext = new TypeScriptExtractor('src/add.js', src)
990
+ const fns = ext.extractFunctions()
991
+ expect(fns[0].purpose).toBeDefined()
992
+ })
993
+ })
994
+
995
+ describe('Decorator-like Patterns', () => {
996
+ test('handles higher-order functions', () => {
997
+ const src = `
998
+ function withLogging(fn) {
999
+ return function(...args) {
1000
+ console.log('Calling', fn.name)
1001
+ return fn.apply(this, args)
1002
+ }
1003
+ }
1004
+
1005
+ @withLogging
1006
+ function decorated() {}
1007
+ `
1008
+ const ext = new JavaScriptExtractor('src/decorator.js', src)
1009
+ const fns = ext.extractFunctions()
1010
+ expect(fns.length).toBe(2)
1011
+ })
1012
+ })
1013
+
1014
+ describe('Web/Node APIs', () => {
1015
+ test('handles fetch API', async () => {
1016
+ const src = `
1017
+ async function fetchData(url) {
1018
+ const response = await fetch(url)
1019
+ const data = await response.json()
1020
+ return data
1021
+ }
1022
+ `
1023
+ const ext = new JavaScriptExtractor('src/fetch.js', src)
1024
+ const fns = ext.extractFunctions()
1025
+ expect(fns[0].isAsync).toBe(true)
1026
+ expect(fns[0].calls.length).toBe(2)
1027
+ })
1028
+
1029
+ test('handles Express routers', () => {
1030
+ const src = `
1031
+ const express = require('express')
1032
+ const router = express.Router()
1033
+
1034
+ router.get('/users', getUsers)
1035
+ router.post('/users', createUser)
1036
+ router.put('/users/:id', updateUser)
1037
+ router.delete('/users/:id', deleteUser)
1038
+ `
1039
+ const ext = new JavaScriptExtractor('src/routes.js', src)
1040
+ const routes = ext.extractRoutes()
1041
+ expect(routes.length).toBe(4)
1042
+ })
1043
+ })
1044
+
1045
+ describe('Chained Methods', () => {
1046
+ test('handles method chaining', () => {
1047
+ const src = `
1048
+ const result = items
1049
+ .filter(x => x.active)
1050
+ .map(x => x.value)
1051
+ .reduce((a, b) => a + b, 0)
1052
+ `
1053
+ const ext = new JavaScriptExtractor('src/chain.js', src)
1054
+ expect(() => ext.extractFunctions()).not.toThrow()
1055
+ })
1056
+ })
1057
+
1058
+ describe('Large Files', () => {
1059
+ test('handles many functions', () => {
1060
+ const fns = Array.from({ length: 500 }, (_, i) =>
1061
+ `function fn${i}() { return ${i} }`
1062
+ ).join('\n')
1063
+
1064
+ const ext = new JavaScriptExtractor('src/many.js', fns)
1065
+ const result = ext.extractFunctions()
1066
+ expect(result.length).toBe(500)
1067
+ })
1068
+ })
1069
+
1070
+ describe('Unicode and Special Chars', () => {
1071
+ test('handles unicode in function names', () => {
1072
+ const src = `
1073
+ function 验证() {
1074
+ return true
1075
+ }
1076
+
1077
+ const 用户 = { name: 'test' }
1078
+ `
1079
+ const ext = new JavaScriptExtractor('src/unicode.js', src)
1080
+ const fns = ext.extractFunctions()
1081
+ expect(fns.length).toBe(1)
1082
+ })
1083
+
1084
+ test('handles emoji', () => {
1085
+ const src = `
1086
+ function 🎉() {
1087
+ return 'celebration'
1088
+ }
1089
+ `
1090
+ const ext = new JavaScriptExtractor('src/emoji.js', src)
1091
+ const fns = ext.extractFunctions()
1092
+ expect(fns.length).toBe(1)
1093
+ })
1094
+ })
1095
+ })