diff --git a/gitnexus/src/core/ingestion/parsing-processor.ts b/gitnexus/src/core/ingestion/parsing-processor.ts index e820f90c9..1d8f0affb 100644 --- a/gitnexus/src/core/ingestion/parsing-processor.ts +++ b/gitnexus/src/core/ingestion/parsing-processor.ts @@ -6,7 +6,7 @@ import { getProvider } from './languages/index.js'; import { generateId } from '../../lib/utils.js'; import { SymbolTable } from './symbol-table.js'; import { ASTCache } from './ast-cache.js'; -import { SupportedLanguages, getLanguageFromFilename } from 'gitnexus-shared'; +import { getLanguageFromFilename } from 'gitnexus-shared'; import { yieldToEventLoop } from './utils/event-loop.js'; import { getDefinitionNodeFromCaptures, @@ -504,7 +504,9 @@ const processParsingSequential = async ( }); if (provider.isRouteFile?.(file.path) && provider.deferredRouteExtractor) { - extractedData.deferredRouteCandidates.push(...(provider.deferredRouteExtractor(tree, file.path))); + extractedData.deferredRouteCandidates.push( + ...provider.deferredRouteExtractor(tree, file.path), + ); } } @@ -557,13 +559,7 @@ export const processParsing = async ( } } - const data = await processParsingSequential( - graph, - files, - symbolTable, - astCache, - onFileProgress, - ); + const data = await processParsingSequential(graph, files, symbolTable, astCache, onFileProgress); return { data, usedWorkers: false, diff --git a/gitnexus/src/core/ingestion/route-extractors/spring-java.ts b/gitnexus/src/core/ingestion/route-extractors/spring-java.ts index dec74cf74..f93d8833c 100644 --- a/gitnexus/src/core/ingestion/route-extractors/spring-java.ts +++ b/gitnexus/src/core/ingestion/route-extractors/spring-java.ts @@ -31,7 +31,10 @@ function getAnnotationName(node: SyntaxNode): string | null { return nameNode?.text ?? null; } -function getElementValuePairParts(node: SyntaxNode): { key: string | null; value: SyntaxNode | null } { +function getElementValuePairParts(node: SyntaxNode): { + key: string | null; + value: SyntaxNode | null; +} { const keyNode = node.childForFieldName('key') ?? node.namedChild(0); const valueNode = node.childForFieldName('value') ?? node.namedChild(1); return { @@ -52,7 +55,9 @@ function extractOwnerPath(node: SyntaxNode | null | undefined): string[] | null return [...objectPath, fieldNode.text]; } -function extractRoutePathExpression(node: SyntaxNode | null | undefined): SpringRoutePathExpression | null { +function extractRoutePathExpression( + node: SyntaxNode | null | undefined, +): SpringRoutePathExpression | null { if (!node) return null; const literal = extractJavaStringLiteral(node); @@ -92,7 +97,11 @@ function extractRequestMappingPath(annotation: SyntaxNode): { const direct = extractRoutePathExpression(child); if (direct) return { expression: direct, hasExplicitPath: true }; - if (child.type === 'string_literal' || child.type === 'identifier' || child.type === 'field_access') { + if ( + child.type === 'string_literal' || + child.type === 'identifier' || + child.type === 'field_access' + ) { return { expression: null, hasExplicitPath: true }; } @@ -160,7 +169,10 @@ function joinRoutePaths(prefix: string | null, routePath: string | null): string return '/'; } -function findAnnotation(modifiersNode: SyntaxNode | null, names: ReadonlySet): SyntaxNode | null { +function findAnnotation( + modifiersNode: SyntaxNode | null, + names: ReadonlySet, +): SyntaxNode | null { if (!modifiersNode) return null; for (let i = 0; i < modifiersNode.namedChildCount; i++) { @@ -197,7 +209,9 @@ function resolveUniqueClassLike( ): SymbolDefinition | null { const resolved = ctx.resolve(name, filePath); if (!resolved) return null; - const classLikes = resolved.candidates.filter((candidate) => CLASS_LIKE_TYPES.has(candidate.type)); + const classLikes = resolved.candidates.filter((candidate) => + CLASS_LIKE_TYPES.has(candidate.type), + ); return classLikes.length === 1 ? classLikes[0] : null; } @@ -240,7 +254,10 @@ function resolvePathExpression( case 'identifier': { const sameClass = resolveUniqueClassLike(className, filePath, ctx); if (sameClass) { - const local = ctx.symbols.lookupFieldByOwner(sameClass.nodeId, expression.name)?.constantValue; + const local = ctx.symbols.lookupFieldByOwner( + sameClass.nodeId, + expression.name, + )?.constantValue; if (local) return local; } @@ -248,7 +265,9 @@ function resolvePathExpression( if (imported) return imported; const sameFile = firstConstantValue( - ctx.symbols.lookupExactAll(filePath, expression.name).filter((def) => def.type === 'Property'), + ctx.symbols + .lookupExactAll(filePath, expression.name) + .filter((def) => def.type === 'Property'), ); if (sameFile) return sameFile; diff --git a/gitnexus/test/integration/resolvers/spring-route-mapping.test.ts b/gitnexus/test/integration/resolvers/spring-route-mapping.test.ts index 028b7c93e..ef6128e3a 100644 --- a/gitnexus/test/integration/resolvers/spring-route-mapping.test.ts +++ b/gitnexus/test/integration/resolvers/spring-route-mapping.test.ts @@ -9,6 +9,11 @@ import { type PipelineResult, } from './helpers.js'; +function expectDefined(value: T | undefined): T { + expect(value).toBeDefined(); + return value as T; +} + describe('Spring route mapping', () => { let result: PipelineResult; @@ -41,31 +46,36 @@ describe('Spring route mapping', () => { const health = routes.find((r) => r.name === '/health'); const status = routes.find((r) => r.name === '/status'); - expect(users).toBeDefined(); - expect(users!.properties.httpMethod).toBe('GET'); - expect(users!.properties.controllerName).toBe('UserController'); - expect(users!.properties.methodName).toBe('listUsers'); - expect(users!.properties.prefix).toBe('/api'); + const usersRoute = expectDefined(users); + const createRoute = expectDefined(create); + const profileRoute = expectDefined(profile); + const searchRouteNode = expectDefined(search); + const fqcnRouteNode = expectDefined(fqcn); + const healthRouteNode = expectDefined(health); + const statusRouteNode = expectDefined(status); - expect(create!.properties.httpMethod).toBe('POST'); - expect(create!.properties.methodName).toBe('createUser'); - expect(profile!.properties.httpMethod).toBe('PATCH'); - expect(profile!.properties.methodName).toBe('updateProfile'); - expect(search!.properties.httpMethod).toBe('POST'); - expect(search!.properties.methodName).toBe('searchUsers'); - expect(fqcn!.properties.httpMethod).toBe('PUT'); - expect(fqcn!.properties.methodName).toBe('fullyQualifiedUsers'); + expect(usersRoute.properties.httpMethod).toBe('GET'); + expect(usersRoute.properties.controllerName).toBe('UserController'); + expect(usersRoute.properties.methodName).toBe('listUsers'); + expect(usersRoute.properties.prefix).toBe('/api'); - expect(health).toBeDefined(); - expect(health!.properties.httpMethod).toBe('GET'); - expect(health!.properties.controllerName).toBe('HealthController'); - expect(health!.properties.methodName).toBe('health'); - expect(health!.properties.prefix).toBeUndefined(); + expect(createRoute.properties.httpMethod).toBe('POST'); + expect(createRoute.properties.methodName).toBe('createUser'); + expect(profileRoute.properties.httpMethod).toBe('PATCH'); + expect(profileRoute.properties.methodName).toBe('updateProfile'); + expect(searchRouteNode.properties.httpMethod).toBe('POST'); + expect(searchRouteNode.properties.methodName).toBe('searchUsers'); + expect(fqcnRouteNode.properties.httpMethod).toBe('PUT'); + expect(fqcnRouteNode.properties.methodName).toBe('fullyQualifiedUsers'); - expect(status).toBeDefined(); - expect(status!.properties.httpMethod).toBe('GET'); - expect(status!.properties.controllerName).toBe('HealthController'); - expect(status!.properties.methodName).toBe('status'); + expect(healthRouteNode.properties.httpMethod).toBe('GET'); + expect(healthRouteNode.properties.controllerName).toBe('HealthController'); + expect(healthRouteNode.properties.methodName).toBe('health'); + expect(healthRouteNode.properties.prefix).toBeUndefined(); + + expect(statusRouteNode.properties.httpMethod).toBe('GET'); + expect(statusRouteNode.properties.controllerName).toBe('HealthController'); + expect(statusRouteNode.properties.methodName).toBe('status'); }); it('creates HANDLES_ROUTE edges from controller files', () => { @@ -76,16 +86,11 @@ describe('Spring route mapping', () => { const healthRoute = edges.find((edge) => edge.target === '/health'); const statusRoute = edges.find((edge) => edge.target === '/status'); - expect(usersRoute).toBeDefined(); - expect(usersRoute!.sourceFilePath).toContain('UserController.java'); - expect(searchRoute).toBeDefined(); - expect(searchRoute!.sourceFilePath).toContain('UserController.java'); - expect(fqcnRoute).toBeDefined(); - expect(fqcnRoute!.sourceFilePath).toContain('UserController.java'); - expect(healthRoute).toBeDefined(); - expect(healthRoute!.sourceFilePath).toContain('HealthController.java'); - expect(statusRoute).toBeDefined(); - expect(statusRoute!.sourceFilePath).toContain('HealthController.java'); + expect(expectDefined(usersRoute).sourceFilePath).toContain('UserController.java'); + expect(expectDefined(searchRoute).sourceFilePath).toContain('UserController.java'); + expect(expectDefined(fqcnRoute).sourceFilePath).toContain('UserController.java'); + expect(expectDefined(healthRoute).sourceFilePath).toContain('HealthController.java'); + expect(expectDefined(statusRoute).sourceFilePath).toContain('HealthController.java'); }); it('creates framework CALLS edges from controller files to handler methods', () => { @@ -97,13 +102,17 @@ describe('Spring route mapping', () => { edges.some((edge) => edge.source === 'UserController.java' && edge.target === 'createUser'), ).toBe(true); expect( - edges.some((edge) => edge.source === 'UserController.java' && edge.target === 'updateProfile'), + edges.some( + (edge) => edge.source === 'UserController.java' && edge.target === 'updateProfile', + ), ).toBe(true); expect( edges.some((edge) => edge.source === 'UserController.java' && edge.target === 'searchUsers'), ).toBe(true); expect( - edges.some((edge) => edge.source === 'UserController.java' && edge.target === 'fullyQualifiedUsers'), + edges.some( + (edge) => edge.source === 'UserController.java' && edge.target === 'fullyQualifiedUsers', + ), ).toBe(true); expect( edges.some((edge) => edge.source === 'HealthController.java' && edge.target === 'health'),