mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-08 03:08:13 +00:00
fixed source code viewer blue node placeholder issue, fixed react component queries, synced worker threads, fixed async query issue
This commit is contained in:
parent
e99a636bc3
commit
07e177dbd6
11 changed files with 1298 additions and 130 deletions
|
|
@ -176,6 +176,9 @@ export const IGNORE_PATTERNS = new Set([
|
|||
// Build & Distribution Directories
|
||||
'build', 'dist', 'out', 'target', 'bin', 'obj', '.gradle', '_build',
|
||||
|
||||
// Static Assets and Public Directories
|
||||
'public', 'assets', 'static',
|
||||
|
||||
// IDE & Editor Directories
|
||||
'.vs', '.vscode', '.idea', '.eclipse', '.settings',
|
||||
|
||||
|
|
|
|||
|
|
@ -155,41 +155,202 @@ export class ParsingProcessor implements GraphProcessor<ParsingInput> {
|
|||
options?: { directoryFilter?: string; fileExtensions?: string }): string[] {
|
||||
|
||||
let filtered = filePaths;
|
||||
let filteringStats = {
|
||||
initial: filtered.length,
|
||||
afterDirectoryFilter: 0,
|
||||
afterExtensionFilter: 0,
|
||||
afterIgnorePatterns: 0,
|
||||
afterContentFilter: 0,
|
||||
final: 0
|
||||
};
|
||||
|
||||
// Apply directory filter if specified
|
||||
if (options?.directoryFilter) {
|
||||
filtered = filtered.filter(path => path.includes(options.directoryFilter ?? ''));
|
||||
console.log(`Filtering by directory '${options.directoryFilter}': ${filteringStats.initial} -> ${filtered.length} files`);
|
||||
}
|
||||
filteringStats.afterDirectoryFilter = filtered.length;
|
||||
|
||||
// Apply extension filter if specified
|
||||
if (options?.fileExtensions) {
|
||||
const extensions = options.fileExtensions.split(',').map(ext => ext.trim()).filter(ext => ext.length);
|
||||
filtered = filtered.filter(path => extensions.some(ext => path.endsWith(ext)));
|
||||
console.log(`Filtering by extensions [${extensions.join(', ')}]: ${filteringStats.afterDirectoryFilter} -> ${filtered.length} files`);
|
||||
}
|
||||
filteringStats.afterExtensionFilter = filtered.length;
|
||||
|
||||
filtered = filtered.filter(path =>
|
||||
![...IGNORE_PATTERNS].some(pattern => {
|
||||
if (typeof pattern === 'string') {
|
||||
return path.includes(pattern);
|
||||
}
|
||||
return false;
|
||||
})
|
||||
);
|
||||
|
||||
// Apply ignore patterns (be more selective to avoid over-filtering)
|
||||
const beforeIgnoreFilter = filtered.length;
|
||||
filtered = filtered.filter(path => {
|
||||
const content = fileContents.get(path);
|
||||
return content && content.trim().length > 0;
|
||||
// More precise ignore pattern matching
|
||||
for (const pattern of IGNORE_PATTERNS) {
|
||||
if (typeof pattern === 'string') {
|
||||
// Only ignore if the pattern is a complete directory component
|
||||
if (path.includes(`/${pattern}/`) ||
|
||||
path.startsWith(`${pattern}/`) ||
|
||||
path.endsWith(`/${pattern}`) ||
|
||||
path === pattern) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Additional filtering for files that shouldn't be in KG
|
||||
const fileName = path.split('/').pop()?.toLowerCase() || '';
|
||||
|
||||
// Skip documentation and readme files
|
||||
if (fileName.includes('readme') ||
|
||||
fileName.includes('license') ||
|
||||
fileName.includes('changelog') ||
|
||||
fileName.includes('contributing') ||
|
||||
fileName.includes('authors') ||
|
||||
fileName.includes('maintainers')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip git and version control files
|
||||
if (fileName.startsWith('.git') ||
|
||||
fileName.includes('.gitignore') ||
|
||||
fileName.includes('.gitattributes')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip common non-source files
|
||||
if (fileName.includes('dockerfile') ||
|
||||
fileName.includes('docker-compose') ||
|
||||
fileName.endsWith('.md') ||
|
||||
fileName.endsWith('.txt') ||
|
||||
fileName.endsWith('.log') ||
|
||||
fileName.endsWith('.lock')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
if (beforeIgnoreFilter !== filtered.length) {
|
||||
const excludedCount = beforeIgnoreFilter - filtered.length;
|
||||
console.log(`Filtering by ignore patterns and file types: ${beforeIgnoreFilter} -> ${filtered.length} files (${excludedCount} excluded)`);
|
||||
|
||||
// Log what types of files were excluded for transparency
|
||||
if (excludedCount <= 10) {
|
||||
const excluded = filePaths.slice(0, beforeIgnoreFilter).filter(path => !filtered.includes(path));
|
||||
console.log('📋 Excluded files sample:', excluded.slice(0, 5).map(p => p.split('/').pop()));
|
||||
}
|
||||
}
|
||||
filteringStats.afterIgnorePatterns = filtered.length;
|
||||
|
||||
// Apply content filter (only exclude truly empty files)
|
||||
const beforeContentFilter = filtered.length;
|
||||
const emptyFiles: string[] = [];
|
||||
filtered = filtered.filter(path => {
|
||||
const content = fileContents.get(path);
|
||||
if (!content || content.trim().length === 0) {
|
||||
emptyFiles.push(path);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (emptyFiles.length > 0) {
|
||||
console.log(`Filtering empty files: ${beforeContentFilter} -> ${filtered.length} files (${emptyFiles.length} empty)`);
|
||||
if (emptyFiles.length <= 5) {
|
||||
console.log('Empty files:', emptyFiles);
|
||||
}
|
||||
}
|
||||
filteringStats.afterContentFilter = filtered.length;
|
||||
filteringStats.final = filtered.length;
|
||||
|
||||
// Log comprehensive filtering summary
|
||||
console.log('📊 File Filtering Summary:', filteringStats);
|
||||
|
||||
// Show sample of filtered files by type
|
||||
const filesByExtension = filtered.reduce((acc, path) => {
|
||||
const ext = pathUtils.extname(path).toLowerCase() || 'no-extension';
|
||||
acc[ext] = (acc[ext] || 0) + 1;
|
||||
return acc;
|
||||
}, {} as Record<string, number>);
|
||||
console.log('📋 Files by extension:', filesByExtension);
|
||||
|
||||
// Debug: Check for specific TSX/TS files
|
||||
const tsxFiles = filtered.filter(path => path.endsWith('.tsx'));
|
||||
const tsFiles = filtered.filter(path => path.endsWith('.ts'));
|
||||
if (tsxFiles.length > 0) {
|
||||
console.log(`🔍 TSX FILES: Found ${tsxFiles.length} TSX files:`, tsxFiles.slice(0, 5).map(p => p.split('/').pop()));
|
||||
}
|
||||
if (tsFiles.length > 0) {
|
||||
console.log(`🔍 TS FILES: Found ${tsFiles.length} TS files:`, tsFiles.slice(0, 5).map(p => p.split('/').pop()));
|
||||
}
|
||||
|
||||
// Specifically check for ChatInterface.tsx
|
||||
const chatInterface = filtered.find(path => path.includes('ChatInterface.tsx'));
|
||||
if (chatInterface) {
|
||||
console.log(`🎯 FOUND ChatInterface.tsx in filtered files: ${chatInterface}`);
|
||||
} else {
|
||||
const originalChatInterface = filePaths.find(path => path.includes('ChatInterface.tsx'));
|
||||
if (originalChatInterface) {
|
||||
console.warn(`⚠️ ChatInterface.tsx was FILTERED OUT: ${originalChatInterface}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Warn about potentially inappropriate files
|
||||
const suspiciousFiles = filtered.filter(path => {
|
||||
const fileName = path.split('/').pop()?.toLowerCase() || '';
|
||||
return fileName.includes('test') ||
|
||||
fileName.includes('spec') ||
|
||||
fileName.includes('mock') ||
|
||||
fileName.includes('fixture') ||
|
||||
path.includes('/test/') ||
|
||||
path.includes('/tests/') ||
|
||||
path.includes('/__tests__/') ||
|
||||
path.includes('/spec/');
|
||||
});
|
||||
|
||||
if (suspiciousFiles.length > 0) {
|
||||
console.warn(`⚠️ Found ${suspiciousFiles.length} test/spec files that will be processed:`);
|
||||
console.warn('Test files sample:', suspiciousFiles.slice(0, 3).map(p => p.split('/').pop()));
|
||||
console.warn('Consider if these should be excluded from the knowledge graph');
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
private isSourceFile(filePath: string): boolean {
|
||||
const sourceExtensions = ['.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.cpp', '.c', '.h', '.hpp', '.cs', '.php', '.rb', '.go', '.rs'];
|
||||
// Only include actual programming language source files
|
||||
const sourceExtensions = [
|
||||
// JavaScript/TypeScript (core web technologies)
|
||||
'.js', '.ts', '.jsx', '.tsx',
|
||||
// Python
|
||||
'.py',
|
||||
// Java
|
||||
'.java',
|
||||
// C/C++
|
||||
'.cpp', '.c', '.cc', '.cxx', '.h', '.hpp', '.hxx',
|
||||
// C#
|
||||
'.cs',
|
||||
// Only include other languages if they're commonly used
|
||||
'.php', '.rb', '.go', '.rs'
|
||||
// Removed: .mjs, .cjs (might be build artifacts)
|
||||
// Removed: .html, .htm, .xml (markup, not source code)
|
||||
// Removed: .vue, .svelte (framework-specific)
|
||||
// Removed: .kt, .scala, .swift (less common)
|
||||
];
|
||||
return sourceExtensions.some(ext => filePath.toLowerCase().endsWith(ext));
|
||||
}
|
||||
|
||||
private isConfigFile(filePath: string): boolean {
|
||||
const configFiles = ['package.json', 'tsconfig.json', 'webpack.config.js', 'vite.config.ts', '.eslintrc.js', '.prettierrc'];
|
||||
const configExtensions = ['.json', '.yaml', '.yml', '.toml'];
|
||||
// Only include config files that might contain meaningful definitions
|
||||
const configFiles = [
|
||||
'package.json', 'tsconfig.json', 'jsconfig.json',
|
||||
'webpack.config.js', 'vite.config.ts', 'vite.config.js',
|
||||
'.eslintrc.js', '.eslintrc.json',
|
||||
'babel.config.js', 'rollup.config.js'
|
||||
// Removed: .prettierrc (formatting, no definitions)
|
||||
// Removed: pyproject.toml, setup.py (might be worth including if Python project)
|
||||
// Removed: requirements.txt (just dependencies)
|
||||
// Removed: Dockerfile, docker-compose.yml (deployment, not source)
|
||||
// Removed: .gitignore, .gitattributes (git config, no definitions)
|
||||
// Removed: README.md, LICENSE (documentation, no definitions)
|
||||
];
|
||||
const configExtensions = ['.json']; // Only JSON configs, removed .yaml, .yml, .toml, .ini, .cfg
|
||||
|
||||
return configFiles.some(name => filePath.endsWith(name)) ||
|
||||
configExtensions.some(ext => filePath.toLowerCase().endsWith(ext));
|
||||
|
|
@ -226,10 +387,11 @@ export class ParsingProcessor implements GraphProcessor<ParsingInput> {
|
|||
|
||||
private async parseFile(graph: KnowledgeGraph, filePath: string, content: string): Promise<void> {
|
||||
const language = this.detectLanguage(filePath);
|
||||
const fileName = pathUtils.getFileName(filePath);
|
||||
|
||||
// Skip compiled/minified files for JavaScript
|
||||
if (language === 'javascript' && this.isCompiledOrMinified(content, filePath)) {
|
||||
console.log(`Skipping compiled/minified file: ${filePath}`);
|
||||
console.log(`Skipping compiled/minified file: ${fileName}`);
|
||||
await this.parseGenericFile(graph, filePath, content);
|
||||
return;
|
||||
}
|
||||
|
|
@ -240,7 +402,7 @@ export class ParsingProcessor implements GraphProcessor<ParsingInput> {
|
|||
// Check cache first - TEMPORARILY DISABLED FOR DEBUGGING
|
||||
const cachedResult = null; // this.lruCache.getParsedFile(cacheKey);
|
||||
if (cachedResult) {
|
||||
console.log(`Cache hit for file: ${filePath}`);
|
||||
console.log(`Cache hit for file: ${fileName}`);
|
||||
this.astMap.set(filePath, { tree: cachedResult.ast });
|
||||
await this.addDefinitionsToGraph(graph, filePath, cachedResult.definitions);
|
||||
return;
|
||||
|
|
@ -249,125 +411,278 @@ export class ParsingProcessor implements GraphProcessor<ParsingInput> {
|
|||
const langParser = this.languageParsers.get(language);
|
||||
|
||||
if (!langParser || !this.parser) {
|
||||
// Skip file with reduced logging to avoid console spam
|
||||
console.warn(`⚠️ SKIPPING: ${fileName} - No parser available (language: ${language}, hasLangParser: ${!!langParser}, hasMainParser: ${!!this.parser})`);
|
||||
await this.parseGenericFile(graph, filePath, content);
|
||||
return;
|
||||
}
|
||||
|
||||
this.parser.setLanguage(langParser);
|
||||
const tree = this.parser.parse(content);
|
||||
this.astMap.set(filePath, { tree });
|
||||
const definitions: ParsedDefinition[] = [];
|
||||
try {
|
||||
this.parser.setLanguage(langParser);
|
||||
const tree = this.parser.parse(content);
|
||||
this.astMap.set(filePath, { tree });
|
||||
const definitions: ParsedDefinition[] = [];
|
||||
|
||||
const queries = this.getQueriesForLanguage(language);
|
||||
if (!queries) {
|
||||
console.warn(`No queries available for language: ${language}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process queries with caching
|
||||
for (const [queryName, queryString] of Object.entries(queries)) {
|
||||
const queryCacheKey = this.lruCache.generateQueryCacheKey(language, queryString);
|
||||
let queryResults: Parser.QueryMatch[] = [];
|
||||
|
||||
// Check query cache - TEMPORARILY DISABLED FOR DEBUGGING
|
||||
const cachedQuery = null; // this.lruCache.getQueryResult(queryCacheKey);
|
||||
if (cachedQuery) {
|
||||
queryResults = cachedQuery.results;
|
||||
} else {
|
||||
const query = langParser.query(queryString as string);
|
||||
queryResults = query.matches(tree.rootNode);
|
||||
|
||||
// Cache query results
|
||||
this.lruCache.setQueryResult(queryCacheKey, {
|
||||
query: queryString,
|
||||
results: queryResults,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
const queries = this.getQueriesForLanguage(language);
|
||||
if (!queries) {
|
||||
console.warn(`No queries available for language: ${language} (${fileName}).`);
|
||||
await this.parseGenericFile(graph, filePath, content);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const match of queryResults) {
|
||||
for (const capture of match.captures) {
|
||||
const node = capture.node;
|
||||
const definition = this.extractDefinition(node, queryName, filePath);
|
||||
if (definition) {
|
||||
definitions.push(definition);
|
||||
let totalMatches = 0;
|
||||
// Process queries with caching
|
||||
for (const [queryName, queryString] of Object.entries(queries)) {
|
||||
const queryCacheKey = this.lruCache.generateQueryCacheKey(language, queryString);
|
||||
let queryResults: Parser.QueryMatch[] = [];
|
||||
|
||||
try {
|
||||
// Check query cache - TEMPORARILY DISABLED FOR DEBUGGING
|
||||
const cachedQuery = null; // this.lruCache.getQueryResult(queryCacheKey);
|
||||
if (cachedQuery) {
|
||||
queryResults = cachedQuery.results;
|
||||
} else {
|
||||
const query = langParser.query(queryString as string);
|
||||
queryResults = query.matches(tree.rootNode);
|
||||
totalMatches += queryResults.length;
|
||||
|
||||
// Debug: Log what specific definitions we're finding
|
||||
if (language === 'python' && filePath.endsWith('.py')) {
|
||||
console.log(`🔍 DEBUG: Found ${queryName} -> ${definition.type}: "${definition.name}" in ${filePath.split('/').pop()}`);
|
||||
// Cache query results
|
||||
this.lruCache.setQueryResult(queryCacheKey, {
|
||||
query: queryString,
|
||||
results: queryResults,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
for (const match of queryResults) {
|
||||
for (const capture of match.captures) {
|
||||
const node = capture.node;
|
||||
const definition = this.extractDefinition(node, queryName, filePath);
|
||||
if (definition) {
|
||||
definitions.push(definition);
|
||||
|
||||
// Debug: Log what specific definitions we're finding for certain files
|
||||
if (fileName.endsWith('.py') || fileName.endsWith('.ts') || fileName.endsWith('.js')) {
|
||||
console.log(`🔍 DEBUG: Found ${queryName} -> ${definition.type}: "${definition.name}" in ${fileName}:${definition.startLine}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (queryError) {
|
||||
console.warn(`Query error in ${fileName} for ${queryName}:`, queryError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the parsed file results
|
||||
this.lruCache.setParsedFile(cacheKey, {
|
||||
ast: tree,
|
||||
definitions,
|
||||
language,
|
||||
lastModified: Date.now(),
|
||||
fileSize: content.length
|
||||
});
|
||||
// Cache the parsed file results
|
||||
this.lruCache.setParsedFile(cacheKey, {
|
||||
ast: tree,
|
||||
definitions,
|
||||
language,
|
||||
lastModified: Date.now(),
|
||||
fileSize: content.length
|
||||
});
|
||||
|
||||
// Debug: Log definition extraction results for Python files
|
||||
if (language === 'python' && filePath.endsWith('.py')) {
|
||||
console.log(`🔍 DEBUG: ${filePath.split('/').pop()} -> ${definitions.length} definitions extracted (content: ${content.length} chars, hash: ${contentHash.substring(0, 8)})`);
|
||||
// Enhanced debug logging
|
||||
console.log(`🔍 PARSING: ${fileName} (${language}) -> ${definitions.length} definitions from ${totalMatches} matches (content: ${content.length} chars)`);
|
||||
|
||||
// Log definition types for debugging
|
||||
const definitionTypes = definitions.reduce((acc, def) => {
|
||||
acc[def.type] = (acc[def.type] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
if (definitions.length > 0) {
|
||||
console.log(`🔍 DEBUG: Definition types: ${JSON.stringify(definitionTypes)}`);
|
||||
}
|
||||
|
||||
if (definitions.length === 0 && content.length > 100) {
|
||||
console.log(`🚨 DEBUG: Large Python file with no definitions: ${filePath} (${content.length} chars)`);
|
||||
const definitionTypes = definitions.reduce((acc, def) => {
|
||||
acc[def.type] = (acc[def.type] || 0) + 1;
|
||||
return acc;
|
||||
}, {} as Record<string, number>);
|
||||
console.log(`🔍 TYPES: ${fileName} -> ${JSON.stringify(definitionTypes)}`);
|
||||
} else if (content.length > 100 && language !== 'generic') {
|
||||
console.warn(`🚨 NO DEFINITIONS: ${fileName} (${language}, ${content.length} chars, ${totalMatches} total matches)`);
|
||||
// Log first few lines to understand the content
|
||||
const firstLines = content.split('\n').slice(0, 3).join('\\n');
|
||||
console.log(`🔍 DEBUG: File starts with: ${firstLines}`);
|
||||
const firstLines = content.split('\n').slice(0, 3).join('\\n').substring(0, 150);
|
||||
console.warn(`🔍 CONTENT PREVIEW: ${firstLines}...`);
|
||||
}
|
||||
}
|
||||
|
||||
await this.addDefinitionsToGraph(graph, filePath, definitions);
|
||||
await this.addDefinitionsToGraph(graph, filePath, definitions);
|
||||
} catch (parseError) {
|
||||
console.error(`Error parsing ${fileName}:`, parseError);
|
||||
await this.parseGenericFile(graph, filePath, content);
|
||||
}
|
||||
}
|
||||
|
||||
private extractDefinition(node: Parser.SyntaxNode, queryName: string, filePath: string): ParsedDefinition | null {
|
||||
const nameNode = node.childForFieldName('name');
|
||||
const name = nameNode ? nameNode.text : null;
|
||||
let nameNode = node.childForFieldName('name');
|
||||
let name = nameNode ? nameNode.text : null;
|
||||
|
||||
// Handle different naming patterns for different query types
|
||||
if (!name) {
|
||||
// Try alternative naming strategies based on query type
|
||||
switch (queryName) {
|
||||
case 'variables':
|
||||
case 'constDeclarations':
|
||||
case 'global_variables':
|
||||
// For variable assignments, look for identifier in left side
|
||||
const leftChild = node.namedChildren.find(child => child.type === 'identifier');
|
||||
if (leftChild) name = leftChild.text;
|
||||
break;
|
||||
|
||||
case 'hookCalls':
|
||||
case 'hookDestructuring':
|
||||
// For React hooks, try to get the variable name
|
||||
const hookVar = node.namedChildren.find(child => child.type === 'variable_declarator');
|
||||
if (hookVar) {
|
||||
const hookName = hookVar.childForFieldName('name');
|
||||
if (hookName) {
|
||||
// Handle array destructuring for useState pattern
|
||||
if (hookName.type === 'array_pattern') {
|
||||
const elements = hookName.namedChildren.filter(child => child.type === 'identifier');
|
||||
if (elements.length > 0) {
|
||||
name = elements.map(el => el.text).join(', ');
|
||||
}
|
||||
} else {
|
||||
name = hookName.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'reactComponents':
|
||||
case 'reactConstComponents':
|
||||
case 'defaultExportArrows':
|
||||
// For React components, get the component name
|
||||
const componentVar = node.namedChildren.find(child => child.type === 'variable_declarator');
|
||||
if (componentVar) {
|
||||
const componentName = componentVar.childForFieldName('name');
|
||||
if (componentName) name = componentName.text;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'moduleExports':
|
||||
// For module.exports = something, get the property name
|
||||
const memberExpr = node.namedChildren.find(child => child.type === 'member_expression');
|
||||
if (memberExpr) {
|
||||
const property = memberExpr.childForFieldName('property');
|
||||
if (property) name = property.text;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'decorators':
|
||||
// For decorators, get the decorator name
|
||||
const decoratorChild = node.namedChildren.find(child => child.type === 'identifier');
|
||||
if (decoratorChild) name = decoratorChild.text;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Try to find any identifier child
|
||||
const identifierChild = node.namedChildren.find(child => child.type === 'identifier');
|
||||
if (identifierChild) name = identifierChild.text;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip anonymous definitions - they're usually from compiled/minified code
|
||||
if (!name || name === 'anonymous' || name.trim().length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Skip very short names that are likely noise (but keep single-letter variables like 'i', 'x')
|
||||
if (name.length === 1 && queryName !== 'variables' && queryName !== 'constDeclarations') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Skip common noise patterns
|
||||
const noisePatterns = ['_', '__', '___', 'temp', 'tmp'];
|
||||
if (noisePatterns.includes(name.toLowerCase())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
const definition: ParsedDefinition = {
|
||||
name,
|
||||
type: this.getDefinitionType(queryName),
|
||||
startLine: node.startPosition.row + 1,
|
||||
endLine: node.endPosition.row + 1,
|
||||
filePath,
|
||||
} as ParsedDefinition;
|
||||
};
|
||||
|
||||
// Extract additional metadata based on definition type
|
||||
if (definition.type === 'function' || definition.type === 'method') {
|
||||
// Try to extract parameters
|
||||
const parametersNode = node.childForFieldName('parameters');
|
||||
if (parametersNode) {
|
||||
const params: string[] = [];
|
||||
for (const param of parametersNode.namedChildren) {
|
||||
if (param.type === 'identifier' || param.type === 'formal_parameter') {
|
||||
params.push(param.text);
|
||||
}
|
||||
}
|
||||
if (params.length > 0) {
|
||||
definition.parameters = params;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for async functions - removed async queries as they were causing Tree-sitter errors
|
||||
// if (queryName === 'async_functions' || queryName === 'async_methods') {
|
||||
// definition.isAsync = true;
|
||||
// }
|
||||
|
||||
// Mark React components
|
||||
if (queryName === 'reactComponents' || queryName === 'reactConstComponents') {
|
||||
definition.isAsync = false; // React components are not async by default
|
||||
definition.exportType = 'default'; // Most React components are default exports
|
||||
}
|
||||
}
|
||||
|
||||
if (definition.type === 'class') {
|
||||
// Try to extract inheritance information
|
||||
const superclassNode = node.childForFieldName('superclass');
|
||||
if (superclassNode) {
|
||||
definition.extends = [superclassNode.text];
|
||||
}
|
||||
}
|
||||
|
||||
// Handle variable types with additional context
|
||||
if (definition.type === 'variable') {
|
||||
if (queryName === 'hookCalls' || queryName === 'hookDestructuring') {
|
||||
definition.exportType = 'named'; // React hooks are typically named exports
|
||||
|
||||
// Try to extract hook type from call expression
|
||||
const callExpr = node.descendantsOfType('call_expression')[0];
|
||||
if (callExpr) {
|
||||
const funcNode = callExpr.childForFieldName('function');
|
||||
if (funcNode && funcNode.type === 'identifier') {
|
||||
definition.returnType = funcNode.text; // Store hook function name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
private getDefinitionType(queryName: string): ParsedDefinition['type'] {
|
||||
switch (queryName) {
|
||||
case 'classes': return 'class';
|
||||
case 'methods': return 'method';
|
||||
case 'classes':
|
||||
case 'exportClasses': return 'class';
|
||||
case 'methods':
|
||||
case 'properties':
|
||||
case 'staticmethods':
|
||||
case 'classmethods': return 'method';
|
||||
case 'functions':
|
||||
case 'arrowFunctions':
|
||||
case 'reactComponents':
|
||||
case 'reactConstComponents':
|
||||
case 'defaultExportArrows':
|
||||
case 'variableAssignments':
|
||||
case 'objectMethods': return 'function';
|
||||
case 'objectMethods':
|
||||
case 'exportFunctions':
|
||||
case 'defaultExportFunctions':
|
||||
case 'functionExpressions': return 'function';
|
||||
case 'variables':
|
||||
case 'constDeclarations':
|
||||
case 'hookCalls':
|
||||
case 'hookDestructuring':
|
||||
case 'global_variables': return 'variable';
|
||||
case 'imports':
|
||||
case 'from_imports': return 'import';
|
||||
case 'exports':
|
||||
case 'defaultExports': return 'function'; // Exports usually export functions
|
||||
case 'defaultExports':
|
||||
case 'moduleExports': return 'function'; // Exports usually export functions
|
||||
case 'interfaces': return 'interface';
|
||||
case 'types': return 'type';
|
||||
case 'enums': return 'type';
|
||||
case 'decorators': return 'decorator';
|
||||
default:
|
||||
console.warn(`Unknown query type: ${queryName}, defaulting to 'function'`);
|
||||
|
|
@ -413,12 +728,22 @@ export class ParsingProcessor implements GraphProcessor<ParsingInput> {
|
|||
|
||||
switch (extension) {
|
||||
case '.ts':
|
||||
case '.tsx': return 'typescript';
|
||||
case '.tsx':
|
||||
console.log(`🔍 LANGUAGE: ${filePath.split('/').pop()} -> typescript (${extension})`);
|
||||
return 'typescript';
|
||||
case '.js':
|
||||
case '.jsx': return 'javascript';
|
||||
case '.py': return 'python';
|
||||
case '.java': return 'java';
|
||||
default: return 'generic';
|
||||
case '.jsx':
|
||||
console.log(`🔍 LANGUAGE: ${filePath.split('/').pop()} -> javascript (${extension})`);
|
||||
return 'javascript';
|
||||
case '.py':
|
||||
console.log(`🔍 LANGUAGE: ${filePath.split('/').pop()} -> python (${extension})`);
|
||||
return 'python';
|
||||
case '.java':
|
||||
console.log(`🔍 LANGUAGE: ${filePath.split('/').pop()} -> java (${extension})`);
|
||||
return 'java';
|
||||
default:
|
||||
console.log(`🔍 LANGUAGE: ${filePath.split('/').pop()} -> generic (${extension})`);
|
||||
return 'generic';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -633,4 +958,106 @@ export class ParsingProcessor implements GraphProcessor<ParsingInput> {
|
|||
this.memoryManager.clearCache();
|
||||
this.lruCache.clearAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostic method to analyze why files might not have definitions
|
||||
*/
|
||||
public getDiagnosticInfo(): {
|
||||
processedFiles: number;
|
||||
skippedFiles: number;
|
||||
totalDefinitions: number;
|
||||
definitionsByType: Record<string, number>;
|
||||
definitionsByFile: Record<string, number>;
|
||||
processingErrors: string[];
|
||||
} {
|
||||
const definitionsByType: Record<string, number> = {};
|
||||
const definitionsByFile: Record<string, number> = {};
|
||||
let totalDefinitions = 0;
|
||||
|
||||
// Analyze function registry
|
||||
const allDefs = this.functionTrie.getAllDefinitions();
|
||||
allDefs.forEach(def => {
|
||||
totalDefinitions++;
|
||||
definitionsByType[def.type] = (definitionsByType[def.type] || 0) + 1;
|
||||
definitionsByFile[def.filePath] = (definitionsByFile[def.filePath] || 0) + 1;
|
||||
});
|
||||
|
||||
return {
|
||||
processedFiles: this.processedFiles.size,
|
||||
skippedFiles: 0, // Would need to track this during processing
|
||||
totalDefinitions,
|
||||
definitionsByType,
|
||||
definitionsByFile,
|
||||
processingErrors: [] // Would need to track errors during processing
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to analyze a specific file and explain why it might not have definitions
|
||||
*/
|
||||
public async analyzeFile(filePath: string, content: string): Promise<{
|
||||
language: string;
|
||||
isSourceFile: boolean;
|
||||
isConfigFile: boolean;
|
||||
isCompiled: boolean;
|
||||
contentLength: number;
|
||||
queryResults: Record<string, number>;
|
||||
extractionIssues: string[];
|
||||
}> {
|
||||
const language = this.detectLanguage(filePath);
|
||||
const isSourceFile = this.isSourceFile(filePath);
|
||||
const isConfigFile = this.isConfigFile(filePath);
|
||||
const isCompiled = language === 'javascript' && this.isCompiledOrMinified(content, filePath);
|
||||
const extractionIssues: string[] = [];
|
||||
const queryResults: Record<string, number> = {};
|
||||
|
||||
if (!isSourceFile && !isConfigFile) {
|
||||
extractionIssues.push('File is not recognized as a source or config file');
|
||||
}
|
||||
|
||||
if (isCompiled) {
|
||||
extractionIssues.push('File appears to be compiled/minified and is skipped');
|
||||
}
|
||||
|
||||
if (content.trim().length === 0) {
|
||||
extractionIssues.push('File is empty');
|
||||
}
|
||||
|
||||
const langParser = this.languageParsers.get(language);
|
||||
if (!langParser || !this.parser) {
|
||||
extractionIssues.push(`No parser available for language: ${language}`);
|
||||
} else {
|
||||
try {
|
||||
this.parser.setLanguage(langParser);
|
||||
const tree = this.parser.parse(content);
|
||||
|
||||
const queries = this.getQueriesForLanguage(language);
|
||||
if (queries) {
|
||||
for (const [queryName, queryString] of Object.entries(queries)) {
|
||||
try {
|
||||
const query = langParser.query(queryString as string);
|
||||
const matches = query.matches(tree.rootNode);
|
||||
queryResults[queryName] = matches.length;
|
||||
} catch (queryError) {
|
||||
extractionIssues.push(`Query '${queryName}' failed: ${queryError}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
extractionIssues.push(`No queries available for language: ${language}`);
|
||||
}
|
||||
} catch (parseError) {
|
||||
extractionIssues.push(`Parse error: ${parseError}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
language,
|
||||
isSourceFile,
|
||||
isConfigFile,
|
||||
isCompiled,
|
||||
contentLength: content.length,
|
||||
queryResults,
|
||||
extractionIssues
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -135,6 +135,20 @@ export class GraphPipeline {
|
|||
return graph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get detailed diagnostic information about parsing results
|
||||
*/
|
||||
public getParsingDiagnostics() {
|
||||
return this.parsingProcessor.getDiagnosticInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze a specific file to understand why it might not have definitions
|
||||
*/
|
||||
public async analyzeSpecificFile(filePath: string, content: string) {
|
||||
return await this.parsingProcessor.analyzeFile(filePath, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate graph integrity and identify potential issues
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ export class StructureProcessor {
|
|||
// Build & Distribution Directories
|
||||
'build', 'dist', 'out', 'target', 'bin', 'obj', '.gradle', '_build',
|
||||
|
||||
// Static Assets and Public Directories
|
||||
'public', 'assets', 'static',
|
||||
|
||||
// IDE & Editor Directories
|
||||
'.vs', '.vscode', '.idea', '.eclipse', '.settings',
|
||||
|
||||
|
|
|
|||
|
|
@ -18,12 +18,96 @@ export const TYPESCRIPT_QUERIES = {
|
|||
name: (identifier) @name
|
||||
value: (arrow_function))) @arrow_function
|
||||
`,
|
||||
// React functional components with type annotations (simplified)
|
||||
reactComponents: `
|
||||
(lexical_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @name
|
||||
value: (arrow_function
|
||||
(type_annotation)))) @react_component
|
||||
`,
|
||||
// React functional components as const declarations (simplified)
|
||||
reactConstComponents: `
|
||||
(lexical_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @name
|
||||
value: (as_expression
|
||||
(arrow_function)))) @react_const_component
|
||||
`,
|
||||
// Default export arrow functions (common React pattern)
|
||||
defaultExportArrows: `
|
||||
(export_statement
|
||||
(lexical_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @name
|
||||
value: (arrow_function)))) @default_export_arrow
|
||||
`,
|
||||
// React hooks (useState, useEffect, etc.)
|
||||
hookCalls: `
|
||||
(lexical_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @hook_name
|
||||
value: (call_expression
|
||||
function: (identifier) @hook_function
|
||||
(#match? @hook_function "^use[A-Z].*")))) @hook_call
|
||||
`,
|
||||
// Hook calls with array destructuring (useState pattern)
|
||||
hookDestructuring: `
|
||||
(lexical_declaration
|
||||
(variable_declarator
|
||||
name: (array_pattern) @hook_pattern
|
||||
value: (call_expression
|
||||
function: (identifier) @hook_function
|
||||
(#match? @hook_function "^use[A-Z].*")))) @hook_destructuring
|
||||
`,
|
||||
variables: `
|
||||
(lexical_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @variable)) @var_declaration
|
||||
`,
|
||||
constDeclarations: `
|
||||
(lexical_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @const
|
||||
value: _)) @const_declaration
|
||||
`,
|
||||
// Function expressions assigned to variables
|
||||
functionExpressions: `
|
||||
(lexical_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @name
|
||||
value: (function_expression))) @function_expression
|
||||
`,
|
||||
exports: `
|
||||
(export_statement) @export
|
||||
`,
|
||||
exportFunctions: `
|
||||
(export_statement
|
||||
(function_declaration) @export_function)
|
||||
`,
|
||||
exportClasses: `
|
||||
(export_statement
|
||||
(class_declaration) @export_class)
|
||||
`,
|
||||
// Default exports
|
||||
defaultExports: `
|
||||
(export_statement
|
||||
(identifier) @default_export)
|
||||
`,
|
||||
// Default export functions
|
||||
defaultExportFunctions: `
|
||||
(export_statement
|
||||
(function_declaration) @default_export_function)
|
||||
`,
|
||||
interfaces: `
|
||||
(interface_declaration) @interface
|
||||
`,
|
||||
types: `
|
||||
(type_alias_declaration) @type
|
||||
`,
|
||||
enums: `
|
||||
(enum_declaration) @enum
|
||||
`,
|
||||
};
|
||||
|
||||
// JavaScript queries - similar to TypeScript but without TS-specific syntax
|
||||
|
|
@ -46,6 +130,17 @@ export const JAVASCRIPT_QUERIES = {
|
|||
name: (identifier) @name
|
||||
value: (arrow_function))) @arrow_function
|
||||
`,
|
||||
variables: `
|
||||
(variable_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @variable)) @var_declaration
|
||||
`,
|
||||
constDeclarations: `
|
||||
(lexical_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @const
|
||||
value: _)) @const_declaration
|
||||
`,
|
||||
exports: `
|
||||
(export_statement) @export
|
||||
`,
|
||||
|
|
@ -53,6 +148,14 @@ export const JAVASCRIPT_QUERIES = {
|
|||
(export_statement
|
||||
(identifier) @default_export)
|
||||
`,
|
||||
exportFunctions: `
|
||||
(export_statement
|
||||
(function_declaration) @export_function)
|
||||
`,
|
||||
exportClasses: `
|
||||
(export_statement
|
||||
(class_declaration) @export_class)
|
||||
`,
|
||||
variableAssignments: `
|
||||
(variable_declaration
|
||||
(variable_declarator
|
||||
|
|
@ -65,7 +168,18 @@ export const JAVASCRIPT_QUERIES = {
|
|||
property: (property_identifier) @name)
|
||||
right: (function_expression)) @obj_method
|
||||
`,
|
||||
// Note: No interfaces or types for pure JavaScript
|
||||
moduleExports: `
|
||||
(assignment_expression
|
||||
left: (member_expression
|
||||
object: (identifier) @module
|
||||
property: (property_identifier) @export_name)
|
||||
right: _) @module_export
|
||||
`,
|
||||
functionExpressions: `
|
||||
(assignment_expression
|
||||
left: (identifier) @name
|
||||
right: (function_expression)) @func_expr
|
||||
`,
|
||||
};
|
||||
|
||||
export const PYTHON_QUERIES = {
|
||||
|
|
@ -86,10 +200,48 @@ export const PYTHON_QUERIES = {
|
|||
body: (block
|
||||
(function_definition) @method))
|
||||
`,
|
||||
variables: `
|
||||
(assignment
|
||||
left: (identifier) @variable
|
||||
right: _) @var_assignment
|
||||
`,
|
||||
global_variables: `
|
||||
(module
|
||||
(assignment
|
||||
left: (identifier) @global_var
|
||||
right: _)) @global_assignment
|
||||
`,
|
||||
decorators: `
|
||||
(decorated_definition
|
||||
(decorator) @decorator)
|
||||
`,
|
||||
properties: `
|
||||
(class_definition
|
||||
body: (block
|
||||
(decorated_definition
|
||||
(decorator
|
||||
(identifier) @property_decorator
|
||||
(#eq? @property_decorator "property"))
|
||||
(function_definition) @property)))
|
||||
`,
|
||||
staticmethods: `
|
||||
(class_definition
|
||||
body: (block
|
||||
(decorated_definition
|
||||
(decorator
|
||||
(identifier) @static_decorator
|
||||
(#eq? @static_decorator "staticmethod"))
|
||||
(function_definition) @static_method)))
|
||||
`,
|
||||
classmethods: `
|
||||
(class_definition
|
||||
body: (block
|
||||
(decorated_definition
|
||||
(decorator
|
||||
(identifier) @class_decorator
|
||||
(#eq? @class_decorator "classmethod"))
|
||||
(function_definition) @class_method)))
|
||||
`,
|
||||
};
|
||||
|
||||
export const JAVA_QUERIES = {
|
||||
|
|
|
|||
178
src/lib/kg-file-analyzer.ts
Normal file
178
src/lib/kg-file-analyzer.ts
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/**
|
||||
* Knowledge Graph File Analysis Utility
|
||||
* Helps identify if unnecessary files are being included in the KG
|
||||
*/
|
||||
|
||||
import type { KnowledgeGraph } from '../core/graph/types';
|
||||
|
||||
export interface FileAnalysisResult {
|
||||
totalFiles: number;
|
||||
filesByType: Record<string, number>;
|
||||
suspiciousFiles: Array<{
|
||||
path: string;
|
||||
reason: string;
|
||||
hasDefinitions: boolean;
|
||||
}>;
|
||||
recommendations: string[];
|
||||
}
|
||||
|
||||
export function analyzeKnowledgeGraphFiles(
|
||||
graph: KnowledgeGraph,
|
||||
fileContents?: Map<string, string>
|
||||
): FileAnalysisResult {
|
||||
const fileNodes = graph.nodes.filter(node => node.label === 'File');
|
||||
const filesByType: Record<string, number> = {};
|
||||
const suspiciousFiles: Array<{ path: string; reason: string; hasDefinitions: boolean }> = [];
|
||||
const recommendations: string[] = [];
|
||||
|
||||
// Analyze each file node
|
||||
fileNodes.forEach(fileNode => {
|
||||
const filePath = (fileNode.properties.filePath || fileNode.properties.path) as string;
|
||||
const fileName = filePath.split('/').pop()?.toLowerCase() || '';
|
||||
const extension = fileName.split('.').pop()?.toLowerCase() || 'no-extension';
|
||||
|
||||
// Count by extension
|
||||
filesByType[`.${extension}`] = (filesByType[`.${extension}`] || 0) + 1;
|
||||
|
||||
// Check if file has any definitions
|
||||
const hasDefinitions = graph.relationships.some(rel =>
|
||||
rel.source === fileNode.id &&
|
||||
rel.type === 'DEFINES' &&
|
||||
graph.nodes.some(targetNode =>
|
||||
targetNode.id === rel.target &&
|
||||
['Function', 'Class', 'Method', 'Variable', 'Interface', 'Type'].includes(targetNode.label)
|
||||
)
|
||||
);
|
||||
|
||||
// Check for suspicious files
|
||||
const suspiciousPatterns = [
|
||||
{ pattern: fileName.includes('readme'), reason: 'Documentation file (README)' },
|
||||
{ pattern: fileName.includes('license'), reason: 'License file' },
|
||||
{ pattern: fileName.includes('changelog'), reason: 'Changelog file' },
|
||||
{ pattern: fileName.includes('dockerfile'), reason: 'Docker configuration' },
|
||||
{ pattern: fileName.includes('docker-compose'), reason: 'Docker Compose file' },
|
||||
{ pattern: fileName.includes('.gitignore'), reason: 'Git ignore file' },
|
||||
{ pattern: fileName.includes('.gitattributes'), reason: 'Git attributes file' },
|
||||
{ pattern: fileName.endsWith('.md'), reason: 'Markdown documentation' },
|
||||
{ pattern: fileName.endsWith('.txt'), reason: 'Text file' },
|
||||
{ pattern: fileName.endsWith('.log'), reason: 'Log file' },
|
||||
{ pattern: fileName.endsWith('.lock'), reason: 'Lock file' },
|
||||
{ pattern: fileName.includes('package-lock'), reason: 'Package lock file' },
|
||||
{ pattern: fileName.includes('yarn.lock'), reason: 'Yarn lock file' },
|
||||
{ pattern: filePath.includes('/test/') || filePath.includes('/tests/'), reason: 'Test directory file' },
|
||||
{ pattern: filePath.includes('/__tests__/'), reason: 'Jest test file' },
|
||||
{ pattern: fileName.includes('.test.') || fileName.includes('.spec.'), reason: 'Test/spec file' },
|
||||
{ pattern: fileName.includes('mock'), reason: 'Mock file' },
|
||||
{ pattern: fileName.includes('fixture'), reason: 'Test fixture' },
|
||||
{ pattern: !hasDefinitions && fileName.endsWith('.js'), reason: 'JavaScript file with no definitions' },
|
||||
{ pattern: !hasDefinitions && fileName.endsWith('.ts'), reason: 'TypeScript file with no definitions' },
|
||||
{ pattern: !hasDefinitions && fileName.endsWith('.py'), reason: 'Python file with no definitions' },
|
||||
];
|
||||
|
||||
for (const { pattern, reason } of suspiciousPatterns) {
|
||||
if (pattern) {
|
||||
suspiciousFiles.push({
|
||||
path: filePath,
|
||||
reason,
|
||||
hasDefinitions
|
||||
});
|
||||
break; // Only add one reason per file
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Generate recommendations
|
||||
const suspiciousCount = suspiciousFiles.length;
|
||||
const totalFiles = fileNodes.length;
|
||||
|
||||
if (suspiciousCount > totalFiles * 0.2) {
|
||||
recommendations.push(`⚠️ ${suspiciousCount} out of ${totalFiles} files (${Math.round(suspiciousCount/totalFiles*100)}%) seem suspicious. Consider tightening file filtering.`);
|
||||
}
|
||||
|
||||
const documentationFiles = suspiciousFiles.filter(f =>
|
||||
f.reason.includes('Documentation') ||
|
||||
f.reason.includes('Markdown') ||
|
||||
f.reason.includes('README') ||
|
||||
f.reason.includes('License')
|
||||
);
|
||||
|
||||
if (documentationFiles.length > 0) {
|
||||
recommendations.push(`📚 ${documentationFiles.length} documentation files found. These typically don't contain code definitions and could be excluded.`);
|
||||
}
|
||||
|
||||
const testFiles = suspiciousFiles.filter(f =>
|
||||
f.reason.includes('Test') ||
|
||||
f.reason.includes('Mock') ||
|
||||
f.reason.includes('fixture')
|
||||
);
|
||||
|
||||
if (testFiles.length > 0) {
|
||||
recommendations.push(`🧪 ${testFiles.length} test-related files found. Consider if test files should be included in your knowledge graph.`);
|
||||
}
|
||||
|
||||
const noDefinitionFiles = suspiciousFiles.filter(f =>
|
||||
!f.hasDefinitions &&
|
||||
(f.reason.includes('no definitions'))
|
||||
);
|
||||
|
||||
if (noDefinitionFiles.length > 0) {
|
||||
recommendations.push(`📄 ${noDefinitionFiles.length} source files have no extracted definitions. These might be empty, have parsing issues, or contain only comments.`);
|
||||
}
|
||||
|
||||
return {
|
||||
totalFiles,
|
||||
filesByType,
|
||||
suspiciousFiles,
|
||||
recommendations
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a formatted analysis report to console
|
||||
*/
|
||||
export function printFileAnalysisReport(analysis: FileAnalysisResult): void {
|
||||
console.log('\n🔍 KNOWLEDGE GRAPH FILE ANALYSIS REPORT');
|
||||
console.log('=====================================');
|
||||
|
||||
console.log(`\n📊 Overview:`);
|
||||
console.log(`Total files in KG: ${analysis.totalFiles}`);
|
||||
console.log(`Suspicious files: ${analysis.suspiciousFiles.length} (${Math.round(analysis.suspiciousFiles.length/analysis.totalFiles*100)}%)`);
|
||||
|
||||
console.log(`\n📋 Files by type:`);
|
||||
Object.entries(analysis.filesByType)
|
||||
.sort(([,a], [,b]) => b - a)
|
||||
.forEach(([type, count]) => {
|
||||
console.log(` ${type}: ${count} files`);
|
||||
});
|
||||
|
||||
if (analysis.suspiciousFiles.length > 0) {
|
||||
console.log(`\n⚠️ Suspicious files (first 10):`);
|
||||
analysis.suspiciousFiles.slice(0, 10).forEach(file => {
|
||||
const fileName = file.path.split('/').pop();
|
||||
const definitionStatus = file.hasDefinitions ? '✅' : '❌';
|
||||
console.log(` ${definitionStatus} ${fileName} - ${file.reason}`);
|
||||
});
|
||||
|
||||
if (analysis.suspiciousFiles.length > 10) {
|
||||
console.log(` ... and ${analysis.suspiciousFiles.length - 10} more`);
|
||||
}
|
||||
}
|
||||
|
||||
if (analysis.recommendations.length > 0) {
|
||||
console.log(`\n💡 Recommendations:`);
|
||||
analysis.recommendations.forEach((rec, index) => {
|
||||
console.log(` ${index + 1}. ${rec}`);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('\n=====================================\n');
|
||||
}
|
||||
|
||||
// Make it available globally for browser console use
|
||||
if (typeof window !== 'undefined') {
|
||||
(window as any).analyzeKGFiles = (graph: any, fileContents?: Map<string, string>) => {
|
||||
const analysis = analyzeKnowledgeGraphFiles(graph, fileContents);
|
||||
printFileAnalysisReport(analysis);
|
||||
return analysis;
|
||||
};
|
||||
}
|
||||
|
|
@ -236,10 +236,21 @@ export class GitHubArchiveService {
|
|||
*/
|
||||
private isBinaryFile(path: string): boolean {
|
||||
const binaryExtensions = [
|
||||
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2',
|
||||
'.ttf', '.eot', '.pdf', '.zip', '.tar', '.gz', '.rar', '.7z',
|
||||
'.exe', '.dll', '.so', '.dylib', '.class', '.pyc', '.o', '.a',
|
||||
'.mp4', '.mp3', '.wav', '.avi', '.mov', '.webm', '.webp'
|
||||
// Images
|
||||
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.bmp', '.tiff', '.webp',
|
||||
// Fonts
|
||||
'.woff', '.woff2', '.ttf', '.eot', '.otf',
|
||||
// Documents
|
||||
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
||||
// Archives
|
||||
'.zip', '.tar', '.gz', '.rar', '.7z', '.bz2', '.xz', '.lzma',
|
||||
// Executables & Libraries
|
||||
'.exe', '.dll', '.so', '.dylib', '.class', '.pyc', '.o', '.a', '.lib', '.wasm',
|
||||
// Package formats
|
||||
'.jar', '.war', '.ear', '.deb', '.rpm', '.dmg', '.msi', '.pkg', '.apk', '.ipa',
|
||||
// Media
|
||||
'.mp4', '.mp3', '.wav', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv', '.m4v',
|
||||
'.aac', '.ogg', '.flac', '.m4a', '.wma'
|
||||
];
|
||||
|
||||
return binaryExtensions.some(ext => path.toLowerCase().endsWith(ext));
|
||||
|
|
|
|||
|
|
@ -273,6 +273,10 @@ export class GitHubService {
|
|||
'logs',
|
||||
'tmp',
|
||||
'.tmp',
|
||||
// Static assets and public files
|
||||
'public',
|
||||
'assets',
|
||||
'static',
|
||||
// Additional common directories to skip
|
||||
'coverage',
|
||||
'.coverage',
|
||||
|
|
@ -366,13 +370,15 @@ export class GitHubService {
|
|||
/\.7z$/,
|
||||
/\.gz$/,
|
||||
// Media files
|
||||
/\.(jpg|jpeg|png|gif|bmp|svg|ico)$/i,
|
||||
/\.(mp4|avi|mov|wmv|flv|webm)$/i,
|
||||
/\.(mp3|wav|flac|aac|ogg)$/i,
|
||||
/\.(jpg|jpeg|png|gif|bmp|svg|ico|tiff|webp)$/i,
|
||||
/\.(mp4|avi|mov|wmv|flv|webm|mkv|m4v)$/i,
|
||||
/\.(mp3|wav|flac|aac|ogg|m4a|wma)$/i,
|
||||
// Document files
|
||||
/\.(pdf|doc|docx|xls|xlsx|ppt|pptx)$/i,
|
||||
// Other binary files
|
||||
/\.(exe|dll|dylib)$/i,
|
||||
/\.(exe|dll|dylib|wasm|jar|war|ear|deb|rpm|dmg|msi|pkg|apk|ipa)$/i,
|
||||
// Font files
|
||||
/\.(woff|woff2|ttf|eot|otf)$/i,
|
||||
// Minified files and source maps
|
||||
/\.min\.(js|css)$/,
|
||||
/\.map$/,
|
||||
|
|
@ -425,10 +431,21 @@ export class GitHubService {
|
|||
|
||||
// Skip binary files that shouldn't have content
|
||||
const binaryExtensions = [
|
||||
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2',
|
||||
'.ttf', '.eot', '.pdf', '.zip', '.tar', '.gz', '.rar', '.7z',
|
||||
'.exe', '.dll', '.so', '.dylib', '.class', '.pyc', '.o', '.a',
|
||||
'.mp4', '.mp3', '.wav', '.avi', '.mov', '.webm', '.webp'
|
||||
// Images
|
||||
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.bmp', '.tiff', '.webp',
|
||||
// Fonts
|
||||
'.woff', '.woff2', '.ttf', '.eot', '.otf',
|
||||
// Documents
|
||||
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
||||
// Archives
|
||||
'.zip', '.tar', '.gz', '.rar', '.7z', '.bz2', '.xz', '.lzma',
|
||||
// Executables & Libraries
|
||||
'.exe', '.dll', '.so', '.dylib', '.class', '.pyc', '.o', '.a', '.lib', '.wasm',
|
||||
// Package formats
|
||||
'.jar', '.war', '.ear', '.deb', '.rpm', '.dmg', '.msi', '.pkg', '.apk', '.ipa',
|
||||
// Media
|
||||
'.mp4', '.mp3', '.wav', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv', '.m4v',
|
||||
'.aac', '.ogg', '.flac', '.m4a', '.wma'
|
||||
];
|
||||
|
||||
return binaryExtensions.some(ext => pathLower.endsWith(ext));
|
||||
|
|
|
|||
|
|
@ -413,6 +413,10 @@ export class ZipService {
|
|||
'logs',
|
||||
'tmp',
|
||||
'.tmp',
|
||||
// Static assets and public files
|
||||
'public',
|
||||
'assets',
|
||||
'static',
|
||||
// Additional common directories to skip
|
||||
'coverage',
|
||||
'.coverage',
|
||||
|
|
@ -506,13 +510,15 @@ export class ZipService {
|
|||
/\.7z$/,
|
||||
/\.gz$/,
|
||||
// Media files
|
||||
/\.(jpg|jpeg|png|gif|bmp|svg|ico)$/i,
|
||||
/\.(mp4|avi|mov|wmv|flv|webm)$/i,
|
||||
/\.(mp3|wav|flac|aac|ogg)$/i,
|
||||
/\.(jpg|jpeg|png|gif|bmp|svg|ico|tiff|webp)$/i,
|
||||
/\.(mp4|avi|mov|wmv|flv|webm|mkv|m4v)$/i,
|
||||
/\.(mp3|wav|flac|aac|ogg|m4a|wma)$/i,
|
||||
// Document files
|
||||
/\.(pdf|doc|docx|xls|xlsx|ppt|pptx)$/i,
|
||||
// Other binary files
|
||||
/\.(exe|dll|dylib)$/i,
|
||||
/\.(exe|dll|dylib|wasm|jar|war|ear|deb|rpm|dmg|msi|pkg|apk|ipa)$/i,
|
||||
// Font files
|
||||
/\.(woff|woff2|ttf|eot|otf)$/i,
|
||||
// Minified files and source maps
|
||||
/\.min\.(js|css)$/,
|
||||
/\.map$/,
|
||||
|
|
@ -564,10 +570,21 @@ export class ZipService {
|
|||
|
||||
// Skip binary files that shouldn't have content
|
||||
const binaryExtensions = [
|
||||
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2',
|
||||
'.ttf', '.eot', '.pdf', '.zip', '.tar', '.gz', '.rar', '.7z',
|
||||
'.exe', '.dll', '.so', '.dylib', '.class', '.pyc', '.o', '.a',
|
||||
'.mp4', '.mp3', '.wav', '.avi', '.mov', '.webm', '.webp'
|
||||
// Images
|
||||
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.bmp', '.tiff', '.webp',
|
||||
// Fonts
|
||||
'.woff', '.woff2', '.ttf', '.eot', '.otf',
|
||||
// Documents
|
||||
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
||||
// Archives
|
||||
'.zip', '.tar', '.gz', '.rar', '.7z', '.bz2', '.xz', '.lzma',
|
||||
// Executables & Libraries
|
||||
'.exe', '.dll', '.so', '.dylib', '.class', '.pyc', '.o', '.a', '.lib', '.wasm',
|
||||
// Package formats
|
||||
'.jar', '.war', '.ear', '.deb', '.rpm', '.dmg', '.msi', '.pkg', '.apk', '.ipa',
|
||||
// Media
|
||||
'.mp4', '.mp3', '.wav', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv', '.m4v',
|
||||
'.aac', '.ogg', '.flac', '.m4a', '.wma'
|
||||
];
|
||||
|
||||
return binaryExtensions.some(ext => pathLower.endsWith(ext));
|
||||
|
|
|
|||
209
src/ui/components/DiagnosticPanel.tsx
Normal file
209
src/ui/components/DiagnosticPanel.tsx
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
interface DiagnosticInfo {
|
||||
processedFiles: number;
|
||||
skippedFiles: number;
|
||||
totalDefinitions: number;
|
||||
definitionsByType: Record<string, number>;
|
||||
definitionsByFile: Record<string, number>;
|
||||
processingErrors: string[];
|
||||
}
|
||||
|
||||
interface FileAnalysis {
|
||||
language: string;
|
||||
isSourceFile: boolean;
|
||||
isConfigFile: boolean;
|
||||
isCompiled: boolean;
|
||||
contentLength: number;
|
||||
queryResults: Record<string, number>;
|
||||
extractionIssues: string[];
|
||||
}
|
||||
|
||||
interface DiagnosticPanelProps {
|
||||
onGetDiagnostics?: () => DiagnosticInfo | null;
|
||||
onAnalyzeFile?: (filePath: string, content: string) => Promise<FileAnalysis>;
|
||||
fileContents?: Map<string, string>;
|
||||
}
|
||||
|
||||
export default function DiagnosticPanel({
|
||||
onGetDiagnostics,
|
||||
onAnalyzeFile,
|
||||
fileContents
|
||||
}: DiagnosticPanelProps) {
|
||||
const [diagnostics, setDiagnostics] = useState<DiagnosticInfo | null>(null);
|
||||
const [fileAnalysis, setFileAnalysis] = useState<FileAnalysis | null>(null);
|
||||
const [selectedFile, setSelectedFile] = useState<string>('');
|
||||
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
||||
|
||||
const handleGetDiagnostics = () => {
|
||||
if (onGetDiagnostics) {
|
||||
const result = onGetDiagnostics();
|
||||
setDiagnostics(result);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAnalyzeFile = async () => {
|
||||
if (!onAnalyzeFile || !selectedFile || !fileContents) return;
|
||||
|
||||
const content = fileContents.get(selectedFile);
|
||||
if (!content) {
|
||||
alert('File content not found');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsAnalyzing(true);
|
||||
try {
|
||||
const result = await onAnalyzeFile(selectedFile, content);
|
||||
setFileAnalysis(result);
|
||||
} catch (error) {
|
||||
console.error('Error analyzing file:', error);
|
||||
alert('Error analyzing file: ' + error);
|
||||
} finally {
|
||||
setIsAnalyzing(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fileList = fileContents ? Array.from(fileContents.keys()).sort() : [];
|
||||
|
||||
const containerStyle: React.CSSProperties = {
|
||||
padding: '20px',
|
||||
backgroundColor: '#f8f9fa',
|
||||
border: '1px solid #e0e0e0',
|
||||
borderRadius: '8px',
|
||||
margin: '10px',
|
||||
fontFamily: 'Arial, sans-serif'
|
||||
};
|
||||
|
||||
const sectionStyle: React.CSSProperties = {
|
||||
marginBottom: '20px',
|
||||
padding: '15px',
|
||||
backgroundColor: 'white',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid #ddd'
|
||||
};
|
||||
|
||||
const buttonStyle: React.CSSProperties = {
|
||||
padding: '8px 16px',
|
||||
backgroundColor: '#007bff',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
marginRight: '10px'
|
||||
};
|
||||
|
||||
const selectStyle: React.CSSProperties = {
|
||||
padding: '8px',
|
||||
marginRight: '10px',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid #ccc',
|
||||
minWidth: '300px'
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<h2>🔍 Parsing Diagnostics</h2>
|
||||
|
||||
{/* Overall Diagnostics Section */}
|
||||
<div style={sectionStyle}>
|
||||
<h3>Overall Parsing Statistics</h3>
|
||||
<button style={buttonStyle} onClick={handleGetDiagnostics}>
|
||||
Get Diagnostics
|
||||
</button>
|
||||
|
||||
{diagnostics && (
|
||||
<div style={{ marginTop: '15px' }}>
|
||||
<h4>📊 Summary</h4>
|
||||
<ul>
|
||||
<li><strong>Processed Files:</strong> {diagnostics.processedFiles}</li>
|
||||
<li><strong>Total Definitions:</strong> {diagnostics.totalDefinitions}</li>
|
||||
</ul>
|
||||
|
||||
<h4>📋 Definitions by Type</h4>
|
||||
<ul>
|
||||
{Object.entries(diagnostics.definitionsByType).map(([type, count]) => (
|
||||
<li key={type}><strong>{type}:</strong> {count}</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<h4>📁 Top Files by Definition Count</h4>
|
||||
<ul>
|
||||
{Object.entries(diagnostics.definitionsByFile)
|
||||
.sort(([,a], [,b]) => b - a)
|
||||
.slice(0, 10)
|
||||
.map(([file, count]) => (
|
||||
<li key={file}><strong>{file.split('/').pop()}:</strong> {count} definitions</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Individual File Analysis Section */}
|
||||
<div style={sectionStyle}>
|
||||
<h3>Individual File Analysis</h3>
|
||||
<div style={{ marginBottom: '15px' }}>
|
||||
<select
|
||||
style={selectStyle}
|
||||
value={selectedFile}
|
||||
onChange={(e) => setSelectedFile(e.target.value)}
|
||||
>
|
||||
<option value="">Select a file to analyze...</option>
|
||||
{fileList.map(file => (
|
||||
<option key={file} value={file}>
|
||||
{file}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<button
|
||||
style={buttonStyle}
|
||||
onClick={handleAnalyzeFile}
|
||||
disabled={!selectedFile || isAnalyzing}
|
||||
>
|
||||
{isAnalyzing ? 'Analyzing...' : 'Analyze File'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{fileAnalysis && (
|
||||
<div style={{ marginTop: '15px' }}>
|
||||
<h4>📄 Analysis Results for: {selectedFile.split('/').pop()}</h4>
|
||||
|
||||
<div style={{ marginBottom: '10px' }}>
|
||||
<strong>Language:</strong> {fileAnalysis.language}<br/>
|
||||
<strong>Source File:</strong> {fileAnalysis.isSourceFile ? '✅ Yes' : '❌ No'}<br/>
|
||||
<strong>Config File:</strong> {fileAnalysis.isConfigFile ? '✅ Yes' : '❌ No'}<br/>
|
||||
<strong>Compiled/Minified:</strong> {fileAnalysis.isCompiled ? '⚠️ Yes' : '✅ No'}<br/>
|
||||
<strong>Content Length:</strong> {fileAnalysis.contentLength} characters
|
||||
</div>
|
||||
|
||||
<h5>🔍 Query Results</h5>
|
||||
<ul>
|
||||
{Object.entries(fileAnalysis.queryResults).map(([query, count]) => (
|
||||
<li key={query}><strong>{query}:</strong> {count} matches</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{fileAnalysis.extractionIssues.length > 0 && (
|
||||
<>
|
||||
<h5>⚠️ Extraction Issues</h5>
|
||||
<ul>
|
||||
{fileAnalysis.extractionIssues.map((issue, index) => (
|
||||
<li key={index} style={{ color: '#d32f2f' }}>{issue}</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
|
||||
{fileAnalysis.extractionIssues.length === 0 &&
|
||||
Object.values(fileAnalysis.queryResults).every(count => count === 0) && (
|
||||
<div style={{ color: '#f57c00', fontWeight: 'bold' }}>
|
||||
ℹ️ No extraction issues found, but no definitions were extracted.
|
||||
This might be a file with no parseable definitions (like documentation, empty files, or pure data files).
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -259,8 +259,8 @@ const FloatingSourceViewer: React.FC<FloatingSourceViewerProps> = ({
|
|||
}
|
||||
|
||||
// If not found through relationships, search through file contents
|
||||
// BUT ONLY for Function, Method, Class, and Variable nodes - NOT for Folder or File nodes
|
||||
if (!filePath && fileContents && ['Function', 'Method', 'Class', 'Variable'].includes(nodeType)) {
|
||||
// For Function, Method, Class, Variable nodes OR if this is a File node that doesn't have proper path
|
||||
if (!filePath && fileContents && (['Function', 'Method', 'Class', 'Variable'].includes(nodeType) || nodeType === 'File')) {
|
||||
console.log('FloatingSourceViewer - Searching file contents for node:', nodeName, 'of type:', nodeType);
|
||||
|
||||
// Use more specific search patterns instead of just checking if content includes nodeName
|
||||
|
|
@ -337,28 +337,165 @@ const FloatingSourceViewer: React.FC<FloatingSourceViewerProps> = ({
|
|||
};
|
||||
}
|
||||
|
||||
// Return mock content for nodes without file association
|
||||
const mockContent = `// ${nodeType}: ${nodeName}
|
||||
// This is a ${nodeType.toLowerCase()} definition in the knowledge graph
|
||||
// Special handling for File nodes - display actual file content
|
||||
if (nodeType === 'File') {
|
||||
const fileNodePath = node.properties.filePath as string || node.properties.path as string || nodeName;
|
||||
|
||||
// Try to find the file content using various path resolution strategies
|
||||
let content = fileContents.get(fileNodePath);
|
||||
|
||||
if (!content) {
|
||||
// Try different path variations
|
||||
const pathVariations = [
|
||||
fileNodePath,
|
||||
fileNodePath.replace(/^[./]*/, ''), // Remove leading ./ or /
|
||||
fileNodePath.startsWith('/') ? fileNodePath.substring(1) : `/${fileNodePath}`, // Toggle leading slash
|
||||
`src/${fileNodePath}`, // Try under src/
|
||||
fileNodePath.replace(/\\/g, '/'), // Convert backslashes to forward slashes
|
||||
fileNodePath.replace(/\//g, '\\') // Convert forward slashes to backslashes
|
||||
];
|
||||
|
||||
for (const variation of pathVariations) {
|
||||
content = fileContents.get(variation);
|
||||
if (content) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (content) {
|
||||
// Found file content - display it
|
||||
const language = fileNodePath.split('.').pop() || 'text';
|
||||
return {
|
||||
fileName: fileNodePath.split('/').pop() || fileNodePath,
|
||||
filePath: fileNodePath,
|
||||
content: content,
|
||||
nodeType,
|
||||
nodeName,
|
||||
language
|
||||
};
|
||||
}
|
||||
|
||||
// If no content found, show file info with explanation
|
||||
return {
|
||||
fileName: fileNodePath.split('/').pop() || fileNodePath,
|
||||
filePath: 'file-not-found',
|
||||
content: `# File: ${nodeName}
|
||||
|
||||
⚠️ **File Content Not Available**
|
||||
|
||||
This file was detected in the project structure but its content could not be loaded.
|
||||
|
||||
## Possible Reasons:
|
||||
|
||||
1. **File Filtering**: The file may have been filtered out during upload
|
||||
2. **File Size**: Large files might be excluded from processing
|
||||
3. **Path Mismatch**: The file path in the graph doesn't match the uploaded content
|
||||
4. **Upload Issues**: The file might not have been included in the ZIP upload
|
||||
|
||||
## File Information:
|
||||
- **Expected Path**: \`${fileNodePath}\`
|
||||
- **Node Type**: ${nodeType}
|
||||
- **Available Files**: ${fileContents.size} files in memory
|
||||
|
||||
## Debug Information:
|
||||
Tried these path variations:
|
||||
${['- `' + fileNodePath + '`', '- `' + fileNodePath.replace(/^[./]*/, '') + '`', '- `' + (fileNodePath.startsWith('/') ? fileNodePath.substring(1) : `/${fileNodePath}`) + '`'].join('\n')}`,
|
||||
nodeType,
|
||||
nodeName,
|
||||
language: 'markdown'
|
||||
};
|
||||
}
|
||||
|
||||
// Return detailed information for nodes without file association
|
||||
const isStructuralNode = ['Project', 'Folder'].includes(nodeType); // File nodes should show content, not placeholders
|
||||
const isDefinitionNode = ['Function', 'Class', 'Method', 'Variable', 'Interface', 'Type'].includes(nodeType);
|
||||
|
||||
let mockContent = '';
|
||||
|
||||
if (isStructuralNode) {
|
||||
mockContent = `# ${nodeType}: ${nodeName}
|
||||
|
||||
This is a structural node in the knowledge graph representing ${nodeType.toLowerCase()} organization.
|
||||
|
||||
## Information:
|
||||
- **Type**: ${nodeType}
|
||||
- **Name**: ${nodeName}
|
||||
- **Status**: Structural element (no source code content)
|
||||
|
||||
## Why no content?
|
||||
Structural nodes like projects, folders, and some files represent organizational elements rather than code definitions. They help organize the knowledge graph but don't contain executable code.
|
||||
|
||||
## To view code content:
|
||||
1. Look for definition nodes (Function, Class, Method) within this ${nodeType.toLowerCase()}
|
||||
2. Check if this ${nodeType.toLowerCase()} contains any parsed source files
|
||||
3. Verify that the ingestion pipeline successfully processed files in this location`;
|
||||
} else if (isDefinitionNode) {
|
||||
mockContent = `# ${nodeType}: ${nodeName}
|
||||
|
||||
⚠️ **Content Not Available**
|
||||
|
||||
This ${nodeType.toLowerCase()} definition was found in the knowledge graph but the source content is not accessible.
|
||||
|
||||
## Possible Reasons:
|
||||
|
||||
### 1. External Library
|
||||
This may be a function/class from an external library or framework that wasn't included in the project analysis.
|
||||
|
||||
### 2. Parsing Issues
|
||||
The source file might have:
|
||||
- Syntax errors that prevented parsing
|
||||
- Unsupported language features
|
||||
- Complex code patterns not captured by Tree-sitter queries
|
||||
|
||||
### 3. File Access Issues
|
||||
The original source file might be:
|
||||
- Missing from the uploaded content
|
||||
- Filtered out during ingestion
|
||||
- Located in an ignored directory
|
||||
|
||||
### 4. Incomplete Ingestion
|
||||
The ingestion pipeline might have:
|
||||
- Skipped this file due to size limits
|
||||
- Failed to process this specific definition
|
||||
- Encountered errors during Tree-sitter parsing
|
||||
|
||||
## Debugging Tips:
|
||||
1. Check the browser console for parsing errors
|
||||
2. Verify the file was included in the upload
|
||||
3. Look for related files that might contain this definition
|
||||
4. Try re-running the ingestion process
|
||||
|
||||
## Mock Implementation:
|
||||
\`\`\`${nodeType === 'Function' ? 'javascript' : nodeType === 'Class' ? 'typescript' : 'text'}
|
||||
${nodeType === 'Function' ? `function ${nodeName}() {
|
||||
// Function implementation not available in current context
|
||||
// This may be an external library function or incomplete parsing
|
||||
// Implementation not available
|
||||
// This may be an external library function
|
||||
// or incomplete parsing result
|
||||
}` : nodeType === 'Class' ? `class ${nodeName} {
|
||||
// Class definition not available in current context
|
||||
// This may be an external library class or incomplete parsing
|
||||
}` : nodeType === 'Method' ? `${nodeName}() {
|
||||
// Method implementation not available in current context
|
||||
// Class definition not available
|
||||
// This may be an external library class
|
||||
// or incomplete parsing result
|
||||
}` : `// ${nodeType} definition for ${nodeName}
|
||||
// Content not available in current context`}`;
|
||||
// Content not available in current context`}
|
||||
\`\`\``;
|
||||
} else {
|
||||
mockContent = `# ${nodeType}: ${nodeName}
|
||||
|
||||
This node represents a ${nodeType.toLowerCase()} in the knowledge graph.
|
||||
|
||||
**Status**: Content not available
|
||||
|
||||
**Note**: This may be a specialized node type or an element that doesn't have direct source code representation.`;
|
||||
}
|
||||
|
||||
return {
|
||||
fileName: `${nodeName}`,
|
||||
fileName: `${nodeName} (${nodeType})`,
|
||||
filePath: 'virtual-node',
|
||||
content: mockContent,
|
||||
nodeType,
|
||||
nodeName,
|
||||
language: 'javascript'
|
||||
language: 'markdown'
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue