@getmikk/core 2.0.11 → 2.0.13

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.
@@ -4,6 +4,14 @@ import { OxcParser } from './oxc-parser.js'
4
4
  import { GoParser } from './go/go-parser.js'
5
5
  import { UnsupportedLanguageError } from '../utils/errors.js'
6
6
  import type { ParsedFile } from './types.js'
7
+ import { hashContent } from '../hash/file-hasher.js'
8
+ import {
9
+ parserKindForExtension,
10
+ languageForExtension,
11
+ getParserExtensions,
12
+ isTreeSitterExtension,
13
+ type ParserKind,
14
+ } from '../utils/language-registry.js'
7
15
 
8
16
  export type {
9
17
  ParsedFile,
@@ -30,42 +38,93 @@ export { JavaScriptResolver } from './javascript/js-resolver.js'
30
38
  export { BoundaryChecker } from './boundary-checker.js'
31
39
  export { TreeSitterParser } from './tree-sitter/parser.js'
32
40
 
41
+ export type ParseDiagnosticStage = 'read' | 'parse' | 'resolve-imports'
42
+ export type ParseDiagnosticReason =
43
+ | 'read-error'
44
+ | 'parse-error'
45
+ | 'resolve-error'
46
+ | 'unsupported-extension'
47
+ | 'parser-unavailable'
48
+
49
+ export interface ParseDiagnostic {
50
+ filePath: string
51
+ extension: string
52
+ parser: ParserKind
53
+ stage: ParseDiagnosticStage
54
+ reason: ParseDiagnosticReason
55
+ message: string
56
+ }
57
+
58
+ export interface ParseFilesSummary {
59
+ requestedFiles: number
60
+ parsedFiles: number
61
+ fallbackFiles: number
62
+ unreadableFiles: number
63
+ unsupportedFiles: number
64
+ diagnostics: number
65
+ }
66
+
67
+ export interface ParseFilesResult {
68
+ files: ParsedFile[]
69
+ diagnostics: ParseDiagnostic[]
70
+ summary: ParseFilesSummary
71
+ }
72
+
73
+ const isLikelyParserUnavailable = (parser: ParserKind, message: string): boolean => {
74
+ if (parser !== 'tree-sitter') return false
75
+ const normalized = message.toLowerCase()
76
+ return normalized.includes('web-tree-sitter') ||
77
+ normalized.includes('tree-sitter') ||
78
+ normalized.includes('cannot find module')
79
+ }
80
+
81
+
82
+ const buildFallbackParsedFile = (filePath: string, content: string, ext: string): ParsedFile => ({
83
+ path: filePath,
84
+ language: languageForExtension(ext) as ParsedFile['language'],
85
+ functions: [],
86
+ classes: [],
87
+ generics: [],
88
+ imports: [],
89
+ exports: [],
90
+ routes: [],
91
+ variables: [],
92
+ calls: [],
93
+ hash: hashContent(content),
94
+ parsedAt: Date.now(),
95
+ })
96
+
97
+ const normalizeErrorMessage = (err: unknown): string => {
98
+ if (!err) return 'Unknown error'
99
+ if (err instanceof Error) return err.message
100
+ return String(err)
101
+ }
102
+
33
103
  /** Get the appropriate parser for a file based on its extension */
34
104
  export function getParser(filePath: string): BaseParser {
35
105
  const ext = nodePath.extname(filePath).toLowerCase()
36
- switch (ext) {
37
- case '.ts':
38
- case '.tsx':
39
- case '.js':
40
- case '.mjs':
41
- case '.cjs':
42
- case '.jsx':
106
+ const parserKind = parserKindForExtension(ext)
107
+
108
+ switch (parserKind) {
109
+ case 'oxc':
43
110
  return new OxcParser()
44
- case '.go':
111
+ case 'go':
45
112
  return new GoParser()
46
- case '.py':
47
- case '.java':
48
- case '.c':
49
- case '.h':
50
- case '.cpp':
51
- case '.cc':
52
- case '.hpp':
53
- case '.cs':
54
- case '.rs':
55
- case '.php':
56
- case '.rb':
57
- // Tree-sitter parser - dynamically imported to handle missing web-tree-sitter
113
+ case 'tree-sitter':
58
114
  return createTreeSitterParser()
59
115
  default:
60
- throw new UnsupportedLanguageError(ext)
116
+ throw new UnsupportedLanguageError(ext || '<no extension>')
61
117
  }
62
118
  }
63
119
 
64
- const _treeSitterParserInstance: BaseParser | null = null
120
+ let _treeSitterParserInstance: BaseParser | null = null
65
121
 
66
122
  const createTreeSitterParser = (): BaseParser => {
67
- // Return a lazy-loading wrapper that handles missing tree-sitter gracefully
68
- return new LazyTreeSitterParser()
123
+ if (!_treeSitterParserInstance) {
124
+ // Return a lazy-loading wrapper that handles missing tree-sitter gracefully.
125
+ _treeSitterParserInstance = new LazyTreeSitterParser()
126
+ }
127
+ return _treeSitterParserInstance
69
128
  }
70
129
 
71
130
  class LazyTreeSitterParser extends BaseParser {
@@ -96,26 +155,15 @@ class LazyTreeSitterParser extends BaseParser {
96
155
  }
97
156
 
98
157
  getSupportedExtensions(): string[] {
99
- return ['.py', '.java', '.c', '.h', '.cpp', '.cc', '.hpp', '.cs', '.rs', '.php', '.rb']
158
+ return [...getParserExtensions('tree-sitter')]
100
159
  }
101
160
 
102
161
  private buildEmptyFile(filePath: string, content: string): ParsedFile {
103
162
  const ext = nodePath.extname(filePath).toLowerCase()
104
- let lang: ParsedFile['language'] = 'unknown'
105
- switch (ext) {
106
- case '.py': lang = 'python'; break
107
- case '.java': lang = 'java'; break
108
- case '.c': case '.h': lang = 'c'; break
109
- case '.cpp': case '.cc': case '.hpp': lang = 'cpp'; break
110
- case '.cs': lang = 'csharp'; break
111
- case '.go': lang = 'go'; break
112
- case '.rs': lang = 'rust'; break
113
- case '.php': lang = 'php'; break
114
- case '.rb': lang = 'ruby'; break
115
- }
163
+ const lang = languageForExtension(ext)
116
164
  return {
117
165
  path: filePath,
118
- language: lang,
166
+ language: lang as ParsedFile['language'],
119
167
  functions: [],
120
168
  classes: [],
121
169
  generics: [],
@@ -124,97 +172,276 @@ class LazyTreeSitterParser extends BaseParser {
124
172
  routes: [],
125
173
  variables: [],
126
174
  calls: [],
127
- hash: '',
175
+ hash: hashContent(content),
128
176
  parsedAt: Date.now(),
129
177
  }
130
178
  }
131
179
  }
132
180
 
133
- /**
134
- * Parse multiple files, resolve their imports, and return ParsedFile[].
135
- *
136
- * Path contract (critical for graph correctness):
137
- * - filePaths come from discoverFiles() as project-root-relative strings
138
- * - We resolve them to ABSOLUTE posix paths before passing to parse()
139
- * - ParsedFile.path is therefore always absolute + forward-slash
140
- * - OxcResolver also returns absolute paths → import edges always consistent
141
- */
142
- export async function parseFiles(
181
+ export interface ParseFilesOptions {
182
+ strictParserPreflight?: boolean
183
+ treeSitterRuntimeAvailable?: boolean
184
+ }
185
+
186
+ async function isTreeSitterRuntimeAvailable(): Promise<boolean> {
187
+ try {
188
+ const { TreeSitterParser } = await import('./tree-sitter/parser.js')
189
+ const parser = new TreeSitterParser()
190
+ if (typeof (parser as any).isRuntimeAvailable !== 'function') {
191
+ return true
192
+ }
193
+ return await (parser as any).isRuntimeAvailable()
194
+ } catch {
195
+ return false
196
+ }
197
+ }
198
+
199
+ export async function parseFilesWithDiagnostics(
143
200
  filePaths: string[],
144
201
  projectRoot: string,
145
- readFile: (fp: string) => Promise<string>
146
- ): Promise<ParsedFile[]> {
147
- // Shared parser instances — avoid re-initialisation overhead per file
202
+ readFile: (fp: string) => Promise<string>,
203
+ options: ParseFilesOptions = {},
204
+ ): Promise<ParseFilesResult> {
205
+ // Shared parser instances — avoid re-initialisation overhead per file.
148
206
  const oxcParser = new OxcParser()
149
207
  const goParser = new GoParser()
150
208
 
151
- // Lazily loaded to avoid mandatory dep on tree-sitter
209
+ // Lazily loaded to avoid mandatory dependency on tree-sitter for TS/JS-only projects.
152
210
  let treeSitterParser: BaseParser | null = null
153
211
  const getTreeSitter = async (): Promise<BaseParser> => {
154
212
  if (!treeSitterParser) {
155
213
  const { TreeSitterParser } = await import('./tree-sitter/parser.js')
156
214
  treeSitterParser = new TreeSitterParser()
157
215
  }
158
- return treeSitterParser!
216
+ return treeSitterParser
159
217
  }
160
218
 
161
- const tsExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'])
162
- const goExtensions = new Set(['.go'])
163
- const treeSitterExtensions = new Set(['.py', '.java', '.c', '.h', '.cpp', '.cc', '.hpp', '.cs', '.rs', '.php', '.rb'])
219
+ const diagnostics: ParseDiagnostic[] = []
220
+ const addDiagnostic = (diagnostic: ParseDiagnostic) => diagnostics.push(diagnostic)
221
+
222
+ const treeSitterNeeded = filePaths.some(fp => {
223
+ const ext = nodePath.extname(fp).toLowerCase()
224
+ return isTreeSitterExtension(ext)
225
+ })
226
+
227
+ let treeSitterAvailable = true
228
+ if (treeSitterNeeded) {
229
+ treeSitterAvailable =
230
+ typeof options.treeSitterRuntimeAvailable === 'boolean'
231
+ ? options.treeSitterRuntimeAvailable
232
+ : await isTreeSitterRuntimeAvailable()
233
+ if (!treeSitterAvailable) {
234
+ addDiagnostic({
235
+ filePath: '*',
236
+ extension: '*',
237
+ parser: 'tree-sitter',
238
+ stage: 'parse',
239
+ reason: 'parser-unavailable',
240
+ message: 'Tree-sitter runtime unavailable. Install web-tree-sitter and language grammars.',
241
+ })
242
+ if (options.strictParserPreflight) {
243
+ return {
244
+ files: [],
245
+ diagnostics,
246
+ summary: {
247
+ requestedFiles: filePaths.length,
248
+ parsedFiles: 0,
249
+ fallbackFiles: 0,
250
+ unreadableFiles: 0,
251
+ unsupportedFiles: 0,
252
+ diagnostics: diagnostics.length,
253
+ },
254
+ }
255
+ }
256
+ }
257
+ }
164
258
 
165
- // Normalised project root for absolute path construction
259
+ // Normalized project root for absolute path construction.
166
260
  const normalizedRoot = nodePath.resolve(projectRoot).replace(/\\/g, '/')
167
261
 
168
- // Group by parser to enable batch resolveImports
262
+ // Group by parser to enable batch resolveImports.
169
263
  const oxcFiles: ParsedFile[] = []
170
264
  const goFiles: ParsedFile[] = []
171
265
  const treeFiles: ParsedFile[] = []
266
+ const fallbackFiles: ParsedFile[] = []
267
+
268
+ let parsedFilesCount = 0
269
+ let fallbackFilesCount = 0
270
+ let unreadableFiles = 0
271
+ let unsupportedFiles = 0
172
272
 
173
273
  // Parse sequentially to avoid races in parser implementations that keep
174
274
  // mutable per-instance state (e.g. language switching/counters).
175
275
  for (const fp of filePaths) {
176
276
  const ext = nodePath.extname(fp).toLowerCase()
277
+ const parserKind = parserKindForExtension(ext)
177
278
 
178
- // Build absolute posix path — this is the single source of truth for all IDs
279
+ // Build absolute posix path — this is the single source of truth for all IDs.
179
280
  const absoluteFp = nodePath.resolve(normalizedRoot, fp).replace(/\\/g, '/')
180
281
 
181
282
  let content: string
182
283
  try {
183
284
  content = await readFile(absoluteFp)
184
- } catch {
185
- // File unreadable — skip silently (deleted, permission error, binary)
285
+ } catch (err: unknown) {
286
+ unreadableFiles += 1
287
+ addDiagnostic({
288
+ filePath: absoluteFp,
289
+ extension: ext,
290
+ parser: parserKind,
291
+ stage: 'read',
292
+ reason: 'read-error',
293
+ message: normalizeErrorMessage(err),
294
+ })
295
+ continue
296
+ }
297
+
298
+ if (parserKind === 'unknown') {
299
+ unsupportedFiles += 1
300
+ fallbackFilesCount += 1
301
+ fallbackFiles.push(buildFallbackParsedFile(absoluteFp, content, ext))
302
+ addDiagnostic({
303
+ filePath: absoluteFp,
304
+ extension: ext,
305
+ parser: parserKind,
306
+ stage: 'parse',
307
+ reason: 'unsupported-extension',
308
+ message: `Unsupported extension: ${ext || '<none>'}`,
309
+ })
186
310
  continue
187
311
  }
188
312
 
189
313
  try {
190
- if (tsExtensions.has(ext)) {
314
+ if (parserKind === 'oxc') {
191
315
  const parsed = await oxcParser.parse(absoluteFp, content)
192
316
  oxcFiles.push(parsed)
193
- } else if (goExtensions.has(ext)) {
317
+ parsedFilesCount += 1
318
+ } else if (parserKind === 'go') {
194
319
  const parsed = await goParser.parse(absoluteFp, content)
195
320
  goFiles.push(parsed)
196
- } else if (treeSitterExtensions.has(ext)) {
321
+ parsedFilesCount += 1
322
+ } else {
323
+ if (!treeSitterAvailable) {
324
+ fallbackFilesCount += 1
325
+ fallbackFiles.push(buildFallbackParsedFile(absoluteFp, content, ext))
326
+ addDiagnostic({
327
+ filePath: absoluteFp,
328
+ extension: ext,
329
+ parser: 'tree-sitter',
330
+ stage: 'parse',
331
+ reason: 'parser-unavailable',
332
+ message: 'Tree-sitter runtime unavailable. Falling back to empty parsed file.',
333
+ })
334
+ continue
335
+ }
197
336
  const ts = await getTreeSitter()
198
337
  const parsed = await ts.parse(absoluteFp, content)
199
338
  treeFiles.push(parsed)
339
+ parsedFilesCount += 1
200
340
  }
201
- } catch {
202
- // Parser error — skip this file, don't abort the whole run
341
+ } catch (err: unknown) {
342
+ fallbackFilesCount += 1
343
+ const message = normalizeErrorMessage(err)
344
+ const reason: ParseDiagnosticReason = isLikelyParserUnavailable(parserKind, message)
345
+ ? 'parser-unavailable'
346
+ : 'parse-error'
347
+
348
+ fallbackFiles.push(buildFallbackParsedFile(absoluteFp, content, ext))
349
+ addDiagnostic({
350
+ filePath: absoluteFp,
351
+ extension: ext,
352
+ parser: parserKind,
353
+ stage: 'parse',
354
+ reason,
355
+ message,
356
+ })
203
357
  }
204
358
  }
205
359
 
206
- // Resolve imports batch-wise per parser (each has its own resolver)
207
- let resolvedTreeFiles: ParsedFile[] = treeFiles
360
+ // Resolve imports batch-wise per parser (each has its own resolver).
361
+ let resolvedOxcFiles = oxcFiles
362
+ if (oxcFiles.length > 0) {
363
+ try {
364
+ resolvedOxcFiles = await oxcParser.resolveImports(oxcFiles, normalizedRoot)
365
+ } catch (err: unknown) {
366
+ addDiagnostic({
367
+ filePath: '*',
368
+ extension: '*',
369
+ parser: 'oxc',
370
+ stage: 'resolve-imports',
371
+ reason: 'resolve-error',
372
+ message: normalizeErrorMessage(err),
373
+ })
374
+ }
375
+ }
376
+
377
+ let resolvedGoFiles = goFiles
378
+ if (goFiles.length > 0) {
379
+ try {
380
+ resolvedGoFiles = await goParser.resolveImports(goFiles, normalizedRoot)
381
+ } catch (err: unknown) {
382
+ addDiagnostic({
383
+ filePath: '*',
384
+ extension: '*',
385
+ parser: 'go',
386
+ stage: 'resolve-imports',
387
+ reason: 'resolve-error',
388
+ message: normalizeErrorMessage(err),
389
+ })
390
+ }
391
+ }
392
+
393
+ let resolvedTreeFiles = treeFiles
208
394
  if (treeFiles.length > 0) {
209
- const treeParser = treeSitterParser ?? await getTreeSitter()
210
- resolvedTreeFiles = await treeParser.resolveImports(treeFiles, normalizedRoot)
395
+ try {
396
+ const treeParser = treeSitterParser ?? await getTreeSitter()
397
+ resolvedTreeFiles = await treeParser.resolveImports(treeFiles, normalizedRoot)
398
+ } catch (err: unknown) {
399
+ addDiagnostic({
400
+ filePath: '*',
401
+ extension: '*',
402
+ parser: 'tree-sitter',
403
+ stage: 'resolve-imports',
404
+ reason: 'resolve-error',
405
+ message: normalizeErrorMessage(err),
406
+ })
407
+ }
211
408
  }
212
409
 
213
410
  const resolved: ParsedFile[] = [
214
- ...await oxcParser.resolveImports(oxcFiles, normalizedRoot),
215
- ...await goParser.resolveImports(goFiles, normalizedRoot),
411
+ ...resolvedOxcFiles,
412
+ ...resolvedGoFiles,
216
413
  ...resolvedTreeFiles,
414
+ ...fallbackFiles,
217
415
  ]
218
416
 
219
- return resolved
417
+ return {
418
+ files: resolved,
419
+ diagnostics,
420
+ summary: {
421
+ requestedFiles: filePaths.length,
422
+ parsedFiles: parsedFilesCount,
423
+ fallbackFiles: fallbackFilesCount,
424
+ unreadableFiles,
425
+ unsupportedFiles,
426
+ diagnostics: diagnostics.length,
427
+ },
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Parse multiple files, resolve their imports, and return ParsedFile[].
433
+ *
434
+ * Path contract (critical for graph correctness):
435
+ * - filePaths come from discoverFiles() as project-root-relative strings
436
+ * - We resolve them to ABSOLUTE posix paths before passing to parse()
437
+ * - ParsedFile.path is therefore always absolute + forward-slash
438
+ * - OxcResolver also returns absolute paths → import edges always consistent
439
+ */
440
+ export async function parseFiles(
441
+ filePaths: string[],
442
+ projectRoot: string,
443
+ readFile: (fp: string) => Promise<string>
444
+ ): Promise<ParsedFile[]> {
445
+ const result = await parseFilesWithDiagnostics(filePaths, projectRoot, readFile)
446
+ return result.files
220
447
  }
@@ -219,12 +219,13 @@ function extractCalls(node: any, lineIndex: LineIndex): CallExpression[] {
219
219
  const walk = (n: any): void => {
220
220
  if (!n || typeof n !== 'object') return;
221
221
 
222
- if (n.type === 'CallExpression' && n.span) {
222
+ if (n.type === 'CallExpression') {
223
223
  const { name, type } = resolveCallIdentity(n.callee);
224
224
  if (name) {
225
+ const span = getSpan(n);
225
226
  calls.push({
226
227
  name,
227
- line: lineIndex.getLine(n.span.start),
228
+ line: lineIndex.getLine(span.start),
228
229
  type,
229
230
  });
230
231
  }
@@ -36,6 +36,11 @@ function isExportedByLanguage(ext: string, name: string, nodeText: string): bool
36
36
  return !name.startsWith('_')
37
37
  case '.java':
38
38
  return /\bpublic\b/.test(nodeText)
39
+ case '.kt':
40
+ case '.kts':
41
+ return !/\bprivate\b/.test(nodeText) && !/\binternal\b/.test(nodeText) && !/\bprotected\b/.test(nodeText)
42
+ case '.swift':
43
+ return !/\bprivate\b/.test(nodeText) && !/\bfileprivate\b/.test(nodeText)
39
44
  case '.cs':
40
45
  return /\bpublic\b/.test(nodeText) && !/\binternal\b/.test(nodeText)
41
46
  case '.go':
@@ -181,7 +186,7 @@ export class TreeSitterParser extends BaseParser {
181
186
  private wasmLoadError = false
182
187
 
183
188
  getSupportedExtensions(): string[] {
184
- return ['.py', '.java', '.c', '.cpp', '.cc', '.h', '.hpp', '.cs', '.go', '.rs', '.php', '.rb']
189
+ return ['.py', '.java', '.kt', '.kts', '.swift', '.c', '.cpp', '.cc', '.cxx', '.h', '.hpp', '.hxx', '.hh', '.cs', '.go', '.rs', '.php', '.rb']
185
190
  }
186
191
 
187
192
  private async init() {
@@ -193,6 +198,11 @@ export class TreeSitterParser extends BaseParser {
193
198
  }
194
199
  }
195
200
 
201
+ async isRuntimeAvailable(): Promise<boolean> {
202
+ await this.init()
203
+ return Boolean(this.parser)
204
+ }
205
+
196
206
  async parse(filePath: string, content: string): Promise<ParsedFile> {
197
207
  this.nameCounter.clear()
198
208
  await this.init()
@@ -591,12 +601,19 @@ export class TreeSitterParser extends BaseParser {
591
601
  return { lang: await this.loadLang('python'), query: Queries.PYTHON_QUERIES }
592
602
  case '.java':
593
603
  return { lang: await this.loadLang('java'), query: Queries.JAVA_QUERIES }
604
+ case '.kt':
605
+ case '.kts':
606
+ return { lang: await this.loadLang('kotlin'), query: Queries.KOTLIN_QUERIES }
607
+ case '.swift':
608
+ return { lang: await this.loadLang('swift'), query: Queries.SWIFT_QUERIES }
594
609
  case '.c':
595
610
  case '.h':
596
611
  return { lang: await this.loadLang('c'), query: Queries.C_QUERIES }
597
612
  case '.cpp':
598
613
  case '.cc':
614
+ case '.cxx':
599
615
  case '.hpp':
616
+ case '.hxx':
600
617
  case '.hh':
601
618
  return { lang: await this.loadLang('cpp'), query: Queries.CPP_QUERIES }
602
619
  case '.cs':
@@ -619,8 +636,14 @@ function extensionToLanguage(ext: string): ParsedFile['language'] {
619
636
  switch (ext) {
620
637
  case '.py': return 'python'
621
638
  case '.java': return 'java'
639
+ case '.kt':
640
+ case '.kts':
641
+ return 'kotlin'
642
+ case '.swift':
643
+ return 'swift'
622
644
  case '.c': case '.h': return 'c'
623
645
  case '.cpp': case '.cc': case '.hpp': return 'cpp'
646
+ case '.cxx': case '.hxx': case '.hh': return 'cpp'
624
647
  case '.cs': return 'csharp'
625
648
  case '.go': return 'go'
626
649
  case '.rs': return 'rust'
@@ -65,6 +65,33 @@ export const JAVA_QUERIES = `
65
65
  (class_declaration name: (identifier) @heritage.class (super_interfaces (type_list (type_identifier) @heritage.implements))) @heritage.impl
66
66
  `;
67
67
 
68
+ export const KOTLIN_QUERIES = `
69
+ (class_declaration name: (type_identifier) @name) @definition.class
70
+ (object_declaration name: (type_identifier) @name) @definition.class
71
+ (function_declaration name: (simple_identifier) @name) @definition.function
72
+ (property_declaration (variable_declaration (simple_identifier) @name)) @definition.property
73
+ (type_alias (type_identifier) @name) @definition.type
74
+ (import_header (identifier) @import.source) @import
75
+ (call_expression (simple_identifier) @call.name) @call
76
+ (call_expression
77
+ (navigation_expression
78
+ (navigation_suffix (simple_identifier) @call.name))) @call
79
+ (constructor_invocation
80
+ (user_type (type_identifier) @call.name)) @call
81
+ `;
82
+
83
+ export const SWIFT_QUERIES = `
84
+ (class_declaration name: (type_identifier) @name) @definition.class
85
+ (protocol_declaration name: (type_identifier) @name) @definition.interface
86
+ (function_declaration name: (simple_identifier) @name) @definition.function
87
+ (property_declaration (pattern (simple_identifier) @name)) @definition.property
88
+ (import_declaration (identifier) @import.source) @import
89
+ (call_expression (simple_identifier) @call.name) @call
90
+ (call_expression
91
+ (navigation_expression
92
+ (navigation_suffix (simple_identifier) @call.name))) @call
93
+ `;
94
+
68
95
  export const C_QUERIES = `
69
96
  (function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function
70
97
  (declaration declarator: (function_declarator declarator: (identifier) @name)) @definition.function
@@ -116,7 +116,7 @@ export interface ParsedRoute {
116
116
  /** Everything extracted from a single file */
117
117
  export interface ParsedFile {
118
118
  path: string; // normalized absolute path
119
- language: 'python' | 'go' | 'typescript' | 'javascript' | 'java' | 'c' | 'cpp' | 'csharp' | 'rust' | 'php' | 'ruby' | 'unknown';
119
+ language: 'python' | 'go' | 'typescript' | 'javascript' | 'java' | 'kotlin' | 'swift' | 'c' | 'cpp' | 'csharp' | 'rust' | 'php' | 'ruby' | 'unknown';
120
120
  functions: ParsedFunction[];
121
121
  classes: ParsedClass[];
122
122
  variables: ParsedVariable[];