diff --git a/gitnexus/package-lock.json b/gitnexus/package-lock.json index ff28a06d8..48d34f4cb 100644 --- a/gitnexus/package-lock.json +++ b/gitnexus/package-lock.json @@ -26,7 +26,7 @@ "mnemonist": "^0.39.0", "onnxruntime-node": "^1.24.0", "pandemonium": "^2.4.0", - "tree-sitter": "^0.21.0", + "tree-sitter": "^0.22.4", "tree-sitter-c": "^0.21.0", "tree-sitter-c-sharp": "^0.21.0", "tree-sitter-cpp": "^0.22.0", @@ -59,7 +59,7 @@ }, "optionalDependencies": { "tree-sitter-kotlin": "^0.3.8", - "tree-sitter-swift": "^0.6.0" + "tree-sitter-swift": "^0.7.1" } }, "node_modules/@babel/helper-string-parser": { @@ -3480,13 +3480,6 @@ "graphology-types": ">=0.20.0" } }, - "node_modules/graphology-types": { - "version": "0.24.8", - "resolved": "https://registry.npmjs.org/graphology-types/-/graphology-types-0.24.8.tgz", - "integrity": "sha512-hDRKYXa8TsoZHjgEaysSRyPdT6uB78Ci8WnjgbStlQysz7xR52PInxNsmnB7IBOM1BhikxkNyCVEFgmPKnpx3Q==", - "license": "MIT", - "peer": true - }, "node_modules/graphology-utils": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/graphology-utils/-/graphology-utils-2.5.2.tgz", @@ -3548,16 +3541,6 @@ "node": ">= 0.4" } }, - "node_modules/hono": { - "version": "4.11.7", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz", - "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=16.9.0" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -4941,14 +4924,14 @@ } }, "node_modules/tree-sitter": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.21.1.tgz", - "integrity": "sha512-7dxoA6kYvtgWw80265MyqJlkRl4yawIjO7S5MigytjELkX43fV2WsAXzsNfO7sBpPPCF5Gp0+XzHk0DwLCq3xQ==", + "version": "0.22.4", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.22.4.tgz", + "integrity": "sha512-usbHZP9/oxNsUY65MQUsduGRqDHQOou1cagUSwjhoSYAmSahjQDAVsh9s+SlZkn8X8+O1FULRGwHu7AFP3kjzg==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "node-addon-api": "^8.0.0", - "node-gyp-build": "^4.8.0" + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" } }, "node_modules/tree-sitter-c": { @@ -5267,9 +5250,9 @@ "license": "MIT" }, "node_modules/tree-sitter-swift": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tree-sitter-swift/-/tree-sitter-swift-0.6.0.tgz", - "integrity": "sha512-9vOJZes4/UFjBr4COHtp6ZHVuZYwfChSQbpneXQog04dAstfx5px3ybVX2cN+ylvLqsvVpmXLpidxxgF2rDQ7A==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/tree-sitter-swift/-/tree-sitter-swift-0.7.1.tgz", + "integrity": "sha512-pneKVTuGamaBsqqqfB9BvNQjktzh/0IVPR54jLB5Fq/JTDQwYHd0Wo6pVyZ5jAYpbztzq+rJ/rpL9ruxTmSoKw==", "hasInstallScript": true, "license": "MIT", "optional": true, @@ -5280,7 +5263,7 @@ "which": "2.0.2" }, "peerDependencies": { - "tree-sitter": "^0.21.1" + "tree-sitter": "^0.22.1" }, "peerDependenciesMeta": { "tree_sitter": { diff --git a/gitnexus/package.json b/gitnexus/package.json index fb29ad17e..1478f750e 100644 --- a/gitnexus/package.json +++ b/gitnexus/package.json @@ -66,7 +66,7 @@ "lru-cache": "^11.0.0", "mnemonist": "^0.39.0", "pandemonium": "^2.4.0", - "tree-sitter": "^0.21.0", + "tree-sitter": "^0.22.4", "tree-sitter-c": "^0.21.0", "tree-sitter-c-sharp": "^0.21.0", "tree-sitter-cpp": "^0.22.0", @@ -82,7 +82,7 @@ }, "optionalDependencies": { "tree-sitter-kotlin": "^0.3.8", - "tree-sitter-swift": "^0.6.0" + "tree-sitter-swift": "^0.7.1" }, "devDependencies": { "@types/cli-progress": "^3.11.6", diff --git a/gitnexus/src/core/ingestion/import-processor.ts b/gitnexus/src/core/ingestion/import-processor.ts index bcbbcfe35..6796f20b2 100644 --- a/gitnexus/src/core/ingestion/import-processor.ts +++ b/gitnexus/src/core/ingestion/import-processor.ts @@ -5,6 +5,8 @@ import { isLanguageAvailable, loadParser, loadLanguage } from '../tree-sitter/pa import { LANGUAGE_QUERIES } from './tree-sitter-queries.js'; import { generateId } from '../../lib/utils.js'; import { getLanguageFromFilename, isVerboseIngestionEnabled, yieldToEventLoop } from './utils.js'; +import { SupportedLanguages } from '../../config/supported-languages.js'; +import type { SwiftPackageConfig } from './language-config.js'; import type { ExtractedImport } from './workers/parse-worker.js'; import { getTreeSitterBufferSize } from './constants.js'; import { loadImportConfigs } from './language-config.js'; @@ -97,6 +99,80 @@ function createImportEdgeHelpers(graph: KnowledgeGraph, importMap: ImportMap) { return { addImportEdge, addImportGraphEdge, getResolvedCount: () => totalImportsResolved }; } +/** + * Group Swift files by target for implicit module visibility. + * + * If SwiftPackageConfig is available, use SPM target → directory mappings. + * Otherwise, group all Swift files under a single "default" target + * (assumes a single-module Xcode project). + */ +function groupSwiftFilesByTarget( + swiftFiles: string[], + swiftPackageConfig: SwiftPackageConfig | null, +): Map { + const groups = new Map(); + + if (swiftPackageConfig && swiftPackageConfig.targets.size > 0) { + for (const file of swiftFiles) { + const normalized = file.replace(/\\/g, '/'); + let assigned = false; + for (const [targetName, targetDir] of swiftPackageConfig.targets) { + const dirPrefix = targetDir + '/'; + const idx = normalized.indexOf(dirPrefix); + if (idx === 0 || (idx > 0 && normalized[idx - 1] === '/')) { + if (!groups.has(targetName)) groups.set(targetName, []); + groups.get(targetName)!.push(file); + assigned = true; + break; + } + } + if (!assigned) { + if (!groups.has('__default__')) groups.set('__default__', []); + groups.get('__default__')!.push(file); + } + } + } else { + groups.set('__default__', [...swiftFiles]); + } + + return groups; +} + +/** + * Add implicit IMPORTS edges between all Swift files in the same module/target. + * Swift has no file-level imports — all files in a module see each other. + */ +function addSwiftImplicitImports( + files: { path: string }[], + swiftPackageConfig: SwiftPackageConfig | null, + importMap: Map>, + addImportEdge: (src: string, target: string) => void, + logSuffix = '', +): void { + const swiftFiles = files + .filter(f => getLanguageFromFilename(f.path) === SupportedLanguages.Swift) + .map(f => f.path); + + if (swiftFiles.length <= 1) return; + + const targetGroups = groupSwiftFilesByTarget(swiftFiles, swiftPackageConfig); + + for (const group of targetGroups.values()) { + for (const srcFile of group) { + const existing = importMap.get(srcFile); + for (const otherFile of group) { + if (srcFile === otherFile) continue; + if (existing?.has(otherFile)) continue; + addImportEdge(srcFile, otherFile); + } + } + } + + if (isDev) { + console.log(`📊 Swift: ${swiftFiles.length} files in ${targetGroups.size} target group(s), implicit imports added${logSuffix}`); + } +} + /** * Apply an ImportResult: emit graph edges and update ImportMap/PackageMap. * If namedBindings are provided and the import resolves to a single file,