@getmikk/core 1.8.3 → 1.9.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/package.json +3 -1
- package/src/constants.ts +285 -0
- package/src/contract/contract-generator.ts +7 -0
- package/src/contract/index.ts +2 -3
- package/src/contract/lock-compiler.ts +66 -35
- package/src/contract/lock-reader.ts +24 -4
- package/src/contract/schema.ts +21 -0
- package/src/error-handler.ts +430 -0
- package/src/graph/cluster-detector.ts +45 -20
- package/src/graph/confidence-engine.ts +60 -0
- package/src/graph/graph-builder.ts +298 -255
- package/src/graph/impact-analyzer.ts +130 -119
- package/src/graph/index.ts +4 -0
- package/src/graph/memory-manager.ts +345 -0
- package/src/graph/query-engine.ts +79 -0
- package/src/graph/risk-engine.ts +86 -0
- package/src/graph/types.ts +89 -65
- package/src/parser/change-detector.ts +99 -0
- package/src/parser/go/go-extractor.ts +18 -8
- package/src/parser/go/go-parser.ts +2 -0
- package/src/parser/index.ts +88 -38
- package/src/parser/javascript/js-extractor.ts +1 -1
- package/src/parser/javascript/js-parser.ts +2 -0
- package/src/parser/oxc-parser.ts +675 -0
- package/src/parser/oxc-resolver.ts +83 -0
- package/src/parser/tree-sitter/parser.ts +19 -10
- package/src/parser/types.ts +100 -73
- package/src/parser/typescript/ts-extractor.ts +229 -589
- package/src/parser/typescript/ts-parser.ts +16 -171
- package/src/parser/typescript/ts-resolver.ts +11 -1
- package/src/search/bm25.ts +5 -2
- package/src/utils/minimatch.ts +1 -1
- package/tests/contract.test.ts +2 -2
- package/tests/dead-code.test.ts +7 -7
- package/tests/esm-resolver.test.ts +75 -0
- package/tests/graph.test.ts +20 -20
- package/tests/helpers.ts +11 -6
- package/tests/impact-classified.test.ts +37 -41
- package/tests/parser.test.ts +7 -5
- package/tests/ts-parser.test.ts +27 -52
- package/test-output.txt +0 -373
package/src/parser/index.ts
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as nodePath from 'node:path'
|
|
2
2
|
import { BaseParser } from './base-parser.js'
|
|
3
|
-
import {
|
|
3
|
+
import { OxcParser } from './oxc-parser.js'
|
|
4
4
|
import { GoParser } from './go/go-parser.js'
|
|
5
|
-
import { JavaScriptParser } from './javascript/js-parser.js'
|
|
6
5
|
import { UnsupportedLanguageError } from '../utils/errors.js'
|
|
7
6
|
import type { ParsedFile } from './types.js'
|
|
8
7
|
|
|
9
|
-
export type {
|
|
8
|
+
export type {
|
|
9
|
+
ParsedFile,
|
|
10
|
+
ParsedFunction,
|
|
11
|
+
ParsedImport,
|
|
12
|
+
ParsedExport,
|
|
13
|
+
ParsedClass,
|
|
14
|
+
ParsedParam,
|
|
15
|
+
ParsedVariable,
|
|
16
|
+
CallExpression,
|
|
17
|
+
ParsedGeneric,
|
|
18
|
+
ParsedRoute
|
|
19
|
+
} from './types.js'
|
|
10
20
|
export { BaseParser } from './base-parser.js'
|
|
11
21
|
export { TypeScriptParser } from './typescript/ts-parser.js'
|
|
12
22
|
export { TypeScriptExtractor } from './typescript/ts-extractor.js'
|
|
@@ -19,22 +29,20 @@ export { JavaScriptExtractor } from './javascript/js-extractor.js'
|
|
|
19
29
|
export { JavaScriptResolver } from './javascript/js-resolver.js'
|
|
20
30
|
export { BoundaryChecker } from './boundary-checker.js'
|
|
21
31
|
export { TreeSitterParser } from './tree-sitter/parser.js'
|
|
22
|
-
import { TreeSitterParser } from './tree-sitter/parser.js'
|
|
23
32
|
|
|
24
33
|
/** Get the appropriate parser for a file based on its extension */
|
|
25
34
|
export function getParser(filePath: string): BaseParser {
|
|
26
|
-
const ext =
|
|
35
|
+
const ext = nodePath.extname(filePath).toLowerCase()
|
|
27
36
|
switch (ext) {
|
|
28
37
|
case '.ts':
|
|
29
38
|
case '.tsx':
|
|
30
|
-
return new TypeScriptParser()
|
|
31
39
|
case '.js':
|
|
32
40
|
case '.mjs':
|
|
33
41
|
case '.cjs':
|
|
34
42
|
case '.jsx':
|
|
35
|
-
return new
|
|
43
|
+
return new OxcParser()
|
|
36
44
|
case '.go':
|
|
37
|
-
return new GoParser()
|
|
45
|
+
return new GoParser()
|
|
38
46
|
case '.py':
|
|
39
47
|
case '.java':
|
|
40
48
|
case '.c':
|
|
@@ -46,55 +54,97 @@ export function getParser(filePath: string): BaseParser {
|
|
|
46
54
|
case '.rs':
|
|
47
55
|
case '.php':
|
|
48
56
|
case '.rb':
|
|
49
|
-
|
|
57
|
+
throw new UnsupportedLanguageError(ext)
|
|
50
58
|
default:
|
|
51
59
|
throw new UnsupportedLanguageError(ext)
|
|
52
60
|
}
|
|
53
61
|
}
|
|
54
62
|
|
|
55
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Parse multiple files, resolve their imports, and return ParsedFile[].
|
|
65
|
+
*
|
|
66
|
+
* Path contract (critical for graph correctness):
|
|
67
|
+
* - filePaths come from discoverFiles() as project-root-relative strings
|
|
68
|
+
* - We resolve them to ABSOLUTE posix paths before passing to parse()
|
|
69
|
+
* - ParsedFile.path is therefore always absolute + forward-slash
|
|
70
|
+
* - OxcResolver also returns absolute paths → import edges always consistent
|
|
71
|
+
*/
|
|
56
72
|
export async function parseFiles(
|
|
57
73
|
filePaths: string[],
|
|
58
74
|
projectRoot: string,
|
|
59
75
|
readFile: (fp: string) => Promise<string>
|
|
60
76
|
): Promise<ParsedFile[]> {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const tsParser = new TypeScriptParser()
|
|
64
|
-
const jsParser = new JavaScriptParser()
|
|
77
|
+
// Shared parser instances — avoid re-initialisation overhead per file
|
|
78
|
+
const oxcParser = new OxcParser()
|
|
65
79
|
const goParser = new GoParser()
|
|
66
|
-
const treeSitterParser = new TreeSitterParser()
|
|
67
80
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
default: return null
|
|
81
|
+
// Lazily loaded to avoid mandatory dep on tree-sitter
|
|
82
|
+
let treeSitterParser: BaseParser | null = null
|
|
83
|
+
const getTreeSitter = async (): Promise<BaseParser> => {
|
|
84
|
+
if (!treeSitterParser) {
|
|
85
|
+
const { TreeSitterParser } = await import('./tree-sitter/parser.js')
|
|
86
|
+
treeSitterParser = new TreeSitterParser()
|
|
75
87
|
}
|
|
88
|
+
return treeSitterParser!
|
|
76
89
|
}
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
91
|
+
const tsExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'])
|
|
92
|
+
const goExtensions = new Set(['.go'])
|
|
93
|
+
const treeSitterExtensions = new Set(['.py', '.java', '.c', '.h', '.cpp', '.cc', '.hpp', '.cs', '.rs', '.php', '.rb'])
|
|
94
|
+
|
|
95
|
+
// Normalised project root for absolute path construction
|
|
96
|
+
const normalizedRoot = nodePath.resolve(projectRoot).replace(/\\/g, '/')
|
|
97
|
+
|
|
98
|
+
// Group by parser to enable batch resolveImports
|
|
99
|
+
const oxcFiles: ParsedFile[] = []
|
|
100
|
+
const goFiles: ParsedFile[] = []
|
|
101
|
+
const treeFiles: ParsedFile[] = []
|
|
82
102
|
|
|
103
|
+
const parsePromises = filePaths.map(async (fp) => {
|
|
104
|
+
const ext = nodePath.extname(fp).toLowerCase()
|
|
105
|
+
|
|
106
|
+
// Build absolute posix path — this is the single source of truth for all IDs
|
|
107
|
+
const absoluteFp = nodePath.resolve(normalizedRoot, fp).replace(/\\/g, '/')
|
|
108
|
+
|
|
109
|
+
let content: string
|
|
83
110
|
try {
|
|
84
|
-
|
|
85
|
-
const parsed = await parser.parse(fp, content)
|
|
86
|
-
|
|
87
|
-
if (!parsersMap.has(parser)) parsersMap.set(parser, [])
|
|
88
|
-
parsersMap.get(parser)!.push(parsed)
|
|
111
|
+
content = await readFile(absoluteFp)
|
|
89
112
|
} catch {
|
|
90
|
-
//
|
|
113
|
+
// File unreadable — skip silently (deleted, permission error, binary)
|
|
114
|
+
return
|
|
91
115
|
}
|
|
92
|
-
}
|
|
93
116
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
117
|
+
try {
|
|
118
|
+
if (tsExtensions.has(ext)) {
|
|
119
|
+
const parsed = await oxcParser.parse(absoluteFp, content)
|
|
120
|
+
oxcFiles.push(parsed)
|
|
121
|
+
} else if (goExtensions.has(ext)) {
|
|
122
|
+
const parsed = await goParser.parse(absoluteFp, content)
|
|
123
|
+
goFiles.push(parsed)
|
|
124
|
+
} else if (treeSitterExtensions.has(ext)) {
|
|
125
|
+
const ts = await getTreeSitter()
|
|
126
|
+
const parsed = await ts.parse(absoluteFp, content)
|
|
127
|
+
treeFiles.push(parsed)
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
// Parser error — skip this file, don't abort the whole run
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
await Promise.all(parsePromises)
|
|
135
|
+
|
|
136
|
+
// Resolve imports batch-wise per parser (each has its own resolver)
|
|
137
|
+
let resolvedTreeFiles: ParsedFile[] = treeFiles
|
|
138
|
+
if (treeFiles.length > 0) {
|
|
139
|
+
const treeParser = treeSitterParser ?? await getTreeSitter()
|
|
140
|
+
resolvedTreeFiles = treeParser.resolveImports(treeFiles, normalizedRoot)
|
|
97
141
|
}
|
|
98
142
|
|
|
99
|
-
|
|
143
|
+
const resolved: ParsedFile[] = [
|
|
144
|
+
...oxcParser.resolveImports(oxcFiles, normalizedRoot),
|
|
145
|
+
...goParser.resolveImports(goFiles, normalizedRoot),
|
|
146
|
+
...resolvedTreeFiles,
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
return resolved
|
|
100
150
|
}
|
|
@@ -231,7 +231,7 @@ export class JavaScriptExtractor extends TypeScriptExtractor {
|
|
|
231
231
|
returnType: rhs.type ? rhs.type.getText(this.sourceFile) : 'void',
|
|
232
232
|
isExported: true,
|
|
233
233
|
isAsync,
|
|
234
|
-
calls: this.
|
|
234
|
+
calls: this.extractCallsFromNode(rhs),
|
|
235
235
|
hash: hashContent(rhs.getText(this.sourceFile)),
|
|
236
236
|
purpose: this.extractPurpose(node),
|
|
237
237
|
edgeCasesHandled: this.extractEdgeCases(rhs),
|